Time Travel Debugging - サンプル アプリのチュートリアル

オプション 説明

e

execute (CPU がアドレスから命令をフェッチする場合)

r

読み取り/書き込み (CPU がアドレスに対して読み取りまたは書き込みを行う場合)

write (CPU がアドレスに書き込む場合)

設定できるデータ ブレークポイントは任意の時点で 4 つしか設定できません。データを正しく配置するか、ブレークポイントをトリガーしない必要があります (単語は 2 で割り切れるアドレスで終わる必要があります。dword は 4 で割り切り、quadwords は 0 または 8 で割り切る必要があります)。

ベース ポインターのメモリ アクセス ブレークポイントのブレークを設定する

  1. トレースのこの時点で、ベース ポインター (この例では 00effe44) への書き込みメモリ アクセスにブレークポイントを設定します。 これを行うには、監視 するアドレス を使用して ba コマンドを使用します。 4 バイトの書き込みを監視する必要があります。そのため、w4 を指定します。

    0:000> ba w4 00effe44
    
  2. [表示 ] を選択し 、[ブレークポイント ] を選択して、意図した設定を確認します。

    1 つのブレークポイントを含むブレークポイント ウィンドウを示す WinDbg プレビュー。

  3. [ホーム] メニューの [戻 る] を選択して、ブレークポイントにヒットするまで過去に戻ります。

    0:000> g-
    Breakpoint 0 hit
    Time Travel Position: 5B:92
    eax=0000000f ebx=003db000 ecx=00000000 edx=00cc1a6c esi=00d41046 edi=0053fde8
    eip=00d4174a esp=0053fcf8 ebp=0053fde8 iopl=0         nv up ei pl nz ac pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
    DisplayGreeting!DisplayGreeting+0x3a:
    00d4174a c745e000000000  mov     dword ptr [ebp-20h],0 ss:002b:0053fdc8=cccccccc
    
  4. [表示 ] 、 [ ローカル] の順 に選択します。 ローカル ウィンドウでは、変換先変数にメッセージの一部しか含まれているのに対し、ソースにはすべてのテキストが含まれているのが確認できます。 この情報は、スタックが破損したという考えをサポートしています。

    WinDbg プレビューの [ローカル] ウィンドウのスクリーンショット。

  5. この時点で、プログラム スタックを調べて、アクティブなコードを確認できます。 [表示] リボン 、[スタック] を 選択します

    WinDbg プレビュー スタック ウィンドウのスクリーンショット。

Microsoft が提供する wscpy_s() 関数にこのようなコード バグが含まれる可能性は非常に低く、スタックをさらに詳しく見てみる必要があります。 スタックには、Greeting!main が Greeting! を呼び出すというメッセージが表示されます。GetCppConGreeting。 非常に小さなコード サンプルでは、この時点でコードを開くだけで、エラーが見つけやすい可能性があります。 しかし、より大規模で複雑なプログラムで使用できる手法を説明するために、さらに調査するために新しいブレークポイントを設定します。

