シンボルを使用したデバッグ

この記事では、デバッグ プロセスでのシンボルの最適な使用方法の概要について説明します。 Microsoft シンボル サーバーの使用方法と、独自のプライベート シンボル サーバーを設定して使用する方法について説明します。 これらのベスト プラクティスは、問題に関連するすべてのシンボルと実行可能ファイルがコンピューターに存在しない場合でも、問題をデバッグする有効性と能力を高めるのに役立ちます。

シンボル

デバッグには、さまざまな種類のシンボルを使用できます。 これには、CodeView シンボル、COFF、DBG、SYM、PDB、さらにはバイナリ ファイル エクスポート テーブルから生成されたエクスポート シンボルも含まれます。 このホワイト ペーパーでは、最新の優先形式である、VS.NET と PDB 形式のシンボルについてのみ説明します。 Visual Studio を使用してコンパイルされるプロジェクトでは、これらが既定で生成されます。

リリース実行可能ファイルの PDB ファイルを生成しても、最適化に影響を与えたり、生成されるファイルのサイズが大幅に変化したりすることはありません。 通常、唯一の違いはパスであり、PDB ファイルのファイル名が実行可能ファイルに埋め込まれます。 このため、実行可能ファイルと一緒に配布しない場合でも、常に PDB ファイルを生成してください。

PDB ファイルが生成されるのは、/Zi または /ZI (PDB 情報の生成) コンパイラ スイッチと /DEBUG (デバッグ情報の生成) リンカー スイッチを使用してプロジェクトをビルドした場合です。 コンパイラによって生成された PDB ファイルは結合されて、1 つの PDB ファイルに書き込まれ、実行可能ファイルと同じディレクトリに配置されます。

既定では、PDB ファイルには次の情報が含まれます。

  • パブリック シンボル (通常、すべての関数、静的変数、グローバル変数)
  • 実行可能ファイル内のコード セクションに対応するオブジェクト ファイルの一覧
  • フレーム ポインター最適化情報 (FPO)
  • ローカル変数とデータ構造の名前と型の情報
  • ソース ファイルと行番号の情報

PDB ファイル情報を使用して実行可能ファイルのリバース エンジニアリングを行う人について心配がある場合は、/PDBSTRIPPED:filename リンカー オプションを使用して、ストリップされた PDB ファイルを生成することもできます。 既存の PDB ファイルからプライベート情報を削除する必要がある場合は、Windows のデバッグ ツールの一部である pdbcopy というツールを使用できます。

既定では、ストリップされた PDB ファイルには次の情報が含まれます。

  • パブリック シンボル (通常は非静的関数とグローバル変数のみ)
  • 実行可能ファイル内のコード セクションに対応するオブジェクト ファイルの一覧
  • フレーム ポインター最適化情報 (FPO)

これは、信頼性の高いデバッグを可能にするために必要な最小限の情報です。 最小限の情報により、元のソース コードに関する追加情報を取得することも困難になります。 ストリップされた PDB ファイルと通常の PDB ファイルの両方が生成されるため、限られたデバッグ機能を必要とする可能性があるユーザーにはストリップされたバージョンを提供し、完全な PDB は機密情報として維持できます。 /PDBSTRIPPED では 2 つ目の小さい PDB ファイルが生成されるので、広く配布するビルドを生成するときは、必ず正しい PDB ファイルを使用してください。 一般的なプロジェクトの場合、通常の PDB のサイズは数メガバイトですが、ストリップされたバージョンの PDB は数百キロバイトに過ぎません。

デバッグでのシンボルの使用

クラッシュしたアプリケーションをデバッグする場合、デバッガーはクラッシュの原因となったスタック上の関数を表示しようとします。 PDB ファイルがないと、デバッガーは関数名、パラメーター、およびスタックに格納されているローカル変数を解決できません。 32 ビットの実行可能ファイルをデバッグする場合、シンボルなしでは信頼性の高いスタック トレースすら取得できない場合があります。 場合によっては、スタック上の生の値を確認し、どの値が戻りアドレスである可能性があるかを確認できますが、これらは関数参照やデータと混同されがちです。

