クラッシュ ダンプの分析

XNA デベロッパー コネクション (XDC)

2005 年 12 月

リリース前にすべてのバグを見つけることはできません。つまり、例外をスローするバグをリリース前にすべて見つけるのは不可能です。Microsoft では、ユーザーによって発見された例外に関する情報を収集するための関数を Platform SDK で提供しています。MiniDumpWriteDump 関数は、プロセス空間全体を保存することなく、必要なクラッシュ ダンプ情報をファイルに書き込みます。このクラッシュ ダンプ情報ファイルは、ミニダンプと呼ばれます。

この技術関連記事では、以下のトピックについて説明します。

  • ミニダンプの作成
  • スレッド セーフティ
  • コードによるミニダンプの作成
  • Dumpchk.exe の使用
  • ミニダンプの分析
  • まとめ

ミニダンプの作成

ミニダンプの作成に関する基本的なオプションは次のとおりです。

  1. 何もしません。プログラムからハンドルされていない例外がスローされるたびに、Windows が自動的にミニダンプを生成します。ミニダンプの自動生成を利用できるのは、Windows XP と Windows Vista だけです。ユーザーが許可すると、ミニダンプは Microsoft Windows エラー報告 (WER) を通じて開発者ではなく Microsoft に送信されます。開発者は、Microsoft Windows Gaming & Graphics developer relations group に連絡して、WER サービスへのアクセスを手配できます。

    WER を使用するための条件は次のとおりです。

    • 開発者が Authenticode を使用してアプリケーションに署名する必要があります。
    • 開発者が WinQual サービスにアクセスするための VeriSign ID を取得する必要があります。
    • アプリケーションのすべての実行可能ファイルと DLL に、有効な VERSIONINFO リソースが必要です。

    ハンドルされていない例外用にカスタム ルーチンを実装する場合は、例外ハンドラーで ReportFault 関数を使用して、自動生成されたミニダンプも WER に送信するようにしてください。ReportFault 関数では、WER への接続とミニダンプの送信に関連するすべての処理が行われます。WER にミニダンプを送信しないと、Games for Windows の要件に違反します。

    WER のしくみについては、「Windows エラー報告のしくみ」を参照してください。登録の詳細については、MSDN の ISV Zone で「Introducing Windows Error Reporting (Windows エラー報告の概要)」を参照してください。

  2. Microsoft Visual Studio® Team System の製品を使用します。[デバッグ](Debug) メニューの [名前を付けてダンプを保存](Save Dump As) をクリックしてダンプのコピーを保存します。社内でのテストとデバッグには、ローカルに保存したダンプを使用するしかありません。

  3. プロジェクトにコードを追加します。ミニダンプを保存し、開発者に直接送信するための MiniDumpWriteDump 関数と適切な例外処理コードを追加します。この記事では、このオプションを実装する方法を示します。ただし、MiniDumpWriteDump は現時点ではマネージ コードに対して機能しません。また、Windows XP と Windows Vista でしか使用できないので注意してください。

スレッド セーフティ

MiniDumpWriteDump は DBGHELP ライブラリに含まれています。このライブラリはスレッドセーフではないため、MiniDumpWriteDump を使用するプログラムでは MiniDumpWriteDump を呼び出す前にすべてのスレッドを同期する必要があります。

コードによるミニダンプの作成

実際の実装は難しくありません。MiniDumpWriteDump の簡単な使用例を次に示します。

#include <dbghelp.h>
#include <shellapi.h>
#include <shlobj.h>

int GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{
    BOOL bMiniDumpSuccessful;
    WCHAR szPath[MAX_PATH]; 
    WCHAR szFileName[MAX_PATH]; 
    WCHAR* szAppName = L"AppName";
    WCHAR* szVersion = L"v1.0";
    DWORD dwBufferSize = MAX_PATH;
    HANDLE hDumpFile;
    SYSTEMTIME stLocalTime;
    MINIDUMP_EXCEPTION_INFORMATION ExpParam;

    GetLocalTime( &stLocalTime );
    GetTempPath( dwBufferSize, szPath );

    StringCchPrintf( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );
    CreateDirectory( szFileName, NULL );

    StringCchPrintf( szFileName, MAX_PATH, L"%s%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp", 
               szPath, szAppName, szVersion, 
               stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, 
               stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond, 
               GetCurrentProcessId(), GetCurrentThreadId());
    hDumpFile = CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, 
                FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);

    ExpParam.ThreadId = GetCurrentThreadId();
    ExpParam.ExceptionPointers = pExceptionPointers;
    ExpParam.ClientPointers = TRUE;

    bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), 
                    hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);

    return EXCEPTION_EXECUTE_HANDLER;
}


void SomeFunction()
{
    __try
    {
        int *pBadPtr = NULL;
        *pBadPtr = 0;
    }
    __except(GenerateDump(GetExceptionInformation()))
    {
    }
}

この例は、MiniDumpWriteDump の基本的な使用法と、その呼び出しに必要な最小限の情報を示しています。ダンプ ファイルの名前は開発者が自由に付けられますが、ファイル名が競合しないように、アプリケーションの名前とバージョン番号、プロセスとスレッドの ID、および日付と時刻からファイル名を生成することをお勧めします。また、ミニダンプをアプリケーションとバージョンでグループ化しておくと役に立ちます。ミニダンプのファイル名を区別するために使用する情報の長さは、開発者に任されています。

前述の例のパス名は、GetTempPath 関数を呼び出して一時ファイル用に指定されたディレクトリのパスを取得することで生成されています。このディレクトリは最小の権限を持つユーザー アカウントでも使用でき、不要となったミニダンプがハード ドライブ領域を占有することを防ぎます。

日常のビルド プロセスで製品をアーカイブする場合は、製品の古いバージョンのデバッグが必要となった場合にそれを実行できるように、必ずビルドのシンボルも含めてください。また、シンボルを生成しながらコンパイラの最大限の最適化を維持するための対策を講じる必要があります。そのためには、開発環境でプロジェクトのプロパティを開き、リリース構成に対して以下の操作を行います。

  1. プロジェクトのプロパティ ページの左側で [C/C++] をクリックします。既定では、これによって [全般](General) 設定が表示されます。プロジェクトのプロパティ ページの右側で、[デバッグ情報の形式](Debug Information Format) を [プログラム データベース (/Zi)](Program Database (/Zi)) に設定します。
  2. プロパティ ページに左側で、[リンカー](Linker) を展開し、[デバッグ](Debugging) をクリックします。プロパティ ページの右側で、[デバッグ情報の生成](Generate Debug Info) を [はい (/DEBUG)](Yes (/DEBUG)) に設定します。
  3. [最適化](Optimization) をクリックし、[参照](References) を [参照されないデータを除去する (/OPT:REF)](Eliminate Unreferenced Data (/OPT:REF)) に設定します。
  4. [COMDAT の圧縮](Enable COMDAT Folding) を [冗長な COMDAT を削除する (/OPT:ICF)](Remove Redundant COMDATs (/OPT:ICF)) に設定します。

MSDN に、MINIDUMP_EXCEPTION_INFORMATION 構造体および MiniDumpWriteDump 関数の詳細情報が掲載されています。

Dumpchk.exe の使用

Dumpchk.exe は、ダンプ ファイルが正しく作成されたことを確認するコマンド ライン ユーティリティです。Dumpchk.exe がエラーを生成した場合は、ダンプ ファイルが壊れて分析できない状態になっています。Dumpchk.exe の使用方法については、「Dumpchk.exe を使用してメモリー ダンプ ファイルをチェックする方法」を参照してください。

Dumpchk.exe は Windows XP の製品 CD に収録されており、この CD の Support\Tools\ フォルダーにある Setup.exe を実行することで、システム ドライブ\Program Files\Support Tools\ にインストールできます。

