アプリケーション検証ツール - アプリケーション検証ツール停止のデバッグ

デバッガーのインストールとセットアップ

一部のアプリケーション検証ツール アクションでは、例外が発生する可能性があります。 アプリケーション検証ツール自体が最初の機会の例外を処理するため、2 回目の機会にこれらの例外をキャッチするようにデバッガーを設定する必要があります。

発生する例外には、次の3つのタイプがあります。

  • ヒープ オプションがヒープ バッファー オーバーランを検出すると、アクセス違反例外 (0xC0000005) が生成されます。 場合によっては、[システム パスの使用を確認する] オプションによって、アクセス違反が発生する場合もあります。

  • 無効なハンドルの使用を検出オプションが無効なハンドル操作を検出すると、無効なハンドル例外 (0xC0000008) が生成されます。

  • スタック オーバーフロー例外 (0xC00000FD) は、[適切なスタックのチェック] オプションで初期スタックが短すぎることが検出されると生成されます。

これらのイベントに備える 1 つの方法は、次のようにコマンド ラインでデバッガーを起動することです。

windbg -xd av -xd ch -xd sov ApplicationCommandLine

または

cdb -xd av -xd ch -xd sov ApplicationCommandLine

デバッガーを既に起動している場合は、sxd (例外の設定) コマンドを使用して、すべてのアクセス違反、無効なハンドル、スタック オーバーフローを秒次例外としてキャッチできます。

0:000> sxd av 

0:000> sxd ch 

0:000> sxd sov 1

理論的には、カーネル デバッガーを使用してアプリケーション検証ツールを制御できます。 ただし、これは推奨されません。.process コマンドと .pagein コマンドを頻繁に使用する必要があります。ただし、ユーザー モード デバッガーを使用するよりも強力ではありません。

デバッグ ツールのインストール

最新バージョンのツールをダウンロードするには、「Windows 用デバッグ ツールのダウンロード」を参照してください。

ユーザー モード デバッグ用のハードウェアの構成

ユーザー モード デバッグは、通常、1 台のコンピューターで実行されます。デバッガーは、失敗したアプリケーションと同じコンピューター上で実行されます。

この場合、特定のハードウェアセットアップは必要ありません。 このトピックでは、ホスト コンピューターとターゲット コンピューターという用語を交換できます。

ユーザー モード デバッグ用のソフトウェアの構成

基本的なユーザー モード構成 - ユーザー モードのデバッグを開始する前に、必要なシンボル ファイルをダウンロードし、特定の環境変数を設定する必要があります。

シンボル ファイル

デバッグ中のユーザー モード プロセスのシンボル ファイルをダウンロードする必要があります。 作成したアプリケーションの場合は、完全なシンボル ファイルを使用してビルドする必要があります。 商用アプリケーションの場合は、シンボル ファイルが Web サーバーで入手できる場合やダウンロードできる場合は、製造元にお問い合わせください。

リモート デバッグを実行している場合、シンボル ファイルの場所は、使用している方法によって異なります。

  • デバッガーを使用してリモート デバッグを実行する場合、シンボル ファイルは、デバッグ サーバーがあるコンピューター上にある必要があります。

  • remote.exe を使用してリモート デバッグを実行する場合、シンボル ファイルは、デバッガーがあるコンピューター上にある必要があります。

  • プロセス サーバーまたは KD 接続サーバーを介してリモート デバッグを実行する場合は、シンボル ファイルがスマート クライアントを使用するコンピューター上にある必要があります。

  • カーネル デバッガーからユーザー モード デバッガーを制御する場合は、シンボル ファイルが両方のコンピューター上にある必要があります。

環境変数の設定

デバッガーでは、さまざまな環境変数を使用して、いくつかの重要な設定を示します。

デバッガーの詳細は、「Windows のデバッグの概要」をご覧ください。

コマンド ラインを使用したデバッガーでのアプリケーション検証ツールの構成

アプリケーション検証ツールを構成するには、CDB または NTSD コマンド ラインを使用します。

次のコマンド ラインを使用します。

cdb OtherOptions -vf:Flags Target

ターゲットはターゲット アプリケーションの名前で、フラグは、このターゲットに適用する目的のアプリケーション検証ツールオプションを指定します。

フラグは、必要なオプションを表すビットの合計である必要があります。 個々のビット値は次のとおりです。

フラグの値 意味
00000001 HEAP チェック
00000004 チェックの処理
00000008 リソースが少ない SIM チェック
00000020 TLS チェック
00000040 ダーティ スタック
00000200 危険なAPI
00001000 例外チェック
00002000 メモリの確認
00020000 その他の検査
00040000 LOCK チェック

!avrf を使用したデバッグ

!avrf拡張機能は、アプリケーション検証ツールの設定を制御し、アプリケーション検証ツールによって生成されたさまざまな出力を表示します。 !arvrf 拡張機能の詳細については、デバッガードキュメントの !avrf を参照してください。

