[xperf] 查 memory leak 初步
這幾天在查程式的 memory leak,查的真是昏天暗地…
要嘛覺得是這邊,修改一下,沒什麼效果,
要嘛覺得有可能是這邊,可是邏輯上又看不出有什麼問題 Orz…
後來還是決定用 xperf 來看看能不能幫上忙…
最後是有幫上忙,但是在使用 xperf 時也遇到了不少問題,
一邊還要解決這些 xperf 的問題,真的是焦頭爛額…
不過幸好,最後「似乎是」有把 memory leak 找到原因了~
(說「似乎是」是因為…暫時的看不出來記憶體的持續增長狀況,
但你永遠不知道是不是還有潛藏的問題…
基本上,就先這樣吧~~ :P)
重點1:xperf 最好是使用在 Windows 7 以上
雖然微軟說你也可以把 xperf 的檔案複製到 Windows XP 上跑(參考這裡),
但最後分析還是要放在 Windows 7 的電腦上,
而且在 Windows 7 上資料收集可以比較集全,
因此,最好還是選擇在 Windows 7 上使用囉~~
反正現在虛擬機器很流行,這應該不是大問題~
重點2:xperf 不能與 SysInternal Process Explorer 一起跑
Process Explorer 是寫 Windows 程式的人很喜歡用的工具,
我也幾乎總是一直開著它,就算在家裡的電腦也會開來看看現在什麼 process 在跑 😛
但是!由於未知的原因(其實是我懶得查),
Process Explorer 在執行的時候,是無法用 xperf 來記錄事件的!
因此,想用 xperf 的時候,請乖乖的用回工作管理員吧~
接下來就看看要怎麼用 xperf 來查 memory leak 吧~
步驟1:首先開啟一些基本的 logging:
xperf -on Base -BufferSize 2048 -MinBuffers 10 -MaxBuffers 16 -f kernel.etl
步驟2:接下來,用下列的指令來讓 xperf 開始監視 pid 是 xxx 的 process
xperf -start HeapSession -heap -Pids xxx -BufferSize 2048 -MinBuffers 128 -MaxBuffers 128 -stackwalk HeapAlloc+HeapRealloc -f heap.etl
步驟3:開始做一些會讓受測的 process 產生 memory leak 的動作,
像我這次測的程式在平常放著不管它,或是複製檔案時都會有 leak(太慘了 =_=),
因此我就會測試這兩種情境~
步驟4:用下列的指令停止 xperf logging 並將 kernel.etl 和 heap.etl 合併:
xperf -stop -stop HeapSession xperf -merge kernel.etl heap.etl merge.etl
這邊要注意的是,合併完的檔案大小不一定會等於 kernel.etl 和 heap.etl 的相加大小,
不過應該至少要比它們兩個都大!
步驟5:產生 memory leak 分析報告,下面的指令是列出前 25 名最嚴重的部分:
xperf -i merge.etl -o stacks.txt -symbols verbose dbghelplog -a heap -stacks so -top 25
你可以把 -top 的值設比較大,不過相對應的處理時間也會變長…
處理完後的 stacks.txt 就會包含 memory leak 產生的地方的 stack,下面是一個例子:
Results for process yyy.exe (4716): --------------------------------------------------------------------- GLOBAL ALLOCATIONS: Alloc : 17727522, 4925268.6 KB Realloc : 169 Outstanding : 12828, 1529.1 KB --------------------------------------------------------------------- TOP 25: Alloc : 13988, 1644.1 KB Realloc : 0 Outstanding : 10117, 1271.2 KB --------------------------------------------------------------------- ntdll.dll! ?? ::FNODOBFM::`string' xxx.dll!calloc xxx.dll!_wcsdup xxx.dll!Function8 xxx.dll!Function7 xxx.dll!Function6 xxx.dll!Function5 xxx.dll!Function4 xxx.dll!Function3 xxx.dll!Function2 xxx.dll!Function1 kernel32.dll!BaseThreadInitThunk ntdll.dll!RtlUserThreadStart Alloc : 2264, 327.9 KB Realloc : 0 Outstanding : 2264, 327.9 KB
從上面的例子就可以看到,xxx.dll 裡面有個 memory leak,
接著只要再去相關的程式碼裡面看看就可以了~~
其實雖然這裡已經列出來函式名稱,不過並沒有參數內容,
因此有時候其實還是不容易看出來問題在哪裡,
必須仔細的 trace code 才行…
重點3:如果在 stacks.txt 裡面發現有 xxx.dll!? 這種字串的話,表示 symbol 不正確。
請將正確的 symbol file (*.pdb) 放在 symbol path 下面。
Symbol path 可以用 _NT_SYMBOL_PATH 這個環境變數來設定:
_NT_SYMBOL_PATH=C:\SymPrivate;SRV*C:\SymServer*http://msdl.microsoft.com/download/symbols
重點4:合併完的 merge.etl 如果太大的話,無法成功產生 memory leak 分析報告!
目前我還不知道上限是多少,或者跟電腦的記憶體大小有沒有關係,
像是在我的電腦上,大小在 7~8GB 的檔案都 OK,
但十幾GB的檔案幾乎都無法成功作出 stacks.txt…
因此要特別注意!!
其實這一點很麻煩,導致我必須一直觀察現在的檔案大小有沒有錄太大了…
因此後來寫了一個 AutoIt script 來輔助作這件事情…
下次再來分享囉~~ 🙂