ミニダンプの分析

ミニダンプを開いて分析する作業は、ミニダンプを作成する場合と同様に簡単です。

ミニダンプを分析するには

  1. Visual Studio を起動します。
  2. [ファイル](File) メニューの [プロジェクトを開く](Open Project) をクリックします。
  3. [ファイルの種類](Files of type) を [ダンプ ファイル](Dump Files) に設定し、ダンプ ファイルに移動して選択し、[開く](Open) をクリックします。
  4. デバッガーを実行します。

デバッガーによってシミュレートされたプロセスが作成されます。シミュレートされたプロセスは、クラッシュを起こした命令で停止します。

Microsoft パブリック シンボル サーバーの使用

ドライバレベルまたはシステムレベルのクラッシュのスタックを取得するには、Microsoft パブリック シンボル サーバーをポイントするように Visual Studio を構成する必要があります。

Microsoft シンボル サーバーへのパスを設定するには

  1. [デバッグ](Debug) メニューの [オプション](Options) をクリックします。
  2. [オプション](Options) ダイアログ ボックスで、[デバッグ](Debugging) ノードを開き、[シンボル](Symbols) をクリックします。
  3. デバッグ時にシンボルを手動で読み込む場合を除き、[シンボルが手動で読み込まれるときのみ上記の場所を探す](Search the above locations only when symbols are loaded manually) が選択されていないことを確認します。
  4. リモート シンボル サーバーのシンボルを使用している場合は、シンボルのコピー先としてローカル ディレクトリを指定することでパフォーマンスを改善できます。そのためには、[シンボル サーバーからシンボルをキャッシュするディレクトリ](Cache symbols from symbol server to this directory) にパスを入力します。Microsoft パブリック シンボル サーバーに接続するには、この設定を有効にする必要があります。リモート コンピューターでプログラムをデバッグしている場合は、キャッシュ ディレクトリがリモート コンピューター上のディレクトリを参照するので注意してください。
  5. [OK](OK) をクリックします。
  6. Microsoft パブリック シンボル サーバーを使用しているので、[使用許諾契約書](End User License Agreement) ダイアログ ボックスが表示されます。[はい](Yes) をクリックして契約書に同意し、シンボルをローカル キャッシュにダウンロードします。

WinDbg によるミニダンプのデバッグ

Windows デバッグ ツールに含まれている WinDbg デバッガーを使用してミニダンプをデバッグすることもできます。WinDbg でデバッグする場合は、Visual Studio を使用する必要がありません。Windows デバッグ ツールをダウンロードするには、Windows Hardware Developer Central で「Windows 用デバッグ ツール」を参照してください。

Windows デバッグ ツールをインストールした後に、WinDbg でシンボル パスを入力する必要があります。

WinDbg でシンボル パスを入力するには

  1. [ファイル](File) メニューの [シンボル パス](Symbol Path) をクリックします。

  2. [シンボル検索パス](Symbol Search Path) ウィンドウで、次のように入力します。

    srv*c:\cache*http://msdl.microsoft.com/download/symbols;

ミニダンプでのコピー防止ツールの使用

開発者は、コピー防止スキームがミニダンプにどのように影響するかを認識しておく必要があります。ほとんどのコピー防止スキームには独自のスクランブル解除ツールが用意されており、MiniDumpWriteDump でこのようなツールをどのように使用するかは開発者に任されています。

まとめ

MiniDumpWriteDump 関数は、製品がリリースされた後にバグを収集して解決するための非常に便利なツールです。開発者は、MiniDumpWriteDump を使用するカスタムの例外ハンドラーを作成することで、情報収集をカスタマイズし、デバッグ プロセスを強化することができます。この関数は、C++ ベースのあらゆるプロジェクトで使用できる柔軟性を備えているので、プロジェクトの安定化プロセスに組み込むことを検討してください。