为什么!address和!heap的输出不匹配?

时间:2015-06-05 15:22:43

标签: memory windbg

我正在调试Windbg中的以下C程序:

int main()
{
    size_t size = 500*1024*1024;
    void *p = malloc(size);
    memset(p, 'a', size);
    printf("%p", p);
}

我使用以下方法编译程序: cl / Zi leak.c 并且有一个leak.exe生成。

我在printf行设置断点。我运行以下命令:

0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                     21          5f0f6000 (   1.485 Gb)           74.27%
Heap                                      3          1f501000 ( 501.004 Mb)  95.07%   24.46%
<unknown>                                39           1436000 (  20.211 Mb)   3.84%    0.99%
Image                                    35            300000 (   3.000 Mb)   0.57%    0.15%
MappedFile                                4            182000 (   1.508 Mb)   0.29%    0.07%
Stack                                     3            100000 (   1.000 Mb)   0.19%    0.05%
Other                                     6             3f000 ( 252.000 kb)   0.05%    0.01%
TEB                                       1              1000 (   4.000 kb)   0.00%    0.00%
PEB                                       1              1000 (   4.000 kb)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                              21          207f0000 ( 519.938 Mb)  98.66%   25.39%
MEM_IMAGE                                64            54c000 (   5.297 Mb)   1.01%    0.26%
MEM_MAPPED                                7            1be000 (   1.742 Mb)   0.33%    0.09%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                 21          5f0f6000 (   1.485 Gb)           74.27%
MEM_COMMIT                               73          1f9c9000 ( 505.785 Mb)  95.98%   24.70%
MEM_RESERVE                              19           1531000 (  21.191 Mb)   4.02%    1.03%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE                           27          1f45f000 ( 500.371 Mb)  94.95%   24.43%
PAGE_EXECUTE_READ                         9            376000 (   3.461 Mb)   0.66%    0.17%
PAGE_READONLY                            26            1e4000 (   1.891 Mb)   0.36%    0.09%
PAGE_WRITECOPY                            9              c000 (  48.000 kb)   0.01%    0.00%
PAGE_READWRITE|PAGE_GUARD                 2              4000 (  16.000 kb)   0.00%    0.00%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free                                        1fb51000          5590f000 (   1.337 Gb)
Heap                                          750000          1f401000 ( 500.004 Mb)
<unknown>                                   7f0e0000            f00000 (  15.000 Mb)
Image                                       77bc0000             d6000 ( 856.000 kb)
MappedFile                                  7efe5000             fb000 (1004.000 kb)
Stack                                         210000             fd000 (1012.000 kb)
Other                                       7efa0000             33000 ( 204.000 kb)
TEB                                         7efdd000              1000 (   4.000 kb)
PEB                                         7efde000              1000 (   4.000 kb)

我可以看到堆大约是500MB,这是预期的。

但是!heap命令看不到这个信息:

只有1个堆。

0:000> !heap
Index   Address  Name      Debugging options enabled
  1:   00650000                 tail checking free checking validate parameters

0:000> !heap -a 00650000
Index   Address  Name      Debugging options enabled
  1:   00650000 
    Segment at 00650000 to 00750000 (0000f000 bytes committed) // Why so few memory committed.
    Flags:                40000062
    ForceFlags:           40000060
    Granularity:          8 bytes
    Segment Reserve:      00100000
    Segment Commit:       00002000
    DeCommit Block Thres: 00000200
    DeCommit Total Thres: 00002000
    Total Free Size:      00000517
    Max. Allocation Size: 7ffdefff
    Lock Variable at:     00650138
    Next TagIndex:        0000
    Maximum TagIndex:     0000
    Tag Entries:          00000000
    PsuedoTag Entries:    00000000
    Virtual Alloc List:   006500a0
    Unable to read nt!_HEAP_VIRTUAL_ALLOC_ENTRY structure at 00750000
    Uncommitted ranges:   00650090
            0065f000: 000f1000  (987136 bytes)
    FreeList[ 00 ] at 006500c4: 0065b340 . 0065b340  
        0065b338: 00458 . 028b8 [104] - free

    Segment00 at 00650000:
        Flags:           00000000
        Base:            00650000
        First Entry:     00650588
        Last Entry:      00750000
        Total Pages:     00000100
        Total UnCommit:  000000f1
        Largest UnCommit:00000000
        UnCommitted Ranges: (1)

0:000> dt p
Local var @ 0x30ff04 Type void*
0x00750020 
Void
0:000> !heap -p -a 0x00750020 
    address 00750020 found in
    _HEAP @ 650000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        00750018 3e80200 0000  [00]   00750020    1f400000 - (busy VirtualAlloc)
0:000> !heap -s 
NtGlobalFlag enables following debugging aids for new heaps:
    tail checking
    free checking
    validate parameters