構文

!avrf

パラメーターを指定しない !avrf コマンドは、アプリケーション検証ツールの設定と、現在および以前のアプリケーション検証ツールに関する情報が表示されます (存在する場合)。

!avrf –vs { Length | -aAddress }

仮想領域操作ログを表示します。 長さは、最新のものから順に表示するレコードの数を指定します。 アドレスは仮想アドレスを指定します。 この仮想アドレスを含む仮想操作のレコードが表示されます。

!avrf -hp { Length | -a Address }

ヒープ操作ログを表示します。 アドレスはヒープアドレスを指定します。 このヒープ アドレスを含むヒープ操作のレコードが表示されます。

!avrf -cs { Length | -a Address }

クリティカル セクションの削除ログを表示します。 長さは、最新のものから順に表示するレコードの数を指定します。 アドレスはクリティカルセクションのアドレスを指定します。 アドレスを指定すると、特定のクリティカル セクションのレコードが表示されます。

!avrf -dlls [ Length ]

DLLのロード/アンロードログを表示します。 長さは、最新のものから順に表示するレコードの数を指定します。

!avrf -trm

終了したすべてのスレッドと中断されたスレッドのログを表示します。

!avrf -ex [ Length ]

例外ログを表示します。 アプリケーション検証ツールは、アプリケーションで発生するすべての例外を追跡します。

!avrf -threads [ ThreadID ]

対象プロセスのスレッドに関する情報を表示します。 子スレッドの場合は、親によって指定されたスタック サイズと CreateThread フラグも表示されます。 スレッド ID を指定すると、その特定のスレッドの情報のみが表示されます。

!avrf -tp [ ThreadID ]

スレッドプール ログを表示します。 このログには、スレッド アフィニティ マスクの変更、スレッド優先度の変更、スレッド メッセージの投稿、COM の初期化、スレッド プール コールバック内からの COM の初期化解除など、さまざまな操作のスタック トレースが含まれる場合があります。 スレッド ID を指定すると、その特定のスレッドの情報のみが表示されます。

!avrf -srw [ Address | Address Length ] [ -stats ]

スリム リーダー/ライター (SRW) ログを表示します。 アドレスを指定すると、その SRW ロック アドレスに関連するレコードが表示されます。 アドレスと共に Length を指定すると、そのアドレス範囲内のすべての 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 はブレーク イベントのタイプ番号を指定します。 使用可能な型の一覧については、 !avrf -brkを使用します 。

!avrf -flt [ EventTypeProbability ]

これがフォールト インジェクション コマンドであることを指定します。 追加のパラメーターなしで !avrf -flt を使用すると、現在のフォールト インジェクション設定が表示されます。 EventType はイベントのタイプ番号を指定します。 Probabilityは、イベントが失敗する頻度を指定します。 これには、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拡張機能を含む完全なモジュール名を含める必要があります。 パス情報は含めないようにしてください。 すべて指定すると、すべてのターゲット範囲または除外範囲がリセットされます。 Time を指定すると、実行が再開された後の時間 (ミリ秒) のすべてのエラーが抑制されます。

デバッガーの !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を使用します。

アプリケーション検証ツールの停止が発生した場合、パラメータを指定しない !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

このアプリケーション検証ツールの停止を続行できることを示すメッセージが表示されていることに注意してください。 何が発生したかを理解したら、ターゲット アプリケーションの実行を続けることができます。

まず、!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] アドレス - このアドレスに重要なセクションをダンプします。

!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] ]

ハンドルが指定されていない場合、または 0 の場合は、プロセス内のすべてのハンドルに関する情報が表示されます。 プロセスが指定されていない場合は、現在のプロセスが使用されます。

ユーザー モード デバッガーの構文は次のとおりです。

!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 を使用してより広範な -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 拡張コマンドを使用して、最後のいくつかのレコードを表示できます。長さはレコードの数を指定します。

指定したアドレスに影響を与えたすべてのヒープ領域操作を表示するために !avrf -hp -a Address を使用できます。 割り当て操作の場合は、割り当てられたヒープ ブロックに 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)。 malloc/new ユーザーには、ゼロ以外の割り当てが一般的です。 F0F0F0F0、E0E0E0E0、C0C0C0C0などのアドレスで読み取り/書き込みが試行されるエラー (アクセス違反) が発生した場合は、ほとんどの場合、これらのケースのいずれかが発生します。

F0F0F0F0での読み取り/書き込みは、解放された後にブロックが使用されたことを意味します。 残念ながら、あなたはこれを引き起こしたブロックを見つけるためにいくつかの探偵の仕事が必要になります。 エラーのスタック トレースを取得し、スタック上の関数のコードを調べる必要があります。 そのうちの 1 つは、割り当てが有効であるという間違った仮定を行う可能性があります。

