このガイドでは、WinDbg を使用してユーザー モード アプリケーションをデバッグする方法について説明します。 実行中のプロセスにアタッチし、ブレークポイントを設定し、アプリケーションのクラッシュを診断することで、基本的なデバッグ スキルを練習します。
この記事では、次の操作を行います。
- WinDbg をメモ帳にアタッチし、基本的なデバッグを調べる
- 独自のアプリケーションをデバッグし、クラッシュを分析する
- 使用するべき重要なデバッグコマンドを習得する
学習内容
- 実行中のプロセスに WinDbg をアタッチする
- 読み取り可能なデバッグ出力用にシンボル ファイルを構成する
- ブレークポイントを設定し、プログラムの実行を制御する
- アプリケーションのクラッシュを分析する
- スレッドとコールスタックのナビゲート
[前提条件]
- コンピューターに WinDbg をインストールします。 インストール手順については、「 WinDbg Windows デバッガーのダウンロードとインストール」を参照してください。
- コマンド ライン インターフェイスに関する基本的な知識
- (省略可能)2 番目の演習のデバッグ シンボルを含むコンパイル済みアプリケーション
推定所要時間: 30 分
メモ帳を開き、WinDbg をアタッチする
インストール ディレクトリに移動し、WinDbg.exeを開きます。
[ファイル] メニューから [実行可能プログラムの起動] を選択します。 [ 実行可能ファイルの起動 ] ダイアログで、 notepad.exeを含むフォルダーに移動します。 ( 通常、notepad.exe ファイルは
C:\Windows\System32にあります。[ ファイル名] に「 notepad.exe」と入力します 。 を選択し、を開きます。
WinDbg が読み取り可能な関数と変数名を表示できるようにシンボル ファイルを構成します。
WinDbg ウィンドウの下部付近のコマンド ラインで、次のコマンドを入力します。
出力は次の例のようになります。
Symbol search path is: srv* Expanded Symbol search path is: cache*;SRVシンボルとは シンボル (PDB) ファイルには、関数名や変数名などのコード モジュールに関する情報が含まれています。 シンボルがない場合は、メモリ アドレスのみが表示されます。
次に、次のコマンドを入力します。
.reloadコマンドは、シンボル ファイルを検索して読み込む最初の検索を行うように WinDbg に指示します。notepad.exe モジュールのシンボルを表示するには、次のコマンドを入力します。
手記
出力が表示されない場合は、「
.reload /f」と入力してシンボルの読み込みを強制します。 追加のシンボル読み込み情報を表示するには、!sym noisy を使用します。を含む
mainモジュール内のシンボルを表示するには、検査シンボル コマンドを使用して、マスクに一致するモジュールを一覧表示します。x notepad!wWin*出力は次の例のようになります。
00007ff6`6e76b0a0 notepad!wWinMain (wWinMain) 00007ff6`6e783db0 notepad!wWinMainCRTStartup (wWinMainCRTStartup)ブレークポイントを
notepad!wWinMainに配置するには、次のコマンドを入力します。ブレークポイントを設定したことを確認するには、次のコマンドを入力します。
出力は次の例のようになります。
0 e Disable Clear 00007ff6`6e76b0a0 0001 (0001) 0:**** notepad!wWinMainメモ帳プロセスを開始するには、次のコマンドを入力します。
メモ帳は
WinMain関数まで実行されてから、デバッガーを中断します。Breakpoint 0 hit notepad!wWinMain: 00007ff6`6e76b0a0 488bc4 mov rax,rspメモ帳プロセスに現在読み込まれているコード モジュールの一覧を表示するには、次のコマンドを入力します。
出力は次の例のようになります。
0:000> lm start end module name 00007ff6`6e760000 00007ff6`6e798000 notepad (pdb symbols) C:\ProgramData\Dbg\sym\notepad.pdb\BC04D9A431EDE299D4625AD6201C8A4A1\notepad.pdb 00007ff8`066a0000 00007ff8`067ab000 gdi32full (deferred) 00007ff8`067b0000 00007ff8`068b0000 ucrtbase (deferred) 00007ff8`06a10000 00007ff8`06aad000 msvcp_win (deferred) 00007ff8`06ab0000 00007ff8`06ad2000 win32u (deferred) 00007ff8`06b40000 00007ff8`06e08000 KERNELBASE (deferred) 00007ff8`07220000 00007ff8`072dd000 KERNEL32 (deferred) 00007ff8`07420000 00007ff8`07775000 combase (deferred) 00007ff8`07820000 00007ff8`079c0000 USER32 (deferred) 00007ff8`079c0000 00007ff8`079f0000 IMM32 (deferred) 00007ff8`07c00000 00007ff8`07c2a000 GDI32 (deferred) 00007ff8`08480000 00007ff8`085ab000 RPCRT4 (deferred) 00007ff8`085b0000 00007ff8`0864e000 msvcrt (deferred) 00007ff8`08c40000 00007ff8`08cee000 shcore (deferred) 00007ff8`08db0000 00007ff8`08fa5000 ntdll (pdb symbols) C:\ProgramData\Dbg\sym\ntdll.pdb\53F12BFE149A2F50205C8D5D66290B481\ntdll.pdb 00007fff`f8580000 00007fff`f881a000 COMCTL32 (deferred)スタック トレースを表示するには、次のコマンドを入力します。
出力は次の例のようになります。
0:000> k 00 000000c8`2647f708 00007ff6`6e783d36 notepad!wWinMain 01 000000c8`2647f710 00007ff8`07237034 notepad!__scrt_common_main_seh+0x106 02 000000c8`2647f750 00007ff8`08e02651 KERNEL32!BaseThreadInitThunk+0x14 03 000000c8`2647f780 00000000`00000000 ntdll!RtlUserThreadStart+0x21メモ帳をもう一度起動するには、次のコマンドを入力します。
次に: 実行を中断し、読み込まれたモジュールを調べていきます。
メモ帳を中断するには、[ファイル] メニューの [中断] を選択します。
ZwWriteFileでブレークポイントを設定して確認するには、次のコマンドを入力します。メモ帳をもう一度起動するには、「 g」と入力します。 メモ帳ウィンドウで、テキストを入力します。 [ファイル] メニューで、 [保存] を選択します。 実行中のコードは、
ZwCreateFileでエラーが発生して中断します。 k コマンドを入力して、スタック トレースを確認します。
WinDbg ウィンドウのコマンド ラインの左側に、プロセッサとスレッド番号が表示されます。 この例では、現在のプロセッサ番号は 0、現在のスレッド番号は 11 (
0:011>) です。 このウィンドウには、プロセッサ 0 で実行されているスレッド 11 のスタック トレースが表示されます。メモ帳プロセス内のすべてのスレッドの一覧を表示するには、次のコマンド (チルダ) を入力します。
出力は次の例のようになります。
0:011> ~ 0 Id: 5500.34d8 Suspend: 1 Teb: 000000c8`262c4000 Unfrozen 1 Id: 5500.3960 Suspend: 1 Teb: 000000c8`262c6000 Unfrozen 2 Id: 5500.5d68 Suspend: 1 Teb: 000000c8`262c8000 Unfrozen 3 Id: 5500.4c90 Suspend: 1 Teb: 000000c8`262ca000 Unfrozen 4 Id: 5500.4ac4 Suspend: 1 Teb: 000000c8`262cc000 Unfrozen 5 Id: 5500.293c Suspend: 1 Teb: 000000c8`262ce000 Unfrozen 6 Id: 5500.53a0 Suspend: 1 Teb: 000000c8`262d0000 Unfrozen 7 Id: 5500.3ca4 Suspend: 1 Teb: 000000c8`262d4000 Unfrozen 8 Id: 5500.808 Suspend: 1 Teb: 000000c8`262da000 Unfrozen 10 Id: 5500.3940 Suspend: 1 Teb: 000000c8`262dc000 Unfrozen . 11 Id: 5500.28b0 Suspend: 1 Teb: 000000c8`262de000 Unfrozen 12 Id: 5500.12bc Suspend: 1 Teb: 000000c8`262e0000 Unfrozen 13 Id: 5500.4c34 Suspend: 1 Teb: 000000c8`262e2000 Unfrozenこの例では、14 個のスレッドのインデックスは 0 から 13 です。
スレッド 0 のスタック トレースを確認するには、次のコマンドを入力します。
出力は次の例のようになります。
0:011> ~0s 0:011> ~0s win32u!NtUserGetProp+0x14: 00007ff8`06ab1204 c3 ret 0:000> k # Child-SP RetAddr Call Site 00 000000c8`2647bd08 00007ff8`07829fe1 win32u!NtUserGetProp+0x14 01 000000c8`2647bd10 00007fff`f86099be USER32!GetPropW+0xd1 02 000000c8`2647bd40 00007ff8`07d12f4d COMCTL32!DefSubclassProc+0x4e 03 000000c8`2647bd90 00007fff`f8609aba SHELL32!CAutoComplete::_EditWndProc+0xb1 04 000000c8`2647bde0 00007fff`f86098b7 COMCTL32!CallNextSubclassProc+0x9a 05 000000c8`2647be60 00007ff8`0782e858 COMCTL32!MasterSubclassProc+0xa7 06 000000c8`2647bf00 00007ff8`0782de1b USER32!UserCallWinProcCheckWow+0x2f8 07 000000c8`2647c090 00007ff8`0782d68a USER32!SendMessageWorker+0x70b 08 000000c8`2647c130 00007ff8`07afa4db USER32!SendMessageW+0xdaデバッグを終了し、メモ帳プロセスからデタッチするには、次のコマンドを入力します。
独自のアプリケーションを開き、WinDbg をアタッチする
たとえば、この小さなコンソール アプリケーションを作成してビルドしたとします。
...
void MyFunction(long p1, long p2, long p3)
{
long x = p1 + p2 + p3;
long y = 0;
y = x / p2;
}
void main ()
{
long a = 2;
long b = 0;
MyFunction(a, b, 5);
}
この演習では、ビルドされたアプリケーション (MyApp.exe) とシンボル ファイル (MyApp.pdb) が C:\MyApp\x64\Debug にあると仮定します。 また、アプリケーションのソース コードが C:\MyApp\MyApp and that the target machine compiled MyApp.exe内にあることを前提としています。
WinDbg を開きます。
[ファイル] メニューから [実行可能プログラムの起動] を選択します。 [ 実行可能ファイルの起動 ] ダイアログで、C:\MyApp\x64\Debug に移動します。 ファイル名に「MyApp.exe」と入力します。 を選択し、を開きます。
次のコマンドを入力します。
.sympath+ C:\MyApp\x64\Debug
これらのコマンドは、アプリケーションのシンボルとソース コードを検索する場所を WinDbg に指示します。 この場合、シンボルにはソース ファイルへの完全修飾パスがあるため、 .srcpath を使用してソース コードの場所を設定する必要はありません。
次のコマンドを入力します。
アプリケーションは、
main関数に到達するとデバッガーで中断します。WinDbg には、ソース コードとコマンド ウィンドウが表示されます。
[デバッグ] メニューの [ステップ イン] を選択します (または F11 キーを押します)。
MyFunctionにステップインするまでステップを続けます。y = x / p2行にステップ インすると、アプリケーションがクラッシュし、デバッガーに分割されます。出力は次の例のようになります。
(1450.1424): Integer divide-by-zero - code c0000094 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. MyApp!MyFunction+0x44: 00007ff6`3be11064 f77c2428 idiv eax,dword ptr [rsp+28h] ss:00000063`2036f808=00000000次のコマンドを入力します。
WinDbg では、問題の分析が表示されます (この場合、0 による除算)。
FAULTING_IP: MyApp!MyFunction+44 [c:\myapp\myapp\myapp.cpp @ 7] 00007ff6`3be11064 f77c2428 idiv eax,dword ptr [rsp+28h] EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 00007ff63be11064 (MyApp!MyFunction+0x0000000000000044) ExceptionCode: c0000094 (Integer divide-by-zero) ExceptionFlags: 00000000 NumberParameters: 0 ... STACK_TEXT: 00000063`2036f7e0 00007ff6`3be110b8 : ... : MyApp!MyFunction+0x44 00000063`2036f800 00007ff6`3be1141d : ... : MyApp!main+0x38 00000063`2036f840 00007ff6`3be1154e : ... : MyApp!__tmainCRTStartup+0x19d 00000063`2036f8b0 00007ffc`b1cf16ad : ... : MyApp!mainCRTStartup+0xe 00000063`2036f8e0 00007ffc`b1fc4629 : ... : KERNEL32!BaseThreadInitThunk+0xd 00000063`2036f910 00000000`00000000 : ... : ntdll!RtlUserThreadStart+0x1d STACK_COMMAND: dt ntdll!LdrpLastDllInitializer BaseDllName ;dt ntdll!LdrpFailureData ;.cxr 0x0 ;kb FOLLOWUP_IP: MyApp!MyFunction+44 [c:\myapp\myapp\myapp.cpp @ 7] 00007ff6`3be11064 f77c2428 idiv eax,dword ptr [rsp+28h] FAULTING_SOURCE_LINE: c:\myapp\myapp\myapp.cpp FAULTING_SOURCE_FILE: c:\myapp\myapp\myapp.cpp FAULTING_SOURCE_LINE_NUMBER: 7 FAULTING_SOURCE_CODE: 3: void MyFunction(long p1, long p2, long p3) 4: { 5: long x = p1 + p2 + p3; 6: long y = 0; > 7: y = x / p2; 8: } 9: 10: void main () 11: { 12: long a = 2; ...
次のステップ
システム アプリケーションと独自のコードの両方をデバッグしたら、より高度なデバッグ シナリオを調べる準備ができました。
- WinDbg (カーネル モード) の概要 - Windows カーネルとドライバーのデバッグ
- デバッガー操作 - デバッガー 操作の概念について説明します
- デバッグ手法 - 高度なデバッグ方法を調べる
コマンドの概要
このチュートリアルで使用する基本的なコマンドを次に示します。
セットアップとシンボル:
- .sympath (シンボル パスの設定) - WinDbg がシンボル ファイルを検索する場所を構成する
- .reload (モジュールの再読み込み) - シンボル ファイルを読み込む
実行の制御:
- g (Go) - プログラムの実行を続行する
- bu (ブレークポイントの設定) - 特定の関数で実行を一時停止する
-
Step Into(F11) - 1 つの命令を実行し、関数にステップ インする
プログラムの検査:
- x (シンボルを調べる) - 関数と変数を一覧表示する
- lm (読み込まれたモジュールの一覧表示) - 読み込 まれたすべての DLL と実行可能ファイルを表示する
- k (スタック バックトレースの表示) - 呼び出し履歴を表示する
- ~ (スレッドの状態) - すべてのスレッドを一覧表示する
- !analyze -v - クラッシュを自動的に分析する
参照: