Linux 下的 crash dump 处理相对简单,这里不再赘述。

Windows 下稍复杂。常见做法是通过 SEH(结构化异常处理),用 SetUnhandledExceptionFilter 注册顶层异常处理回调,再调用 MiniDumpWriteDump 将进程状态写入 .dmp 文件。不过这种方法并不能覆盖所有崩溃场景,例如多次 delete 同一指针这类错误仍可能漏掉。更完整的方案是在注册表中开启 Windows 自动生成 dump 的功能,与代码层面的捕获配合使用,两者互为补充。

#if _WIN32

#include <windows.h>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")

void myInvalidParameterHandler(const wchar_t* expression,
	const wchar_t* function,
	const wchar_t* file,
	unsigned int line,
	uintptr_t pReserved)
{

	throw 1;
}

LONG CustomCrashHandledExceptionFilter(_EXCEPTION_POINTERS *ExceptionInfo)
{
	CHAR strDumpFile[MAX_PATH] = { 0 };
	SYSTEMTIME tm;
	HANDLE hFile = NULL;

	time_t rawtime;
	struct tm * t;
	time(&rawtime);
	t = localtime(&rawtime);

	sprintf(strDumpFile, "%d%s%d-%04d-%02d-%02d-%02d-%02d-%02d.dmp", sid, name.c_str(), g_Inner_id, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);

	hFile = CreateFileA(strDumpFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile != INVALID_HANDLE_VALUE)
	{
		MINIDUMP_EXCEPTION_INFORMATION ExInfo;
		ExInfo.ThreadId = GetCurrentThreadId();
		ExInfo.ExceptionPointers = ExceptionInfo;
		ExInfo.ClientPointers = NULL;
		BOOL bOK = MiniDumpWriteDump(
			GetCurrentProcess(),
			GetCurrentProcessId(),
			hFile,
			MiniDumpNormal,
			&ExInfo,
			NULL,
			NULL);

		CloseHandle(hFile);
	}
	return EXCEPTION_CONTINUE_SEARCH;
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI EmptySetUnhandledExceptionFilter(
	LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
	return NULL;
}

void myPurecallHandler(void)
{

	throw 1;
}

BOOL HookSetUnhandledExceptionFilter()
{
	HMODULE hKernel32 = LoadLibraryA(("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 = &EmptySetUnhandledExceptionFilter;
	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;
}

class CrashHandler
{
public:
	CrashHandler()
	{
	//	SetErrorMode(SEM_FAILCRITICALERRORS);
		SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)CustomCrashHandledExceptionFilter);
		_purecall_handler old_pure_handle;
		old_pure_handle = _set_purecall_handler(myPurecallHandler);
		_invalid_parameter_handler oldHandler;
		oldHandler = _set_invalid_parameter_handler(myInvalidParameterHandler);
		HookSetUnhandledExceptionFilter();
	}
};
#else
class CrashHandler
{
public:
	CrashHandler()
	{
		assert(false);
	}
};
#endif