CrashReport,BugReport的核心--創建dump文件

// minidmp.h
#pragma once

#include <windows h="">
#include <imagehlp h="">
#include <stdlib h="">
#include <strsafe h="">
#pragma comment(lib, "dbghelp.lib")

inline BOOL IsDataSectionNeeded(const WCHAR *pModuleName)
{
	if(pModuleName == 0)
	{
		return FALSE;
	}
	
	WCHAR szFileName[_MAX_FNAME] = L"";
	_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
	if(wcsicmp(szFileName, L"ntdll") == 0)
		return TRUE;
		
	return FALSE;
}

inline BOOL CALLBACK MiniDumpCallback(PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
	if(pInput == 0 || pOutput == 0)
		return FALSE;
		
	switch(pInput->CallbackType)
	{
		case ModuleCallback:
			if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
				if(!IsDataSectionNeeded(pInput->Module.FullPath))
					pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
			
			break;
					
		case IncludeModuleCallback:
		case IncludeThreadCallback:
		case ThreadCallback:
		case ThreadExCallback:
			return TRUE;
			
		default:
			break;
	}
	
	return FALSE;
}

//創建Dump文件
inline void CreateMiniDump(EXCEPTION_POINTERS *pep, LPCWSTR strFileName)
{
	HANDLE hFile = CreateFileW(strFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
	{
		MINIDUMP_EXCEPTION_INFORMATION mdei;
		mdei.ThreadId           = GetCurrentThreadId();
		mdei.ExceptionPointers  = pep;
		mdei.ClientPointers     = FALSE;
		MINIDUMP_CALLBACK_INFORMATION mci;
		mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
		mci.CallbackParam       = 0;
		MINIDUMP_TYPE mdt       = (MINIDUMP_TYPE)0x0000ffff;
		MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, NULL, &mci);
		CloseHandle(hFile);
	}
}

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)
{
	WCHAR szPath[MAX_PATH];
	WCHAR szFileName[MAX_PATH];
	WCHAR *szAppName = L"DumpFile";
	HANDLE hDumpFile;
	SYSTEMTIME stLocalTime;
	MINIDUMP_EXCEPTION_INFORMATION ExpParam;
	GetLocalTime( &stLocalTime );
	//GetTempPath( dwBufferSize, szPath );
	GetModuleFileNameW(NULL, szPath, MAX_PATH);
	wchar_t *pstr = wcsrchr(szPath, '\\');
	memset(pstr + 1, 0, 2);
	
	StringCchPrintfW( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );
	CreateDirectoryW( szFileName, NULL );
	StringCchPrintfW( szFileName, MAX_PATH, L"%s%s\\%04d%02d%02d-%02d%02d%02d.dmp", szPath, szAppName,
	                  stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
	                  stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
	                  
	CreateMiniDump(pException, szFileName);
	// TODO: 這裏可以將dump文件通過郵件發送給開發者,也可以通過http發送給服務端,服務端管理dump文件
	FatalAppExit(-1,  _T("*** Unhandled Exception! ***"));
	
	return EXCEPTION_CONTINUE_SEARCH;
}

//運行異常處理
void RunCrashHandler()
{
	SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
	PreventSetUnhandledExceptionFilter();
}
// Test.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include "minidmp.h"

void CrashTest()
{
	strcpy(NULL,"adfadfg");
}

int _tmain(int argc, _TCHAR* argv[])
{
	//設置異常處理回調函數
	RunCrashHandler();

	CrashTest();

	getchar();
	return 0;
}

核心就是這樣了,注意點:程序發佈時記得帶上dbghelp.dll,這個是系統dll,開發機上肯定都會有。剩下的就是把dump文件上傳到服務器或者直接使用郵件發到指定郵箱了。

有了dump文件,結合發佈版本的pdb文件以及源代碼就能很快定位到崩潰點了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章