GetCppConGreeting 関数のアクセス ブレークポイントのブレークを設定する

  1. ブレークポイント ウィンドウを使用して既存のブレークポイントをクリアするには、既存のブレークポイントを右クリックし、 [削除] を選択 します

  2. DisplayGreeting のアドレスを決定します。 dx コマンドを使用した GetCppConGreeting 関数。

    0:000> dx &DisplayGreeting!GetCppConGreeting
    &DisplayGreeting!GetCppConGreeting                 : 0xb61720 [Type: void (__cdecl*)(wchar_t *,unsigned int)]
        [Type: void __cdecl(wchar_t *,unsigned int)]
    
  3. ba コマンド を使用 して、メモリ アクセスにブレークポイントを設定します。 関数は実行のためにメモリから読み取り専用なので、r - 読み取りブレークポイントを設定する必要があります。

    0:000> ba r4 b61720
    
  4. [Hardware Read]/(ハードウェア読み取り)ブレークポイントが [ブレークポイント] ウィンドウでアクティブな状態を確認します。

    1 つのハードウェア読み取りブレークポイントを持つブレークポイント ウィンドウを示す WinDbg プレビュー。

  5. あいさつ文字列のサイズについて疑問に思っているので、sizeof(greeting) の値を表示するウォッチ ウィンドウを設定します。 [表示] リボンで [ウォッチ]を選択し、sizeof(greeting) を指定します

    ウォッチ ローカル ウィンドウが表示されている WinDbg プレビュー。

  6. [Time Travel]/(タイム トラベル)メニューで 、[Time travel]/( タイム トラベル)を使用して開始するか、コマンドを使用してトレース の開始位置に移動します。

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  7. [ホーム] メニューの [ 移動 ] を選択するか、 コマンドを使用して、ブレークポイントにヒットするまでコード 内を進します。

    0:000> g
    Breakpoint 2 hit
    Time Travel Position: 4B:1AD
    eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046
    eip=00b61721 esp=00ddf7a4 ebp=00ddf864 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!GetCppConGreeting+0x1:
    00b61721 8bec            mov     ebp,esp
    
  8. [ホーム] メニューの [ステップ アウト バック] を選択 するか、コマンド を使用して 1 つのステップを戻します。

    0:000> g-u
    Time Travel Position: 4B:1AA
    eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046
    eip=00b61917 esp=00ddf7ac ebp=00ddf864 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!main+0x27:
    00b61917 e8def7ffff      call    DisplayGreeting!ILT+245(?GetCppConGreetingYAXPA_WIZ) (00b610fa)
    
  9. 根本原因が見つかったように見えます。 宣言 した greeting 配列の長さは 50 文字ですが、GetCppConGreeting に渡す sizeof(greeting) は 0x64,100) です。

    X64 を示す [ウォッチ ローカル] ウィンドウが表示された [あいさつコードを表示する] ウィンドウを示す WinDbg プレビュー。

    サイズの問題をさらに見て、メッセージの長さが 75 文字で、文字列文字の末尾を含む 76 文字である点も確認できます。

    HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!
    
  10. コードを修正する方法の 1 つは、文字配列のサイズを 100 に拡張する方法です。

    std::array <wchar_t, 100> greeting{};
    

    また、このコード行で sizeof(greeting) を size(greeting) に変更する必要があります。

     GetCppConGreeting(greeting.data(), size(greeting));
    
  11. これらの修正プログラムを検証するために、コードを再コンパイルし、エラーなしで実行されるのを確認できます。

ソース ウィンドウを使用したブレークポイントの設定

  1. この調査を実行する別の方法は、コード行をクリックしてブレークポイントを設定する方法です。 たとえば、ソース ウィンドウの std:array 定義行の右側をクリックすると、そこにブレークポイントが設定されます。

    std:array に設定されたブレークポイントを示すソース ウィンドウのスクリーンショット。

  2. [Time Travel]/(タイム トラベル)メニューで 、Time travel to start コマンドを使用してトレースの開始位置に移動します。

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  3. [ホーム] リボンで、ブレークポイント にヒット するまで [移動] をクリックして戻ります。

    Breakpoint 0 hit
    Time Travel Position: 5B:AF
    eax=0000000f ebx=00c20000 ecx=00000000 edx=00000000 esi=013a1046 edi=00effa60
    eip=013a17c1 esp=00eff970 ebp=00effa60 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!DisplayGreeting+0x41:
    013a17c1 8bf4            mov     esi,esp
    

あいさつ変数のアクセス ブレークポイントのブレークを 設定 する

この調査を実行するもう 1 つの方法は、疑わしい変数にブレークポイントを設定し、変更しているコードを調べる方法です。 たとえば、GetCppConGreeting メソッドの greeting 変数にブレークポイントを設定するには、次の手順を使用します。

