クラッシュ時にダンプファイルを出力する

プログラムがクラッシュした際、ダンプファイルを出力するようにしておくと便利なことがあります。
例えば、ユーザー環境でクラッシュした際にダンプファイルを取得できれば、どこに問題があったのか解決しやすくなります。

Windows の構造化例外処理を用い、クラッシュ時にダンプファイルを出力してみます。

ほぼ、これそのままです。
[クラッシュ ダンプの分析](https://msdn.microsoft.com/ja-jp/library/bb204861(v=vs.85).aspx)

# クラッシュ時にダンプファイルを出力する

クラッシュ時に処理を行うには、Windows の構造化例外処理を用います。
https://msdn.microsoft.com/ja-jp/library/swezty51.aspx
メイン関数の中身を *__try* 、 *__except* で囲みます。

ダンプファイルの作成には *MiniDumpWriteDump* を使用します。
https://msdn.microsoft.com/ja-jp/library/windows/desktop/ms680360(v=vs.85).aspx

こんな感じです。

```c++:main.cpp
#include <Windows.h>
#include <dbghelp.h>
#include <shellapi.h>
#include <shlobj.h>
#include <strsafe.h>
#include <tchar.h>

#include <iostream>

int GenerateDump(EXCEPTION_POINTERS* expPtr) {
    TCHAR desktopDir[MAX_PATH];
    SHGetSpecialFolderPath(NULL, desktopDir, CSIDL_DESKTOPDIRECTORY, FALSE);

    SYSTEMTIME localTime;
    GetLocalTime(&localTime);

    TCHAR* appName = _T("dump_test");

    TCHAR dumpPath[MAX_PATH];
    StringCchPrintf(
        dumpPath, MAX_PATH, _T("%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp"),
        desktopDir, appName, localTime.wYear, localTime.wMonth, localTime.wDay,
        localTime.wHour, localTime.wMinute, localTime.wSecond,
        GetCurrentProcessId(), GetCurrentThreadId());

    HANDLE dumpFile =
        CreateFile(dumpPath, GENERIC_READ | GENERIC_WRITE,
                   FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);

    MINIDUMP_EXCEPTION_INFORMATION expInfo;
    expInfo.ThreadId = GetCurrentThreadId();
    expInfo.ExceptionPointers = expPtr;
    expInfo.ClientPointers = TRUE;

    BOOL miniDumpSuccessful =
        MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile,
                          MiniDumpWithDataSegs, &expInfo, NULL, NULL);

    return EXCEPTION_EXECUTE_HANDLER;
}

int main() {
    __try {
        int* badPtr = NULL;
        *badPtr = 0;
    } __except (GenerateDump(GetExceptionInformation())) {
    }

    return 0;
}
```

badPtr をアクセスした際にクラッシュします。

ダンプファイルはデスクトップに **dump_test-<日付>-<時間>-<プロセスID>-<スレッドID>.dmp** の形式で出力されます。

## PDBファイルについて
ビルド時に出力されるPDBファイルはダンプファイルを解析する際に必要になります。
実行ファイルとPDBファイルをともに保存しておきます。

### CMake で生成されるプロジェクトについて

CMake で生成されるプロジェクトの構成には以下のものがあります。

- Debug
- MinSizeRel
- Release
- RelWithDebInfo

このうち、「MinSizeRel」と「Release」についてはPDBファイルが生成されないので、注意が必要です。

# クラッシュダンプを解析する

ユーザー環境などから出力されたダンプファイルを取得出来たら、Visual Studio で解析します。
この時、配布時と同じソースコードが必要になるので、ソースコード管理ツール等でタグ付けしておきましょう。
また、実行ファイル、PDBファイル、ダンプファイルを同一ディレクトリに配置しておくと、デバッグの際 PDB の検索が簡単になります。

.dmp ファイルをダブルクリックすると、次のような画面が表示されます。

![debug_01.png](https://qiita-image-store.s3.amazonaws.com/0/158895/ce045082-7af7-a45e-58d0-8f66a9b21fad.png)

**ネイティブのみでデバッグ** をクリックします。

![debug_02.png](https://qiita-image-store.s3.amazonaws.com/0/158895/77d336a5-2896-cfe2-8fb5-272eed0d885f.png)

デバッガがクラッシュした箇所で停止していることが確認できます。

## ソースコードのパスが違う場合

ビルド環境と開発環境で、ビルドパスが違う場合があります。
その際、ソースファイルの検索画面が表示されるので、ソースのあるディレクトリを指定します。

![debug_03.png](https://qiita-image-store.s3.amazonaws.com/0/158895/42e4b253-2d1c-79e9-6074-56c0bc69ea8b.png)

## ソースコードが変更されている場合

ソースコードが変更されている場合は、次の画面が表示されます。

![debug_04.png](https://qiita-image-store.s3.amazonaws.com/0/158895/1b2de0a1-cd32-7a90-f352-a868fe7fc128.png)

「いいえ」を選択すると、ソースファイルの検索画面が表示されるので、同じバージョンのソースを指定します。

# 参照

https://github.com/benikabocha/minidump_test