[事前準備]
システムの環境変数にシンボルファイルのパスを指定しておく
変数名: _NT_SYMBOL_PATH
変数値: C:\MySymbols;srv*C:\MSSymbols*http://msdl.microsoft.com/download/symbols
⇒
UMDHが参照するシンボルファイルの置き場所。
ロードされたモジュールに対するシンボルファイルを上記パスの左から順番に探索してゆく。
区切り文字は";"。
上記例だと、以下の流れでシンボルが探索されてゆく。
1. 「C:\MySymbols」配下からシンボルファイルを探索
※自作モジュールのシンボル置き場
2. 1で見つからなければ、「C:\MSSymbols」配下から探索
※System32配下やCLRなどMS製モジュールのシンボル置き場
※「http://msdl.microsoft.com/download/symbols」はシンボルサーバーのパス
※MSのシンボルサーバーから自動でDLされたシンボルが配置されてゆく
シンボルファイルの置き場所は任意。
「C:\MySymbols」については、自作ソフトのインストール先(exeと同場所)にシンボルファイルが存在するならそこを直接指定するなど適宜アレンジする。
[対象のアプリ] "Leak.exe"とする。
while(true) { std::cin >> input; for (int index = 0; index < 10; index++) { BSTR bstrData = ::SysAllocString(_T("漏れ漏れ漏れ漏れ漏れ")); //. ::SysFreeString(bstrData); } ・・・・・・1回の試行でBSTRを10個分リークさせる。
[UMDHでダンプを取得するためのバッチ]
cd C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x86 gflags.exe -i Leak.exe +ust pause umdh.exe -pn:Leak.exe -f:C:\MemoryLeak\Start-Dump.txt echo Please do target operation pause umdh.exe -pn:Leak.exe -f:C:\MemoryLeak\Finish-Dump.txt umdh.exe -d C:\MemoryLeak\Start-Dump.txt C:\MemoryLeak\Finish-Dump.txt -f:C:\MemoryLeak\Result.txt gflags.exe -i Leak.exe -ust
[ダンプ取得手順]
1. 上記バッチファイルを叩く。
⇒Leak.exeに対してustのフラグが立つ。
2. Leak.exeを起動。
3. 1回、リークを発生させる操作を実施。
⇒BSTRが10個分リークする。(★1)
3. 上記バッチファイルでENTER。
⇒この時点のメモリのスナップショット(Start-Dump.txt)が生成される。
4. 1回、リークを発生させる操作を実施。
⇒BSTRがさらに10個分リークする。
5. 上記バッチファイルでENTER。
⇒この時点のメモリのスナップショット(Finish-Dump.txt)が生成される。
⇒1回目と2回目のスナップショットの差分が抽出される(Result.txt)。
⇒Leak.exeに対してustのフラグが落ちる。(★2)
(★1)
初回実行時にのみ実施される初期化処理などが存在すると、
これらの処理でアロケートされたオブジェクトが差分として抽出されてしまう。
このようなノイズを除外するため、何度が該当操作を行った上で、
スナップショットを取得した方が良い。
(★2)
gflagsで立てるフラグ値はレジストリに格納されるが、これらはOS再起動後も初期化されない。
解析が終わったら、フラグ値を元に戻しておくのがお作法。
[結果] "Result.txt"
// Debug library initialized ...
70000-92FFF DBGHELP: Leak - private symbols & lines
C:\MySymbols\Leak.pdb
779C0000-77B38FFF DBGHELP: ntdll - public symbols
C:\MSSymbols\wntdll.pdb\F42E56BB23DF4C2A9CAA683DA90E032F1\wntdll.pdb
・・・
+ 320 ( 640 - 320) 20 allocs BackTrace411B170
+ 10 ( 20 - 10) BackTrace411B170 allocations
verifier!AVrfDebugPageHeapAllocate+23C
ntdll!RtlDebugAllocateHeap+3C
ntdll!RtlpAllocateHeap+4CFDE
ntdll!RtlpAllocateHeapInternal+146
ntdll!RtlAllocateHeap+28
combase!CRetailMalloc_Alloc+16 (d:\th\com\combase\class\memapi.cxx, 641)
OLEAUT32!APP_DATA::AllocCachedMem+7A
OLEAUT32!SysAllocString+59
Leak!wmain+77 (c:\dev\sampleproject\trunk\consoleapplication1\consoleapplication1\leak.cpp, 20)
Leak!__tmainCRTStartup+199 (f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c, 623)
Leak!wmainCRTStartup+D (f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c, 466)
KERNEL32!BaseThreadInitThunk+24
ntdll!__RtlUserThreadStart+2F
ntdll!_RtlUserThreadStart+1B
1つ1つ見てゆくと、
70000-92FFF DBGHELP: Leak - private symbols & lines
C:\MySymbols\Leak.pdb
⇒
適切に本アプリのシンボルが読み込まれている事が分かる。
仮にシンボルが見つからない場合は、"exports・・"や"no symbols・・"のようなメッセージが出力されるため、シンボルが揃っているか、或はビルド時のシンボルと合っているか再確認。
+ 320 ( 640 - 320) 20 allocs BackTrace411B170
+ 10 ( 20 - 10) BackTrace411B170 allocations
⇒
1回目のスナップショット取得時 : 10個で計320Byte
2回目のスナップショット取得時 : 20個で計640Byte
差分として10個で計320Byteのリークが発生している。
OLEAUT32!SysAllocString+59
Leak!wmain+77 (c:\dev\sampleproject\trunk\consoleapplication1\consoleapplication1\leak.cpp, 20)
⇒
BSTRのSysAllocStringのソースコードの行番号まで出力されている。
[ちなみにシンボルが存在しないと・・・]
// Debug library initialized ...
AD0000-AF2FFF DBGHELP: Leak - no symbols loaded
779C0000-77B38FFF DBGHELP: ntdll - public symbols
C:\MSSymbols\wntdll.pdb\F42E56BB23DF4C2A9CAA683DA90E032F1\wntdll.pdb
・・・
+ 320 ( 640 - 320) 20 allocs BackTrace431ACF8
+ 10 ( 20 - 10) BackTrace431ACF8 allocations
verifier!AVrfDebugPageHeapAllocate+23C
ntdll!RtlDebugAllocateHeap+3C
ntdll!RtlpAllocateHeap+4CFDE
ntdll!RtlpAllocateHeapInternal+146
ntdll!RtlAllocateHeap+28
combase!CRetailMalloc_Alloc+16 (d:\th\com\combase\class\memapi.cxx, 641)
OLEAUT32!APP_DATA::AllocCachedMem+7A
OLEAUT32!SysAllocString+59
Leak!???+0 : AE5327
Leak!???+0 : AE5C89
Leak!???+0 : AE5E7D
KERNEL32!BaseThreadInitThunk+24
ntdll!__RtlUserThreadStart+2F
ntdll!_RtlUserThreadStart+1B
0 件のコメント:
コメントを投稿