E0E0E0E0/C0C0C0C0での読み取り/書き込みは、アプリケーションが割り当てを適切に初期化しなかったことを意味します。 これには、現在のスタック トレース内の関数のコード検査も必要です。 この種の障害の例を次に示します。 テスト プロセスでは、アドレス E0E0E0E0で HeapFree を実行しているときにアクセス違反が発生しました。 テストで構造体が割り当てられ、正しく初期化されず、オブジェクトのデストラクターが呼び出されたことが判明しました。 特定のフィールドが null でないため (その中にE0E0E0E0がありました)、そのフィールドに対して delete と呼ばれます。

ページ ヒープの技術的な詳細

ヒープの破損 (オーバーフローまたはアンダーフロー) を検出するために、AppVerifier は、要求されたメモリを書き込み不可の完全なページまたは割り当てられたメモリの前後に特別なタグで埋め込むことで、メモリの割り当て方法を変更します。 これを行うには、検証中のプロセスにVerifier.dllを読み込み、アプリケーションによって呼び出された Win32 ヒープ API の一部を対応するVerifier.dll API にリダイレクトします。

要求されたメモリを完全な書き込み不可ページで埋め込む場合 (FULL 設定はページ ヒープ プロパティ セクションで有効になっており、既定の設定です)、AppVerifier は大量の仮想メモリを消費しますが、オーバーフローまたはアンダーフローが発生したときにヒープ破損イベントがリアルタイムでキャッシュされるという利点があります。 このモードのメモリは、[AppVerifier 読み取り専用ヒープ ページ (4k)] [テスト対象アプリケーションによって要求されたメモリの量] または [テスト対象のアプリケーションによって要求されたメモリの量] [AppVerifier 読み取り専用ヒープ ページ (4k)]のようになります。

ヒープ チェックは、Backward プロパティに応じて、割り当ての先頭または末尾にガード ページを配置します。 Backward が False (既定値) に設定されている場合、バッファー オーバーランをキャッチするために、割り当ての最後にガード ページが配置されます。 True に設定すると、バッファー アンダーランをキャッチするために、割り当ての先頭にガード ページが配置されます。

要求されたメモリを特別なタグで埋め込むと (ヒープ プロパティの "Full" チェック ボックス項目をクリアすることで有効になります)、AppVerifier はチェックし、このメモリが解放されたときにアラートを生成します。 この手法を使用するメインの問題は、メモリが解放されたときにのみメモリの破損が検出される場合があるため (最小メモリ ブロックの量は 8 バイト)、3 バイト変数または 5 バイト オーバーフローが発生しても、すぐには検出されないことです。

アンダーフロー イベントでは、読み取り専用ページへの書き込みが試行されます。 これにより、例外がトリガーされます。 この例外は、ターゲット アプリケーションがデバッガーで実行されている場合にのみキャッチできることに注意してください。 完全ページ ヒープ モードでは、パディング + ガード ページが使用されるため、これらのエラーも検出されることに注意してください。 ライト ページ ヒープを使用する理由は、コンピューターがフル ページ ヒープの高いメモリ制約を許容できない場合です。

メモリ負荷の高いアプリケーションの場合、または長時間 (ストレス テストなど) に AppVerifier を使用する必要がある場合は、パフォーマンスの低下のためにフル モードではなく、通常の (軽い) ヒープ テストを実行することをお勧めします。 ただし、問題が発生した場合は、ページ全体のヒープを有効にしてさらに調査します。

カスタム ヒープ (ヒープのオペレーティング システムの実装をバイパスするヒープ) を使用しているアプリケーションでは、ページ ヒープを使用する利点が十分に得られない場合や、有効になっていると誤動作する可能性があります。

メモリ エラーのデバッグ

メモリ検証ツール デバッガー拡張機能

仮想空間操作ログは、プロセスの仮想空間を変更するすべてのルーチンを任意の方法で追跡します。 これには、VirtualAlloc、VirtualFree、MapViewOfFile、UnmapViewOfFile などがあります。

!avrf -vs Length 拡張コマンドを使用して、最後のいくつかのレコードを表示できます。長さはレコードの数を指定します。

!avrf -vs -a Address を使用すると、指定したアドレスに影響を与えたすべての仮想空間操作を表示できます。 割り当ての場合は、割り当てられたブロックに 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ページをコミットした大きなリージョンが割り当てられていることを示しています。 その後、ページをコミットし、仮想リージョン全体を解放しました。

参照

アプリケーション検証ツール - 概要

アプリケーション検証ツール - アプリケーションのテスト

アプリケーション検証ツール - アプリケーション検証ツール内のテスト

アプリケーション検証ツール - 停止コードと定義

アプリケーション検証ツール - よく寄せられる質問