アプリケーション検証ツール - アプリケーション検証ツール停止のデバッグ
デバッガーのインストールとセットアップ
一部のアプリケーション検証ツールアクションでは、例外が発生する可能性があります。 このような例外をキャッチするようにデバッガーを設定する必要があります。これはアプリケーション検証ツール自身が初回例外を処理するためです。
発生する例外は、次の3種類です。
ヒープオプションでヒープバッファーオーバーランが検出されると、アクセス違反例外 (0xC0000005) が生成されます。 場合によっては、システムパス使用法の確認オプションによってアクセス違反が発生することもあります。
Invalid handle usage オプションで無効なハンドル操作が検出されると、無効なハンドル例外 (0xC0000008) が生成されます。
スタックオーバーフロー例外 (0xC00000FD) は、十分なスタックオプションを確認すると、初期スタックが短すぎることが検出されたときに生成されます。
これらのイベントを準備する方法の1つは、次のようにコマンドラインでデバッガーを起動することです。
windbg -xd av -xd ch -xd sov ApplicationCommandLine
or
cdb -xd av -xd ch -xd sov ApplicationCommandLine
デバッガーを既に開始している場合は、sxd (Set Exceptions) コマンドを使用して、2回目の例外として、すべてのアクセス違反、無効なハンドル、およびスタックオーバーフローをキャッチできます。
0:000> sxd av
0:000> sxd ch
0:000> sxd sov 1
理論的には、カーネルデバッガーを使用してアプリケーション検証ツールを制御することができます。 ただし、これは推奨されていません。ページインコマンドを頻繁に使用する必要がありますが、ユーザーモードのデバッガーを使用する場合よりも電力は不要です。
デバッグツールのインストール
最新バージョンのツールをダウンロードするには、「 Windows 用デバッグツールのダウンロード」を参照してください。
User-Mode デバッグ用のハードウェアの構成
ユーザーモードのデバッグは、通常、1台のコンピューターで実行されます。デバッガーは、失敗したアプリケーションと同じコンピューター上で実行されます。
この場合、特定のハードウェアのセットアップは必要ありません。 このトピックでは、ホストコンピューターとターゲットコンピューターという用語は、この場合は交換可能です。
User-Mode デバッグ用のソフトウェアの構成
基本 User-Mode 構成-ユーザーモードのデバッグを開始する前に、必要なシンボルファイルをダウンロードし、特定の環境変数を設定する必要があります。
シンボル ファイル
デバッグされているユーザーモードプロセスのシンボルファイルをダウンロードする必要があります。 このアプリケーションを作成した場合は、完全なシンボルファイルを使用してビルドする必要があります。 商用アプリケーションの場合は、web サーバーまたはダウンロードのためにシンボルファイルが使用可能である可能性があります。製造元に問い合わせてください。
リモートデバッグを実行している場合、シンボルファイルの場所は、使用している方法によって異なります。
デバッガーを使用してリモートデバッグを実行している場合は、デバッグサーバーを使用しているコンピューターにシンボルファイルを配置する必要があります。
remote.exe を使用してリモートデバッグを実行している場合は、シンボルファイルがデバッガーを使用してコンピューター上にある必要があります。
プロセスサーバーまたは KD 接続サーバーを使用してリモートデバッグを実行している場合は、スマートクライアントのコンピューターにシンボルファイルを配置する必要があります。
ユーザーモードのデバッガーをカーネルデバッガーから制御している場合は、シンボルファイルが両方のコンピューターに存在する必要があります。
環境変数の構成
デバッガーでは、さまざまな環境変数を使用して、いくつかの重要な設定を示します。
デバッガーの詳細については、「はじめに Windows デバッグ」を参照してください。
コマンドラインを使用したデバッガーでのアプリケーション検証ツールの構成
アプリケーション検証ツールを構成するには、CDB または NTSD のコマンドラインを使用します。
次のコマンドラインを使用します。
cdb OtherOptions -vf:Flags Target
Target はターゲットアプリケーションの名前です。 Flags は、このターゲットに適用される目的のアプリケーション検証ツールオプションを指定します。
フラグは、必要なオプションを表すビットの合計でなければなりません。 個々のビット値は次のとおりです。
フラグ値 | 説明 |
---|---|
00000001 | ヒープチェック |
00000004 | ハンドルチェック |
00000008 | リソース SIM の低いチェック |
00000020 | TLS チェック |
00000040 | ダーティスタック |
00000200 | 危険な API |
00001000 | 例外チェック |
00002000 | メモリチェック |
00020000 | その他のチェック |
00040000 | ロックチェック |
! Avrf を使用したデバッグ
! Avrf 拡張機能はアプリケーション検証ツールの設定を制御し、アプリケーション検証ツールによって生成されるさまざまな出力を表示します。 ! Arvrf 拡張機能の詳細については、デバッガーのドキュメントの「 ! avrf 」を参照してください。
構文
!avrf
パラメーターを指定せずに! avrf コマンドを実行すると、アプリケーション検証ツールの設定と、現在と以前のアプリケーション検証ツールに関する情報 (存在する場合) が表示されます。
!avrf –vs { Length | -aAddress }
仮想領域操作ログを表示します。 Length には、最新のものから表示するレコードの数を指定します。 アドレス仮想アドレスを指定します。 この仮想アドレスを含む仮想操作のレコードが表示されます。
!avrf -hp { Length | -a Address }
ヒープ操作ログを表示します。 アドレスヒープアドレスを指定します。 このヒープアドレスを含むヒープ操作のレコードが表示されます。
!avrf -cs { Length | -a Address }
クリティカルセクションの [ログの削除] を表示します。 Length には、最新のものから表示するレコードの数を指定します。 アドレスクリティカルセクションアドレスを指定します。 アドレスが指定されている場合、特定のクリティカルセクションのレコードが表示されます。
!avrf -dlls [ Length ]
DLL の読み込み/アンロードログを表示します。 Length には、最新のものから表示するレコードの数を指定します。
!avrf -trm
終了したスレッドおよび中断されたスレッドのログを表示します。
!avrf -ex [ Length ]
例外ログを表示します。 アプリケーション検証ツールは、アプリケーションで発生しているすべての例外を追跡します。
!avrf -threads [ ThreadID ]
ターゲットプロセスのスレッドに関する情報を表示します。 子スレッドの場合、親によって指定されたスタックサイズと CreateThread フラグも表示されます。 スレッド ID を指定すると、その特定のスレッドの情報だけが表示されます。
!avrf -tp [ ThreadID ]
スレッドプールログを表示します。 このログには、スレッドの関係マスクの変更、スレッドの優先順位の変更、スレッドメッセージの送信、COM の初期化、およびスレッドプールのコールバック内からの COM の初期化解除など、さまざまな操作のスタックトレースが含まれている場合があります。 スレッド ID を指定すると、その特定のスレッドの情報だけが表示されます。
!avrf -srw [ Address | Address Length ] [ -stats ]
スリムリーダー/ライター (SRW) ログを表示します。 アドレスを指定すると、その SRW ロックアドレスに関連するレコードが表示されます。 [長さ] をアドレスと共に指定すると、そのアドレス範囲内のすべての SRW ロックが表示されます。 -Stats オプションを指定すると、SRW ロック統計がダンプされます。
!avrf -leak [ -m ModuleName ] [ -r ResourceType ] [ -a Address ] [ -t ]
未処理のリソース ログを表示します。 これらのリソースは、特定の時点でリークする場合と漏えいしない場合があります。 ModuleName (拡張機能を含む) を指定すると、指定したモジュール内のすべての未処理リソースが表示されます。 ResourceType を指定すると、その特定のリソースの種類の未処理のリソースが表示されます。 アドレスを指定すると、そのアドレスを持つ未処理のリソースのレコードがダンプされます。 ResourceType には、次のいずれかを指定できます。
- ヒープ: Win32 ヒープ API を使用してヒープ割り当てを表示します
- ローカル: ローカル/グローバル割り当てを表示します
- CRT: CRT API を使用して割り当てを表示します
- 仮想: 仮想予約を表示します
- BSTR: BSTR の割り当てを表示します
- レジストリ: レジストリ キーが開くと表示されます
- 電源: 電源通知オブジェクトを表示します
- ハンドル: スレッド、ファイル、およびイベント ハンドルの割り当てを表示します
!avrf –trace TraceIndex
指定したトレース インデックスのスタック トレースを表示します。 一部の構造体では、この 16 ビットインデックス番号を使用してスタック トレースが識別されます。 このインデックスは、スタック トレース データベース内の場所を示します。 このような構造を分析する場合は、この構文が役に立ちます。
!avrf -cnt
グローバル カウンターの一覧を表示します。
!avrf -brk [ BreakEventType ]
これが break-event コマンドを指定します。 を !avrf -brk
追加のパラメーターと一緒に使用すると、ブレーク イベントの設定が表示されます。 BreakEventType は、break イベントの型番号を指定します。 使用できる型の一覧については、 を使用します !avrf -brk
。
!avrf -flt [ EventTypeProbability ]
これがフォールト インジェクション コマンドを指定します。 を !avrf -flt
追加のパラメーターと一緒に使用すると、現在のフォールト インジェクション設定が表示されます。 EventType は、イベントの型番号を指定します。 確率は、イベントが失敗する頻度を指定します。 0 ~ 1,000,000 (0xF4240) の整数を指定できます。
!avrf -flt break EventType
このアプリケーション検証ツールが挿入されるたび、デバッガーに割り込みます。
!avrf -flt stacks Length
最新のエラー挿入操作のスタック トレースの長さの数を表示します。
!avrf -trg [ StartEnd | dll Module | all ]
これがターゲット範囲コマンドを指定します。 追加のパラメーターを指定しない -trg を使用すると、現在のターゲット範囲が表示されます。 Start は、ターゲット範囲または除外範囲の開始アドレスを指定します。 End は、ターゲット範囲または除外範囲の終了アドレスを指定します。 [モジュール] では、対象または除外するモジュールの名前を指定します。 モジュールには、完全なモジュール名を含める必要があります(.exeまたは.dllしてください。 パス情報は含めません。 [すべて] を指定すると、すべてのターゲット範囲または除外範囲がリセットされます。
!avrf -skp [ StartEnd | dll Module | all | Time ]
これが除外範囲コマンドを指定します。 Start は、ターゲット範囲または除外範囲の開始アドレスを指定します。 End は、ターゲット範囲または除外範囲の終了アドレスを指定します。 [モジュール] では、対象または除外するモジュールの名前を指定します。 モジュールには、完全なモジュール名を含める必要があります(.exeまたは.dllしてください。 パス情報は含めません。 [すべて] を指定すると、すべてのターゲット範囲または除外範囲がリセットされます。 [時間] を指定すると、実行が再開された後の時間 (ミリ秒) に対してすべてのエラーが抑制されます。
デバッガーの !avrf コマンドによって提供される出力を次に示します。
0:000> !avrf
Application verifier settings (816431A7):
- full page heap
- COM
- RPC
- Handles
- Locks
- Memory
- TLS
- Exceptions
- Threadpool
- Leak
- SRWLock
No verifier stop active.
Note: Sometimes bugs found by verifier manifest themselves as raised
exceptions (access violations, stack overflows, invalid handles),
and it is not always necessary to have a verifier stop.
!avrf 拡張機能のコメント
!avrf 拡張機能をパラメーターを指定して使用すると、現在のオプションがアプリケーション検証ツールされます。
!avrf 拡張機能では、デバッガーExts.dllを使用します。
Stop がアプリケーション検証ツール場合、パラメーターを指定した !avrf 拡張機能は、停止の性質と原因を明らかにします。
一部のntdll.dllのverifier.dllが見つからない場合、!avrf 拡張機能によってエラー メッセージが生成されます。
コンティニブルストップと非コンティニブル ストップ
コンティニブル ストップのデバッグ
[無効なハンドルの使用を検出する] オプションによって発生した無効なハンドル例外の例を次に示します。
最初に、次のメッセージが表示されます。
Invalid handle - code c0000008 (first chance)
===================================================
VERIFIER STOP 00000300: pid 0x558: invalid handle exception for current stack trace
C0000008 : Exception code.
0012FBF8 : Exception record. Use .exr to display it.
0012FC0C : Context record. Use .cxr to display it.
00000000 :
===================================================
This verifier stop is continuable.
After debugging it use 'go' to continue.
===================================================
Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=6a27c280 ecx=6a226447 edx=0012fa4c esi=00942528 edi=6a27c260
eip=6a22629c esp=0012facc ebp=0012faf0 iopl=0 nv up ei pl zr na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
6a22629c cc int 3
このメッセージには、Stop を継続できるアプリケーション検証ツール示されているのに注意してください。 何がトランスピラーされたのかを理解した後は、ターゲット アプリケーションの実行を続行できます。
まず、!avrf 拡張機能を使用する必要があります。 これにより、現在のエラーに関する情報が表示されます。
0:000> !avrf
Global flags: 00000100
Application verifier global flag is set.
Application verifier settings (00000004):
- no heap checking enabled!
- handle checks
Page heap is not active for this process.
Current stop 00000300 : c0000008 0012fbf8 0012fc0c 00000000 .
Using an invalid handle (either closed or simply bad).
この表示の最後の行は、問題をまとめたものです。
この時点で、いくつかのログを確認できます。 完了したら、g (Go) コマンドを使用してアプリケーションを再度起動します。
0:000> g
## Debugging a Non-Continuable Stop
Here is an example of an access violation that has been raised by the page heap option.
First, the following message appears:
Access violation - code c0000005 (first chance)
===================================================
VERIFIER STOP 00000008: pid 0x504: exception raised while verifying block header
00EC1000 : Heap handle
00F10FF8 : Heap block
00000000 : Block size
00000000 :
===================================================
This verifier stop is not continuable. Process will be terminated when you use the 'go' debugger command.
===================================================
Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=6a226447 edx=0012fab7 esi=00f10ff8 edi=00000008
eip=6a22629c esp=0012fb5c ebp=0012fb80 iopl=0 nv up ei pl zr na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
6a22629c cc int 3
この場合、この停止を続けアプリケーション検証ツールメッセージが表示されます。 このエラーは、プロセスが実行を続行するには深刻すぎるので、プロセスをアプリケーション検証ツールする方法はありません。
!avrf 拡張機能を使用して、現在のエラーに関する情報を提供できます。
0:000> !avrf
Global flags: 02000100
Application verifier global flag is set.
Page heap global flag is set.
Application verifier settings (00000001):
- full page heap
Page heaps active in the process (format: pageheap, lightheap, flags):
00941000 , 00a40000 , 3 (pageheap traces )
00b41000 , 00c40000 , 3 (pageheap traces )
00cb1000 , 00db0000 , 3 (pageheap traces )
00ec1000 , 00fc0000 , 3 (pageheap traces )
Current stop 00000008 : 00ec1000 00f10ff8 00000000 00000000 .
Corrupted heap block.
この表示の最後の行は、問題をまとめたものです。
この時点でいくつかのログを確認することもできます。 この時点で 、.restart (ターゲット アプリケーションの再起動) コマンドを使用できます。 または、アプリケーション セッションを終了しアプリケーション検証ツールコードのバグの修正を開始することもできます。
重大セクションエラーのデバッグ
!cs デバッガー拡張機能
!cs は、ユーザー モード デバッガーとカーネル デバッガーの両方で使用して、現在のプロセスの重要なセクションに関する情報を表示できます。 !cs 拡張機能の詳細については、デバッガードキュメント の!cs に関 するページを参照してください。
型情報を持つ一致するシンボルが必要です (特にntdll.dll。
この拡張機能の構文は次のとおりです。
!cs [-s] - 現在のプロセスのすべてのアクティブなクリティカル セクションをダンプします。
!cs [-s] address - このアドレスのダンプ クリティカル セクション。
!cs [-s] -d address - このアドレスの DebugInfo に対応するダンプ クリティカル セクション。
-s は、クリティカル セクション初期化スタック トレースが使用可能な場合はダンプします。
例 :
そのアドレスを使用してクリティカル セクションに関する情報をダンプする
0:001> ! cs 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
初期化スタック トレースなど、そのアドレスを使用してクリティカル セクションに関する情報をダンプする
0:001> !cs -s 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
デバッグ情報アドレスを使用してクリティカル セクションに関する情報をダンプする
0:001> !cs -d 0x6A262080
DebugInfo = 0x6A262080
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
デバッグ情報アドレスを使用してクリティカル セクションに関する情報をダンプします (初期化スタック トレースを含む)
0:001> !cs -s -d 0x6A262080
DebugInfo = 0x6A262080
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE
現在のプロセスのすべてのアクティブなクリティカル セクションに関するダンプ情報
0:001> !cs
-----------------------------------------
DebugInfo = 0x6A261D60
Critical section = 0x6A262820 (ntdll!RtlCriticalSectionLock+0x0)
LOCKED
LockCount = 0x0
OwningThread = 0x460
RecursionCount = 0x1
LockSemaphore = 0x0
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x6A261D80
Critical section = 0x6A262580 (ntdll!DeferedCriticalSection+0x0)
NOT LOCKED
LockSemaphore = 0x7FC
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x6A262600
Critical section = 0x6A26074C (ntdll!LoaderLock+0x0)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
.....
初期化スタック トレースを含む、現在のプロセスのすべてのアクティブなクリティカル セクションに関するダンプ情報
0:001> !cs -s
...
-----------------------------------------
DebugInfo = 0x6A261EA0
Critical section = 0xA8001C (+0xA8001C)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
No stack trace saved
-----------------------------------------
DebugInfo = 0x6A261EC0
Critical section = 0x6A263560 (ntdll!RtlpDphTargetDllsLock+0x0)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
No stack trace saved
-----------------------------------------
DebugInfo = 0x6A261EE0
Critical section = 0xA90608 (+0xA90608)
NOT LOCKED
LockSemaphore = 0x7EC
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A261EE0:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A20B0DC: ntdll!CsrpConnectToServer+0x1BE
0x6A20B2AA: ntdll!CsrClientConnectToServer+0x148
0x77DBE83F: KERNEL32!BaseDllInitialize+0x11F
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
-----------------------------------------
DebugInfo = 0x6A261F00
Critical section = 0x77E1AEB8 (KERNEL32!BaseDllRegistryCache+0x18)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A261F00:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
例外エラーのデバッグ
例外ログには、ターゲット プロセスで発生した例外すべてが記録されます。
!avrf -ex Length 拡張コマンドを使用して、最後のいくつかの例外を表示できます。Length は例外の数を指定します。 Length を省略すると、すべての例外が表示されます。
たとえば次のようになります。
0:000> !avrf -ex 4
=================================
Thread ID: 0000052c
Exception code: c0000008
Exception address: 6a226663
Exception record: 0012fb50
Context record: 0012fb64
Displayed 1 exception log entries.
デバッグによってエラーが処理される
!htrace は、ユーザー モード デバッガーとカーネル デバッガーの両方で使用して、プロセス内の 1 つ以上のハンドルのスタック トレース情報を表示できます。 この情報は、プロセスでハンドル トレースが有効になっている場合に使用できます。アプリケーション検証ツールでハンドル チェックが有効になっている場合は、自動的に有効になります。 スタック トレースは、プロセスがハンドルを開く、閉じる、または無効なハンドルを参照している場合に毎回保存されます。 !htrace 拡張機能の詳細については、デバッガードキュメント の!htrace に関する ページを参照してください。
この拡張機能のカーネル デバッガー構文は次のとおりです。
!htrace [ handle [process] ]
handle が指定されていない場合、または が 0 の場合、プロセス内のすべてのハンドルに関する情報が表示されます。 process が指定されていない場合は、現在のプロセスが使用されます。
ユーザー モード デバッガーの構文は次のとおりです。
!htrace [handle]
ユーザー モード デバッガー拡張機能では、常に現在のデバッグ先プロセスに関する情報が表示されます。
例 :
プロセス 815328b0 のハンドル 7CC に関するダンプ情報
kd> !htrace 7CC 815328b0
Loaded \\...\kdexts extension DLL
Process 0x815328B0
ObjectTable 0xE15ECBB8
--------------------------------------
Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6
--------------------------------------
Handle 0x7CC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3180: ntoskrnl!ObpCreateHandle+0x304
0x801E1563: ntoskrnl!ObOpenObjectByName+0x1E9
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6
--------------------------------------
Parsed 0x1CA stack traces.
Dumped 0x2 stack traces.
プロセス 815328b0 のすべてのハンドルに関するダンプ情報
kd> !htrace 0 81400300
Process 0x81400300
ObjectTable 0xE10CCF60
--------------------------------------
Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7CC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE23B2: KERNEL32!CreateSemaphoreA+0x66
0x010011C5: badhandle!main+0x45
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - BAD REFERENCE:
0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Parsed 0x6 stack traces.
Dumped 0x5 stack traces.
現在のプロセスのハンドル 7DC に関するダンプ情報
kd> !htrace 7DC
Process 0x81400300
ObjectTable 0xE10CCF60
--------------------------------------
Handle 0x7DC - BAD REFERENCE:
0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Parsed 0x6 stack traces.
Dumped 0x3 stack traces.
ヒープ エラーのデバッグ
ヒープ検証ツール デバッガー拡張機能
ヒープ検証ツール デバッガー拡張機能は、!heap 拡張機能 (NT ヒープ デバッガー拡張機能) の一部です。 !heap -? を使用すると、簡単なヘルプを取得できます。 または!heap -p -? を使用して、より広範な . ページ ヒープがプロセスに対して有効になっている場合、現在の拡張機能は独自に検出し、対応する動作を行います。 現在、拡張機能のユーザーは、ページ ヒープが有効になっていると知り、!heap -p というプレフィックスが付くコマンドを使用する必要があります。 !htrace 拡張機能の詳細については、デバッガードキュメントの !heap に関するページを参照してください。
!heap -p
プロセスで作成されたページ全体ヒープのアドレスをダンプします。
!heap -p -h ADDRESS-OF-HEAP
ADDRESS-OF-HEAP でのページ全体ヒープの完全ダンプ。
!heap -p -a ADDRESS
ADDRESS にヒープ ブロックが設定されている場合は、その確認を試みようとします。 この値は、ブロックの開始のアドレスである必要があります。 コマンドは、メモリ領域の性質に関する手掛かりがない場合に便利です。
ヒープ操作ログ
ヒープ操作ログは、すべてのヒープ ルーチンを追跡します。 これには、HeapAlloc、HeapReAlloc、HeapFree が含まれます。
拡張コマンドを使用して !avrf -hp Length
、最後のいくつかのレコードを表示できます。Length は、レコードの数を指定します。
を使用すると、 !avrf -hp -a Address
指定したアドレスに影響を与えたすべてのヒープ領域操作を表示できます。 割り当て操作では、割り当てられたヒープ ブロックにアドレスを含め、十分です。 空き操作の場合は、ブロックの先頭の正確なアドレスを指定する必要があります。
ログ内のエントリごとに、次の情報が表示されます。
- 呼び出されたヒープ関数。
- ルーチンを呼び出したスレッドのスレッド ID。
- 呼び出しに関連するアドレスです。これは、割り当てルーチンによって返されたアドレス、または空きルーチンに渡されたアドレスです。
- 呼び出しに関連するリージョンのサイズ。
- 呼び出しのスタック トレース。
最新のエントリが最初に表示されます。
この例では、最新の 2 つのエントリが表示されます。
0:001> !avrf -hp 2
alloc (tid: 0xFF4):
address: 00ea2fd0
size: 00001030
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00404ff3: Prymes!_stbuf+0xC3
00401c23: Prymes!printf+0x43
00401109: Prymes!main+0xC9
00402039: Prymes!mainCRTStartup+0xE9
77e7a278: kernel32!BaseProcessStart+0x23
alloc (tid: 0xFF4):
address: 00ea07d0
size: 00000830
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00403225: Prymes!_calloc_dbg+0x25
00401ad5: Prymes!__initstdio+0x45
00401f38: Prymes!_initterm+0x18
00401da1: Prymes!_cinit+0x21
00402014: Prymes!mainCRTStartup+0xC4
77e7a278: kernel32!BaseProcessStart+0x23
一般的なデバッグ シナリオ
発生する可能性があるいくつかの障害シナリオがあります。 一部の人は、全体像を取得するためにかなりの検出作業を必要とします。
アクセスできないページのアクセス違反
これは、テスト済みアプリケーションがバッファーの末尾を超えてアクセスする場合に、ページ全体のヒープが有効になっている場合に発生します。 解放されたブロックに触れた場合にも発生する可能性があります。 例外が発生したアドレスの性質を理解するには、次を使用する必要があります。
!heap –p –a ADDRESS-OF-AV
破損したブロック メッセージ
割り当ての有効期間 (割り当て、ユーザーフリー、実際の空き時間) の間に、ページ ヒープ マネージャーは、ブロックにすべての塗りつぶしパターンがそのまま含まれており、ブロック ヘッダーに一貫性のあるデータが含まれています。 そうではない場合は、検証ツールの停止が表示されます。
ブロックがページ全体ヒープ ブロックである場合 (たとえば、すべての割り当てに対してページ全体のヒープが有効になっていることを確認している場合)、"!heap –p –a ADDRESS" を使用して、ブロックの特性を確認できます。
ブロックがライト ページ ヒープ ブロックの場合は、ブロック ヘッダーの開始アドレスを見つける必要があります。 開始アドレスを見つけるには、報告されたアドレスの下に 30 ~ 40 バイトをダンプし、ブロック ヘッダーのマジック の開始/終了パターン (ABCDAAAA、ABCDBBBB、ABCDAAA9、ABCDBBBA) を探します。
ヘッダーは、エラーを理解するために必要なすべての情報を提供します。 特に、マジック パターンは、ブロックがライト ページ ヒープまたはフル ページ ヒープ ブロックの場合に、ブロックが割り当てられているか解放されるかを示します。 ここでの情報は、問題のある呼び出しと慎重に一致する必要があります。
たとえば、ブロックのアドレスに 4 バイトを加えた HeapFree の呼び出しが行われた場合、破損したメッセージが表示されます。 ブロック ヘッダーは問題ありませんが、ヘッダーの末尾の後の最初のバイト (0xDCBAXXXX マジック値の後の最初のバイト) のアドレスが異なる場合は、呼び出しのアドレスが異なっている必要があります。
特殊な塗りつぶしポインター
ページ ヒープ マネージャーは、カーネル ポインターとして表示される値でユーザー割り当てを設定します。 これは、ブロックが解放され (塗りつぶしの値が F0)、ブロックが割り当てられるがブロックがゼロに対して要求が行われた場合に発生します (塗りつぶしの値は、ライト ページ ヒープの場合は E0、フル ページ ヒープの場合は C0 です)。 0 以外の割り当ては、malloc/new ユーザーに対して一般的です。 F0F0F0F0F0、E0E0E0E0、C0C0C0C0C0 のようなアドレスで読み取り/書き込みを試行するエラー (アクセス違反) がある場合は、ほとんどの場合、これらのケースのいずれかをヒットします。
F0F0F0F0 での読み取り/書き込みは、ブロックが解放された後に使用されたブロックを意味します。 残念ながら、これを引き起こしたブロックを特定するには、いくつかの検出作業が必要です。 エラーのスタック トレースを取得し、スタック上の関数のコードを検査する必要があります。 そのうちの 1 つは、割り当てが生きていると間違った想定を行う可能性があります。
E0E0E0E0/C0C0C0C0 での読み取り/書き込みは、アプリケーションが割り当てを正しく初期化しなかったことを意味します。 これには、現在のスタック トレース内の関数のコード検査も必要です。 この種のエラーの例を次に示します。 テスト プロセスで、アドレス E0E0E0E0 で HeapFree を実行している間にアクセス違反が検出されました。 テストで 構造体が割り当てられたので、正しく初期化されていないので、 オブジェクトのデストラクターが呼び出されました。 特定のフィールドが null で (E0E0E0E0E0 を含む) ので、そのフィールドで delete と呼ばされました。
ページ ヒープの技術的な詳細
ヒープの破損 (オーバーフローまたはアンダーフロー) を検出するために、AppVerifier は、要求されたメモリを書き込み不可の完全なページで埋め込むか、割り当てられたメモリの前と後に特殊なタグを使用して、メモリの割り当て方法を変更します。 AppVerifier は、検証中のプロセスに Verifier.dll を読み込み、アプリケーションによって呼び出された Win32 ヒープ API の一部を対応する Verifier.dll API にリダイレクトすることで、これを行います。
要求されたメモリを書き込み不可の完全なページで埋め込む場合 (FULL 設定はページ ヒープのプロパティ セクションで有効で、既定の設定です)、AppVerifier は大量の仮想メモリを消費しますが、オーバーフローまたはアンダーフローが発生するとヒープ破損イベントがリアルタイムでキャッシュされるという利点があります。 このモードのメモリは、[AppVerifier Read-Only Heap Page (4k)] [テスト中のアプリケーションによって要求されたメモリの量] またはこの [テスト中のアプリケーションによって要求されたメモリの量] [AppVerifier Read-Only Heap Page (4k)] のいずれかになります。
ヒープ チェックでは、Backward プロパティに応じて、割り当ての先頭または末尾にガード ページが配置されます。 Backward が False (既定値) に設定されている場合は、割り当ての最後にガード ページが配置され、バッファー オーバーランがキャッチされます。 True に設定されている場合、ガード ページは割り当ての先頭に配置され、バッファーアンダーランをキャッチします。
要求されたメモリに特殊なタグを埋め込む場合 (ヒープ プロパティの [Full]チェック ボックス項目をオフにすることで有効)、AppVerifier は、このメモリが解放された時点でチェックとアラートを生成します。 この手法を使用する際の主な問題は、メモリが解放された場合にのみメモリの破損が検出される場合 (メモリ ブロックの最小量が 8 バイト)、3 バイトの変数または 5 バイトのオーバーフローが発生した場合、すぐには検出されない場合があります。
アンダーフロー イベントでは、新しいページへの書き込Read-Onlyされます。 これにより、例外がトリガーされます。 この例外は、ターゲット アプリケーションがデバッガーで実行されている場合にのみキャッチできます。 また、ページ全体のヒープ モードでは、パディング +ガード ページが使用され、これらのエラーも検出されます。 ライト ページ ヒープを使用する理由は、コンピューターがフル ページ ヒープの高メモリ制約を許容できない場合です。
メモリ集中型アプリケーションの場合、または長時間 (ストレス テストなど) に AppVerifier を使用する必要がある場合は、パフォーマンスの低下により、フル モードではなく通常の (ライト) ヒープ テストを実行する方が適切です。 ただし、問題が発生した場合は、ページ全体のヒープを有効にし、さらに調査します。
カスタム ヒープ (オペレーティング システムのヒープの実装をバイパスするヒープ) を使用しているアプリケーションは、ページ ヒープを使用する利点を完全に得ない場合や、有効にした場合に誤動作する可能性があります。
メモリ エラーのデバッグ
メモリ検証ツール デバッガー拡張機能
仮想空間操作ログは、プロセスの仮想空間を変更するルーチンを追跡します。 これには、VirtualAlloc、VirtualFree、MapViewOfFile、UnmapViewOfFile が含まれます。
拡張コマンドを使用して !avrf -vs Length
、最後のいくつかのレコードを表示できます。Length は、レコードの数を指定します。
!avrf -vs -a Address を使用すると、指定したアドレスに影響を与えたすべての仮想空間操作を表示できます。 割り当てでは、割り当てられたブロックにアドレスを含め、十分です。 無料では、リージョンの先頭の正確なアドレスを指定する必要があります。
ログ内のエントリごとに、次の情報が表示されます。
- という関数
- ルーチンを呼び出したスレッドのスレッド ID
- 呼び出しに関連するアドレス 。 これは、割り当てルーチンによって返されたアドレス、または空きルーチンに渡されたアドレスです。
- 呼び出しに関係するリージョンのサイズ
- メモリ操作の種類 (AllocationType パラメーター)
- 要求された保護の種類
- 呼び出しのスタックトレース
例
最新のエントリが最初に表示されます。
次の例では、2つの最新のエントリが表示されています。
0:001> !avrf -vs 2
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef6525: mshtml+0x116525
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00001000 op:4000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef65ae: mshtml+0x1165AE
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
これは、スレッド0xB4 が最初にページをデコミットした後、仮想領域全体を解放したことを出力から確認できます。
次に、アドレス0x4BB1000 に影響するすべての操作の表示を示します。
0:001> !avrf -vs -a 4bb1000
Searching in vspace log for address 04bb1000 ...
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef6525: mshtml+0x116525
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualFree (tid: 0xB4): addr:04bb1000 sz:00001000 op:4000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef65ae: mshtml+0x1165AE
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00010000 op:1000 prot:4
00aa1ac2: verifier!VsLogCall+0x42
00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
68925ca3: kernel32!VirtualAllocEx+0x61
68926105: kernel32!VirtualAlloc+0x16
75ef63f3: mshtml+0x1163F3
VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00400000 op:2000 prot:4
00aa1ac2: verifier!VsLogCall+0x42
00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
68925ca3: kernel32!VirtualAllocEx+0x61
68926105: kernel32!VirtualAlloc+0x16
75ef63d9: mshtml+0x1163D9
この出力を読み取るには、エントリが最新のものから順にダンプされていることに注意してください。 したがって、このログは、スレッド0xB4 がページをコミットした大きな領域を割り当てたことを示しています。 後でページをデコミットし、仮想領域全体を解放します。