HOME IP Messenger FastCopy Tech-memo Diary Twitter [English]

Windowsで不要なデータを除いたダンプを作成する

白水啓章
作成 2018/02/06

概要

MiniDumpWriteDumpを使ったダンプ出力で、必要ない巨大テンポラリ領域等を除外する方法です。

詳細

XP以降で増設されたMiniDumpWriteDump APIを利用することで、例外発生時や任意のタイミングで、VisualStudioやWinDbgで解析可能なダンプファイルを出力することが出来ます。

この時、ヒープやコード等の種類毎に、ダンプに含めるかの制御もMiniDumpWriteDumpの引数として渡すMINIDUMP_TYPEフラグの組み合わせで可能になっており、全てのデータをダンプするにはMiniDumpWithFullMemoryフラグを指定します。
(さすがに full だと無駄なデータが多すぎ(mmapされたdll領域等も含む)なので、個人的には MiniDumpWithPrivateReadWriteMemory + α程度を利用しています)

この時、巨大なテンポラリ領域等が存在すると、巨大なダンプが作成されることになります。
それを避けるためには、MiniDumpWriteDumpを呼び出す際に渡す、MINIDUMP_CALLBACK_INFORMATION にコールバック関数を設定します。

// SetUnhandledExceptionFilternで設定したハンドラ等
LONG WINAPI UnhandledExceptionFilter(EXCEPTION_POINTERS *ep)
{
  HANDLE hFile = CreateFile("exception.dmp", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);

  MINIDUMP_EXCEPTION_INFORMATION mei={};
  mei.ThreadId = GetCurrentThreadId();
  mei.ExceptionPointers = ep;

  MINIDUMP_CALLBACK_INFORMATION  mci={};
  mci.CallbackRoutine = MyMiniDumpCallback; // コールバック設定

  MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
    (MINIDUMP_TYPE)MiniDumpWithFullMemory, &mei, NULL, &mci);
  :
}

その上で、コールバック呼び出しで mci->CallbackType が RemoveMemoryCallback の時を狙って、除外したいポインタアドレスとそのサイズを設定します。

BOOL MyMiniDumpCallback(
  void *param
  const PMINIDUMP_CALLBACK_INPUT  mci,
        PMINIDUMP_CALLBACK_OUTPUT mco
) {
  if (mci->CallbackType == RemoveMemoryCallback) {
    mco->MemoryBase = (ULONG64)garbage_ptr; // 除外したいメモリアドレス
    mco->MemorySize = (LONG)garbage_size;
  }
  return TRUE;
}

MemoryBase/MemorySize を 0以外にセットすると、再び、RemoveMemoryCallback が呼ばれるため、複数のエリアの除外が可能になっています。
(なお、mco->MemorySizeはLONGのため、4GB以上のエリアの場合、ポインタをずらしながら複数回呼びます)

結果

これにより、FastCopyのような巨大メインバッファを確保するアプリケーションでも、メインバッファを除外してダンプを出すことで、気軽にダンプ利用ができるようになりました。
(個人的には、スタック内容とレジスタをダンプした16進数を追うより、解析が100倍楽になりました)

なお、PDBとダンプファイルを使った解析は、下記のURLが参考になるかもしれません。
DSAS開発者の部屋 Win32 プログラムのデバッグTips

参考リンク(MSDN)

MiniDumpWriteDump
MINIDUMP_TYPE
MiniDumpCallback callback
MINIDUMP_CALLBACK_TYPE