ptmalloc2 中的一个调试辅助函数,用于遍历并打印一个 heap_info 所管理的整个堆段的 chunk 布局。它通常由 malloc_stats、malloc_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_state,ptr指向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);
}
遍历逻辑:
-
打印当前 chunk 的地址和大小(
chunksize_nomask返回原始size字段,包含标志位)。 -
终止条件一:如果当前 chunk 是 Top Chunk(
p == top(heap->ar_ptr)),打印(top)并退出。 -
终止条件二:如果当前 chunk 的大小是
0 | PREV_INUSE(即size = 0x1),说明遇到了栅栏(fence)——这是堆段末尾的特殊标记,打印(fence)并退出。 -
否则,移动到下一个物理相邻的 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)。
注意事项
-
仅用于调试:
dump_heap是调试辅助函数,不应在生产环境中调用。 -
输出到
stderr:使用fprintf(stderr, ...),不干扰标准输出。 -
chunksize_nomask:返回原始size字段(含标志位),而不是屏蔽后的值。这样可以看到标志位的状态。 -
栅栏检测:
size == 0x1是栅栏的标志,不是合法的 chunk 大小。 -
非主 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 堆布局的有力工具。

337

被折叠的 条评论
为什么被折叠?



