使用Windbg定位內存泄露


在網上看了兩篇文章,整理一下,大致內容如下:

 

場景一:運行Debug版本程序,用Windbg attach 上去,等程序退出時,基於內存泄露報告,定位內存泄露的位置。

 

首先使用windbg工具gflags.exe設置內存啟動跟蹤內存泄露進程的user stack

啟動方法就是運行下面指令<span style="BACKGROUND-COLOR: #ff6666">gflags.exe /i test.exe +ust</span><span style="BACKGROUND-COLOR: #ffffff"> <span style="color:#ff0000;">需要通過gflags.exe工具打開開關,才能調試內存泄露!
</span>
</span>等價於HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options,命令“gflags.exe /i test.exe +ust”實際上就是在該路徑下創建一個子鍵“test.exe”並創建一個名為GlobalFlag內容為0x00001000的REG_DWORD值。

使用windbg加載test.exe,運行關閉時windbg中會提示內存泄露

normal block at 0x026A5F98, 4000 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
可以發現地址0x026A5F98就是內存泄漏的地址泄漏4000個字節

通過!heap命令對該地址進行分析可以發現具體的調用堆棧

0:000> <span style="BACKGROUND-COLOR: #ff99ff">!heap -p -a 0x026A5F98
</span>    address 026a5f98 found in
    _HEAP @ 14f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        026a5f60 01fc 0000  [00]   026a5f78    00fc4 - (busy)
        77a1b234 ntdll!RtlAllocateHeap+0x00000274
        584d7743 MSVCR100D!_heap_alloc_base+0x00000053
        584e5d8c MSVCR100D!_heap_alloc_dbg_impl+0x000001fc
        584e5b2f MSVCR100D!_nh_malloc_dbg_impl+0x0000001f
        584e5adc MSVCR100D!_nh_malloc_dbg+0x0000002c
        584e5a91 MSVCR100D!_malloc_dbg+0x00000021
        58694dd6 mfc100ud!operator new+0x00000026
        58694e6a mfc100ud!operator new[]+0x0000001a
        58694768 mfc100ud!operator new[]+0x00000018
*** WARNING: Unable to verify checksum for SendMsgEx.exe
        2a3c25 <span style="BACKGROUND-COLOR: #ff99ff">SendMsgEx!CSendMsgExDlg::Thread1Proc</span>+0x00000055
        767c1174 kernel32!BaseThreadInitThunk+0x0000000e
        779fb3f5 ntdll!__RtlUserThreadStart+0x00000070
        779fb3c8 ntdll!_RtlUserThreadStart+0x0000001b
可以發現內存泄漏的地址在CSendMsgExDlg::Thread1Proc這個地址里面調用了new[]導致內存泄漏

DWORD WINAPI CSendMsgExDlg::Thread1Proc(__in  LPVOID lpParameter)
{
    INT *pVal = new INT[1000];
        //..................
}


結論:場景一適用於Debug版本程序,而且需要在程序退出時才能定位泄露

 

場景二:程序運行一段時間,用Windbg attach上去,查看堆內存使用情況;detach 讓程序繼續運行;過一段時間再 attach,查看堆內存使用情況,與第一次對比,找到內存明顯增長的堆

我們開發的系統需要在客戶的電腦上持續運行,可是客戶報告在運行幾天后,程序會占用越來越多的內存,最后會產生下面的兩個錯誤使之不能繼續:

1. The application has no enough resource to display

2. The application crash with a log like "memory allocation failed".

怎么辦?上windbg。

准備工作

Configure the symbol file path to the Microsoft symbol server “SRV*c:\symbols*http://msdl.microsoft.com/download/symbols”,也可以一個環境變量_NT_SYMBOL_PATH到系統路徑下,省得每次配置它
Add your program EXE/DLL PDB (program database) path to the symbol file path.(<span style="color:#ff0000;">設置PDB路徑</span>)
Configure the Operating System's flag to enable user stack trace for the process which has memory leaks.
Gflags.exe -i excel.exe +ust(<span style="color:#ff0000;">和場景一一樣,設置gflags.exe啟動調試</span>)

第一次記錄

讓系統<span style="BACKGROUND-COLOR: #ff99ff">運行一段時間,用windbg attach 它的進程</span>,運行下面的命令

0:025> <span style="BACKGROUND-COLOR: #ff99ff">!heap -s
</span>LFH Key                   : 0xeaafe2e0
Heap     Flags   Reserv Commit Virt   Free List   UCR Virt Lock Fast
                    (k)     (k)    (k)     (k) length      blocks cont. heap