現在のスタック上の関数がフレーム ポインターの省略 (/Oy) 最適化を使用してコンパイルされており、かつシンボルが存在しない場合、デバッガーは現在の関数を呼び出した関数を正確には判断できません。 これは、PDB に含まれるフレーム ポインター最適化 (FPO) 情報がないと、デバッガーはフレーム ポインター レジスタ (EBP) を利用して、保存された前のフレーム ポインターと親関数の戻りアドレスを指すことができないためです。 代わりに、推測します。 時には正しいこともあります。 しかし、それは多くの場合間違っており、誤解を招く可能性があります。 次の例のように、シンボルが見つからない、またはシンボルが読み込まれていないことに関する警告が表示される場合は、その時点以降のスタックを信頼しないでください。

SWPerfTest.exe!TextFunction(... ...)    Line 59    C++
d3dx9d.dll!008829b5()
[Frames below may be incorrect and/or missing, no symbols loaded for d3dx9d.dll]
SWPerfTest.exe!main(int argc=, const char * * argv=)  Line 328 + 0x12 bytes     C++
SWPerfTest.exe!__mainCRTStartup() Line 716 + 0x17 bytes    C
kernel32.dll!@BaseThreadInitThunk@12() + 0x12 bytes
ntdll.dll!__RtlUserThreadStart@8() + 0x27 bytes

多くの場合、問題が正確なシンボルがある場所で発生しており、コール スタックでそれ以上関数を確認する必要がないために、シンボルなしでデバッグを続けることができます。 コール スタック内のライブラリで PDB が使用できない場合でも、フレーム ポインターを使用してコンパイルされていれば、デバッガーは親関数で正しく推測できます。 Windows XP Service Pack 2 以降、デバッグがより正確になるため、すべての Windows DLL ファイルと実行可能ファイルは FPO を無効にしてコンパイルされています。 FPO を無効にすると、パフォーマンスへの影響を最小限に抑えながら、サンプリング プロファイラーで実行時にスタックを調べることもできます。 Windows XP SP2 より前のバージョンの Windows では、すべてのオペレーティング システム バイナリで、正確なデバッグとプロファイリングを可能にするために、FPO 情報を含む一致するシンボル ファイルが必要です。

64 ビット ネイティブの実行可能ファイルをデバッグする場合、有効なスタック トレースを生成するためにシンボル ファイルは必要ありません。これは、x64 オペレーティング システムとコンパイラはそれらを必要としないように設計されているためです。 ただし、関数名、呼び出しパラメーター、ローカル変数を取得するにはシンボル ファイルが必要です。

ただし、シンボルがないとデバッグが特に困難になる場合があります。 たとえば、PDB ファイルをビルドしたプログラムをデバッグし、シンボルがない DLL 内の関数からのコールバックでクラッシュしている場合、スタックをデコードできないため、コールバックの原因となった関数を確認できません。 これは、サードパーティ製ライブラリで PDB が提供されていない場合、または古いオペレーティング システム コンポーネントで PDB が使用できない場合に頻繁に発生します。 コールバックは、多くの場合、メッセージの受け渡し、列挙、メモリ割り当て、または例外処理中に発生します。 正確なスタックなしでこれらの関数をデバッグすることは非常に困難です。

別のコンピューターで生成されたミニ ダンプ、または所有していないコードでクラッシュしたミニ ダンプを確実にデバッグするには、ミニ ダンプで参照されている実行可能ファイルのすべてのシンボルとバイナリにアクセスできることが重要です。 シンボルとバイナリがシンボル サーバーから入手できる場合は、デバッガーによって自動的に取得されます。 ミニダンプの詳細については、「クラッシュ ダンプの分析」ホワイト ペーパーを参照してください。

必要なシンボルの取得

