Application Verifierは非常に強力なツールであり、ヒープの使用方法の診断に加えて、ハンドルの不適切な処理、マルチスレッドの実装エラーの検出、そのような状況でのプログラムの正しい動作を確認するためのリソース不足のエミュレートなど、他の多くのことを実行できます回。
ツール
Application Verifierに加えて、 Microsoft Debugging Tools for Windowsに含まれている無料のデバッガーであるWinDBGが必要です。 以前は、デバッグツールは個別にダウンロードできましたが、現在では何らかの理由でWindows SDKまたはWindows Driver Kitの一部にすぎません。 ただし、 以前のバージョンは個別にダウンロードできます。これは、タスクに最適です。 さて、またはここに最新バージョン(6.12.2.633)を投稿しました。SDK全体をダウンロードしないようにするためです: dbg_x86.msi 、 dbg_amd64.msi 。
また、Visual C ++(任意のバージョン、新しいバージョン、おそらくVS2003、 Expressが可能)またはWindows SDKのC ++コンパイラも必要です。 必要なのは、WinDBGが理解するPDBデバッグ情報が必要なため、MinGWではなくMicrosoftのコンパイラです。
例を置く
上記の問題のソースを取得します ( pastieのコピー )。 デバッグ情報(コンパイラーの場合はキー/ Ziまたは/ ZI、リンカーの場合は/ DEBUG)と最適化を無効にして収集します。 コンソールからビルドするコマンドラインは次のようになります。
cl /D_DEBUG /Zi /Od /EHsc /DEBUG /MDd vector_misuse.cpp
アプリケーション検証ツールを構成する
- 管理者権限でAppVerifierを起動します。
- [ファイル]-> [アプリケーションの追加](またはCtrl + A)を選択し、misused_vector.exeを見つけて[開く]をクリックします。
- 基本ノードからすべてのチェックマークを削除します。
- [基本]-> [ヒープ]ノードでチェックマークを設定します。 念のため、このノードのプロパティに移動して(それを右クリック->プロパティ)、Full(一番上)と反対のTraces(ダイアログのほぼ中央)の反対側のチェックボックスがオンになっていることを確認してください。 オンになっていない場合は、オンにして[OK]をクリックします。
- [保存]ボタンをクリックします。
デバッガーを構成する
- [ファイル]-> [シンボルファイルのパス]に移動し、
srv*c:\mysymbols*http://msdl.microsoft.com/download/symbols
という行を入力します。 これは、デバッガーが最初にc:\ mysymbolsディレクトリ内の文字を検索し、そうでない場合は、Microsoft Symbol Storeからインターネットからダウンロードすることを意味します。 美しい呼び出しスタックを表示するには、パブリックシンボルが必要です。.symfix+ c:\mysymbols
を使用できますが、アプリケーションがデバッガーにロードされた後です。 - [ファイル]-> [実行可能ファイルを開く](Ctrl + E)でmisused_vector.exeを選択します。 ワークスペースを維持するという提案に同意します。 デバッガはイメージをメモリにロードしますが、実行を開始しません。
- 実行時にサンプルを起動します-Debug-> Go(またはF5、またはデバッガプロンプトでg)。
秋の理由を見つける
プログラムを実行した後、Access Violationでクラッシュします。
スタック-呼び出しスタックの表示(または招待でAlt + 6またはkp)を見て、ネストの第2レベルで関数fに含まれるものを確認します。 関数の引数を[呼び出しスタック]ウィンドウに表示するには、[ソース引数]ボタンをクリックします。 コード行へのリンクを表示するには、「ソース」ボタンをクリックします。 kpコマンドは、この情報をデバッガのコマンドウィンドウに表示します。 ソーステキストを含むウィンドウも開き、現在の行が強調表示されます。
OK、問題は次の行にあることがわかります
v[i] += f(x / 2);
しかし、彼女の何が間違っているのでしょうか? デバッガーは、正しく尋ねるとこの質問に答えます。 プロンプトで書きます!analyze -v
を
!analyze -v
Enterを押します。
デバッガーは、次のことに興味があるテキストのシートをダンプします。
DEFAULT_BUCKET_ID:INVALID_POINTER_READ-無効なポインターで読み取ろうとしました
READ_ADDRESS:060a0ff4-読み取ろうとしたアドレス自体。
すでに見た呼び出しスタックと、例外が発生したマークされた行を持つソースの一部も印刷されます。
これはもちろん非常に興味深いですが、なぜこの記憶が読めないのか知りたいですか? AppVerifierで行った設定のおかげで、システムは呼び出しスタックを収集し、各メモリの割り当てと割り当て解除でそれらを慎重に保存したため、要求に応じて親切に提供することができました。
デバッガーの招待状を
!heap -p -a 060a0ff4
ます
!heap -p -a 060a0ff4
(ここでは、READ_ADDRESSにあるアドレスを置き換える必要がありますが、ほとんどの場合異なります。デバッガーは、このアドレスがそのようなヒープに属することを通知します。呼び出しスタックを使用して、次のように(解放された割り当てで)解放されたサイズの:
5da190b2 verifier!AVrfDebugPageHeapFree + 0x000000c2 77cd1464 ntdll!RtlDebugFreeHeap + 0x0000002f 77c8ab3a ntdll!RtlpFreeHeap + 0x0000005d 77c33472 ntdll!RtlFreeHeap + 0x00000142 75cc14dd kernel32!HeapFree + 0x00000014 5c677f59 MSVCR100D!_Free_base + 0x00000029 5c687a4e MSVCR100D!_Free_dbg_nolock + 0x000004ae 5c687560 MSVCR100D!_Free_dbg + 0x00000050 5c686629 MSVCR100D!演算子削除+ 0x000000b9 00f71af0 vector_misuse!Std ::アロケーター<int> :: deallocate + 0x00000010 00f7193b vector_misuse!Std :: vector <int、std :: allocator <int >> :: reserve + 0x0000010b 00f716db vector_misuse!Std :: vector <int、std :: allocator <int >> :: _ Reserve + 0x0000005b 00f714c4 vector_misuse!Std :: vector <int、std :: allocator <int >> :: push_back + 0x000000c4 00f712dc vector_misuse!F + 0x0000002c 00f7130b vector_misuse!F + 0x0000005b 00f7130b vector_misuse!F + 0x0000005b 00f7134b vector_misuse!Main + 0x0000000b 00f7323f vector_misuse!__ tmainCRTStartup + 0x000001bf 00f7306f vector_misuse!MainCRTStartup + 0x0000000f 75cc33ca kernel32!BaseThreadInitThunk + 0x0000000e 77c39ed2 ntdll!__ RtlUserThreadStart + 0x00000070 77c39ea5 ntdll!_RtlUserThreadStart + 0x0000001b
したがって、再帰のネストの第3レベルで、次のベクトル:: push_backで、ベクトルはそのサイズ(ベクトル::予約)を変更することを決定し、このベクトル自体の再割り当て(std ::アロケーター::割り当て解除、さらにスタックの下位)につながることを学びました2番目のレベルに戻るときの解放されたメモリへの後続のアクセス。
合計
私はいつも美しい結論と要約を書くことに問題を抱えていたので、問題はありません。 賢い人々、彼ら自身が必要な結論を引き出します:)
ご清聴ありがとうございました。 :)