チュートリアルのこの部分では、前のセクションのブレークポイントにまだ存在する必要があります。

  1. [ビュー ]、 [ローカル] の順に選択します。 ローカル ウィンドウでは、 現在の コンテキストであいさつを使用できます。そのため、メモリの場所を特定できます。

  2. dx コマンド を使用 して、あいさつ配列 を確認 します。

    0:000> dx &greeting
    &greeting                 : 0xddf800 [Type: std::array<wchar_t,50> *]
       [+0x000] _Elems           : "꽘棶檙瞝???" [Type: wchar_t [50]]
    

    このトレースでは 、greeting は ddf800 のメモリ内に位置します。

  3. ブレークポイント ウィンドウを使用して既存のブレークポイントをクリアするには、既存のブレークポイントを右クリックし、 [削除] を選択 します

  4. 書き込みアクセスを監視するメモリ アドレスを使用して 、ba コマンドでブレークポイントを設定します。

    ba w4 ddf800
    
  5. [Time Travel]/(タイム トラベル)メニューで 、Time travel to start コマンドを使用してトレースの開始位置に移動します。

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  6. [ホーム] メニューの [ 移動] を選択して、あいさつ配列の最初のメモリ アクセスポイントに進む。

    0:000> g-
    Breakpoint 0 hit
    Time Travel Position: 5B:9C
    eax=cccccccc ebx=002b1000 ecx=00000000 edx=68d51a6c esi=013a1046 edi=001bf7d8
    eip=013a1735 esp=001bf6b8 ebp=001bf7d8 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!GetCppConGreeting+0x25:
    013a1735 c745ec04000000  mov     dword ptr [ebp-14h],4 ss:002b:001bf7c4=cccccccc
    

    または、トレースの末尾に移動し、コードを逆方向に実行して、配列のメモリ位置が書き込まれたトレース内の最後のポイントを見つける方法があります。

TTD を使用します。メモリ アクセスを表示するメモリ オブジェクト

トレース メモリ内のアクセスされたポイントを特定するもう 1 つの方法は、TTD を使用する方法です。メモリ オブジェクトと dx コマンド。

  1. dx コマンド を使用 して、あいさつ配列 を確認 します。

    0:000> dx &greeting
    &greeting                 : 0xddf800 [Type: std::array<wchar_t,50> *]
       [+0x000] _Elems           : "꽘棶檙瞝???" [Type: wchar_t [50]]
    

    このトレースでは 、greeting は ddf800 のメモリ内に位置します。

  2. dx コマンド を使用 して、読み取り書き込みアクセス権を持つそのアドレスから始まるメモリ内の 4 バイトを確認します。

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")
    @$cursession.TTD.Memory(0x1bf7d0,0x1bf7d4, "rw")                
        [0x0]           
        [0x1]           
        [0x2]           
        [0x3]           
        [0x4]           
        [0x5]           
        [0x6]           
        [0x7]           
        [0x8]           
        [0x9]           
        [0xa]           
        ...         
    
  3. 任意の出現箇所をクリックすると、メモリ アクセスの発生に関する詳細が表示されます。

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5]
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5]                
        EventType        : MemoryAccess
        ThreadId         : 0x710
        UniqueThreadId   : 0x2
        TimeStart        : 27:3C1 [Time Travel]
        TimeEnd          : 27:3C1 [Time Travel]
        AccessType       : Write
        IP               : 0x6900432f
        Address          : 0xddf800
        Size             : 0x4
        Value            : 0xddf818
    
  4. [Time Travel] をクリックして、トレースをその時点に配置します。

    0:000> dx @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5].TimeStart.SeekTo()
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5].TimeStart.SeekTo()
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 27:3C1
    eax=00ddf81c ebx=00fa2000 ecx=00ddf818 edx=ffffffff esi=00000000 edi=00b61046
    eip=6900432f esp=00ddf804 ebp=00ddf810 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    ucrtbased!_register_onexit_function+0xf:
    6900432f 51              push    ecx
    
  5. トレースで最後に発生した読み取り/書き込みメモリ アクセスに関心がある場合は、一覧の最後の項目をクリックするか、 を追加します。dx コマンドの末尾への Last() 関数。

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw").Last()
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw").Last()                
        EventType        : MemoryAccess
        ThreadId         : 0x710
        UniqueThreadId   : 0x2
        TimeStart        : 53:100E [Time Travel]
        TimeEnd          : 53:100E [Time Travel]
        AccessType       : Read
        IP               : 0x690338e4
        Address          : 0xddf802
        Size             : 0x2
        Value            : 0x45
    
  6. 次に、[Time Travel] をクリックしてトレース内のその位置に移動し、このラボで前述した手法を使用して、その時点でのコード実行をさらに確認できます。

