C++生成Dump文件

C++开发的应用程序避免不了各种崩溃,空指针,野指针,栈溢出等等。为了方便定位问题,我们一般在程序崩溃的时候自动生成一个dump文件,然后通过dump文件结合pdb来定位问题。c++生成dump文件的代码网上有很多,但大多使用起来不那么方便;通过本文介绍的方法只需要使用2个宏就可以实现自动生成dump的功能。

功能代码包含在EasyDump.h和EasyDump.cpp 2个文件中:


EasyDump.h

#pragma once

#include 
#include 
#include 

namespace cpp4j {
	typedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

	int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS pExceptPtrs, const TCHAR *szDumpNamePrefix);
	TCHAR *lstrrchr(LPCTSTR string, int ch);
	void DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo);
}

#define WINMAIN_BEGIN(szDumpNamePrefix) \
    int __96A9695E_RUN_WINMAIN_FUNC(HINSTANCE hInstance, LPTSTR lpCmdLine);\
    LONG WINAPI __96A9695E_UnhandledExceptionHandler( _EXCEPTION_POINTERS *pExceptionInfo ) \
    { \
        OutputDebugString(TEXT("Create a dump file sine an exception occurred in sub-thread.\n")); \
        int iRet = cpp4j::RecordExceptionInfo(pExceptionInfo, szDumpNamePrefix); \
        return iRet; \
    } \
    int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) \
    { \
        UNREFERENCED_PARAMETER(hPrevInstance); \
        UNREFERENCED_PARAMETER(nCmdShow); \
        ::SetUnhandledExceptionFilter( __96A9695E_UnhandledExceptionHandler );\
        int ret = 0;\
        __try\
        {\
            ret = __96A9695E_RUN_WINMAIN_FUNC(hInstance, lpCmdLine);\
        }\
        __except(cpp4j::RecordExceptionInfo(GetExceptionInformation(), szDumpNamePrefix))\
        {\
            OutputDebugString(TEXT("Create a dump file sine an exception occurred in main-thread.\n")); \
        }\
        return ret;\
    }\
    int __96A9695E_RUN_WINMAIN_FUNC(HINSTANCE hInstance, LPTSTR lpCmdLine) \
    {

#define WINMAIN_END }




#define MAIN_BEGIN(szDumpName) \
    int __96A9695E_RUN_MAIN_FUNC(int argc, _TCHAR* argv[]);\
    LONG WINAPI __96A9695E_UnhandledExceptionHandler( _EXCEPTION_POINTERS *pExceptionInfo ) \
    { \
        OutputDebugString(TEXT("Create a dump file since an exception occurred in sub-thread.\n")); \
        int iRet = cpp4j::RecordExceptionInfo(pExceptionInfo, szDumpName); \
        return iRet; \
    } \
    int _tmain(int argc, _TCHAR* argv[])\
    { \
        ::SetUnhandledExceptionFilter( __96A9695E_UnhandledExceptionHandler );\
        int ret = 0;\
        __try\
        {\
            ret = __96A9695E_RUN_MAIN_FUNC(argc, argv);\
        }\
        __except(cpp4j::RecordExceptionInfo(GetExceptionInformation(), szDumpName))\
        {\
            OutputDebugString(TEXT("Create a dump file since an exception occurred in main-thread.\n")); \
        }\
        return ret;\
    }\
    int __96A9695E_RUN_MAIN_FUNC(int argc, _TCHAR* argv[]) \
    {

#define MAIN_END }



EasyDump.cpp

#include "EasyDump.h"
#include 
#pragma comment(lib, "Dbghelp.lib")

namespace cpp4j {
	TCHAR *lstrrchr(LPCTSTR string, int ch) {
		TCHAR *start = (TCHAR *)string;

		while (*string++)
			;

		while (--string != start && *string != (TCHAR)ch)
			;

		if (*string == (TCHAR)ch)
			return (TCHAR *)string;

		return NULL;
	}

	inline void DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo) {
		if (!excpInfo) {
			static int iTimes = 0;
			if (iTimes++ > 1)
				return;

			__try {
				RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
			}
			__except (DumpMiniDump(hFile, GetExceptionInformation()),
				EXCEPTION_CONTINUE_EXECUTION) {
			}
		}
		else {
			MINIDUMP_EXCEPTION_INFORMATION eInfo;
			eInfo.ThreadId = GetCurrentThreadId();
			eInfo.ExceptionPointers = excpInfo;
			eInfo.ClientPointers = FALSE;

			MiniDumpWriteDump(
				GetCurrentProcess(),
				GetCurrentProcessId(),
				hFile,
				MiniDumpNormal,
				excpInfo ? &eInfo : NULL,
				NULL,
				NULL);
		}
	}

	int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS pExceptPtrs, const TCHAR *szDumpNamePrefix) {
		static bool bFirstTime = true;

		if (!bFirstTime)
			return EXCEPTION_CONTINUE_SEARCH;

		bFirstTime = false;

		// Dmp文件命名:前缀_年月日.时.分.秒.毫秒.dmp
		//
		TCHAR szLocalTime[50] = { 0 };
		SYSTEMTIME st;
		GetLocalTime(&st);
		StringCchPrintf(szLocalTime, 50, TEXT("%04d%02d%02d.%02d.%02d.%02d.%04d"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);

		TCHAR szExeDir[MAX_PATH + 1] = { 0 };

		GetModuleFileName(NULL, szExeDir, MAX_PATH);

		if (TCHAR *p = lstrrchr(szExeDir, TEXT('\\'))) {
			*(p + 1) = 0;
		}

		TCHAR szDumpFileName[MAX_PATH + 1] = { 0 };
		_stprintf_s(szDumpFileName, MAX_PATH, TEXT("%s%s_%s.dmp"), szExeDir, szDumpNamePrefix, szLocalTime);

		HANDLE hMiniDumpFile = CreateFile(
			szDumpFileName,
			GENERIC_WRITE,
			0,
			NULL,
			CREATE_ALWAYS,
			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
			NULL);

		if (hMiniDumpFile != INVALID_HANDLE_VALUE) {
			DumpMiniDump(hMiniDumpFile, pExceptPtrs);

			CloseHandle(hMiniDumpFile);
			hMiniDumpFile = NULL;
		}

		return EXCEPTION_EXECUTE_HANDLER;
	}
}



使用方法:

#include "EasyDump.h"

MAIN_BEGIN(TEXT("Test"))

int i = 0;
int *p = &i;
p = NULL;
*p = 5;

return 0;

MAIN_END


使用MAIN_BEGIN替换main,WINMAIN_BEGIN替换WinMain即可。

MAIN_BEGIN中的参数为生成的dump文件的前缀,dump文件命名方式:Dmp文件命名: 前缀_年月日.时.分.秒.毫秒.dmp

如上面的代码,会在程序的当前目录生成一个名为Test_20171101.14.49.57.0264.dmp的dump文件。

你可能感兴趣的:(C/C++,软件调试技巧)