Visual Studio やその他の Microsoft デバッガー (WinDbg など) は、通常、自分のコンピューターでアプリケーションをビルドしてデバッグする場合にだけ機能するように設定されます。 実行可能ファイルを他のユーザーに提供する必要がある場合、コンピューターに複数のバージョンの DLL または .exe ファイルがある場合、または Windows あるいは DirectX などの他のライブラリを使用するアプリケーションを正確にデバッグする必要がある場合は、デバッガーがシンボルを検索して読み込む方法を理解する必要があります。 デバッガーでは、ユーザーが指定したシンボル検索パス (Visual Studio の Options\Debugging\Symbols にあります) または _NT_SYMBOL_PATH 環境変数が使用されます。 通常、デバッガーは次の場所にある一致する PDB を検索します。

  • DLL または実行可能ファイル内で指定されている場所。

    自分のコンピューター上で DLL または実行可能ファイルをビルドした場合、既定では、リンカーは、関連付けられている PDB ファイルの完全なパスとファイル名を DLL または実行可能ファイル内に配置します。 デバッグ時に、デバッガーはまず、DLL または実行可能ファイル内で指定された場所にシンボル ファイルが存在するかどうかを確認します。 これは、自分のコンピューターでコンパイルしたコードの場合、常にシンボルを使用できるため、役に立ちます。

  • DLL または実行可能ファイルと同じフォルダーに存在する可能性がある PDB。

  • 任意のローカル シンボル キャッシュ フォルダー。

  • ローカル ネットワーク ファイル共有のシンボル サーバー。

  • Microsoft シンボル サーバーなどのインターネット シンボル サーバー。

正確なデバッグに必要なすべての PDB を確保するには、Windows 用のデバッグ ツールをインストールします。 32 ビットと 64 ビットのバージョンが、「Windows 用デバッグ ツール」にあります。

このパッケージでインストールされる便利なツールに symchk.exe があります。 これは、不足しているシンボルや正しくないシンボルを識別するのに役立ちます。 このツールには、使用可能な多数のコマンド ライン オプションがあります。 特に便利で一般的に使用される 2 つを次に示します。

特定の DLL または .exe ファイルが同じフォルダー内の PDB が一致するかどうかを確認する

"c:\Program Files\Debugging Tools for Windows\symchk" testing.dll /s .

SYMCHK: FAILED files = 0
SYMCHK: PASSED + IGNORED files = 1

/s . オプションは、シンボル サーバーを検索せず、現在のフォルダー内でのみシンボルを検索するように symchk に指示します。

一連のフォルダー内のすべての DLL と実行可能ファイルに一致する PDB があるかどうかを確認する

"c:\Program Files\Debugging Tools for Windows\symchk" *.* /r

/r オプションは、すべての実行可能ファイルに一致する PDB があることをチェックするために、フォルダーを再帰的に走査するように symchk を設定します。 /s オプションを指定しない場合、symchk は現在の _NT_SYMBOL_PATH を使用して、任意のプライベートまたはローカル サーバー、または Microsoft シンボル サーバーでシンボルを検索します。 symchk ツールは、実行可能ファイル (.exe、.dll など) のシンボルのみを検索します。 非実行可能ファイルのシンボルにワイルド カード検索を使用することはできません。

symchk のしくみ

リンカーが .dll、実行可能ファイル、PDB ファイルを生成するときに、各ファイルに同じ GUID が格納されます。 この GUID は、特定の PDB ファイルが DLL または実行可能ファイルと一致するかどうかを判断するためにツールによって使用されます。 リソース エディターまたはコピー保護エンコードを使用するか、バージョン情報を変更して DLL または実行可能ファイルを変更すると、GUID が更新され、デバッガーは PDB ファイルを読み込めません。 このため、リンカーによって作成された後に DLL または実行可能ファイルを操作しないようにすることが非常に重要です。

また、VS.NET に付属する DUMPBIN ユーティリティを使用して、検索されるシンボル パスを表示したり、特定の DLL または実行可能ファイルに一致するシンボル ファイルが見つかるかどうかを確認したりすることもできます。 次に例を示します。

DUMPBIN /PDBPATH:VERBOSE filename.exe

シンボル サーバー

シンボル サーバーは、複数のバージョンの実行可能ファイルとシンボル ファイルのためのリポジトリです。 シンボル ファイル自体、または関連付けられているシンボル ファイルへのポインターが格納されています。 デバッガーはシンボル サーバーの使用方法を理解し、それらを使用して不足しているシンボルまたは不明なシンボルを検索できます。

