一、引言
dump文件是C++程序發生異常時,保存當時程序運行狀態的文件,是調試異常程序重要的方法,所以程序崩潰時,除了日志文件,dump文件便成了我們查找錯誤的最后一根救命的稻草。windows程序產生dump文件和linux程序產生dump文件的方式不一樣,linux默認是不讓產生core dump文件,只要在用戶自己的~/.bash_profile文件中增加
ulimit -S -c unlimited > /dev/null 2>&1
這樣程序崩潰就可以產生可調試的core dump文件了。但是windows環境就得寫代碼才能實現了。
二、原理
windows程序當遇到異常,沒有try-catch或者try-catch也無法捕獲到的異常時,程序就會自動退出,如果這時候沒有dump文件的話,我們是沒有得到任何程序退出的信息。在windows程序異常退出之前,會預先調用一個在程序中注冊的異常處理回調函數(默認是沒有設置),只要我們在這個回調函數中調用MiniDumpWriteDump函數就可以產生我們想要的dump文件。
三、實現
1.調用SetUnhandledExceptionFilter注冊一個自定義的異常處理回調函數
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
異常處理回調函數的原型
LONG __stdcall MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);
2.CreateFile創建dump文件,調用MiniDumpWriteDump函數往dump文件寫異常信息
- inline?void?CreateMiniDump(PEXCEPTION_POINTERS?pep,?LPCTSTR?strFileName)??
- {??
- ????HANDLE?hFile?=?CreateFile(strFileName,?GENERIC_READ?|?GENERIC_WRITE,??
- ????????FILE_SHARE_WRITE,?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?????=?NULL;??
- ??
- ????????MINIDUMP_CALLBACK_INFORMATION?mci;??
- ????????mci.CallbackRoutine?????=?(MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;??
- ????????mci.CallbackParam???????=?0;??
- ??
- ????????::MiniDumpWriteDump(::GetCurrentProcess(),?::GetCurrentProcessId(),?hFile,?MiniDumpNormal,?(pep?!=?0)???&mdei?:?0,?NULL,?&mci);??
- ??
- ????????CloseHandle(hFile);??
- ????}??
- }??
CreateMiniDump函數是在異常處理回調函數MyUnhandledExceptionFilter中調用的
- LONG?__stdcall?MyUnhandledExceptionFilter(PEXCEPTION_POINTERS?pExceptionInfo)??
- {??
- ????CreateMiniDump(pExceptionInfo,?"core.dmp");??
- ??
- ????return?EXCEPTION_EXECUTE_HANDLER;??
- }??
3.將SetUnhandledExceptionFilter失效
vs2005中,編譯的過程中,編譯器會自動給你的程序加上一句SetUnhandledExceptionFilter(NULL),這就會導致你之前自定義的
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
無效,就有可能不會產生dump文件,因此我們必須在自定義的SetUnhandledExceptionFilter之后,讓之后調用的SetUnhandledExceptionFilter無效。增加以下代碼:
- //?此函數一旦成功調用,之后對?SetUnhandledExceptionFilter?的調用將無效??
- void?DisableSetUnhandledExceptionFilter()??
- {??
- ????void*?addr?=?(void*)GetProcAddress(LoadLibrary("kernel32.dll"),??
- ????????"SetUnhandledExceptionFilter");??
- ??
- ????if?(addr)??
- ????{??
- ????????unsigned?char?code[16];??
- ????????int?size?=?0;??
- ??
- ????????code[size++]?=?0x33;??
- ????????code[size++]?=?0xC0;??
- ????????code[size++]?=?0xC2;??
- ????????code[size++]?=?0x04;??
- ????????code[size++]?=?0x00;??
- ??
- ????????DWORD?dwOldFlag,?dwTempFlag;??
- ????????VirtualProtect(addr,?size,?PAGE_READWRITE,?&dwOldFlag);??
- ????????WriteProcessMemory(GetCurrentProcess(),?addr,?code,?size,?NULL);??
- ????????VirtualProtect(addr,?size,?dwOldFlag,?&dwTempFlag);??
- ????}??
- }??
最終代碼整理:
//minidump.h
- #pragma?once??
- #include?<windows.h>??
- #include?<DbgHelp.h>??
- #include?<stdlib.h>??
- #pragma?comment(lib,?"dbghelp.lib")??
- ??
- #ifndef?_M_IX86??
- #error?"The?following?code?only?works?for?x86!"??
- #endif??
- ??
- 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);??
- ????case?IncludeModuleCallback:??
- ????case?IncludeThreadCallback:??
- ????case?ThreadCallback:??
- ????case?ThreadExCallback:??
- ????????return?TRUE;??
- ????default:;??
- ????}??
- ??
- ????return?FALSE;??
- }??
- ??
- inline?void?CreateMiniDump(PEXCEPTION_POINTERS?pep,?LPCTSTR?strFileName)??
- {??
- ????HANDLE?hFile?=?CreateFile(strFileName,?GENERIC_READ?|?GENERIC_WRITE,??
- ????????FILE_SHARE_WRITE,?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?????=?NULL;??
- ??
- ????????MINIDUMP_CALLBACK_INFORMATION?mci;??
- ????????mci.CallbackRoutine?????=?(MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;??
- ????????mci.CallbackParam???????=?0;??
- ??
- ????????::MiniDumpWriteDump(::GetCurrentProcess(),?::GetCurrentProcessId(),?hFile,?MiniDumpNormal,?(pep?!=?0)???&mdei?:?0,?NULL,?&mci);??
- ??
- ????????CloseHandle(hFile);??
- ????}??
- }??
- ??
- LONG?__stdcall?MyUnhandledExceptionFilter(PEXCEPTION_POINTERS?pExceptionInfo)??
- {??
- ????CreateMiniDump(pExceptionInfo,?"core.dmp");??
- ??
- ????return?EXCEPTION_EXECUTE_HANDLER;??
- }??
- ??
- //?此函數一旦成功調用,之后對?SetUnhandledExceptionFilter?的調用將無效??
- void?DisableSetUnhandledExceptionFilter()??
- {??
- ????void*?addr?=?(void*)GetProcAddress(LoadLibrary("kernel32.dll"),??
- ????????"SetUnhandledExceptionFilter");??
- ??
- ????if?(addr)??
- ????{??
- ????????unsigned?char?code[16];??
- ????????int?size?=?0;??
- ??
- ????????code[size++]?=?0x33;??
- ????????code[size++]?=?0xC0;??
- ????????code[size++]?=?0xC2;??
- ????????code[size++]?=?0x04;??
- ????????code[size++]?=?0x00;??
- ??
- ????????DWORD?dwOldFlag,?dwTempFlag;??
- ????????VirtualProtect(addr,?size,?PAGE_READWRITE,?&dwOldFlag);??
- ????????WriteProcessMemory(GetCurrentProcess(),?addr,?code,?size,?NULL);??
- ????????VirtualProtect(addr,?size,?dwOldFlag,?&dwTempFlag);??
- ????}??
- }??
- ??
- void?InitMinDump()??
- {??
- ????//注冊異常處理函數??
- ????SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);??
- ??
- ????//使SetUnhandledExceptionFilter??
- ????DisableSetUnhandledExceptionFilter();??
- }??
4.測試代碼
//test.cpp
- #include?<iostream>??
- #include?"minidump.h"??
- void?test()??
- {??
- ????std::string?s?=?"abcd";??
- ??
- ????try{??
- ????????s[100]?=?'b';??
- ????}??
- ????catch(std::exception&?e)??
- ????{??
- ????????std::cout?<<?"with?exception:["?<<?e.what()?<<?"]"?<<?std::endl;??
- ????}??
- ????catch(...)??
- ????{??
- ????????std::cout?<<?"with?unknown?exception"?<<?std::endl;??
- ????}??
- }??
- ??
- void?main()??
- {??
- ????InitMinDump();??
- ??
- ????test();??
- ??
- ????system("pause");??
- }??