-----------------------------------------------------------------------------
00160000 00000002   32768 28204 28492   1460   239    19    0      f   LFH
00260000 00001002      64     40     40      3     1     1    0      0   L 
00270000 00008000      64     12     12     10     1     1    0      0     
004e0000 00000002      64      8      8      0     0     1    0      0   L 
00030000 00001002    1088     72     72      9     2     1    0      0   L 
00480000 00001002    7232   3444   3444     36     5     2    0      0   L 
004c0000 00001002    1088    252    252      5     1     1    0      0   L 
004d0000 00001002      64     12     12      4     1     1    0      0   L 
01060000 00001002      64     16     16      2     2     1    0      0   L 
01120000 00000002    1024     24     24      0     0     1    0      0   L 
010b0000 00001002     256     32     32      0     0     1    0      0   L 
01660000 00001002    3136   2796   2828    377    13     7    0      0   L 
    External fragmentation 13 % (13 free blocks)
01680000 00001002      64     32     32      3     0     1    0      0   L 
01690000 00041002     256     12     12      0     0     1    0      0   L 
01790000 00001003     256    104    116     60     9     3    0    bad     
017d0000 00001003     256      4      4      2     1     1    0    bad     
01810000 00001003     256      4      4      2     1     1    0    bad     
030d0000 00001003     256      4      4      2     1     1    0    bad     
03110000 00001003     256      4      4      2     1     1    0    bad     
01850000 00001002      64     20     20      2     1     1    0      0   L 
03560000 00001002    1280    664    776     22     7     4    0      0   L 
04780000 00001003     256      8      8      2     1     1    0    bad     
047c0000 00001003     256      4      4      2     1     1    0    bad     
04800000 00001003     256      4      4      2     1     1    0    bad     
04840000 00001003     256      4      4      2     1     1    0    bad     
04880000 00001003     256      4      4      2     1     1    0    bad     
048e0000 00001002     256     16     16      4     1     1    0      0   L 
<span style="color:#000000;BACKGROUND-COLOR: #ff9900">04920000 00001002    1088   1012   1024    111     7     3    0      0   L 
</span>04930000 00001002    3136    940    940    153     9     2    0     8d   L 
04ce0000 00001002      64     16     16      0     0     1    0      0   L 
04cf0000 00001002    1088    192    192      6     2     1    0      0   L 
05850000 00001002      64     28     28      1     1     1    0      0   L 
05de0000 00001002      64     12     12      3     1     1    0      0   L 
第二次記錄

Detatch the windbg from the excel process(<span style="color:#ff0000;">windbg 和 應用程序detach, 如果不detach,程序被中斷到調試狀態,無法運行</span>)

讓它再運行一段時間,用windbg attach 它的進程,運行下面的命令

0:025> !heap -s
LFH Key                   : 0xeaafe2e0
Heap     Flags   Reserv Commit Virt   Free List   UCR Virt Lock Fast
                    (k)     (k)    (k)     (k) length      blocks cont. heap
-----------------------------------------------------------------------------
00160000 00000002   32768 28204 28492   1460   239    19    0      f   LFH
00260000 00001002      64     40     40      3     1     1    0      0   L 
00270000 00008000      64     12     12     10     1     1    0      0     
004e0000 00000002      64      8      8      0     0     1    0      0   L 
00030000 00001002    1088     72     72      9     2     1    0      0   L 
00480000 00001002    7232   3444   3444     36     5     2    0      0   L 
004c0000 00001002    1088    252    252      5     1     1    0      0   L 
004d0000 00001002      64     12     12      4     1     1    0      0   L 
01060000 00001002      64     16     16      2     2     1    0      0   L 
01120000 00000002    1024     24     24      0     0     1    0      0   L 
010b0000 00001002     256     32     32      0     0     1    0      0   L 
01660000 00001002    3136   2796   2828    377    13     7    0      0   L 
    External fragmentation 13 % (13 free blocks)
01680000 00001002      64     32     32      3     0     1    0      0   L 
01690000 00041002     256     12     12      0     0     1    0      0   L 
01790000 00001003     256    104    116     60     9     3    0    bad     
017d0000 00001003     256      4      4      2     1     1    0    bad     
01810000 00001003     256      4      4      2     1     1    0    bad     
030d0000 00001003     256      4      4      2     1     1    0    bad     
03110000 00001003     256      4      4      2     1     1    0    bad     
01850000 00001002      64     20     20      2     1     1    0      0   L 
03560000 00001002    1280    664    776     22     7     4    0      0   L 
04780000 00001003     256      8      8      2     1     1    0    bad     
047c0000 00001003     256      4      4      2     1     1    0    bad     
04800000 00001003     256      4      4      2     1     1    0    bad     
04840000 00001003     256      4      4      2     1     1    0    bad     
04880000 00001003     256      4      4      2     1     1    0    bad     
048e0000 00001002     256     16     16      4     1     1    0      0   L 
<span style="BACKGROUND-COLOR: #ff6600">04920000 00001002    1088   3012   3024    511     7     3    0      0   L 
</span>04930000 00001002    3136    940    940    153     9     2    0     8d   L 
04ce0000 00001002      64     16     16      0     0     1    0      0   L 
04cf0000 00001002    1088    192    192      6     2     1    0      0   L 
05850000 00001002      64     28     28      1     1     1    0      0   L 
05de0000 00001002      64     12     12      3     1     1    0      0   L 
<span style="BACKGROUND-COLOR: #ff6666">比較第一次和第二次,發現在0x04920000上的內存有明顯的增長