DLL ファイルと実行可能ファイルも Microsoft シンボル サーバーから入手できます。 これにより、クラッシュをデバッグし、コンピューターに存在しないオペレーティング システム ファイルのコードを調べることができます。 デバッグに使用しているシステム上に存在しない実行可能ファイルまたは DLL がデバッガーによって検出された場合、シンボルとバイナリ ファイルのコピーの両方が Microsoft シンボル サーバーに対して自動的に要求されます。 これは、msvcrt.dll などの多くのバージョンがあるコンポーネントをデバッグしていて、コンピューターに存在しないバージョンのコードを調べる必要がある場合に役立ちます。 これは、デバッグに使用しているシステムとは異なるオペレーティング システムで生成されたミニ ダンプのデバッグにも役立ちます。

Microsoft は、すべてのオペレーティング システムとその他の再配布コンポーネント (DirectX SDK など) のすべての PDB ファイルを、外部からアクセス可能なシンボル サーバーで公開しています。 これにより、これらの DLL または実行可能ファイルを使用するアプリケーションを簡単にデバッグできます。 Microsoft シンボル サーバーを使用して、コンピューター上でビルドされたコンポーネントのローカル シンボルと一緒にシンボルを解決できます。

Microsoft シンボル サーバーを使用するようにコンピューターを設定すると、すべての Microsoft シンボル ファイルにアクセスできます。 また、会社、チーム、またはネットワーク用のプライベート シンボル サーバーを設定することもできます。これは、作業中のプロジェクトの複数の古いバージョンを格納したり、Microsoft シンボル サーバーから使用するシンボルのローカル キャッシュを提供したりするために使用できます。

シンボル サーバーを使用するには、環境変数 _NT_SYMBOL_PATH で検索パスを指定します。 デバッガーと最新のツール (WinDbg、NTSD、Visual Studio など) では、このパスを自動的に使用してシンボルを検索します。

デバッガーは、シンボルを検索するときに、最初にローカルで検索します。 次に、シンボル サーバーを検索します。 一致するシンボルが見つかると、シンボル ファイルがローカル キャッシュに転送されます。 一般的な DLL または実行可能ファイルのシンボルのサイズは 1 から 100 MB です。 そのため、多くの DLL を含むプロセスをデバッグする場合、すべてのシンボルを解決してローカル キャッシュに転送するまでに時間がかかる場合があります。

Microsoft シンボル サーバーの使用

Microsoft シンボル サーバーを使用すると、修正プログラムが適用されたファイルや更新されたファイルのシンボルを含め、すべての最新のシンボルを取得できます。 Microsoft シンボル サーバーは、https://msdl.microsoft.com/download/symbols で利用できます。

シンボル サーバーには、次のいずれかの方法でアクセスできます。

  • サーバー アドレスを直接入力する。 Visual Studio の [ツール] メニューの [オプション] 選択し、[デバッグ][シンボル] の順に選択します。

  • 環境変数 _NT_SYMBOL_PATH を使用する。 これがお勧めしているメソッドです。

    これは、すべてのデバッグ ツールで使用されます。 また、Visual Studio でも使用され、Visual Studio が開くと読み取られてデコードされます。 そのため、変更した場合は、Visual Studio を再起動する必要があります。

    この環境変数を使用して、内部プライベート シンボル サーバーなど、複数のシンボル サーバーを指定できます。 また、内部とインターネットの両方で、シンボル サーバーから検索するすべてのシンボルの PDB を格納するローカル キャッシュ ディレクトリを指定することもできます。

_NT_SYMBOL_PATH 変数の構文は次のとおりです。

srv*[local cache]*[private symbol server]*https://msdl.microsoft.com/download/symbols

[ローカル キャッシュ] を、使用したシンボルのキャッシュを格納するコンピューター上のディレクトリの名前 (%SYSTEMROOT%\Symbols、c:\symbols など) に置き換えます。

[プライベート シンボル サーバー] は省略可能です。 これは、ネットワーク上にあるシンボル サーバーを指すか、チーム、製品グループ、または会社によって共有されているシンボル サーバーを指すことができます。

