不使用user-mode dump heap (UMDH) 定位内存泄漏-Windbg直接到精通

作者:Kim.  那年,他们都叫我Kim.实际上只是有人早就叫了Stone而已......

搜索国内网站,凡是涉及到内存泄漏的,基本都是介绍UMDH的使用,我们不否认UMDH的强大。只是,面对千篇一律的,你是否会凌乱?别搞啥从入门到精通,我们直接进入正题:

核心思想

使用windbg同时打开多个dmp文件,利用dmp找到堆中增量增加的地址(delta)。

你可以掌握的知识

Windbg命令提示符的完整形式

在下面图片中红色框中你可以看到显示的内容为:||1:1:009

其格式为:

[||system_index:]process_index:thread_index>

用户模式下其含义分别为:系统序号、进程序号、线程序号

划重点:Windbg可以附加到多个系统跟打开多个dmp文件,如果打开多个dmp,每个dmp会被认为是一个系统。

页面的三种状态

The pages of a process's virtual address space can be in one of the following states.

StateDescription
FreeThe page is neither committed nor reserved. The page is not accessible to the process. It is available to be reserved, committed, or simultaneously reserved and committed. Attempting to read from or write to a free page results in an access violation exception.
A process can use the VirtualFree or VirtualFreeEx function to release reserved or committed pages of its address space, returning them to the free state.
ReservedThe page has been reserved for future use. The range of addresses cannot be used by other allocation functions. The page is not accessible and has no physical storage associated with it. It is available to be committed.
A process can use the VirtualAlloc or VirtualAllocEx function to reserve pages of its address space and later to commit the reserved pages. It can use VirtualFree or VirtualFreeEx to decommit committed pages and return them to the reserved state.
CommittedMemory charges have been allocated from the overall size of RAM and paging files on disk. The page is accessible and access is controlled by one of the memory protection constants. The system initializes and loads each committed page into physical memory only during the first attempt to read or write to that page. When the process terminates, the system releases the storage for committed pages.
A process can use VirtualAlloc or VirtualAllocEx to commit physical pages from a reserved region. They can also simultaneously reserve and commit pages.
The GlobalAlloc and LocalAlloc functions allocate committed pages with read/write access.

!heap -s 跟!heap -p -s的用法

windbg中使用.hh命令,可以打开帮助问题,打开后,你可以搜索!heap,可以!heap -s 以及!heap -p 携带参数-a的使用访问:

-s

Specifies that summary information is being requested. If SummaryOptions and StatHeapAddress are omitted, then summary information is displayed for all heaps associated with the current process.

SummaryOptions

Can be any combination of the following options. The SummaryOptions are not case-sensitive. Type !heap -s -? for additional information.

-p

Specifies that page heap information is being requested. If this is used without any PageHeapOptions, all page heaps will be displayed.

PageHeapOptions

Can be any single one of the following options. The PageHeapOptions are case-sensitive. If no options are specified, then all possible page heap handles will be displayed.

-a Address

Causes the debugger to find the page heap whose block contains Address. Full details of how this address relates to full-page heap blocks will be included, such as whether this address is part of a page heap, its offset inside the block, and whether the block is allocated or was freed. Stack traces are included whenever available. When using this option, size is displayed in multiples of the heap allocation granularity.

具体定位方法

  1. 以32位程序为例,使用gflags 开启堆栈跟踪,开启后程序的堆分配,比如malloc动作就能跟调用栈关联上了,这是很多文章没有解释的。
  2. 使用Windbg打开测试程序MemLeak1.exe,按F5执行,随后暂停取第一个dmp文件,命令如下:.dump /ma D:\base.dmp
  3. 按F5继续执行,(我的程序会手动制造泄漏),暂停,取第二个dump,命令:.dump /ma D:\delta.dmp
  4. 重新打开新的windbg,打开第1个base.dmp,随后使用.opendump d:\delta打开第2(3,4)个dmp,按F5加载dmp,这样两个(多个)dmp就在一个windbg实例中打开了。
  5. 使用 || 查看所有加载的dmp                                                                                                       
  6. 使用||.查看当前所在的dmp,如果不是第一个base,则使用||0s切换到编号为0的dmp,即base.dmp
    1. 使用!heap -s查看base.dmp的堆总览情况,再使用||1s切换到delta.dmp查看堆总览情况,你会发现commit状态的差异,找差异!接下来 定位具体泄漏点,因为两个dmp都是在一个进程转存储的,所以heap handle值一样为:038f0000。首先||0s切换到base.dmp,使用!heap -h 038f0000 display to include all non-LFH entries for the specified heap。如下:
    2. 我们记住倒数几个的地址。随后使用||1s切换到delta.dmp后,继续使用!heap -h 038f0000 查看所有heap entries,随后搜索我们上次在base.dmp记录的地址,如03904650
    3. 相信你可以看到后面多出来的一大坨,有很多大小一样重复的地方了。我们使用其中一个地址,查看其关联的分配信息,就可以看到泄露的地方了!命令:!heap -p -a 03905668
  7. 如果你加载了pdb,并且还在debug模式下可以看到详细的代码函数。我这里是release模式下生成的exe.我们u一下,main+0x00000034。噢,要往前一点,main+0x00000034是返回的要执行的代码的地址。使用u MemLeak1!main+0x00000020可以看到取了malloc地址放到edi,然后push 0x64,然后call,即调用malloc。我们发现0x64的size也跟我们发现的delta dmp的size一样。
  8. 代码
    #include <iostream>
    
    void leak() {
        void* p = malloc(100);
    }
    
    int main()
    {
        std::cout << "Press enter to leak memory or q to quit\n";
        char c = getchar();
        while (c != 'q') {
            leak();
    
    
            std::cout << "Leaking memory. Press enter to leak or q to quit\n";
            c = getchar();
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值