ソース モードでのデバッグ

逆アセンブルされたバイナリではなく、コードのソースを分析できる場合は、アプリケーションのデバッグが簡単になります。

ソース言語が C、C++、またはアセンブリの場合、WinDbg、CDB、および KD はデバッグでソース コードを使用できます。

コンパイルの要件

ソース デバッグを使用するには、バイナリのビルド時にコンパイラまたはリンカーでシンボル ファイル (.pdb ファイル) を作成する必要があります。 これらのシンボル ファイルは、バイナリ命令がソース行にどのように対応するかをデバッガーに示します。

また、シンボル ファイルには実際のソース テキストが含まれていないため、デバッガーは実際のソース ファイルにアクセスできる必要があります。

可能な場合は、コンパイラとリンカーはコードを最適化してはいけません。 コードが最適化されている場合、ソース デバッグとローカル変数へのアクセスはより難しくなり、場合によってはほぼできなくなります。 コンパイラおよびリンカーとしてビルド ユーティリティを使用している場合は、MSC_OPTIMIZATION マクロを /Od /Oi に設定して最適化を回避します。

シンボル ファイルとソース ファイルの検索

ソース モードでデバッグするには、デバッガーがソース ファイルとシンボル ファイルを見つけられる必要があります。 詳細については、「ソース パス」を参照してください。

ソース デバッグの開始

デバッガーは、現在デバッグ中のスレッドに適切なシンボルとソース ファイルがあるときは常にソース情報を表示できます。

デバッガーを使用して新しいユーザー モード アプリケーションを起動すると、Ntdll.dll がアプリケーションを読み込むときに最初の中断が発生します。 デバッガーは Ntdll.dll ソース ファイルにアクセスできないため、この時点ではアプリケーションのソース情報にアクセスできません。

プログラム カウンターをアプリケーションの先頭に移動するには、エントリ ポイントでバイナリにブレークポイントを追加します。 デバッガー コマンド ウィンドウで、次のコマンドを入力します。

bp main
g

その後、アプリケーションが読み込まれ、main 関数に入ると停止します。 (もちろん、main だけではなく、任意のエントリ ポイントを使用できます)。

アプリケーションが例外をスローすると、例外はデバッガーに割り込みます。 この時点でソース情報を利用できます。 ただし、CTRL+CCTRL+BREAK、または [デバッグ | 中断] コマンドを使用して中断を発行した場合は、デバッガーが新しいスレッドを作成するるため、ソース コードを見ることができません。

ソース ファイルがあるスレッドに到達したら、デバッガー コマンド ウィンドウを使用してソース デバッグ コマンドを実行できます。 WinDbg を使用している場合は、[ソース] ウィンドウが表示されます。 [ファイル] メニューの [ソース ファイルを開く] をクリックして既に[ソース] ウィンドウを開いている場合、WinDbg は通常、そのソース用の新しいウィンドウを作成します。 デバッグ プロセスに影響を与えることなく、前のウィンドウを閉じることができます。

WinDbg GUI でのソース デバッグ

WinDbg を使用している場合は、プログラム カウンターがデバッガーにソース情報があるコードに入ると、すぐに [ソース] ウィンドウが表示されます。

WinDbg は、ユーザーまたは WinDbg が開いたソース ファイルごとに 1 つの [ソース] ウィンドウを表示します。 このウィンドウのテキスト プロパティの詳細については、「[ソース] ウィンドウ」を参照してください。

その後、アプリケーションをステップ実行するか、ブレークポイントまたはカーソルまで実行することができます。 ステップ実行コマンドとトレース コマンドの詳細については、「ターゲットの制御」を参照してください。

ソース モードの場合は、アプリケーションをステップ実行するにつれて、適切な [ソース] ウィンドウがフォアグラウンドに移動します。 アプリケーションの実行中に呼び出される Microsoft Windows ルーチンもあるため、この種の呼び出しが発生したときにデバッガーが [逆アセンブリ] ウィンドウをフォアグラウンドに移動する可能性があります (デバッガーがこれらの関数のソースにアクセスできないため)。 プログラム カウンターが既知のソース ファイルに戻ると、適切な [ソース] ウィンドウがアクティブになります。

アプリケーション内を移動すると、WinDbg の [ソース] ウィンドウと [逆アセンブリ] ウィンドウで現在の場所が強調表示されます。 ブレークポイントが設定されている行も強調表示されます。 ソース コードは、言語の解析に従って色付けされます。 [ソース] ウィンドウが選択されている場合は、マウスを使用してシンボルの上にマウス ポインターを合わせて評価できます。 これらの機能と制御方法の詳細については、「[ソース ウィンドウ]」を参照してください。

WinDbg でソース モードをアクティブにするには、l+t コマンドを使用するか、[デバッグ] メニューの [ソース モード] をクリックするか、[ソース モード オン] ボタンをクリックします。 ソース モードがアクティブなとき、ステータス バーで ASM インジケーターは使用不可と表示されます。