Microsoft シンボル サーバーのみをシンボルのローカル キャッシュと共に使用する場合は、インターネット経由のアクセスを高速化するために、_NT_SYMBOL_PATH に次の設定を使用してください。

srv*c:\symbols*https://msdl.microsoft.com/download/symbols

_NT_SYMBOL_PATH の他のオプションは、Microsoft Debugging Tools for Windows パッケージと共にインストールされるヘルプ ファイルで確認できます。

シンボルのない実行可能ファイルでは、シンボル サーバーを使用する場合、デバッガーの起動にかかる時間が長くなる可能性があります。 これは、デバッガーが実行可能ファイルを読み込もうとするたびにシンボル サーバーに対してクエリを実行するためです。 このため、すべてのコンポーネントのシンボルを常に要求することをお勧めします。

ビデオ ドライバーがプロセス空間に DLL を持ち、必要な PDB ファイルが Microsoft シンボル サーバーが提供されている場合など、すべてのコンポーネントのシンボルを要求できない場合があります。 この場合、デバッグ セッションを開始するときに多少の遅延が発生します。

この小さな遅延を回避するには、デバッガーを 1 回実行して、Microsoft シンボル サーバーからすべてのシンボルをローカルにキャッシュします。 その後、NT_SYMBOL_PATH を変更して Microsoft シンボル サーバーを削除します。 実行可能ファイルが変更されない限り、シンボルを持たない実行可能ファイルのチェックではインターネット経由でクエリを実行する必要がなくなります。Microsoft シンボル サーバーから必要なすべてのシンボルのローカル キャッシュ コピーがあるためです。

シンボルを手動で取得する

デバッガーが正しく設定されていると、ローカル キャッシュまたはシンボル サーバーから必要なシンボルが自動的に読み込まれます。 1 つの実行可能ファイルまたは実行可能ファイルのフォルダーに対してシンボルを取得する場合は、symchk を使用できます。 たとえば、Windows の System フォルダー内の d3dx9_30.dll ファイルのシンボルを現在のディレクトリにダウンロードする場合は、次のコマンドを使用できます。

"c:\Program Files\Debugging Tools for Windows\symchk" c:\Windows\System32\d3dx9_30.dll /oc \.

symchk ツールには他にも多くの用途があります。 詳細については、symchk /? を確認するか、Microsoft Debugging Tools for Windows のドキュメントを参照してください。

シンボル サーバーの設定

シンボル サーバーの設定は非常に簡単です。 これは、次のような理由で役立ちます。

  • 帯域幅を節約する、または、会社、チーム、製品のシンボル解決を高速化する。 ネットワーク上のローカル ファイル共有上の内部シンボル サーバーは、Microsoft シンボル サーバーなどの外部シンボル サーバーへのすべての参照をキャッシュします。 ローカルまたは内部のシンボル サーバーには、多くのユーザーが同時にすばやくアクセスできます。 そのため、重複するシンボル要求により発生する可能性がある帯域幅と待機時間が節約されます。
  • アプリケーションの古いビルド、バージョン、または外部リリースのシンボルを格納する。 これらのビルドのシンボルを、簡単にアクセスできるシンボル サーバーに格納することで、デバッガーがあり、ローカル シンボル サーバーへ接続できる任意のコンピューターで、これらのビルドのクラッシュや問題をデバッグできます。 これは、自分でビルドしたものではない実行可能ファイル (つまり、別のプログラマまたはビルド コンピューターによって生成されたビルド) によって生成されたミニ ダンプをデバッグする場合に特に便利です。 これらのビルドのシンボルがシンボル サーバーに格納されている場合は、信頼性が高く正確なデバッグが可能になります。
  • シンボルを最新の状態に保つ。 Windows Update または DirectX SDK によって変更される OS コンポーネントなど、コンポーネントが更新された場合でも、最新のすべてのシンボルを使用してデバッグできます。

独自のローカル ネットワークにシンボル サーバーを設定することは、サーバー上にファイル共有を作成し、共有にアクセスしてファイルとフォルダーを作成するための完全なアクセス許可をユーザーに付与するのと同じくらい簡単です。 この共有は、Windows Server 2003 などのサーバー オペレーティング システム上に作成する必要があります。これにより、共有に同時にアクセスできるユーザー数が制限されません。

