malloc dump_heap

ptmalloc2 中的一个调试辅助函数,用于遍历并打印一个 heap_info 所管理的整个堆段的 chunk 布局。它通常由 malloc_statsmalloc_info 或 MALLOC_DEBUG 相关函数调用,帮助开发者直观地看到堆的物理结构。

源码定义

static void
dump_heap (heap_info *heap)
{
  char *ptr;
  mchunkptr p;

  fprintf (stderr, "Heap %p, size %10lx:\n", heap, (long) heap->size);
  ptr = (heap->ar_ptr != (mstate) (heap + 1)) ?
        (char *) (heap + 1) : (char *) (heap + 1) + sizeof (struct malloc_state);
  p = (mchunkptr) (((uintptr_t) ptr + MALLOC_ALIGN_MASK) &
                   ~MALLOC_ALIGN_MASK);
  for (;; )
    {
      fprintf (stderr, "chunk %p size %10lx", p, (long) chunksize_nomask(p));
      if (p == top (heap->ar_ptr))
        {
          fprintf (stderr, " (top)\n");
          break;
        }
      else if (chunksize_nomask(p) == (0 | PREV_INUSE))
        {
          fprintf (stderr, " (fence)\n");
          break;
        }
      fprintf (stderr, "\n");
      p = next_chunk (p);
    }
}

逐步拆解

1. 打印堆段头部信息
fprintf (stderr, "Heap %p, size %10lx:\n", heap, (long) heap->size);

输出堆段的地址和大小。heap 是 heap_info *heap->size 是该堆段的总大小。

2. 确定第一个 chunk 的起始位置
ptr = (heap->ar_ptr != (mstate) (heap + 1)) ?
      (char *) (heap + 1) : (char *) (heap + 1) + sizeof (struct malloc_state);

这一步需要判断堆段的布局:

  • heap + 1:跳过 heap_info 结构体本身,指向堆段中紧随其后的内存。

  • heap->ar_ptr:指向该堆段所属的 Arena。

  • 判断条件:如果 ar_ptr != (mstate)(heap + 1),说明 malloc_state 不在这个堆段内(例如,这是非主 Arena 的后续堆段,malloc_state 在第一个堆段中)。此时,ptr 直接指向 heap + 1

  • 否则malloc_state 就在 heap + 1 的位置(这是非主 Arena 的第一个堆段)。此时,需要跳过 malloc_stateptr 指向 heap + 1 + sizeof(struct malloc_state)

3. 对齐到 chunk 边界
p = (mchunkptr) (((uintptr_t) ptr + MALLOC_ALIGN_MASK) &
                 ~MALLOC_ALIGN_MASK);

将 ptr 向上对齐到 MALLOC_ALIGNMENT 的倍数。因为 chunk 必须对齐,而 heap_info 和 malloc_state 的大小可能不是对齐的。

4. 遍历所有 chunk
for (;; )
  {
    fprintf (stderr, "chunk %p size %10lx", p, (long) chunksize_nomask(p));
    if (p == top (heap->ar_ptr))
      {
        fprintf (stderr, " (top)\n");
        break;
      }
    else if (chunksize_nomask(p) == (0 | PREV_INUSE))
      {
        fprintf (stderr, " (fence)\n");
        break;
      }
    fprintf (stderr, "\n");
    p = next_chunk (p);
  }

遍历逻辑

  1. 打印当前 chunk 的地址和大小(chunksize_nomask 返回原始 size 字段,包含标志位)。

  2. 终止条件一:如果当前 chunk 是 Top Chunk(p == top(heap->ar_ptr)),打印 (top) 并退出。

  3. 终止条件二:如果当前 chunk 的大小是 0 | PREV_INUSE(即 size = 0x1),说明遇到了栅栏(fence)——这是堆段末尾的特殊标记,打印 (fence) 并退出。

  4. 否则,移动到下一个物理相邻的 chunk(next_chunk(p))。

关键概念

1. heap_info 与 malloc_state 的布局

非主 Arena 的第一个堆段布局:

+------------------+
| heap_info        |  ← heap 指向这里
+------------------+
| malloc_state     |  ← heap + 1 指向这里(第一个堆段)
+------------------+
| 对齐填充         |
+------------------+
| 第一个 chunk     |  ← ptr 对齐后指向这里
+------------------+
| ...              |
+------------------+
| Top Chunk        |
+------------------+
| fence (size=0x1) |  ← 堆段末尾
+------------------+

后续堆段的布局:

+------------------+
| heap_info        |  ← heap 指向这里
+------------------+
| 对齐填充         |
+------------------+
| 第一个 chunk     |  ← heap + 1 对齐后指向这里
+------------------+
| ...              |
+------------------+
| Top Chunk        |
+------------------+
| fence (size=0x1) |
+------------------+
2. 栅栏(fence)

栅栏是堆段末尾的一个特殊 chunk,size 字段被设为 0x1(即 0 | PREV_INUSE)。它的作用是:

  • 标记堆段边界:告诉分配器“这里是堆段的尽头”。

  • 防止越界:如果分配器试图从 Top Chunk 继续切割,会撞到栅栏,从而触发错误。

  • 便于调试dump_heap 通过检测 size == 0x1 来识别堆段结束。

3. top(heap->ar_ptr)

top 是一个宏,获取 Arena 的 Top Chunk 指针:

#define top(av)      (av->top)

遍历到 Top Chunk 时,说明已经到达了当前活跃的分配边界,后面的内存尚未使用。

输出示例

Heap 0x7ffff7faf000, size 0x21000:
chunk 0x7ffff7faf010 size 0x31
chunk 0x7ffff7faf040 size 0x41
chunk 0x7ffff7faf080 size 0x101
chunk 0x7ffff7faf180 size 0x1fec1 (top)

这表示:

  • 堆段起始于 0x7ffff7faf000,大小为 0x21000(132KB)。

  • 第一个 chunk 在 0x7ffff7faf010,大小 0x31(48 字节)。

  • 第二个 chunk 在 0x7ffff7faf040,大小 0x41(64 字节)。

  • 第三个 chunk 在 0x7ffff7faf080,大小 0x101(256 字节)。

  • Top Chunk 在 0x7ffff7faf180,大小 0x1fec1(约 130KB)。

注意事项

  1. 仅用于调试dump_heap 是调试辅助函数,不应在生产环境中调用。

  2. 输出到 stderr:使用 fprintf(stderr, ...),不干扰标准输出。

  3. chunksize_nomask:返回原始 size 字段(含标志位),而不是屏蔽后的值。这样可以看到标志位的状态。

  4. 栅栏检测size == 0x1 是栅栏的标志,不是合法的 chunk 大小。

  5. 非主 Arena 专用dump_heap 主要用于遍历非主 Arena 的堆段,因为主 Arena 没有 heap_info

总结

要点说明
功能遍历并打印一个 heap_info 管理的堆段的所有 chunk
起始位置跳过 heap_info,必要时跳过 malloc_state,然后对齐
遍历方式从第一个 chunk 开始,用 next_chunk 逐个前进
终止条件遇到 Top Chunk 或栅栏(size == 0x1
输出内容每个 chunk 的地址、大小,以及 (top) 或 (fence) 标记
用途调试堆布局、分析碎片、验证合并逻辑

一句话理解

dump_heap 是 ptmalloc2 的“堆段扫描仪”。它从一个 heap_info 开始,跳过管理结构,对齐到第一个 chunk,然后沿着物理内存逐个遍历,打印每个 chunk 的地址和大小,直到遇到 Top Chunk 或堆段末尾的栅栏。它是分析非主 Arena 堆布局的有力工具。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值