"!heap -flt -s xxxx" windbg命令中的不同列代表什么?

21

我一直在研究高内存问题,最近在windbg中进行了大量堆分析,我很好奇"!heap -flt -s xxxx"命令中的不同列实际上意味着什么。

我看过What do the 'size' numbers mean in the windbg !heap output?,并查阅了我的《Windows Internals》书籍,但我仍然有很多问题。因此,以下是各个列和我的问题。

**HEAP_ENTRY** - What does this pointer really point to? How is it different than UserPtr?
**Size** - What does this size mean? How is it different than UserSize?
**Prev** - This just appears to be the negative offset to get to the previous heap entry. Still not sure exactly how it's used.
**Flags** - Is there any documentation on these flags?
**UserPtr** - What is the user pointer? In all cases I've seen it's always 8 bytes higher than the HEAP_ENTRY, but I don't really know what it points to.
**UserSize** - This appears to be the size of the actual allocation.
**state** - This just tells you what state of this heap entry is (free, busy, etc....)

Example:
HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
  0015eeb0 0044 0000  [07]   0015eeb8    00204 - (busy)

好问题..我也想过同样的事情 - pepsi
3个回答

25

堆条目 (HEAP_ENTRY) 堆将已分配的块存储在内存连续段中,每个已分配的块以8字节头开始,后面跟着实际分配的数据。堆条目是分配块头的开始地址。

大小 (Size) 堆管理器按8字节倍数处理块。该列是分配的8字节块数。在您的示例中,0044表示该块占用0x220字节(0x44 * 8)。

前一个 (Prev) 乘以8,以得到到前一个堆块的负偏移量(以字节为单位)。

标志 (Flags) 这是一个位掩码,编码以下信息:

0x01 - HEAP_ENTRY_BUSY
0x02 - HEAP_ENTRY_EXTRA_PRESENT
0x04 - HEAP_ENTRY_FILL_PATTERN
0x08 - HEAP_ENTRY_VIRTUAL_ALLOC
0x10 - HEAP_ENTRY_LAST_ENTRY

UserPtr 这是由HeapAlloc(由malloc/new调用)函数返回给应用程序的指针。由于头部始终为8个字节长,因此它始终为HEAP_ENTRY +8。

UserSize 这是传递给HeapAlloc函数的大小。

state 这是Flags列的解码,告诉条目是否繁忙、释放、其所在段的最后一个等等。

请注意,在Windows 7/2008 R2中,默认情况下使用名为LFH(Low fragmented heap)的前端来使用堆,该前端使用默认堆管理器分配块,在其中分派用户分配的数据。对于这些堆,UserPtr和UserSize将不会指向真正的用户数据。 !heap -s命令的输出显示了哪些堆启用了LFH。


1
你是否知道如何在LFH堆中获取真实的userptr(这就是最初引发这个问题的原因)? - Zipper
!heap -p 知道 LFH 但选项较少。您可以使用 !heap -p -a <address> 从地址获取真实的 userptr。!heap -p -all 将列出所有堆中的用户块。如果将输出重定向到文件中,则可以使用 grep 查找所需用户大小的块。 - plodoc
该线程已经很老了,但请注意对于x64,用户指针位置是HEAP_ENTRY +16,因为ntdll.dll中的sizeof(_HEAP_ENTRY)返回0x10 / 16字节。此外,较新版本的WinDBG似乎正确地将UserPtr映射到数据,尽管我还不知道旧版本为什么没有这样做。 - Samuel

4

从查看Windows调试工具帮助文件中的!heap文档以及MSDN上的堆文档和高级Windows调试中的精彩摘录,我总结出以下内容:

  • HEAP_ENTRY:指向堆内条目的指针。正如您发现的那样,有一个8字节的头,其中包含HEAP_ENTRY结构的数据。HEAP_ENTRY结构的大小为8字节,定义了“堆粒度”大小。这用于确定...
  • SIZE:以粒度为单位的条目大小(即分配大小/8)
  • FLAGS:这些在winbase.h中定义,解释可以在MSDN链接中找到。
  • USERPTR:已分配(或释放)对象的实际指针

1

HEAP_ENTRY和UserPtr之间的主要区别在于堆必须进行索引、分配、填充元数据(例如向用户提供的已分配长度)等操作...否则,你怎么能够释放(p)一些东西而不提供分配了多少字节呢?两个大小字段也是同样的道理:一个是用来索引堆的结构有多大,另一个是可供用户使用的内存区域有多大。

FLAGS基本上指定了分配的内存块的哪些属性,它是否被提交或仅仅是保留的,并且我想,如果需要,内核将使用它们来重新排列或共享内存区域(但正如nithins所指出的那样,它们在MSDN中有记录)。

PREV ptr用于跟踪所有已分配的区域,第一个指针存储在PEB结构中,因此用户空间和内核空间代码都知道已分配的堆池。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接