TTD の詳細については、以下をご覧ください。メモリ オブジェクトについては 、TTD に関するページを参照してください。メモリ オブジェクト

まとめ

この非常に小さなサンプルでは、数行のコードを見て問題を特定できますが、大規模なプログラムでは、ここで示す手法を使用して、問題を特定するために必要な時間を減らできます。

トレースが記録された後、トレースと再現手順を共有できます。問題は任意の PC でオンデマンドで再現できます。

関連項目

Time Travel Debugging - 概要

Time Travel Debugging - 記録

Time Travel Debugging - トレースの再生

Time Travel Debugging - トレース ファイルの使用

時計を示す小さなタイム トラベル ロゴ。

このラボでは、コードの欠陥がある小さなサンプル プログラムを使用して、Time Travel Debugging (TTD) を導入します。 TTD を使って問題をデバッグし、その根本原因を特定します。 この小さなプログラムの問題は見つけやすいですが、一般的な手順は、より複雑なコードで使用できます。 この一般的な手順は、次のように要約できます。

  1. 失敗したプログラムのタイム トラベル トレースをキャプチャします。
  2. dx (デバッガー オブジェクト モデル式の表示) コマンドを使用して、記録に格納されている例外イベントを検索します。
  3. !tt (time travel)コマンドを使用して、トレース内の例外イベントの位置に移動します。
  4. トレースのその時点から、問題のコードがスコープに入るまで、1 ステップ前に進みます。
  5. スコープ内のエラーコードを使用して、ローカル値を確認し、正しくない値を含む可能性のある変数の仮説を作成します。
  6. 正しくない値を持つ変数のメモリ アドレスを確認します。
  7. ba (Break on Access) コマンドを使用して、疑わしい変数のアドレスにメモリ アクセス (ba) ブレークポイントを設定 します。
  8. g- を使用して、疑わしい変数の最後のメモリ アクセスポイントに戻します。
  9. その場所を確認するか、前にいくつかの手順を実行して、コードの欠陥点を確認してください。 そうすれば、完了です。 間違った値が他の変数から取得された場合は、2番目の変数のアクセスブレークポイントで別のブレークポイントを設定します。
  10. 2番目の問題のある変数で、g-を使用して最後のメモリアクセスポイントまで実行し直します。 その場所またはいくつかの手順にコードの欠陥が含まれているかどうかを確認します。 そうすれば、完了です。
  11. エラーの原因となった間違った値が設定されているコードが見つかるまで、このプロセスを繰り返します。

この手順で説明する一般的な手法は、さまざまなコードの問題に適用されますが、一意の方法を必要とする一意のコードの問題があります。 このチュートリアルで示す手法は、デバッグツールセットを拡張するために使用する必要があります。また、TTD トレースでできることをいくつか示します。

ラボの目標

このラボを完了すると、タイムトラベルトレースで一般的な手順を使用して、コード内の問題を見つけることができます。

ラボのセットアップ

ラボを完了するには、次のハードウェアが必要です。

  • Windows 10 を実行しているノート pc またはデスクトップコンピューター (ホスト)

ラボを完成させるには、次のソフトウェアが必要です。

  • WinDbg のプレビュー。 WinDbg Preview のインストールの詳細については、「 Windbg プレビュー-インストール」を参照してください。
  • サンプル C++ コードをビルドする Visual Studio ます。

このラボには、次の3つのセクションがあります。

セクション 1: サンプルコードをビルドする

セクション1では、Visual Studio を使用してサンプルコードをビルドします。

