异常时dump文件的生成

一、引言

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文件写异常信息

 

[cpp]  view plain  copy    print?
  1. inline void CreateMiniDump(PEXCEPTION_POINTERS pep, LPCTSTR strFileName)  
  2. {  
  3.     HANDLE hFile = CreateFile(strFileName, GENERIC_READ | GENERIC_WRITE,  
  4.         FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  
  5.   
  6.     if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))  
  7.     {  
  8.         MINIDUMP_EXCEPTION_INFORMATION mdei;  
  9.         mdei.ThreadId           = GetCurrentThreadId();  
  10.         mdei.ExceptionPointers  = pep;  
  11.         mdei.ClientPointers     = NULL;  
  12.   
  13.         MINIDUMP_CALLBACK_INFORMATION mci;  
  14.         mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;  
  15.         mci.CallbackParam       = 0;  
  16.   
  17.         ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, (pep != 0) ? &mdei : 0, NULL, &mci);  
  18.   
  19.         CloseHandle(hFile);  
  20.     }  
  21. }  


CreateMiniDump函数是在异常处理回调函数MyUnhandledExceptionFilter中调用的

 

 

[cpp]  view plain  copy  

 print?

  1. LONG __stdcall MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)  
  2. {  
  3.     CreateMiniDump(pExceptionInfo, "core.dmp");  
  4.   
  5.     return EXCEPTION_EXECUTE_HANDLER;  
  6. }  


3.将SetUnhandledExceptionFilter失效

 

vs2005中,编译的过程中,编译器会自动给你的程序加上一句SetUnhandledExceptionFilter(NULL),这就会导致你之前自定义的

SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

无效,就有可能不会产生dump文件,因此我们必须在自定义的SetUnhandledExceptionFilter之后,让之后调用的SetUnhandledExceptionFilter无效。增加以下代码:

 

[cpp]  view plain  copy  

 print?

  1. // 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效  
  2. void DisableSetUnhandledExceptionFilter()  
  3. {  
  4.     void* addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),  
  5.         "SetUnhandledExceptionFilter");  
  6.   
  7.     if (addr)  
  8.     {  
  9.         unsigned char code[16];  
  10.         int size = 0;  
  11.   
  12.         code[size++] = 0x33;  
  13.         code[size++] = 0xC0;  
  14.         code[size++] = 0xC2;  
  15.         code[size++] = 0x04;  
  16.         code[size++] = 0x00;  
  17.   
  18.         DWORD dwOldFlag, dwTempFlag;  
  19.         VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);  
  20.         WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);  
  21.         VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);  
  22.     }  
  23. }  


最终代码整理:

 

//minidump.h

 

[cpp]  view plain  copy  

 print?

  1. #pragma once  
  2. #include <windows.h>  
  3. #include <DbgHelp.h>  
  4. #include <stdlib.h>  
  5. #pragma comment(lib, "dbghelp.lib")  
  6.   
  7. #ifndef _M_IX86  
  8. #error "The following code only works for x86!"  
  9. #endif  
  10.   
  11. inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName)  
  12. {  
  13.     if(pModuleName == 0)  
  14.     {  
  15.         return FALSE;  
  16.     }  
  17.   
  18.     WCHAR szFileName[_MAX_FNAME] = L"";  
  19.     _wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);  
  20.   
  21.     if(wcsicmp(szFileName, L"ntdll") == 0)  
  22.         return TRUE;  
  23.   
  24.     return FALSE;  
  25. }  
  26.   
  27. inline BOOL CALLBACK MiniDumpCallback(PVOID                            pParam,  
  28.                                       const PMINIDUMP_CALLBACK_INPUT   pInput,  
  29.                                       PMINIDUMP_CALLBACK_OUTPUT        pOutput)  
  30. {  
  31.     if(pInput == 0 || pOutput == 0)  
  32.         return FALSE;  
  33.   
  34.     switch(pInput->CallbackType)  
  35.     {  
  36.     case ModuleCallback:  
  37.         if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg)  
  38.             if(!IsDataSectionNeeded(pInput->Module.FullPath))  
  39.                 pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);  
  40.     case IncludeModuleCallback:  
  41.     case IncludeThreadCallback:  
  42.     case ThreadCallback:  
  43.     case ThreadExCallback:  
  44.         return TRUE;  
  45.     default:;  
  46.     }  
  47.   
  48.     return FALSE;  
  49. }  
  50.   
  51. inline void CreateMiniDump(PEXCEPTION_POINTERS pep, LPCTSTR strFileName)  
  52. {  
  53.     HANDLE hFile = CreateFile(strFileName, GENERIC_READ | GENERIC_WRITE,  
  54.         FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  
  55.   
  56.     if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))  
  57.     {  
  58.         MINIDUMP_EXCEPTION_INFORMATION mdei;  
  59.         mdei.ThreadId           = GetCurrentThreadId();  
  60.         mdei.ExceptionPointers  = pep;  
  61.         mdei.ClientPointers     = NULL;  
  62.   
  63.         MINIDUMP_CALLBACK_INFORMATION mci;  
  64.         mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;  
  65.         mci.CallbackParam       = 0;  
  66.   
  67.         ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, (pep != 0) ? &mdei : 0, NULL, &mci);  
  68.   
  69.         CloseHandle(hFile);  
  70.     }  
  71. }  
  72.   
  73. LONG __stdcall MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)  
  74. {  
  75.     CreateMiniDump(pExceptionInfo, "core.dmp");  
  76.   
  77.     return EXCEPTION_EXECUTE_HANDLER;  
  78. }  
  79.   
  80. // 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效  
  81. void DisableSetUnhandledExceptionFilter()  
  82. {  
  83.     void* addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),  
  84.         "SetUnhandledExceptionFilter");  
  85.   
  86.     if (addr)  
  87.     {  
  88.         unsigned char code[16];  
  89.         int size = 0;  
  90.   
  91.         code[size++] = 0x33;  
  92.         code[size++] = 0xC0;  
  93.         code[size++] = 0xC2;  
  94.         code[size++] = 0x04;  
  95.         code[size++] = 0x00;  
  96.   
  97.         DWORD dwOldFlag, dwTempFlag;  
  98.         VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);  
  99.         WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);  
  100.         VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);  
  101.     }  
  102. }  
  103.   
  104. void InitMinDump()  
  105. {  
  106.     //注册异常处理函数  
  107.     SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);  
  108.   
  109.     //使SetUnhandledExceptionFilter  
  110.     DisableSetUnhandledExceptionFilter();  
  111. }  


4.测试代码

 

//test.cpp

 

[cpp]  view plain  copy  

 print?

    1. #include <iostream>  
    2. #include "minidump.h"  
    3. void test()  
    4. {  
    5.     std::string s = "abcd";  
    6.   
    7.     try{  
    8.         s[100] = 'b';  
    9.     }  
    10.     catch(std::exception& e)  
    11.     {  
    12.         std::cout << "with exception:[" << e.what() << "]" << std::endl;  
    13.     }  
    14.     catch(...)  
    15.     {  
    16.         std::cout << "with unknown exception" << std::endl;  
    17.     }  
    18. }  
    19.   
    20. void main()  
    21. {  
    22.     InitMinDump();  
    23.   
    24.     test();  
    25.   
    26.     system("pause");  
    27. }  
上一篇:MQTT协议与EMQ笔记


下一篇:Linux 数据泵备份创建Dump目录