動作中のプロセスのダンプファイルを取得する方法

動作中のプロセスのダンプファイルを取得する方法、具体的には次の方法について説明します。

  • タスクマネージャーを使う(GUI)
  • ProcDump を使う(コマンドライン)
  • 自作プログラムを使う

例外発生時に自動的にダンプファイルを取得する方法ではありません。また、カーネルダンプではなくユーザーダンプの話です。

動作確認環境

  • Windows 11 Home 22H2
  • Visual Studio Community 2022 (Visual C++)
  • ProcDump v11.0

タスクマネージャーを使う(GUI)

Windows 標準のタスクマネージャで、任意のプロセスのダンプファイルを取得できます。このとき、対象のプロセスのビット数とタスクマネージャーのビット数を合わせることが重要です。

実行中のプロセスのビット数は、タスクマネージャーの[詳細]タブ – [プラットフォーム]列で確認できます。下図では、「test1.exe」が 64 ビット、「test2.exe」が 32 ビットです。

タスクマネージャーに[プラットフォーム]列がない場合は、ヘッダ領域を右クリックして[列の選択]を選び、一覧から[プラットフォーム]を選択してください。

対象のプロセスが 64 ビットだった場合、64 ビット版のタスクマネージャー「C:\Windows\System32\Taskmgr.exe」でダンプファイルを取得します。

タスクバーを右クリックして[タスクマネージャー]を選択したり、[Ctrl]+[Shift]+[Esc] キーを押したりして普通にタスクマネージャーを起動すると、64 ビット版が起動します。ただし、すでに 32 ビット版のタスクマネージャーが起動していると 64 ビット版は起動せず、32 ビット版がアクティブになるだけなので要注意です。

ビット数の一致を確認後、[詳細]タブから目的のプロセスを右クリックして「メモリダンプファイルの作成]を選択すると、「%LOCALAPPDATA%\Temp」内にダンプファイルが生成されます。

対象のプロセスが 32 ビットだった場合、32 ビット版のタスクマネジャー「C:\Windows\SysWOW64\Taskmgr.exe」をエクスプローラーなどから起動し、ダンプファイルを取得します。ただし、すでに 64 ビット版のタスクマネージャーが起動していると 32 ビット版は起動せず、64 ビット版がアクティブになるだけなので要注意です。

ビット数の一致を確認後、[詳細]タブから目的のプロセスを右クリックして「メモリダンプファイルの作成]を選択すると、「%LOCALAPPDATA%\Temp」内にダンプファイルが生成されます。

タスクマネージャーが生成するダンプファイルは、以下のフラグが有効になっていました。

Flags           421826
                0002 MiniDumpWithFullMemory
                0004 MiniDumpWithHandleData
                0020 MiniDumpWithUnloadedModules
                0800 MiniDumpWithFullMemoryInfo
                1000 MiniDumpWithThreadInfo
                20000 MiniDumpIgnoreInaccessibleMemory
                400000 MiniDumpWithIptTrace

ProcDump を使う(コマンドライン)

コマンドラインからダンプファイルを取得する場合は、ProcDump を使うのがいいでしょう。

マイクロソフトのサイト [1] から Sysinternals の「ProcDump.zip」をダウンロードし、任意のフォルダに展開します。

初回実行時、ライセンスの確認を求めてくるので[Agree]ボタンを押します。

あるいは、「-accepteula」オプションを付けて一度実行します。

C:\tool\Procdump> procdump -accepteula

動作中のプロセスのフルダンプを取得するには、「procdump -ma」に続いて目的のプロセスのファイル名かプロセス ID を指定します。

C:\tool\Procdump> procdump -ma test1.exe
......
[HH:MM:SS] Dump 1 initiated: C:\tool\Procdump\test1.exe_YYMMDD_HHMMSS.dmp
[HH:MM:SS] Dump 1 writing: Estimated dump file size is 9 MB.
[HH:MM:SS] Dump 1 complete: 9 MB written in 0.0 seconds
[HH:MM:SS] Dump count reached.
C:\tool\Procdump> procdump -ma 9160
......
[HH:MM:SS] Dump 1 initiated: C:\tool\Procdump\test1.exe_YYMMDD_HHMMSS.dmp
[HH:MM:SS] Dump 1 writing: Estimated dump file size is 9 MB.
[HH:MM:SS] Dump 1 complete: 9 MB written in 0.0 seconds
[HH:MM:SS] Dump count reached.

対象のプロセスが 64 ビットか 32 ビットかを気にする必要はありません。

ProcDump には、ダンプファイルの種類(フラグ)を変えたり、ダンプ取得の条件を変えたりできる非常に豊富なオプションがあるので、必要に応じてマイクロソフトのサイトなどを参照ください。

なお、ProcDump -ma で生成されるダンプファイルは、以下のフラグが有効になっていました。

Flags           461826
                0002 MiniDumpWithFullMemory
                0004 MiniDumpWithHandleData
                0020 MiniDumpWithUnloadedModules
                0800 MiniDumpWithFullMemoryInfo
                1000 MiniDumpWithThreadInfo
                20000 MiniDumpIgnoreInaccessibleMemory
                40000 MiniDumpWithTokenInformation
                400000 MiniDumpWithIptTrace

自作プログラムを使う

最後に、自作のプログラムでダンプファイルを取得してみましょう。
簡単のため、引数は PID のみ、ダンプファイルの名前は「a.dmp」固定とします。
以下、「MyDump.c」として書いてみました。MiniDumpWriteDump() が核となる Windows API になります。

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