たとえば、\\mainserver\symbols にファイル共有を設定した場合、チームのメンバーは _NT_SYMBOL_PATH を次のように設定します。

Srv*c:\symbols*\\mainserver\symbols*https://msdl.microsoft.com/download/symbols

シンボルが取得されると、ファイルとフォルダーが \\mainserver\symbols 共有ディレクトリおよび c:\symbols ディレクトリ内の個々のキャッシュに表示されます。

これが通常、独自のシンボル サーバーまたは Microsoft シンボル サーバーの設定と使用に必要となるすべての作業です。

シンボル サーバーへのシンボルの追加

シンボル サーバー共有のファイルを追加、削除、または編集するには、symstore.exe ツールを使用します。 このツールは、Microsoft Debugging Tools for Windows パッケージの一部です。 シンボル サーバー、symstore ツール、およびインデックス作成シンボルに関する完全なドキュメントは、Debugging Tools for Windows パッケージに含まれています。

ビルド プロセスの一環として、シンボルを独自のシンボル サーバーに直接追加することも、サードパーティ製のライブラリやツールについてチーム全体でシンボルを使用できるようにすることもできます。 シンボル サーバー ファイル共有にシンボルを追加するプロセスは、シンボルのインデックス作成と呼ばれます。 シンボルのインデックスを作成する一般的な方法は 2 つあります。 シンボル ファイルをシンボル サーバーにコピーできます。 または、シンボルの場所へのポインターをシンボル サーバーにコピーできます。 古いビルドを含むアーカイブ フォルダーがある場合は、シンボルを複製する代わりに、既に共有上にある PDB ファイルへのポインターのインデックスを作成することができます。 シンボルのサイズは数十メガバイトになる場合があるため、開発を通じてプロジェクトのすべてのビルドをアーカイブするために必要な領域の量を事前に計画することをお勧めします。 シンボルへのポインターのみのインデックスを作成する場合は、古いビルドを削除したり、ファイル共有の名前を変更したりすると、問題が発生する可能性があります。

たとえば、2006 年 10 月の DirectX SDK から取得した c:\dxsym\Extras\Symbols 内のすべてのシンボルを、\\mainserver\symbols という名前のシンボル サーバー ファイル共有に再帰的にインデックス作成するには、次のコマンドを使用します。

"c:\Program Files\Debugging Tools for Windows\symstore" add /f "C:\dxsym\Extras\Symbols\*.pdb"
/s \\mainserver\symbols /t "October 2006 DirectX SDK " /r

/t "コメント" パラメーターは、シンボルを追加したトランザクションに説明を追加するために使用されます。 これは、シンボルに対して管理タスクを実行する場合に役に立ちます。

ベスト プラクティス

  • チーム、会社、または製品用に独自のシンボル サーバー ファイル共有を設定します。
  • ローカル キャッシュ、プライベート シンボル サーバー、および Microsoft シンボル サーバーを指す _NT_SYMBOL_PATH を設定します。
  • デバッグしているコンポーネントのシンボルをデバッガーが読み込めない場合は、コンポーネントの所有者に連絡してシンボル (少なくともストリップされた PDB) をリクエストします。
  • 生成されるビルドごとにプライベート シンボル サーバー上でシンボルにインデックスを付ける自動ビルド システムを設定します。 配布するビルドがこのプロセスによって生成されるビルドになるようにします。 これにより、常にシンボルを使用して問題をデバッグできるようになります。
  • Visual Source Safe または Perforce ベースのソース管理システムからデバッガーが特定のモジュールのソース コードに直接アクセスできるようにシンボル サーバーを設定します。 リリース バージョンのゲームのソース ファイル情報とシンボルにインデックスが付けられている場合、シンボル サーバーにアクセスできる開発者は、ビルド環境や古いバージョンのソース ファイルを開発用コンピューターに保持することなく、報告された問題のソース レベルの完全なデバッグを行うことができます。 ソース ファイル情報のインデックスを作成できるようにシンボル サーバーを設定するには、ソース サーバーのドキュメントを参照してください。