</span><span style="BACKGROUND-COLOR: #ff99ff">執行!heap -stat -h 04920000 去觀察這段內存的詳細情況

</span>0:025> !heap -stat -h 04920000
heap @ 04920000
group-by: TOTSIZE max-display: 20
    size     #blocks     total     ( %) (percent of total busy bytes)
     4 21a29 - 82cd0 (94.77)
    d0 2a - 2220 (1.06)
    20 cd - 19a0 (0.79)
    90 2d - 1950 (0.78)
    be0 2 - 17c0 (0.74)
    e0 1b - 17a0 (0.73)
    f0 19 - 1770 (0.73)
    1f0 b - 1550 (0.66)
    200 a - 1400 (0.62)
    40 4f - 13c0 (0.61)
    240 7 - fc0 (0.49)
    bd0 1 - bd0 (0.37)
<span style="BACKGROUND-COLOR: #ff99ff">發現這段內存主要是由size=4的內存構成的</span>,而內存泄漏通常都是同一size的內存只分配,但沒有釋放引起的,所以,這個是值得高度懷疑的。

<span style="BACKGROUND-COLOR: #ff99ff">執行!heap -flt s 4 去查進程中size=4的所有內存</span>,

_HEAP @ 04920000 
        03659ab8 0002 0002 [01]   03659ac0    00004 - (busy)
        03659ac8 0003 0002 [01]   03659ad0    00004 - (busy)
        0365e8e8 0002 0003 [01]   0365e8f0    00004 - (busy)
        0f2b9fe8 0003 0002 [11]   0f2b9ff0    00004 - (busy)
        0f2d9760 0003 0003 [01]   0f2d9768    00004 - (busy)
        0f2dcc20 0002 0003 [01]   0f2dcc28    00004 - (busy)
        0f2dcc50 0002 0002 [01]   0f2dcc58    00004 - (busy)
        0f2dd790 0002 0002 [01]   0f2dd798    00004 - (busy)
        0f2dd7c0 0002 0002 [01]   0f2dd7c8    00004 - (busy)
        0f2de260 0002 0002 [01]   0f2de268    00004 - (busy)
        0f2de290 0002 0002 [01]   0f2de298    00004 - (busy)
        0f2de2a0 0003 0002 [01]   0f2de2a8    00004 - (busy)
        0f2df740 0002 0003 [01]   0f2df748    00004 - (busy)
        0f2e0270 0002 0002 [01]   0f2e0278    00004 - (busy)
        0f2e02a0 0002 0002 [01]   0f2e02a8    00004 - (busy)
        0f2e02e0 0003 0002 [01]   0f2e02e8    00004 - (busy)
        0f2e1270 0002 0003 [01]   0f2e1278    00004 - (busy)
        0f2e1ce0 0002 0002 [01]   0f2e1ce8    00004 - (busy)
        0f2e1d10 0002 0002 [01]   0f2e1d18    00004 - (busy)
        0f2e27d0 0002 0002 [01]   0f2e27d8    00004 - (busy)
        0f2e2800 0002 0002 [01]   0f2e2808    00004 - (busy)
        0f2e2cc0 0002 0002 [01]   0f2e2cc8    00004 - (busy)
        0f2e2cf0 0002 0002 [01]   0f2e2cf8    00004 - (busy)
        0f2e3340 0003 0002 [01]   0f2e3348    00004 - (busy)
        0f2e3d20 0002 0003 [01]   0f2e3d28    00004 - (busy)
        0f2e4890 0002 0002 [01]   0f2e4898    00004 - (busy)
        0f2e48c0 0003 0002 [01]   0f2e48c8    00004 - (busy)

<span style="BACKGROUND-COLOR: #ff99ff">然后執行!heap -p -a 0365e8f0 該內存分配時的堆棧。 這樣就可以定位到內存泄露的根源了。</span>

 

結論:場景二比較試用於大部分情況不需要是Debug版本,在現場調試,也不需要等待進程結束。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com