Visual Studio でサンプルアプリを作成する

  1. Microsoft Visual Studio で、[ FileNew Project/Solution...] をクリックし、 Visual C++テンプレートをクリックします。

    Win32 コンソールアプリケーションを選択します。

    Displaygreetingのプロジェクト名を指定し、[ OK]をクリックします。

  2. セキュリティ開発ライフサイクル (SDL) のチェックをオフにします。

    win32 アプリケーションウィザードのアプリケーション設定。

  3. [ 完了] をクリックします。

  4. Visual Studio の [DisplayGreeting] ウィンドウに次のテキストを貼り付けます。

    // DisplayGreeting.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <array>
    #include <stdio.h>
    #include <string.h>
    
    void GetCppConGreeting(wchar_t* buffer, size_t size)
    {
        wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!";
    
        wcscpy_s(buffer, size, message);
    }
    
    int main()
    {
         std::array <wchar_t, 50> greeting{};
         GetCppConGreeting(greeting.data(), sizeof(greeting));
    
         wprintf(L"%ls\n", greeting.data());
    
         return 0;
    }
    
  5. Visual Studio で、[ Projectdisplaygreeting のプロパティ] をクリックします。 次に、[ C/c + +コード生成] をクリックします。

    次のプロパティを設定します。

    設定
    セキュリティ チェック セキュリティチェックを無効にする (/GS-)
    基本ランタイムチェック Default

    注意

    これらの設定は推奨されていませんが、コーディングを迅速化したり、特定のテスト環境を容易にしたりするために、これらの設定を使用することをユーザーがアドバイスするシナリオを想像することができます。

  6. Visual Studio で、[ビルド] [ソリューションのビルド] の順にクリックし ます。

    すべてうまくいくと、ビルドウィンドウには、ビルドが成功したことを示すメッセージが表示されます。

  7. ビルドされたサンプルアプリファイルの検索

    ソリューションエクスプローラーで、 Displaygreeting プロジェクトを右クリックし、[ エクスプローラーでフォルダーを開く] を選択します。

    このサンプルのコンパイルされた exe ファイルとシンボル pdb ファイルを含むデバッグフォルダーに移動します。 たとえば、プロジェクトが格納されているフォルダーの場合は、 C:\Projects\DisplayGreeting\Debugに移動します。

  8. コードの欠陥を含むサンプルアプリを実行する

    .Exe ファイルをダブルクリックして、サンプルアプリを実行します。

    コンソールで実行されている .exe ファイルを示すスクリーンショット。

    このダイアログボックスが表示されたら、[プログラムを閉じる] を選択します。

    [(ファイル名) .exe 動作を停止しました] ダイアログボックスが表示されたスクリーンショット。

    このチュートリアルの次のセクションでは、サンプルアプリの実行を記録して、この例外が発生した理由を確認できるかどうかを確認します。

セクション 2: "DisplayGreeting" サンプルのトレースを記録する

セクション2では、不適切なサンプルの "DisplayGreeting" アプリのトレースを記録します。

サンプルアプリを起動して TTD トレースを記録するには、次の手順に従います。 TTD トレースの記録に関する一般的な情報については、「タイムトラベルデバッグ-トレースの記録」を参照してください。

  1. タイムトラベルトレースを記録できるように、WinDbg Preview を管理者として実行します。

  2. WinDbg プレビューで、[ファイル] [デバッグ] [ 起動実行可能ファイル (詳細)] を選択します。

  3. 記録するユーザーモードの実行可能ファイルへのパスを入力するか、[ 参照 ] を選択して実行可能ファイルに移動します。 WinDbg Preview で [起動実行可能ファイル] メニューを操作する方法の詳細については、「 Windbg preview-user mode session を開始する」を参照してください。

    [起動可能 (詳細)] 画面に [記録の開始] チェックボックスが表示されている WinDbg プレビューのスクリーンショット。

  4. [ タイムトラベルを含むレコード] デバッグ ボックスをオンにして、実行可能ファイルが起動されたときにトレースを記録します。

  5. [ 構成 ] をクリックして記録を開始します。

  6. [記録の構成] ダイアログボックスが表示されたら、[ 記録 ] をクリックして実行可能ファイルを起動し、記録を開始します。

    Apath が temp に設定された、

  7. トレースが記録されていることを示す [記録] ダイアログが表示されます。 その後すぐに、アプリケーションがクラッシュします。

  8. [ プログラムを閉じる] をクリックして、[displaygreeting が動作を停止しました] ダイアログボックスを閉じます。

    [エラーのあるアプリケーション] ダイアログボックス。

  9. プログラムがクラッシュすると、トレースファイルが閉じられ、ディスクに書き込まれます。

    1/1 キーフレームがインデックス付きの出力を表示する WinDbg Preview のスクリーンショット。

  10. デバッガーは自動的にトレースファイルを開き、インデックスを作成します。 インデックス作成は、トレースファイルの効率的なデバッグを可能にするプロセスです。 このインデックス作成処理にかかる時間は、トレースファイルのサイズが大きいほど長くなります。

    (5120.2540): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: D:0 [Unindexed] Index
    !index
    Indexed 10/22 keyframes
    Indexed 20/22 keyframes
    Indexed 22/22 keyframes
    Successfully created the index in 755ms.
    