int main(int argc, char *argv[])
{
    int iResult = -1;
    HANDLE hFile = INVALID_HANDLE_VALUE;
    HANDLE hProcess = NULL;
    char *pcFilename = "a.dmp";
    BOOL bFileCreated = FALSE;
    BOOL bDumpWritten = FALSE;

    // 引数の数のチェック
    if (argc != 2)
    {
        printf("usage: MyDump.exe <pid>\n");
        iResult = 0;
        goto L_CleanUp;
    }
    
    // 引数からプロセス ID を取得
    int pid = atoi(argv[1]);
    printf("pid=%d\n", pid);

    // プロセスを開く
    hProcess = OpenProcess(
        PROCESS_ALL_ACCESS,
        TRUE,
        pid);
    if (hProcess == NULL)
    {
        printf("OpenProcess, err=%d.\n", GetLastError());
        iResult = 1;
        goto L_CleanUp;
    }

    // ファイルを新規作成する
    hFile = CreateFile("a.dmp",
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("CreateFile, err=%d\n", GetLastError());
        iResult = 2;
        goto L_CleanUp;
    }
    bFileCreated = TRUE; // ファイル作成成功

    // ダンプを出力する
    BOOL bDumpSuccess = MiniDumpWriteDump(hProcess,
        pid,
        hFile,
        MiniDumpWithFullMemory
        | MiniDumpWithHandleData
        | MiniDumpWithUnloadedModules
        | MiniDumpWithFullMemoryInfo
        | MiniDumpWithThreadInfo
        | MiniDumpIgnoreInaccessibleMemory
        | MiniDumpWithTokenInformation
        | MiniDumpWithIptTrace,
        NULL,
        NULL,
        NULL);
    if (! bDumpSuccess)
    {
        printf("MiniDumpWriteDump, err=%d\n", GetLastError());
        iResult = 3;
        goto L_CleanUp;
    }
    bDumpWritten = TRUE; // ダンプ出力成功
    printf("%s, created.\n", pcFilename);

    // 正常終了
    iResult = 0;
    goto L_CleanUp;

L_CleanUp:
    // ファイルを閉じる
    if (hFile != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hFile);
    }
    
    // ファイルを生成したがダンプの出力に失敗した場合は削除する
    if (bFileCreated == TRUE && bDumpWritten == FALSE)
    {
        DeleteFile(pcFilename);
    }

    // プロセスを閉じる
    if (hProcess != NULL)
    {
        CloseHandle(hProcess);
    }

    return iResult;
}

64 ビット版 Visual C++ でビルドします。

C:\tmp> cl /Od MyDump.c dbghelp.lib
Microsoft(R) C/C++ Optimizing Compiler Version 19.31.31105 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

MyDump.c
Microsoft (R) Incremental Linker Version 14.31.31105.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:MyDump.exe
MyDump.obj
dbghelp.lib

「MyDump.exe」が生成されました。

メモ帳のダンプファイルを取得してみましょう。
起動中のメモ帳の PID である 7604 を引数に「MyDump.exe」を実行します。

C:\tmp> MyDump.exe 7604
pid=7604
a.dmp, created.

正しく取得できたようです。

C:\tmp> dir a.dmp
......  347,832,808 a.dmp ......

C:\tmp> dumpchk a.dmp
......
Windows 10 Version 22621 MP (8 procs) Free x64
......
Flags           461826
                0002 MiniDumpWithFullMemory
                0004 MiniDumpWithHandleData
                0020 MiniDumpWithUnloadedModules
                0800 MiniDumpWithFullMemoryInfo
                1000 MiniDumpWithThreadInfo
                20000 MiniDumpIgnoreInaccessibleMemory
                40000 MiniDumpWithTokenInformation
                400000 MiniDumpWithIptTrace

Streams:
Stream 0: type ThreadListStream (3), size 000002A4, RVA 00000684
  14 threads
  RVA 00000688, ID 4280, Teb:000000180AD5D000
  RVA 000006B8, ID 1E24, Teb:000000180AD65000
  RVA 000006E8, ID C58, Teb:000000180AD67000
......
Stream 5: type Memory64ListStream (9), size 00003600, RVA 00027BE8
  863 memory ranges
  RVA 0x2B1E8 BaseRva
  range#    RVA      Address             Size
       0 0002B1E8    00000000`11cd0000   00000000`00001000
       1 0002C1E8    00000000`7ffe0000   00000000`00001000
       2 0002D1E8    00000000`7ffed000   00000000`00001000
       3 0002E1E8    00000018`0ab87000   00000000`00009000
       4 000371E8    00000018`0ad5c000   00000000`00003000
.....
        windir=C:\WINDOWS
        ZES_ENABLE_SYSMAN=1
Finished dump check

なお、32 ビット版 Visual C++ でビルドすると、32 ビットプログラムのダンプファイルが取得できました。

ちなみに本プログラムを作成中、生成物が OS によってウィルスとみなされ隔離される事象が何度か発生しました。「/Od /Zi」オプション付きでビルドすると発生しやすいようでしたが、正確な判断基準は不明です。

おわりに

動作中のプロセスのダンプファイルを取得する 3 つの方法について見てきました。故障解析で役に立つことがあるかもしれません。

参考文献

[1] Microsoft, Sysinternals ProcDump v11.0
https://learn.microsoft.com/ja-jp/sysinternals/downloads/procdump