Debugging Tools for Windows |
!heap 扩展显示堆使用信息,控制堆管理器中的断点,检测泄露的堆块,搜索堆块或者显示页堆(page heap)信息。
Windows 2000的语法
!heap -b [{alloc|realloc|free} [Tag]] [Heap | BreakAddress]
!heap -B {alloc|realloc|free} [Heap | BreakAddress]
!heap -p PageHeapOptions
!heap [-p] -?
Windows XP和之后的语法
!heap -b [{alloc|realloc|free} [Tag]] [Heap | BreakAddress]
!heap -B {alloc|realloc|free} [Heap | BreakAddress]
!heap -l
!heap -s [SummaryOptions] [StatHeapAddress]
!heap -i HeapAddress
!heap -x [-v] Address
!heap -p [PageHeapOptions]
!heap -srch [Size] Pattern
!heap -flt FilterOptions
!heap -stat [-h Handle [-grp GroupBy [MaxDisplay]]]
!heap [-p] -?
选项 | 作用 |
---|---|
-v | 使得调试器验证指定的堆。 |
-a | 显示中包含指定堆的所有信息。这种情况下,大小会被四舍五入到堆的分配粒度。(运行!heap和 –a 选项相当于使用-h -f –m这三个选项,会需要较长时间。) |
-h | 输出中包含指定堆的所有条目。 |
-f | 输出中包含指定堆的所有空闲列表项(free list entries)。 |
-m | 输出中包含指定堆的所有段条目(segment entries)。 |
-t | 使得输出重包含指定堆的标签信息(tag information)。 |
-T | 输出中包含指定堆的伪标签条目(pseudo-tag entries)。 |
-g | 输出中包括全局标签信息(global tag information)。全局标签和每个无标签的分配(untagged allocation)关联。 |
-s | 输出中包含指定堆的摘要信息。 |
-k | (仅x86目标) 输出中包含每个条目关联的堆栈回溯。 |
选项 | 作用 |
---|---|
-D | 禁止指定堆的调用时验证(validate-on-call)。 |
-E | 启用指定堆的调用时验证(validate-on-call)。 |
-d | 禁用指定堆的堆检查(heap checking)。 |
-e | 启用指定堆的堆检查(heap checking)。 |
选项 | 作用 |
---|---|
-v | 验证所有数据块。 |
-b BucketSize | 指定存储单元(bucket)的大小。默认值为1024 bit。 |
-d DumpBlockSize | 指定存储单元(bucket)大小。 |
-a | |
-c | 指示每个块的内容都应该显示出来。 |
选项 | 作用 |
---|---|
-h Handle | 使得调试器显示句柄为Handle的页堆的详细信息。 |
-a Address | 使得调试器查找块中包含Address的页堆。会包含该地址和完整的页堆块的关系的详细信息,如是否该地址是页堆的一部分、它在块中的偏移,以及这个块已经被分配还是空闲的。在可能时还会包含堆栈回溯。使用该选项时,显示的大小是堆分配粒度的倍数。 |
-t[c|s] [Traces] | 使得调试器显示大量使用堆的用户(heavy heap users)的纪录(collected traces)。Traces指定要显示的纪录数量,默认值为4。如果有比指定的数量更多的纪录,则显示前面的部分纪录。如果使用-t 或者-tc ,则纪录以使用记数(count usage)排序。如果使用-ts ,则纪录以大小排序。 (-tc 和-ts 选项仅在Windows XP中支持,-t选项在Windows XP和之前的版本中都支持。) |
-fi [Traces] | (Windows XP 和之后) 使得调试器显示最近的故障注入纪录(fault injection traces)。 Traces 指定要显示的熟练,默认值为4。 |
-all | (Windows XP和之后) 使得调试器显示所有页堆的详细信息。 |
-? | 使得调试器显示页堆帮助(page heap help),包括堆块的图表。(这些图表在下面的注释节中可以看到。) |
使用任何!heap -p 扩展命令之前,目标进程中必须已经启用了页堆。查看后面注释节中的详细说明。
选项 | 作用 |
---|---|
-b | pattern的大小是一个BYTE。 |
-w | pattern的大小是一个WORD。 |
-d | pattern的大小是一个DWORD。 |
-q | pattern的大小是一个QWORD。 |
如果不指定上面任何一个,则会假定pattern 的大小和机器的指针大小一致。
选项 | 作用 |
---|---|
s Size | 限制显示出来的堆必须是指定的大小。 |
r SizeMin SizeMax | 限制显示出来的堆大小在指定范围内。 |
选项 | 作用 |
---|---|
A | 根据分配大小显示使用统计。 |
B | 根据块数量显示使用统计。 |
S | 根据每次分配的总大小显示使用统计。 |
Windows 2000 | Ext.dll Kdextx86.dll Ntsdexts.dll |
Windows XP和之后 | Ext.dll Exts.dll |
该扩展命令可以用来实现几种任务。
标准的!heap 命令用来显示当前进程的堆信息。(仅针对用户模式进程。!pool扩展命令用于系统进程。)
!heap -b 和!heap -B 命令用于在堆管理器中创建或者删除条件断点。
!heap -l 命令检查泄露的堆块。它使用一种垃圾回收算法(garbage collector algorithm)来检测没有被进程地址空间中任何地方引用到的已占用块(busy blocks)。对很大的程序,可能需要花费数分钟才能完成。该命令只在Windows XP和之后版本中可以使用。
!heap -x 命令搜索包含给定地址的堆块。如果使用了-v 选项,该命令还会搜索当前进程的整个虚拟地址空间,以获得指向该堆块的指针。这个命令仅在Windows XP和之后版本中可以使用。
!heap -p 命令显示各种形式的页堆(page heap)信息。使用!heap –p之前,必须启用目标进程的页堆(page heap)。这可以通过全局标志 (gflags.exe) 实用工具实现。打开该工具,在Image File Name 文本框中填入目标进程的名字,选择Image File Options 以及Enable page heap ,然后点击Apply 。也可以从命令提示符窗口输入gflags /i xxx.exe +hpa来运行全局标志工具,xxx.exe是目标程序的名字。
!heap -p -t[c|s] 命令在Windows XP之后就不支持了。可以使用调试器工具包中的UMDH工具来获得类似的结果。
!heap -srch 命令显示包含指定模板的堆条目(heap entries)。该命令仅在Windows XP和之后版本中可以使用。
!heap -flt 命令限制只显示分配大小为指定值的堆。该命令仅在Windows XP和之后版本中可用。
!heap -stat 命令显示堆使用统计。该命令仅在Windows XP和之后版本可用。
这里是一个标准的!heap 命令的示例:
Index Address Name Debugging options enabled
1: 00250000
Segment at 00250000 to 00350000 (00056000 bytes committed)
Flags: 50000062
ForceFlags: 40000060
Granularity: 8 bytes
Segment Reserve: 00100000
Segment Commit: 00004000
DeCommit Block Thres:00000400
DeCommit Total Thres:00002000
Total Free Size: 000003be
Max. Allocation Size:7ffddfff
Lock Variable at: 00250b54
Next TagIndex: 0012
Maximum TagIndex: 07ff
Tag Entries: 00350000
PsuedoTag Entries: 00250548
Virtual Alloc List: 00250050
UCR FreeList: 002504d8
128-bit bitmap of free lists
FreeList Usage: 00000014 00000000 00000000 00000000
Free Free
List List
# Head Blink Flink
FreeList[ 00 ] at 002500b8: 002a4378 . 002a4378
0x02 - HEAP_ENTRY_EXTRA_PRESENT
0x04 - HEAP_ENTRY_FILL_PATTERN
Entry Prev Cur 0x10 - HEAP_ENTRY_LAST_ENTRY
Address Size Size flags
002a4370: 00098 . 01c90 [14] - free
FreeList[ 02 ] at 002500c8: 0025cb30 . 002527b8
002527b0: 00058 . 00010 [04] - free
0025cb28: 00088 . 00010 [04] - free
FreeList[ 04 ] at 002500d8: 00269a08 . 0026e530
0026e528: 00038 . 00020 [04] - free
0026a4d0: 00038 . 00020 [06] - free
0026f9b8: 00038 . 00020 [04] - free
0025cda0: 00030 . 00020 [06] - free
00272660: 00038 . 00020 [04] - free
0026ab60: 00038 . 00020 [06] - free
00269f20: 00038 . 00020 [06] - free
00299818: 00038 . 00020 [04] - free
0026c028: 00038 . 00020 [06] - free
00269a00: 00038 . 00020 [46] - free
Segment00 at 00250b90:
Flags: 00000000
Base: 00250000
First Entry: 00250bc8
Last Entry: 00350000
Total Pages: 00000080
Total UnCommit: 00000055
Largest UnCommit:000aa000
UnCommitted Ranges: (1)
002a6000: 000aa000
Heap entries for Segment00 in Heap 250000
0x01 - HEAP_ENTRY_BUSY
0x02 - HEAP_ENTRY_EXTRA_PRESENT
0x04 - HEAP_ENTRY_FILL_PATTERN
0x08 - HEAP_ENTRY_VIRTUAL_ALLOC
0x10 - HEAP_ENTRY_LAST_ENTRY
0x20 - HEAP_ENTRY_SETTABLE_FLAG1
0x40 - HEAP_ENTRY_SETTABLE_FLAG2
Entry Prev Cur 0x80 - HEAP_ENTRY_SETTABLE_FLAG3
Address Size Size flags (Bytes used) (Tag name)
00250000: 00000 . 00b90 [01] - busy (b90)
00250b90: 00b90 . 00038 [01] - busy (38)
00250bc8: 00038 . 00040 [07] - busy (24), tail fill (NTDLL!LDR Database)
00250c08: 00040 . 00060 [07] - busy (48), tail fill (NTDLL!LDR Database)
00250c68: 00060 . 00028 [07] - busy (10), tail fill (NTDLL!LDR Database)
00250c90: 00028 . 00060 [07] - busy (48), tail fill (NTDLL!LDR Database)
00250cf0: 00060 . 00050 [07] - busy (38), tail fill (Objects= 80)
00250d40: 00050 . 00048 [07] - busy (2e), tail fill (NTDLL!LDR Database)
00250d88: 00048 . 00c10 [07] - busy (bf4), tail fill (Objects>1024)
00251998: 00c10 . 00030 [07] - busy (12), tail fill (NTDLL!LDR Database)
...
002525c0: 00030 . 00060 [07] - busy (48), tail fill (NTDLL!LDR Database)
00252620: 00060 . 00050 [07] - busy (38), tail fill (NTDLL!LDR Database)
00252670: 00050 . 00040 [07] - busy (22), tail fill (NTDLL!CSRSS Client)
002526b0: 00040 . 00040 [07] - busy (24), tail fill (Objects= 64)
002526f0: 00040 . 00040 [07] - busy (24), tail fill (Objects= 64)
00252730: 00040 . 00028 [07] - busy (10), tail fill (Objects= 40)
00252758: 00028 . 00058 [07] - busy (3c), tail fill (Objects= 88)
002527b0: 00058 . 00010 [04] free fill
002527c0: 00010 . 00058 [07] - busy (3c), tail fill (NTDLL!LDR Database)
00252818: 00058 . 002d0 [07] - busy (2b8), tail fill (Objects= 720)
00252ae8: 002d0 . 00330 [07] - busy (314), tail fill (Objects= 816)
00252e18: 00330 . 00330 [07] - busy (314), tail fill (Objects= 816)
00253148: 00330 . 002a8 [07] - busy (28c), tail fill (NTDLL!LocalAtom)
002533f0: 002a8 . 00030 [07] - busy (18), tail fill (NTDLL!LocalAtom)
00253420: 00030 . 00030 [07] - busy (18), tail fill (NTDLL!LocalAtom)
00253450: 00030 . 00098 [07] - busy (7c), tail fill (BASEDLL!LMEM)
002534e8: 00098 . 00060 [07] - busy (44), tail fill (BASEDLL!TMP)
00253548: 00060 . 00020 [07] - busy (1), tail fill (Objects= 32)
00253568: 00020 . 00028 [07] - busy (10), tail fill (Objects= 40)
00253590: 00028 . 00030 [07] - busy (16), tail fill (Objects= 48)
...
0025ccb8: 00038 . 00060 [07] - busy (48), tail fill (NTDLL!LDR Database)
0025cd18: 00060 . 00058 [07] - busy (3c), tail fill (NTDLL!LDR Database)
0025cd70: 00058 . 00030 [07] - busy (18), tail fill (NTDLL!LDR Database)
0025cda0: 00030 . 00020 [06] free fill (NTDLL!Temporary)
0025cdc0: 00020 . 00258 [07] - busy (23c), tail fill (Objects= 600)
0025d018: 00258 . 01018 [07] - busy (1000), tail fill (Objects>1024)
0025e030: 01018 . 00060 [07] - busy (48), tail fill (NTDLL!LDR Database)
...
002a4190: 00028 . 00118 [07] - busy (100), tail fill (BASEDLL!GMEM)
002a42a8: 00118 . 00030 [07] - busy (18), tail fill (Objects= 48)
002a42d8: 00030 . 00098 [07] - busy (7c), tail fill (Objects= 152)
002a4370: 00098 . 01c90 [14] free fill
002a6000: 000aa000 - uncommitted bytes.
这是!heap -l 命令的示例:
1:Heap 00170000
Heap 00280000
Heap 00520000
Heap 00b50000
Heap 00c60000
Heap 01420000
Heap 01550000
Heap 016d0000
Heap 019b0000
Heap 01b40000
Scanning VM ...
Entry User Heap Segment Size PrevSize Flags
----------------------------------------------------------------------
001b2958 001b2960 00170000 00000000 40 18 busy extra
001b9cb0 001b9cb8 00170000 00000000 80 300 busy extra
001ba208 001ba210 00170000 00000000 80 78 busy extra
001cbc90 001cbc98 00170000 00000000 e0 48 busy extra
001cbd70 001cbd78 00170000 00000000 d8 e0 busy extra
001cbe90 001cbe98 00170000 00000000 68 48 busy extra
001cbef8 001cbf00 00170000 00000000 58 68 busy extra
001cc078 001cc080 00170000 00000000 f8 128 busy extra
001cc360 001cc368 00170000 00000000 80 50 busy extra
001cc3e0 001cc3e8 00170000 00000000 58 80 busy extra
001fe550 001fe558 00170000 00000000 150 278 busy extra
001fe6e8 001fe6f0 00170000 00000000 48 48 busy extra
002057a8 002057b0 00170000 00000000 58 58 busy extra
00205800 00205808 00170000 00000000 48 58 busy extra
002058b8 002058c0 00170000 00000000 58 70 busy extra
00205910 00205918 00170000 00000000 48 58 busy extra
00205958 00205960 00170000 00000000 90 48 busy extra
00246970 00246978 00170000 00000000 60 88 busy extra
00251168 00251170 00170000 00000000 78 d0 busy extra user_flag
00527730 00527738 00520000 00000000 40 40 busy extra
00527920 00527928 00520000 00000000 40 80 busy extra
21 leaks detected.
例子中找到了21处泄露。
这是一个!heap -x 命令的示例:
Entry User Heap Segment Size PrevSize Flags
----------------------------------------------------------------------
002057a8 002057b0 00170000 00170640 58 58 busy extra
这是!heap -x -v 命令的示例:
1:Entry User Heap Segment Size PrevSize Flags
----------------------------------------------------------------------
002057a8 002057b0 00170000 00170640 58 58 busy extra
Search VM for address range 002057a8 - 002057ff : 00205990 (002057d0),
该例子中,地址0x00205990处有一个指向该堆块的指针。
这是!heap -flt s 命令的示例:
会显示所有大小为0x50的堆分配。
这是!heap -flt r 命令的示例:
这会显示大小在0x50 和0x7F 之间的堆分配。
下面是!heap -srch 命令的示例:
_HEAP @ 00090000
in HEAP_ENTRY: Size : Prev Flags - UserPtr UserSize - state
00099A48: 0018 : 0005 [01] - 00099A50 (000000B8) - (busy)
ole32!CALLFRAME_CACHE<INTERFACE_HELPER_CLSID>::`vftable'
_HEAP @ 00090000
in HEAP_ENTRY: Size : Prev Flags - UserPtr UserSize - state
00099B58: 0018 : 0005 [01] - 00099B60 (000000B8) - (busy)
ole32!CALLFRAME_CACHE<INTERFACE_HELPER_CLSID>::`vftable'
下面的图表是堆块的格式。这是Windows 2000(Service Pack 1和之后)、Windows XP、以及之后的系统中的形式。
Light page heap 块 — 已分配:
| | | |
+-----+---------------+---+
^ ^ ^
| | 8 字节后缀 (以 0xA0 填充)
| 用户分配(User allocation) (如果没有要求清0,则填充为 E0 )
块首 (以 0xABCDAAAA 开头, 0xDCBAAAAA 结尾)
Light page heap 块 — 已释放:
| | | |
+-----+---------------+---+
^ ^ ^
| | 8 字节后缀 (以 0xA0 填充)
| 用户分配 (如果没有要求清0,则填充为 F0)
块首 (以 0xABCDAAA9 开头, 0xDCBAAA9 结尾)
Full page heap 块 — 已分配:
| | | | ... N/A page
+-----+---------+---+-------
^ ^ ^
| | 0-7 字节后缀 (以 0xD0 填充)
| 用户分配 (如果未要求清0,Windows 2000中填充为 E0
Windows XP 中填充为 C0 )
块首 (以 0xABCDBBBB 开头,0xDCBABBBB 结尾)
Full page heap 块 —已释放:
| | | | ... N/A page
+-----+---------+---+-------
^ ^ ^
| | 0-7 字节后缀 (以 0xD0 填充)
| 用户分配 (以 F0 填充)
块首 (以 0xABCDBBA 开头,0xDCBABBBA 结尾)
要查看Windows 2000下对堆块或者full page heap 块的分配和释放的堆栈回溯,可以对头地址使用dds (Display Words and Symbols)命令。
要查看Windows XP和之后的Windows下对堆块或者full page heap 块的分配和释放的堆栈回溯,对头地址实用dt DPH_BLOCK_INFORMATION,然后对StackTrace字段使用dds。
Windows 2000的Full page heap 块位于包含用户分配内存的页面的开头或者前一个页面的开头。
关于堆的更多信息,查看Microsoft Windows SDK 文档、Windows Driver Kit (WDK)文档、以及Mark Russinovich 和David Solomon 编写的Microsoft Windows Internal。