編寫的windows程序,崩潰時產生crash 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. }  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM