【windbg】利用umdh分析內存泄露

轉載自:http://blog.csdn.net/whatday/article/details/47301145


A.利用工具umdh(user-mode dump heap)分析

1設置gflags.exe標誌

r

 

Gflags標誌設置好後,開啓cmd


鍵入要定位內存泄露的程序gflags.exe /i memroyleak.exe +ust

如圖成功後,開啓memoryleak.exe程序

2利用umdh創建heap快照


命令格式:umdh -pn:memoryleak.exe -f:snap1.log

程序運行一段時間後或者程序佔用內存增加時,然後再次創建heap快照,命令行無差別,snap1.log改爲snap2.log或者其他。

設置好程序的符號路徑,如下圖


設置好後可以開始分析heap前後兩個快照的差異


分析差異命令:umdh -d snap1.log snap2.log -f:result.txt

生成的result.txt文件在 命令行同目錄下 這裏是 D:\WinDDK\7600.16385.0\Debuggers 



分析完成後查看結果result.txt

紅色爲umdh定位出來的泄露點,我們在查看源代碼


這樣我們就可以修改代碼中內存泄露的地方了。


內存泄露實例分析:

兩次快照差異文件實例大致如下:

[cpp]  view plain  copy
  1. // Debug library initialized ...  
  2. D0000-111FFF DBGHELP: Server - private symbols & lines   
  3.         C:\FunctionServer\Release\Server.pdb  
  4. 77E70000-77FEFFFF DBGHELP: ntdll - public symbols    
  5.         c:\mysymbol\wntdll.pdb\B081677DFC724CC4AC53992627BEEA242\wntdll.pdb  
  6. 。。。。  
  7. 等等符號加載信息  
  8.   
  9. 緊接着是內存泄露信息格式說明   
  10. //                                                                           
  11. // Each log entry has the following syntax:                                   
  12. //                                                                            
  13. // + BYTES_DELTA (NEW_BYTES - OLD_BYTES) NEW_COUNT allocs BackTrace TRACEID   
  14. // + COUNT_DELTA (NEW_COUNT - OLD_COUNT) BackTrace TRACEID allocations        
  15. //     ... stack trace ...                                                    
  16. //                                                                            
  17. // where:                                                                     
  18. //                                                                            
  19. //     BYTES_DELTA - increase in bytes between before and after log           
  20. //     NEW_BYTES - bytes in after log                                         
  21. //     OLD_BYTES - bytes in before log                                        
  22. //     COUNT_DELTA - increase in allocations between before and after log     
  23. //     NEW_COUNT - number of allocations in after log                         
  24. //     OLD_COUNT - number of allocations in before log                        
  25. //     TRACEID - decimal index of the stack trace in the trace database       
  26. //         (can be used to search for allocation instances in the original    
  27. //         UMDH logs).                                                        
  28. //                          
  29.   
  30. 接着是具體的內存泄露信息  
  31.   
  32. +    47e0 ( 237238 - 232a58)    1f9 allocs  BackTrace8E5CFAC  
  33. +       4 (   1f9 -   1f5)  BackTrace8E5CFAC    allocations  
  34.   
  35.   
  36.     ntdll!RtlAllocateHeap+274  
  37.     Server!malloc+49 (f:\dd\vctools\crt\crtw32\heap\malloc.c, 92)  
  38.     Server!operator new+1D (f:\dd\vctools\crt\crtw32\heap\new.cpp, 59)  
  39.     Server!CUi::AddItemText+129 (d:\projects\testtest\common\uilibf, 611)  
  40.     Server!CUi::AddItemInt+57 (d:\projects\testtest\common\uilibf, 709)  
  41.     Server!CMainWin::AddOneFunction+1FE (d:\projects\testtest\server\server\, 361)  
  42.     Server!CTest::FunctionPcInfo+3F9 (d:\projects\testtest\server\server\, 306)  
  43.     Server!CTest::FunctionReadDispatch+15D (d:\projects\testtest\server\server\, 105)  
  44.     Server!CTest::FunctionReadCallback+14 (d:\projects\testtest\server\server\, 76)  
  45.     Server!CWSAAsync::ReadProc+10F (d:\projects\testtest\common\wsaasyncselect, 1336)  
  46.     Server!CWSAAsync::ReadProcMiddle+12 (d:\projects\testtest\common\wsaasyncselect, 1296)  
  47.     Server!CWindowsPool::ReadThreadPoolCallback+25 (d:\projects\testtest\common\wsaasyncselect, 332)  
  48.     ntdll!TppWorkpExecuteCallback+10F  
  49.     ntdll!TppWorkerThread+572  
  50.     kernel32!BaseThreadInitThunk+E  
  51.     ntdll!__RtlUserThreadStart+70  
  52.     ntdll!_RtlUserThreadStart+1B  
  53.   
  54. 。。。。  
  55. 等等其他內存泄露塊信息  


根據格式的說明可得到此泄露信息如下:

第一行:+    47e0 ( 237238 - 232a58)    1f9 allocs  BackTrace8E5CFAC

BackTrace8E5CFAC是這個內存塊的標記  237238是生成日誌文件2時該內存塊的大小 232a58是生成日誌文件1該內存塊的大小  差值47e0 是內存泄露的字節數   1f9是分配內存的次數 (其中47e0 個人理解爲申請內存未釋放的字節數,因爲有可能是釋放的時間未到就生成日誌文件2 造成只有申請內存 沒有釋放的情況 所以被判定爲內存泄露 關於這點只是個人意見 不一定正確) 。

第二行:+ 4 ( 1f9 - 1f5) BackTrace8E5CFAC allocations

BackTrace8E5CFAC是內存塊標記和第一行一樣,1f9是生成日誌文件2時該內存分配的次數, 1f5是生成日誌文件1時該內存分配的次數  差值4是這次該內存塊分配的次數。

其他行:是函數調用堆棧,通過分析自己的程序發現,第三行的 Server!CUi::AddItemText+129 (d:\projects\testtest\common\uilibf, 611) 也是內存泄露所在,對應源代碼是:pItemLabel = new CLabelUI; 這樣基本上就定位到問題所在了

驗證一下觀點:每一次分配的大小是47e0 /4=4600(十進制),  程序中代碼驗證了sizeof(CLabelUI)也等於4600, 看來從日誌1 到日誌2 過程中這個地方new了4次 但是在日誌2時 還未釋放這些內存 所以造成內存比較時 會定位處該塊內存的泄露,至於是否真泄露還是要看程序邏輯,但是既然已定位到該代碼 還是要仔細分析一下 看看是邏輯問題 還是真忘了釋放內存。




B.Windbg手動分析內存泄露


1全局標誌設置,參照上邊的設置

2.Windbg調試泄露

開啓memoryleak.exe程序,windbg attach到該進程


命令:!heap –s查看當前進程運行的所有堆的情況

然後F5讓程序運行一段時間或者內存有明顯的增加時再次通過!heap –s查看當前堆的變化

如下圖


通過對比前後兩個堆的變化,發現0x012800000該地址的堆增加的很快而其他堆沒什麼變化

下面進一步定位


命令:!heap –stat –h 查看對應對的狀態,發下該堆的內存基本被長度爲0x424的塊佔用,接下來我們在堆中搜索該進程中哪些模塊佔用0x424長度內存,如下圖


命令:!heap –flt s 424

通過搜索程序內存中的堆發現長度爲424的堆被大量的佔用,進一步查看時誰在使用這個地址


找到泄露點了,紅色部分的,如果程序對應的符號對應我們可以查看內存泄露點在哪一行


內存泄露分析結束,如果你還有什麼好的方法可以共享