【デバドラ講座4(中級):NotMyFaultを使ってみよう②】
前回に引き続き、NotMyFaultを使ったデバッグを行っていきたいと思います。
1. メモリーリークを選択して仮想マシンのメモリをリークさせてみる。
NotMyFaultのLeakタブを選択し、秒単位でリークさせたいメモリ量、PagedかNonPagedのどちらかのメモリを指定してリークさせることができます。
今回は10000(KB)leak/sec.でNonpagedをリークしてみます。(図1)
(図1:NotMyFault_Leakタブ)
ちなみにタスクマネージャのパフォーマンスを開いてみるとメモリが解放されないため、どんどん領域が少なくなっていることがわかります(図2)。
メモリ使用量が最大になるとフリーズ、再起動がおこります。
(図2:タスクマネージャ)
3-1 「!poolused」コマンドを使ってみる。
このコマンドでNonpagedとPagedのメモリ割り当て量(Allcos)と使用量(Used)をみることができます。
Tag:Leakに着目してみると、Nopaged/Allcos:9・Used:9216000と、割り当て量に対してかなり大きなメモリが使われいていることがわかります。
0: kd> !poolused
Sorting by Tag
NonPaged Paged
Tag Allocs Used Allocs Used
…
LSnn 1 368 0 0 SMB2 netname , Binary: srv2.sys
LSwi 1 16520 0 0 SMB1 initial work context , Binary: srv.sys
LSwq 2 1824 0 0 SMB1 BlockTypeWorkQueue , Binary: srv.sys
LeGe 10 8512 0 0 Legacy Registry Mapping Module Buffers , Binary: tcpip.sys
Leak 9 9216000 0 0 UNKNOWN pooltag 'Leak', please update pooltag.txt
Lfs 0 0 11 5328 Lfs allocations
LfsI 1 24 0 0 Lfs allocations
LfsS 1 112 3 131528 UNKNOWN pooltag 'LfsS', please update pooltag.txt
Lfsr 1 69632 0 0 UNKNOWN pooltag 'Lfsr', please update pooltag.txt
LqWi 128 7168 0 0 UNKNOWN pooltag 'LqWi', please update pooltag.txt
…
メモリリークの場合、クラッシュ等が発生するわけではなく表面化しにくいため、先の2例と比べると解析が難しくなります。
メモリ関連で問題が発生していると分かった時の便利な手として、OSにプリインストールされている「DriverVerfier」というツールがあります。
このツールを使うことでメモリの確保状況の様子をダンプ解析することができます。
3-2 「DriverVerfier」を使ってデバッグしてみる。
このツールはpoolリークの検出などpool関連のデバッグに有効なツールです。
DriverVerifier を使用するには、Verifier.exe を実行してコンピュータを再起動します。
詳しい設定の仕方、デバッグ方法は下記Microsoftのブログに書いてあります(2018/12/6現在)
https://blogs.msdn.microsoft.com/jpwdkblog/2015/02/26/driver-verifier/
Verifierでmyfaultに対し標準設定を行った状態で16回/秒、10000KBリークさせてみます。
使うコマンドは「!verfier 3」です。この「3」は全てのドライバのメモリ関連のVerifier情報を表示します。きっかり16回10000KB_ずつリークされていることがわかります。
0: kd> !verifier 3 Pool Allocations: Address Length Tag Caller ---------- ---------- ---- ---------- 0xb1000000 0x009c5000 Leak *** ERROR: Module load completed but symbols could not be loaded for myfault.sys 0xa11f58a5 myfault+0x18a5 0xb0600000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xafc00000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xaf200000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xae800000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xade00000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xad400000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xaca00000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xac000000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xab600000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xaac00000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xaa200000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xa9800000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xa8e00000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xa8400000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5 0xa7a00000 0x009c5000 Leak 0xa11f58a5 myfault+0x18a5
確保されたそれぞれのNonpagedpoolのコールスタックを見ることもできます。16個確保されたうちの最後のアドレス0xb1000000のコールスタックを見てみましょう。
使うコマンドは「!verifier 80 0xb1000000」です。この「80」で対象アドレス(0xb1000000)のNonpagedpoolのコールスタックを下から順に見ていくことができます。
0: kd> !verifier 80 0xb1000000 Log of recent kernel pool Allocate and Free operations: There are up to 0x10000 entries in the log. Parsing 0x00010000 log entries, searching for address 0xb1000000. ====================================================================== Pool block b1000000, Size 009c4000, Thread 86ef16c0 816d5247 nt!XdvExAllocatePoolInternal+0x17 816cc19e nt!VerifierExAllocatePoolWithTag+0x24 a11f58a5 myfault+0x18a5 a11f5a4a myfault+0x1a4a 816ca903 nt!IovCallDriver+0x82 812a9772 nt!IofCallDriver+0x62 814b765e nt!IopSynchronousServiceTail+0x16e 814bb518 nt!IopXxxControlFile+0x3e8 814ba516 nt!NtDeviceIoControlFile+0x2a 8132de17 nt!KiSystemServicePostCall+0 Parsed entry 00010000/00010000... Finished parsing all pool tracking information.
このようにしてメモリーリークの場合はOSにプリインストールされている「verifier」をつかうことで、メモリーリークされている瞬間を確認することが有効です。
コマンド一覧
ここでこれまで使ったコマンドをまとめておきます。
基本的に「Ctrl+Break」でOSをストップさせてから適当なコマンドを打って操作し、「コマンド+Enter」でコマンドが実行されます。
「F1」キーを押すとWindbgのヘルプが表示されます。各コマンドにはオプションがついており、それも「F1」から検索が可能です。
・g OSをスタートさせます。F5キーも同じです。
・!analyze -v 自動解析の結果を表示します。
・!memusage フレーム事のメモリ量を表示します。
・!poolused NonpagedとPagedのメモリ割り当て量と使用量を表示します。
・!process 実行中のプロセスの一覧を表示します。
・!stackusage モジュール毎に使われたメモリ量と回数を表示します。
・!vm 仮想メモリの情報を表示します。
・!verfier メモリリークされたメモリ量や回数などの情報を表示します。
・kf フレーム毎によって使われたスタックメモリーサイズを表示します。
・kv スタックトレースを表示します。
・lm 実行中のモジュールを表示します。