Windowsで不要なデータを除いたダンプを作成する
概要
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