注意

キーフレームは、インデックス作成に使用されるトレース内の場所です。 キーフレームは自動的に生成されます。 大きいトレースには、より多くのキーフレームが含まれます。

  1. この時点で、トレースファイルの先頭に移動して、時間をさかのぼる準備ができています。

    TTD トレースの記録が完了したので、トレースを再生するか、トレースファイルを使用できます。たとえば、同僚と共有できます。 トレースファイルを操作する方法の詳細については、「タイムトラベルデバッグ-トレースファイルの操作」を参照してください。

このラボの次のセクションでは、トレースファイルを分析して、コードに関する問題を見つけます。

セクション 3: トレースファイルの記録を分析してコードの問題を特定する

セクション3では、トレースファイルの記録を分析して、コードの問題を特定します。

WinDbg 環境を構成する

  1. シンボルパスにローカルシンボルの場所を追加し、次のコマンドを入力してシンボルを再度読み込みます。

    .sympath+ C:\MyProjects\DisplayGreeting\Debug
    .reload
    
  2. 次のコマンドを入力して、ソースパスにローカルコードの場所を追加します。

    .srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
    
  3. スタックおよびローカル変数の状態を表示できるようにするには、[WinDbg Preview] リボンで、[ 表示 ]、 [ローカル] 、および [ ビュースタック] を選択します。 ウィンドウを整理して、それらのウィンドウ、ソースコード、およびコマンドウィンドウを同時に表示できるようにします。

  4. [WinDbg Preview] リボンで、[ ソース ] を選択し、[ ソースファイル] を開きます。 DisplayGreeting .cpp ファイルを見つけて開きます。

例外を確認する

  1. トレースファイルが読み込まれると、例外が発生したことを示す情報が表示されます。

    2fa8.1fdc): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68ef8100 ebx=00000000 ecx=77a266ac edx=69614afc esi=6961137c edi=004da000
    eip=77a266ac esp=0023f9b4 ebp=0023fc04 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:0023fac0=00000000
    
  2. Dx コマンドを使用して、記録内のすべてのイベントを一覧表示します。 例外イベントは、イベントに一覧表示されます。

    0:000> dx -r1 @$curprocess.TTD.Events
    ...
    [0x2c]           : Module Loaded at position: 9967:0
    [0x2d]           : Exception at 9BDC:0
    [0x2e]           : Thread terminated at 9C43:0
    ...
    
    

    注意

    このチュートリアルでは、余分な出力が削除されたことを示すために、3つの期間を使用します。

  3. 例外イベントをクリックすると、その TTD イベントに関する情報が表示されます。

    0:000> dx -r1 @$curprocess.TTD.Events[17]
    @$curprocess.TTD.Events[17]                 : Exception at 68:0
        Type             : Exception
        Position         : 68:0 [Time Travel]
        Exception        : Exception of type Hardware at PC: 0X540020
    
  4. 例外フィールドをクリックして、例外データをさらにドリルダウンします。

    0:000> dx -r1 @$curprocess.TTD.Events[17].Exception
    @$curprocess.TTD.Events[17].Exception                 : Exception of type Hardware at PC: 0X540020
        Position         : 68:0 [Time Travel]
        Type             : Hardware
        ProgramCounter   : 0x540020
        Code             : 0xc0000005
        Flags            : 0x0
        RecordAddress    : 0x0
    

    例外データは、これが CPU によってスローされたハードウェア障害であることを示します。 また、これがアクセス違反であることを示す、0xc0000005 の例外コードも提供します。 これは通常、アクセス権のないメモリへの書き込みを試行していることを示しています。

  5. 例外イベントの [Time トラベル] リンクをクリックして、トレース内のその位置に移動します。

    0:000> dx @$curprocess.TTD.Events[17].Exception.Position.SeekTo()
    Setting position: 68:0
    
    @$curprocess.TTD.Events[17].Exception.Position.SeekTo()
    (16c8.1f28): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 68:0
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00540020 esp=00effe4c ebp=00520055 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    00540020 ??
    

    この出力では、スタックと基本ポインターが2つの非常に異なるアドレスを指していることに注意してください。

    esp=00effe4c ebp=00520055
    

    これは、スタックが破損していることを示している可能性があります。関数が返され、スタックが破損している可能性があります。 これを検証するには、CPU 状態が破損する前にに戻り、スタックの破損が発生したことを確認できるかどうかを確認する必要があります。

