読者です 読者をやめる 読者になる 読者になる

プロセスダンプするプログラムを作成してみよう!

 Windows環境においてプロセスをダンプするにはMiniDumpWriteDumpを使います。

BOOL WINAPI MiniDumpWriteDump(
  _In_  HANDLE hProcess,
  _In_  DWORD ProcessId,
  _In_  HANDLE hFile,
  _In_  MINIDUMP_TYPE DumpType,
  _In_  PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  _In_  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  _In_  PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

 第1、第2引数は対象となるプロセス、第3引数は作成するダンプファイルのハンドル、そしてダンプファイルにどこまでの情報を含めるかは、第4引数DumpTypeで指定します。__try__exceptでエラーを検知してそのタイミングでMiniDumpWriteDumpを呼び出し、ダンプファイルを作成してみましょう。

 サンプルコードは以下になります(全サンプルファイルはGitHubにあります)。

// dump01.cpp
// 

#include <windows.h>
#include <tchar.h>
#include <dbghelp.h>
#include <stdio.h>
#include <crtdbg.h>

#pragma comment (lib, "dbghelp.lib")

void CreateDump(EXCEPTION_POINTERS *pep, int level)
{
    TCHAR szFilePath[1024];
    GetModuleFileName(NULL, szFilePath, sizeof(szFilePath));

    _tcscat_s(szFilePath, _T(".dmp"));

    HANDLE hFile = CreateFile(szFilePath, 
        GENERIC_READ | GENERIC_WRITE, 
        0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile == INVALID_HANDLE_VALUE){
        _tprintf(_T("CreateFile failed. Error: %u \n"), 
            GetLastError());
        return;
    }
    
    MINIDUMP_EXCEPTION_INFORMATION mdei;

    mdei.ThreadId          = GetCurrentThreadId();
    mdei.ExceptionPointers = pep;
    mdei.ClientPointers    = FALSE;

    MINIDUMP_CALLBACK_INFORMATION mci;

    mci.CallbackRoutine    = NULL;
    mci.CallbackParam      = 0;

    MINIDUMP_TYPE mdt;

    switch(level)
    {
    case 0:
        mdt = (MINIDUMP_TYPE)(MiniDumpNormal);
        break;
    case 1:
        mdt = (MINIDUMP_TYPE)(
            MiniDumpWithIndirectlyReferencedMemory |
            MiniDumpScanMemory);
        break;
    case 2:
        mdt = (MINIDUMP_TYPE)(
            MiniDumpWithPrivateReadWriteMemory | 
            MiniDumpWithDataSegs | 
            MiniDumpWithHandleData |
            MiniDumpWithFullMemoryInfo | 
            MiniDumpWithThreadInfo | 
            MiniDumpWithUnloadedModules);
        break;
    default:
        mdt = (MINIDUMP_TYPE)(
            MiniDumpWithFullMemory | 
            MiniDumpWithFullMemoryInfo |
            MiniDumpWithHandleData | 
            MiniDumpWithThreadInfo | 
            MiniDumpWithUnloadedModules);
        break;
    }
    
    BOOL rv = MiniDumpWriteDump(
        GetCurrentProcess(), GetCurrentProcessId(), 
        hFile, mdt, (pep != NULL) ? &mdei : NULL, NULL, &mci);
    if(rv == FALSE){
        _tprintf(_T("MiniDumpWriteDump failed. Error: %u \n"), 
            GetLastError());
    }
    
    CloseHandle(hFile);
    return;
}

int main(int argc, char* argv[]) 
{
    int dumplevel = 0; // dumplevel: 0-3

    if(argc >= 2)
        dumplevel = atoi(argv[1]);

    __try
    {
        *(DWORD *)0 = 0x12345678;

    }__except(
        CreateDump(GetExceptionInformation(), dumplevel), 
        EXCEPTION_EXECUTE_HANDLER)
    {
        _tprintf( _T("Dumped!!\n"));
    }

    return 0; 
}

 VisualStudio 2010 Express版でビルドしました。

 引数に0から3までのいずれかの値を指定するとダンプファイル(ファイル名に.dmpを追加したもの)が作成されます。

C:\dumpXX\Release>dump01.exe 0
Dumped!!
※同じフォルダにdump01.exe.dmpというファイルが作られる

 引数には、ダンプファイルにどれだけの情報を含めるかを表すレベルを指定します。0が最小ダンプ、3が完全ダンプとなります。それぞれswitch文で分岐しているMINIDUMP_TYPE mdtの値によって決まります。

 このサンプルでは自分自身(自プロセス)をダンプしていますが、MiniDumpWriteDumpに渡すプロセスIDを任意に指定することで他プロセスもダンプできます(dump03.cppを参照のこと)。MiniDumpWriteDumpのhProcessとExceptionParamはNULLでも問題ないようですが、一応セットできるときはセットしておきましょう。

 プロセスダンプに関する詳細は、英語のサイトですがEFFECTIVE MINIDUMPSがかなり詳しいです。