LFH Key                   : 0x343f9ad2
Termination on corruption : ENABLED
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                    (k)     (k)    (k)     (k) length      blocks cont. heap 
-----------------------------------------------------------------------------
Virtual block: 00fa0000 - 00fa0000 (size 00000000)
006d0000 40000062    1024     36   1024      1     1     1    1      0      
-----------------------------------------------------------------------------

为什么我无法在!heap -s中看到上述信息?如何转储堆中的所有条目?

1 个答案:

答案 0 :(得分:2)

首先需要一些解释......

堆与虚拟内存

恕我直言,堆是发明的,因为VirtualAlloc()(64kB)的粒度对于少量数据来说太浪费了。如果我查看!heap <address>的输出,我可以看到

Heap entries for Segment00 in Heap 00100000
     address: psize . size  flags   state (requested size)
    00100000: 00000 . 00588 [101] - busy (587)

请参阅包含5位数的size列。这可能意味着堆条目的最大有用大小为

0:001> ? fffff
Evaluate expression: 1048575 = 000fffff

大约1 MB。超过这个大小,拥有堆的粒度可能没有意义,你可以直接使用VirtualAlloc()。或者,不是,但malloc()函数会为你决定。

输出中的错误消息

在你的输出中,你注意到两行

Virtual Alloc List:   006500a0
Unable to read nt!_HEAP_VIRTUAL_ALLOC_ENTRY structure at 00750000

这表明堆中有部分由VirtualAlloc()实际处理。但是,WinDbg无法找到它的数据类型:

0:001> dt nt!_HEAP_VIRTUAL_ALLOC_ENTRY
Symbol nt!_HEAP_VIRTUAL_ALLOC_ENTRY not found.

!heap -s的输出缺失,所以我在我的机器上创建了它(WinDbg 6.3.9600.16384):

0:001> !heap 00100000 -s
LFH Key                   : 0x62502d13
Termination on corruption : ENABLED
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                    (k)     (k)    (k)     (k) length      blocks cont. heap 
-----------------------------------------------------------------------------
Virtual block: 005b0000 - 005b0000 (size 00000000)
Virtual block: 006b0000 - 006b0000 (size 00000000)
...

Virtual block: 1fab0000 - 1fab0000 (size 00000000)
00100000 00000002    1024    204   1024      4     7     1  500      0   LFH
-----------------------------------------------------------------------------

在我的演示中,我创建了大约500块1 MB的块。请注意,00000000的大小似乎已损坏。但是,如果我在一个区块上使用!address,则会得到区域大小正确(00100000):

0:001> !address 005b0000                                     
...
Usage:                  Heap
Base Address:           005b0000
End Address:            006b0000
Region Size:            00100000
State:                  00001000    MEM_COMMIT
Protect:                00000004    PAGE_READWRITE
Type:                   00020000    MEM_PRIVATE
Allocation Base:        005b0000
Allocation Protect:     00000004    PAGE_READWRITE

缺少_HEAP_VIRTUAL_ALLOC_ENTRY怎么样?

0:001> dd 770000 L10
00770000  00870000 00670000 00000000 00000000
00770010  00100000 00100000 c8d50110 04000000
00770020  003df5a8 00870020 00000000 00000000
00770030  000ffc00 00000001 000000c1 fdfdfdfd

偏移0处的值870000似乎是到下一个存储块的FLink,偏移4处的值为670000,与前一个存储块闪烁。

偏移10和14都匹配区域大小。

0:001> ? 0n1023*0n1024
Evaluate expression: 1047552 = 000ffc00

偏移量+30是大小(我在这里分配了1023 kB的块)。

FDFDFDFD是一个众所周知的调试幻数,表示无人区,因此很可能是结构的结束。

从XP转储中提取结构

我有一个Windows XP转储。了解nt!_HEAP_VIRTUAL_ALLOC_ENTRY如何解决问题:

0: kd> dt -r1 nt!_HEAP_VIRTUAL_ALLOC_ENTRY
   +0x000 Entry            : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x008 ExtraStuff       : _HEAP_ENTRY_EXTRA
      +0x000 AllocatorBackTraceIndex : Uint2B
      +0x002 TagIndex         : Uint2B
      +0x004 Settable         : Uint4B
      +0x000 ZeroInit         : Uint8B
   +0x010 CommitSize       : Uint4B
   +0x014 ReserveSize      : Uint4B
   +0x018 BusyBlock        : _HEAP_ENTRY
      +0x000 Size             : Uint2B
      +0x002 PreviousSize     : Uint2B
      +0x000 SubSegmentCode   : Ptr32 Void
      +0x004 SmallTagIndex    : UChar
      +0x005 Flags            : UChar
      +0x006 UnusedBytes      : UChar
      +0x007 SegmentIndex     : UChar

结论

对于!heap创建的大块,

malloc()似乎存在问题,在这种情况下实际使用VirtualAlloc()。 WinDbg找不到映射内存内容所需的数据类型。微软应该可以解决这个问题。

!address -summary堆的输出统计数据似乎是合理的。