読み直してみると、読みたくなくなる記事だなwwww
なので端的に…
1. Global Flagsを使って設定
2. WinDbgを実行
3. !heap -stat -h 0
4. プロセス実行
5. !heap -stat -h 0
6. 1.と3.の結果を比較
7.!heap -flt s サイズ
8.!heap -p -a アドレス
この8手順を踏めば、メモリリークの(おそらく)原因の場所が突き止められます。
さて読みにくい本編です。
外部DLLを使っているので、.NETでおきているのか、外部DLLでおきているのかさっぱり解らなかったが
このツールを使ってみると、なんとなーくみえてきた。
で、その使い方と注意事項をまとめてみました。
使ったツール:
WinDbg
Global Flags
ダウンロードファイル:(2012年5月現在)
winsdk_web.exe
URL:http://msdn.microsoft.com/ja-jp/windows/hardware/gg463009/
下の方に「Windows 7 バージョンの Debugging Tools for Windows をインストールする」って書いてある所の、「Microsoft Windows SDK for Windows 7 のダウンロード 」をクリックしてDLして下さいな。
ちなみに、WindowsXPが対象じゃない場合は「Windows 8 Consumer Preview バージョンの Debugging Tools for Windows をインストールする」こっちでも良いと思います。
詳しくはネットで調べてね♪
[WinDbg ダウンロード]とかで調べると出てくるはず。
(2016/08/31現在)
URL:https://developer.microsoft.com/ja-jp/windows/hardware/windows-driver-kit
で、ここからが本番。
WinDbg は、Exeの実行中を無理やりデバッグしたり、メモリリークを確認したり出来るの。
Global Flags は、OS側にデバッグする情報を作らせて保持するかどうかを変更できるの。
1. Global Flags でデバッグ情報をOSに作成させよう!
1.1. Global Flags を起動すると、英語のわけわからん画面が出る。
1.2. System Registry のタブはOSのデバッグ情報の作成を変更する。
Kernel Flags のタブはカーネルのデバッグ情報の作成を変更する。
Image File のタブは各実行ファイルごとのデバッグ情報の作成を変更する。
で、今回は、自分の作った(または関連した)ファイルの情報なので、「Image File」タブを選択します。
1.3. Image:(TAB to refresh) の右のテキストボックスに、実行ファイル名を入れます。
イメージって最初は???が浮いたけど、タスクマネージャのプロセスタブの一番左のところって「イメージ名」って書いてあるよね?なので、プロセスタブのイメージ名を記述することになります。
1.4. デバッグ情報に必要なものにチェックをつけます。(良く解ってません(笑)
私は下記にチェックをつけてます。
"Enable heap tail checking"
"Enable heap free checking"
"Enable heap parameter checking"
"Enable heap tagging"
"Create user mode stack trace database"
"Enable heap tagging by DLL"
ここで注意っ!
"Enable page heap" にチェックを入れると、WinDbg でデバッグ中にエラーになることがあります。
つけない方が無難です。
(エラー内容:"ReadMemory error for address eeddccee Use `!address eeddccee' to check validity of the address.")
例:
![](https://blogimg.goo.ne.jp/thumbnail/4b/b6/c8db0b48a1e4800eb06a605766f0045d_s.jpg)
ちなみに、ここで設定した内容は、イメージ(実行ファイル)を再起動しないと反映されないよ。
という事は、System とか Kernel を変更した場合は、OSの再起動をしないと反映されません。
2. WinDbg を使って、メモリーリークを確認しよう!
2.1. WinDbg を起動すると、また英語のわけわからん画面が出る。
2.2.1. まずは準備、「File」⇒「Symbol File Path(CLTR+S)」を開く
2.2.2. テキストボックスにコレを記述(シンボルデータのDL先のパスは、勝手にDLされる場所だから場所に気をつけよう)
「SRV*シンボルデータのDL先のパス*http://msdl.microsoft.com/download/symbols;」
で、自分の作成したEXEやDLLをデバッグしたい場合は、続きで"pdbファイルの存在するパス"を記述します。
例:SRV*C:\WINDOWS\symbols*http://msdl.microsoft.com/download/symbols;C:\project\bin\debug
(C:\WINDOWS\symbols は好きなパスに変えてね。)
とかです。場所が増える場合は、";"で区切って追加してください。
注意:pdbファイル って何? ⇒ ビルド時に作成されるデバッグ用ファイル。
メソッド名(関数名)などがこのファイルに記述されているようです。
その為、メモリリーク調査やデバッグが楽になる。
2.2.3. OKを押して確定させる。
2.3.1. 「File」⇒「Source File Path(CLTR+P)」を開く
2.3.2. 自分の作ったソースディレクトリのパスを記述する。
場所が増える場合は、";"で区切って追加してください。
2.3.3. OKを押して確定させる。
2.4.1. 「File」⇒「Image File Path(CLTR+I)」を開く
2.4.2. 自分の作った実行ファイルやDLLのパスを記述する。
場所が増える場合は、";"で区切って追加してください。
前記したが、イメージファイル = 実行ファイル(DLL)の事だよ
2.4.3. OKを押して確定させる。
例:
![](https://blogimg.goo.ne.jp/thumbnail/5f/b8/9f6716ab9a14026a5238f8115119ee0c_s.jpg)
シンボルパス例:
![](https://blogimg.goo.ne.jp/thumbnail/7c/40/4ff6cb4abde619e23dbcd8d0ece0ab96_s.jpg)
ソースパス例:
![](https://blogimg.goo.ne.jp/thumbnail/09/3b/b4a8b91bde2697b03d0f8b7e1087100a_s.jpg)
イメージパス例:
![](https://blogimg.goo.ne.jp/thumbnail/26/42/bec01a57c178c6dd0ad82b9fa9525e84_s.jpg)
ここまでで準備完了っ!
2.5. メモリリーク調査開始っ!
2.6.1. まずは、メモリリーク調査対象の実行ファイルを実行させます。
2.6.2. タスクマネージャのプロセスで、実行している事を確認する。+PIDを確認する。
ちなみに、「1.3.」で入力した名前とイメージ名が一致して無いと駄目だよ。
一致していない場合は、「1.3.」に戻りましょう。
2.6.3. 「File」⇒「Attach to a Process(F6)」を開く
2.6.4. アタッチ用ウィンドウが開くので、「2.6.2.」で調べたPIDを元に、選んでください。
OKボタンを押下すると、「プロセスが一時停止します!」気付かずに動いていると思わないで下さいね。
例:
![](https://blogimg.goo.ne.jp/thumbnail/0c/d1/0719d19ce4675ae127fc2b74a1c87fc6_s.jpg)
正しくアタッチが出来た例:
![](https://blogimg.goo.ne.jp/thumbnail/2e/d7/ee3cdbc51bbb31b5d315ba1641c2f661_s.jpg)
2.6.4. まずは、現在のメモリ使用量を調査
「View」⇒「Command(Alt+1)」を開く
コマンドウィンドウが開きますので、画面下のテキストボックスに「!heap -stat -h 0」を入力して実行する。
現在のメモリ使用の表が出力されるので、どっかに保存しておく。
2.6.5. 画面下のテキストボックスに「g」を入力して実行する。(F5でもOK)
gは止まっているプロセスを実行させます。テキストボックスが「Debuggee is running...」になるはずです。
2.6.6. しばらく実行したら、「Debug」⇒「Break(Ctrl+Break)」を開く
コレでまたプロセスが止まるので、テキストボックスに「!heap -stat -h 0」を入力して実行する。
現在のメモリ使用の表が出力されるので、どっかに保存しておく。
2.6.7. 2.6.4. と 2.6.6. で取得したデータの差分を取り、メモリが増えているものを探す。
コピーしたものの簡単な見方です。(数字は%を除き全て16進数です)
表の見方:
・サイズ ブロック - トータルサイズ (パーセント)
285c 51 - 15501c (98.88)
サイズは、1つのメモリーが使用しているサイズです。
ブロックは、同じメモリーサイズが何個(何箇所)使われているかです。
トータルサイズは、「サイズ×ブロック」です。
パーセントは、ヒープ領域内の使用割合です。
上記の例だと、0x285c Byte の領域が、 0x51箇所で参照されていて、15501c Byte 使われている。
となる。
で、このブロックがぶくぶく膨れ上がっているのがメモリリークの疑いがあるわけだ。
2.6.8. テキストボックスに「!heap -flt s 285c」を入力して実行する。
コレが、上記のサイズのデータはどこにありますカー?を調べるコマンドです。
0x51箇所にある場合は、0x51行表示されます。
例:
05756900 086f 0000 [07] 05756908 0435c - (busy)
2.6.9. テキストボックスに「!heap -p -a 05756900」を入力して実行する。
コレで、このメモリが使用されているスタックトレースが表示されるようになります。
例:
0:004> !heap -p -a 05756900
address 05756900 found in
_HEAP @ 05756900
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
05756900 086f 0000 [07] 05756908 0435c - (busy)
Trace: 19ed
7c9afbca ntdll!RtlDebugAllocateHeap+0x000000e1
7c98b244 ntdll!RtlAllocateHeapSlowly+0x00000044
7c959c0c ntdll!RtlAllocateHeap+0x00000e64
7c99571d ntdll!LdrpTagAllocateHeap+0x00000025
7c995b19 ntdll!LdrpTagAllocateHeap33+0x00000015
55d4048 test!CreateMemory+0x0001e8e8
で、このスタックトレースに知ってる名前があったらそこが原因。
上記例では test って言うEXE?(DLL?)の、CreateMemory って言う メソッド(関数)が保持してるメモリー領域が
たくさんあるよーってことになります。
そこを中心に流れを見れば解消するはずっ!!!
修正:
ちょっと画像を入れてみました。
なので端的に…
1. Global Flagsを使って設定
2. WinDbgを実行
3. !heap -stat -h 0
4. プロセス実行
5. !heap -stat -h 0
6. 1.と3.の結果を比較
7.!heap -flt s サイズ
8.!heap -p -a アドレス
この8手順を踏めば、メモリリークの(おそらく)原因の場所が突き止められます。
さて読みにくい本編です。
外部DLLを使っているので、.NETでおきているのか、外部DLLでおきているのかさっぱり解らなかったが
このツールを使ってみると、なんとなーくみえてきた。
で、その使い方と注意事項をまとめてみました。
使ったツール:
WinDbg
Global Flags
ダウンロードファイル:(2012年5月現在)
winsdk_web.exe
URL:http://msdn.microsoft.com/ja-jp/windows/hardware/gg463009/
下の方に「Windows 7 バージョンの Debugging Tools for Windows をインストールする」って書いてある所の、「Microsoft Windows SDK for Windows 7 のダウンロード 」をクリックしてDLして下さいな。
ちなみに、WindowsXPが対象じゃない場合は「Windows 8 Consumer Preview バージョンの Debugging Tools for Windows をインストールする」こっちでも良いと思います。
詳しくはネットで調べてね♪
[WinDbg ダウンロード]とかで調べると出てくるはず。
(2016/08/31現在)
URL:https://developer.microsoft.com/ja-jp/windows/hardware/windows-driver-kit
「デバッグ ツールの入手」の「Debugging Tools for Windows (WinDbg) を SDK から入手する」 で行ける
8.1 用の古い奴もあるけど、10 が7移行対応なので、最新で良いと思う
で、ここからが本番。
WinDbg は、Exeの実行中を無理やりデバッグしたり、メモリリークを確認したり出来るの。
Global Flags は、OS側にデバッグする情報を作らせて保持するかどうかを変更できるの。
1. Global Flags でデバッグ情報をOSに作成させよう!
1.1. Global Flags を起動すると、英語のわけわからん画面が出る。
1.2. System Registry のタブはOSのデバッグ情報の作成を変更する。
Kernel Flags のタブはカーネルのデバッグ情報の作成を変更する。
Image File のタブは各実行ファイルごとのデバッグ情報の作成を変更する。
で、今回は、自分の作った(または関連した)ファイルの情報なので、「Image File」タブを選択します。
1.3. Image:(TAB to refresh) の右のテキストボックスに、実行ファイル名を入れます。
イメージって最初は???が浮いたけど、タスクマネージャのプロセスタブの一番左のところって「イメージ名」って書いてあるよね?なので、プロセスタブのイメージ名を記述することになります。
1.4. デバッグ情報に必要なものにチェックをつけます。(良く解ってません(笑)
私は下記にチェックをつけてます。
"Enable heap tail checking"
"Enable heap free checking"
"Enable heap parameter checking"
"Enable heap tagging"
"Create user mode stack trace database"
"Enable heap tagging by DLL"
ここで注意っ!
"Enable page heap" にチェックを入れると、WinDbg でデバッグ中にエラーになることがあります。
つけない方が無難です。
(エラー内容:"ReadMemory error for address eeddccee Use `!address eeddccee' to check validity of the address.")
例:
![](https://blogimg.goo.ne.jp/thumbnail/4b/b6/c8db0b48a1e4800eb06a605766f0045d_s.jpg)
ちなみに、ここで設定した内容は、イメージ(実行ファイル)を再起動しないと反映されないよ。
という事は、System とか Kernel を変更した場合は、OSの再起動をしないと反映されません。
2. WinDbg を使って、メモリーリークを確認しよう!
2.1. WinDbg を起動すると、また英語のわけわからん画面が出る。
2.2.1. まずは準備、「File」⇒「Symbol File Path(CLTR+S)」を開く
2.2.2. テキストボックスにコレを記述(シンボルデータのDL先のパスは、勝手にDLされる場所だから場所に気をつけよう)
「SRV*シンボルデータのDL先のパス*http://msdl.microsoft.com/download/symbols;」
で、自分の作成したEXEやDLLをデバッグしたい場合は、続きで"pdbファイルの存在するパス"を記述します。
例:SRV*C:\WINDOWS\symbols*http://msdl.microsoft.com/download/symbols;C:\project\bin\debug
(C:\WINDOWS\symbols は好きなパスに変えてね。)
とかです。場所が増える場合は、";"で区切って追加してください。
注意:pdbファイル って何? ⇒ ビルド時に作成されるデバッグ用ファイル。
メソッド名(関数名)などがこのファイルに記述されているようです。
その為、メモリリーク調査やデバッグが楽になる。
2.2.3. OKを押して確定させる。
2.3.1. 「File」⇒「Source File Path(CLTR+P)」を開く
2.3.2. 自分の作ったソースディレクトリのパスを記述する。
場所が増える場合は、";"で区切って追加してください。
2.3.3. OKを押して確定させる。
2.4.1. 「File」⇒「Image File Path(CLTR+I)」を開く
2.4.2. 自分の作った実行ファイルやDLLのパスを記述する。
場所が増える場合は、";"で区切って追加してください。
前記したが、イメージファイル = 実行ファイル(DLL)の事だよ
2.4.3. OKを押して確定させる。
例:
![](https://blogimg.goo.ne.jp/thumbnail/5f/b8/9f6716ab9a14026a5238f8115119ee0c_s.jpg)
シンボルパス例:
![](https://blogimg.goo.ne.jp/thumbnail/7c/40/4ff6cb4abde619e23dbcd8d0ece0ab96_s.jpg)
ソースパス例:
![](https://blogimg.goo.ne.jp/thumbnail/09/3b/b4a8b91bde2697b03d0f8b7e1087100a_s.jpg)
イメージパス例:
![](https://blogimg.goo.ne.jp/thumbnail/26/42/bec01a57c178c6dd0ad82b9fa9525e84_s.jpg)
ここまでで準備完了っ!
2.5. メモリリーク調査開始っ!
2.6.1. まずは、メモリリーク調査対象の実行ファイルを実行させます。
2.6.2. タスクマネージャのプロセスで、実行している事を確認する。+PIDを確認する。
ちなみに、「1.3.」で入力した名前とイメージ名が一致して無いと駄目だよ。
一致していない場合は、「1.3.」に戻りましょう。
2.6.3. 「File」⇒「Attach to a Process(F6)」を開く
2.6.4. アタッチ用ウィンドウが開くので、「2.6.2.」で調べたPIDを元に、選んでください。
OKボタンを押下すると、「プロセスが一時停止します!」気付かずに動いていると思わないで下さいね。
例:
![](https://blogimg.goo.ne.jp/thumbnail/0c/d1/0719d19ce4675ae127fc2b74a1c87fc6_s.jpg)
正しくアタッチが出来た例:
![](https://blogimg.goo.ne.jp/thumbnail/2e/d7/ee3cdbc51bbb31b5d315ba1641c2f661_s.jpg)
2.6.4. まずは、現在のメモリ使用量を調査
「View」⇒「Command(Alt+1)」を開く
コマンドウィンドウが開きますので、画面下のテキストボックスに「!heap -stat -h 0」を入力して実行する。
現在のメモリ使用の表が出力されるので、どっかに保存しておく。
2.6.5. 画面下のテキストボックスに「g」を入力して実行する。(F5でもOK)
gは止まっているプロセスを実行させます。テキストボックスが「Debuggee is running...」になるはずです。
2.6.6. しばらく実行したら、「Debug」⇒「Break(Ctrl+Break)」を開く
コレでまたプロセスが止まるので、テキストボックスに「!heap -stat -h 0」を入力して実行する。
現在のメモリ使用の表が出力されるので、どっかに保存しておく。
2.6.7. 2.6.4. と 2.6.6. で取得したデータの差分を取り、メモリが増えているものを探す。
コピーしたものの簡単な見方です。(数字は%を除き全て16進数です)
表の見方:
・サイズ ブロック - トータルサイズ (パーセント)
285c 51 - 15501c (98.88)
サイズは、1つのメモリーが使用しているサイズです。
ブロックは、同じメモリーサイズが何個(何箇所)使われているかです。
トータルサイズは、「サイズ×ブロック」です。
パーセントは、ヒープ領域内の使用割合です。
上記の例だと、0x285c Byte の領域が、 0x51箇所で参照されていて、15501c Byte 使われている。
となる。
で、このブロックがぶくぶく膨れ上がっているのがメモリリークの疑いがあるわけだ。
2.6.8. テキストボックスに「!heap -flt s 285c」を入力して実行する。
コレが、上記のサイズのデータはどこにありますカー?を調べるコマンドです。
0x51箇所にある場合は、0x51行表示されます。
例:
05756900 086f 0000 [07] 05756908 0435c - (busy)
2.6.9. テキストボックスに「!heap -p -a 05756900」を入力して実行する。
コレで、このメモリが使用されているスタックトレースが表示されるようになります。
例:
0:004> !heap -p -a 05756900
address 05756900 found in
_HEAP @ 05756900
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
05756900 086f 0000 [07] 05756908 0435c - (busy)
Trace: 19ed
7c9afbca ntdll!RtlDebugAllocateHeap+0x000000e1
7c98b244 ntdll!RtlAllocateHeapSlowly+0x00000044
7c959c0c ntdll!RtlAllocateHeap+0x00000e64
7c99571d ntdll!LdrpTagAllocateHeap+0x00000025
7c995b19 ntdll!LdrpTagAllocateHeap33+0x00000015
55d4048 test!CreateMemory+0x0001e8e8
で、このスタックトレースに知ってる名前があったらそこが原因。
上記例では test って言うEXE?(DLL?)の、CreateMemory って言う メソッド(関数)が保持してるメモリー領域が
たくさんあるよーってことになります。
そこを中心に流れを見れば解消するはずっ!!!
修正:
ちょっと画像を入れてみました。