dump文件生成
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <Dbghelp.h>
#include <iostream>
#include <vector>
#include <tchar.h>
using namespace std;
#pragma comment(lib, "Dbghelp.lib")
///
// Function declarations
//
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
BOOL PreventSetUnhandledExceptionFilter();
LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException);
void RunCrashHandler();
void CreateDump(
LPCTSTR lpstrDumpFilePathName,
EXCEPTION_POINTERS* pep
);
BOOL CALLBACK MyMiniDumpCallback(
PVOID pParam,
const PMINIDUMP_CALLBACK_INPUT pInput,
PMINIDUMP_CALLBACK_OUTPUT pOutput
);
///
// Minidump creation function
//
void CreateDump(LPCTSTR lpstrDumpFilePathName, EXCEPTION_POINTERS* pep)
{
// Open the file
HANDLE hFile = CreateFile(lpstrDumpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
// Create the minidump
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pep;
mdei.ClientPointers = FALSE;
MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback;
mci.CallbackParam = 0;
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory);
BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
hFile, mdt, (pep != 0) ? &mdei : 0, 0, &mci);
if (!rv)
_tprintf(_T("MiniDumpWriteDump failed. Error: %u \n"), GetLastError());
else
_tprintf(_T("Dump created.\n"));
// Close the file
CloseHandle(hFile);
}
else
{
_tprintf(_T("CreateFile failed. Error: %u \n"), GetLastError());
}
}
///
// Custom minidump callback
//
BOOL CALLBACK MyMiniDumpCallback(
PVOID pParam,
const PMINIDUMP_CALLBACK_INPUT pInput,
PMINIDUMP_CALLBACK_OUTPUT pOutput
)
{
BOOL bRet = FALSE;
// Check parameters
if (pInput == 0)
return FALSE;
if (pOutput == 0)
return FALSE;
// Process the callbacks
switch (pInput->CallbackType)
{
case IncludeModuleCallback:
{
// Include the module into the dump
bRet = TRUE;
}
break;
case IncludeThreadCallback:
{
// Include the thread into the dump
bRet = TRUE;
}
break;
case ModuleCallback:
{
// Does the module have ModuleReferencedByMemory flag set ?
if (!(pOutput->ModuleWriteFlags & ModuleReferencedByMemory))
{
// No, it does not - exclude it
wprintf(L"Excluding module: %s \n", pInput->Module.FullPath);
pOutput->ModuleWriteFlags &= (~ModuleWriteModule);
}
bRet = TRUE;
}
break;
case ThreadCallback:
{
// Include all thread information into the minidump
bRet = TRUE;
}
break;
case ThreadExCallback:
{
// Include this information
bRet = TRUE;
}
break;
case MemoryCallback:
{
// We do not include any information here -> return FALSE
bRet = FALSE;
}
break;
case CancelCallback:
break;
}
return bRet;
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
return NULL;
}
BOOL PreventSetUnhandledExceptionFilter()
{
HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
if (hKernel32 == NULL)
return FALSE;
void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
if (pOrgEntry == NULL)
return FALSE;
unsigned char newJump[100];
DWORD dwOrgEntryAddr = (DWORD)pOrgEntry;
dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
DWORD dwNewEntryAddr = (DWORD)pNewFunc;
DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
newJump[0] = 0xE9; // JMP absolute
memcpy(&newJump[1], &dwRelativeAddr, sizeof(pNewFunc));
SIZE_T bytesWritten;
BOOL bRet = WriteProcessMemory(GetCurrentProcess(), pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
return bRet;
}
LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
{
TCHAR szMbsFile[MAX_PATH] = { 0 };
::GetModuleFileName(NULL, szMbsFile, MAX_PATH);
TCHAR* pFind = _tcsrchr(szMbsFile, '\\');
if (pFind)
{
*(pFind + 1) = 0;
_tcscat_s(szMbsFile, _T("CrashDump.dmp"));
CreateDump(szMbsFile, pException);
}
// TODO: MiniDumpWriteDump
FatalAppExit(-1, _T("Fatal Error"));
return EXCEPTION_CONTINUE_SEARCH;
}
void RunCrashHandler()
{
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
}
int main( int argc, char* argv[] )
{
RunCrashHandler();
int a = 0;
int b = 10 / a;
return 0;
}
注意事项
- 1、需要配置debug选项:
- 在C/C++选项->常规->调试信息格式(设置为程序数据库(/Zi))
- 在连接器选项—>调试->生成调试信息(设置为是)
- 在C/C++选项->优化禁用
- 2、 可执行文件(exe)必须找到dbghelp.dll,才能生成Dump文件。这个DLL可以从调试工具包中找到。
- 3、.exe、.pdb、.dump、dbghelp.dll 这四个文件需要放在同一目录下才能调试,dump文件必须与pdb文件匹配。双击dump文件时,VS就可以自动关联到出错代码位置。
- 4、为了获取更多更深入的调试信息,需要把程序优化开关设置成禁用。
SetUnhandledExceptionFilter()
MSDN链接:https://msdn.microsoft.com/en-us/library/windows/desktop/ms680634(v=vs.85).aspx
捕获异常的函数是SetUnhandledExceptionFilter(),该函数用于设置顶级异常函数,当程序中有异常发生并且没有被各个堆栈捕获时,最后将进入该函数设置的异常处理函数。
MiniDumpWriteDump()
MSDN链接:https://msdn.microsoft.com/en-us/library/ms680360(VS.85).aspx
dump文件的信息收集很大程度上取决于 MiniDumpWriteDump 函数中的 MINIDUMP_TYPE 参数的设置以及 MiniDumpCallback 的设置。
MINIDUMP_TYPE参数说明
MSDN链接:https://msdn.microsoft.com/en-us/library/ms680519(v=vs.85).aspx
MiniDumpCallback函数说明
MSDN链接:https://msdn.microsoft.com/en-us/library/ms680358(v=vs.85).aspx
参数比较
根据不同参数的设置可以产生不同的dump文件:
Name | MINIDUMP_TYPE | Dump文件大小 | 备注 |
---|---|---|---|
TinyDump | MiniDumpNormal | 2KB | 去掉了所有线程和模块的信息,只包含异常发生的地址、异常时刻的线程上下文、异常代码。调试器甚至无法加载它. |
MiniDump | MiniDumpWithIndirectlyReferencedMemory MiniDumpScanMemory | 50KB | 它比mindump的标准方式(MiniDumpNormal + no MiniDumpCallback))包含了更多的信息量。它允许查看栈上的引用的数据。但是,这个minidump还缺少一些重要的信息。例如,我们看不到全局标量的值,不能查看堆和TLS中分配的数据(除非他们被线程栈引用了)。 |
MidiDump | MiniDumpWithPrivateReadWriteMemory MiniDumpWithDataSegs MiniDumpWithHandleData MiniDumpWithFullMemoryInfo MiniDumpWithThreadInfo MiniDumpWithUnloadedModules | 2MB | 我们可以得到应用程序的几乎所有信息,包括全局变量的值、堆和TLS的内容、PEB、TEB。我们甚至可以得到句柄信息以及虚拟内存布局。这是一个非常有用的dump,并且不是很大。 |
MaxDump | MiniDumpWithFullMemory MiniDumpWithFullMemoryInfo MiniDumpWithHandleData MiniDumpWithThreadInfo MiniDumpWithUnloadedModules | 40MB | 它给了我们在一个mindump中包含所有信息的可能。 |