ソース モードで関数をステップ実行しながら、任意のローカル変数の値を表示または変更できます。 詳細については、「メモリの読み取りと書き込み」を参照してください。

デバッガー コマンド ウィンドウでのソース デバッグ

CDB を使用している場合、別個の [ソース] ウィンドウはありません。 ただし、ソースをステップ実行しながら進行状況を表示できます。

CDB でソース デバッグを行う前に、.lines (ソース行サポートの切り替え) コマンドを発行するか、-lines コマンド ライン オプション を使用してデバッガーを起動して、ソース行シンボルを読み込む必要があります。

l+t コマンドを実行すると、プログラムのすべてのステップ実行が一度に 1 行ずつ実行されます。 l-t を使用して、一度に 1 つのアセンブリ命令をステップ実行します。 WinDbg を使用している場合、このコマンドは、[デバッグ] メニューで [ソース モード] をオンまたはオフにするか、ツールバー ボタンを使用することと同じ効果があります。

l+s コマンドは、プロンプトに現在のソース行と行番号を表示します。 行番号のみを表示する場合は、代わりに l+l を使用します。

l+o および l+s を使用すると、プログラムのステップ実行中にソース行のみが表示されます。 プログラム カウンター、逆アセンブリ コード、およびレジスタ情報は非表示になります。 この種の表示を使用すると、コードをすばやくステップ実行でき、ソース以外は何も表示されません。

lsp (ソース行数の設定) コマンドを使用すると、アプリケーションをステップ実行または実行するときに表示されるソース行の数を正確に指定できます。

以下の一連のコマンドは、ソース ファイルをステップ実行する効果的な方法です。

.lines        enable source line information
bp main       set initial breakpoint
l+t           stepping will be done by source line
l+s           source lines will be displayed at prompt
g             run program until "main" is entered
pr            execute one source line, and toggle register display off
p             execute one source line 

ENTER は最後のコマンドを繰り返すので、ENTER キーを使用してアプリケーションをステップ実行できるようになりました。 ステップごとに、ソース行、メモリ オフセット、アセンブリ コードが表示されます。

逆アセンブリ表示を解釈する方法の詳細については、「アセンブリ モードでのデバッグ」を参照してください。

アセンブリ コードが表示されるとき、アクセスされているメモリ位置が行の右端に表示されます。 d* (メモリの表示) および e* (値の入力) コマンドを使用して、それらの場所の値を表示または変更できます。

オフセットまたはメモリ情報を確認するために各アセンブリ命令を表示する必要がある場合は、l-t を使用して、ソース行ごとではなく、アセンブリ命令ごとにステップ実行します。 ソース行情報は引き続き表示できます。 各ソース行は、1 つ以上のアセンブリ命令に対応します。

これらのコマンドはすべて、WinDbg と CDB で使用できます。 コマンドを使用すると、[ソース] ウィンドウからではなく、WinDbg のデバッガー コマンド ウィンドウからソース行情報を表示できます。

ソース行とオフセット

式エバリュエーターを使用してソース デバッグを実行して、特定のソース行に対応するオフセットを決定することもできます。

次のコマンドは、メモリ オフセットを表示します。

? `[[module!]filename][:linenumber]` 

filename を省略した場合、デバッガーは現在のプログラム カウンターに対応するソース ファイルを検索します。

デバッガーは、現在の既定の基数に関係なく、前に 0x を追加しない限り、linenumber を 10 進数として読み取ります。 linenumber を省略した場合、式はソース ファイルに対応する実行可能ファイルの最初のアドレスとして評価されます。

この構文が CDB で認識されるのは、.lines コマンドまたは -lines コマンド ライン オプションにソース行シンボルが読み込まれている場合だけです。

この手法は、プログラム カウンターが指している場所に関係なく使用できるため、非常に汎用性があります。 たとえば、この手法を利用すると、次のようなコマンドを使用して、事前にブレークポイントを設定できます。

bp `source.c:31` 

詳細については、「ソース行の構文」および「ブレークポイントの使用」を参照してください。

ソース モードでのステップ実行とトレース

ソース モードでデバッグしているとき、1 つのソース行に複数の関数呼び出しが存在することがあります。 p および t コマンドを使用して、これらの関数呼び出しを分離することはできません。

たとえば、次のコマンドでは、t コマンドは GetTickCountprintf の両方にステップ インしますが、p コマンドは両方の関数呼び出しをステップ オーバーします。

printf( "%x\n", GetTickCount() );

他の呼び出しをトレースする間に特定の呼び出しをステップオーバーする場合は、.step_filter (ステップ フィルターの設定) を使用して、ステップ オーバーする呼び出しを指定します。

_step_filter を使用して、フレームワーク関数 (Microsoft Foundation Classes (MFC) や Active Template Library (ATL) の呼び出しなど) をフィルター処理で除外できます。