ローカル変数を調べ、コードのブレークポイントを設定します。

トレースで障害が発生した時点では、エラー処理コードの実際の原因の後にいくつかの手順を終了するのが一般的です。 タイムトラベルを使用すると、実際の根本原因を特定するために、一度に命令を戻すことができます。

  1. [ ホーム ] リボンで、[ ステップイン戻る ] コマンドを使用して、3つの手順を実行します。 このようにして、引き続きスタックウィンドウとメモリウィンドウを確認します。

    コマンドウィンドウには、時間の移動位置とレジスタが表示されます。手順に従って3つの手順を実行します。

    0:000> t-
    Time Travel Position: 67:40
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00540020 esp=00effe4c ebp=00520055 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    00540020 ??              ???
    
    0:000> t-
    Time Travel Position: 67:3F
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=0019193d esp=00effe48 ebp=00520055 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    DisplayGreeting!main+0x4d:
    0019193d c3
    
    0:000> t-
    Time Travel Position: 67:39
    eax=0000004c ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00191935 esp=00effd94 ebp=00effe44 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    DisplayGreeting!main+0x45:
    

    注意

    このチュートリアルでは、コマンドの出力に、コマンドラインの使用法を使用するユーザーがコマンドラインコマンドを使用できるようにする UI メニューオプションの代わりに使用できるコマンドを示します。

  2. トレースのこの時点で、スタックと基本ポインターにはより意味のある値が含まれているため、破損が発生したコードのポイントに近づいているように見えます。

    esp=00effd94 ebp=00effe44
    

    また、[ローカル] ウィンドウには対象アプリからの値が含まれており、[ソースコード] ウィンドウにはトレースのこの時点で実行する準備ができているコード行が強調表示されていることに注目してください。

    メモリ ASCII 出力とソースコードウィンドウを含むローカルウィンドウを表示している WinDbg プレビューのスクリーンショット。

  3. さらに詳しく調査するために、[メモリ] ウィンドウを開いて、 0x00effe44の基本ポインターのメモリアドレスの近くにコンテンツを表示することができます。

  4. 関連する ASCII 文字を表示するには、[メモリ] リボンで、[ テキスト ]、[ ascii] の順に選択します。

    メモリ ascii 出力とソースコードウィンドウを示す winbbg preview のスクリーンショット。

  5. 基本ポインターが命令を指しているのではなく、メッセージテキストを指しています。 ここでは、スタックが破損している時点に近い状態になる可能性があります。 さらに詳しく調査するには、ブレークポイントを設定します。

注意

この非常に小さなサンプルでは、コードを簡単に調べることができますが、数百行のコードがあり、サブルーチンが多数ある場合は、ここで説明する手法を使用して、問題の特定に必要な時間を短縮できます。

TTD とブレークポイント

ブレークポイントの使用は、特定のイベントでコードの実行を一時停止するための一般的な方法です。 TTD を使用すると、トレースが記録された後にブレークポイントがヒットするまで、ブレークポイントを設定し、時間をさかのぼって戻すことができます。 問題が発生した後にプロセスの状態を確認する機能は、ブレークポイントの最適な場所を特定するために、TTD に固有の追加のデバッグワークフローを有効にします。

メモリアクセスのブレークポイント

メモリ位置にアクセスしたときに起動するブレークポイントを設定できます。 次の構文を使用して、 ba (アクセス時の中断) コマンドを使用します。

ba <access> <size> <address> {options}