windows下捕獲dump


     一般要捕獲異常只需要兩個函數: SetUnhandledExceptionFilter截獲異常; MiniDumpWriteDump寫dump文件。但是由於CRT函數可能會在內部調用SetUnhandledExceptionFilter(NULL),解除我們程序設置的異常處理,這導致我們的程序無法完整捕獲崩潰。另外,還有一部分非異常的CRT錯誤,不屬於SEH異常捕獲的范疇,需要通過_set_invalid_parameter_handler、_set_purecall_handler攔截,否則會彈出很丑陋的Runtime Error提示框。為保證所有異常都能由我們捕獲,需要把SetUnhandledExceptionFilter函數Hook掉,不讓“其他人”去設置自己的Exception處理,有Exception我們自己搞定;還有,對CRT錯誤做攔截,避免彈出錯誤窗口:_set_invalid_parameter_handler、_set_purecall_handler。
     chromium的breakpad當前只是使用了上邊提到的_set_invalid_parameter_handler、_set_purecall_handler函數,並沒有屏蔽“其他人”的SetUnhandledExceptionFilter行為,可能導致了部分Crash無法捕獲,為什么不這么做呢?有待考察。(stackoverflow也有人提到這個問題: http://stackoverflow.com/questions/11350801/why-does-google-breakpad-not-handle-all-crashes-how-can-i-debug-these-cases)。
      進程內捕獲dump示例代碼
.h
 1 namespace CatchDumpFile 
 2 {
 3 
 4     void simple_log(const std::wstring& log_msg);
 5         
 6     class CDumpCatch
 7     {
 8     public:
 9         CDumpCatch();
10         ~CDumpCatch();
11     private:
12 
13         static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI TempSetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
14         static BOOL ReleaseDumpFile(const std::wstring& strPath, EXCEPTION_POINTERS *pException);
15         static LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException);
16         static void MyPureCallHandler(void);
17         static void MyInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved);
18 
19 
20         BOOL AddExceptionHandle();
21         BOOL RemoveExceptionHandle();
22         BOOL PreventSetUnhandledExceptionFilter();
23         void SetInvalidHandle();
24         void UnSetInvalidHandle();
25     private:
26         LPTOP_LEVEL_EXCEPTION_FILTER m_preFilter;
27         _invalid_parameter_handler m_preIph;
28         _purecall_handler m_prePch;    
29     };
30 };

.cc

  1 namespace CatchDumpFile
  2 {
  3     void simple_log(const std::wstring& log_msg)
  4     {
  5         std::wstring strLogWnd = L"cswuyg_simple_debug_log";
  6         HWND hSend = ::FindWindow(NULL, strLogWnd.c_str());
  7         COPYDATASTRUCT copydate;
  8         copydate.cbData = (DWORD)(log_msg.length() + 1) * sizeof(wchar_t);
  9         copydate.lpData = (PVOID)log_msg.c_str();
 10         ::SendMessage(hSend, WM_COPYDATA, 0, (LPARAM)&copydate);
 11     }
 12 
 13     void CDumpCatch::MyPureCallHandler(void)
 14     {    
 15        //simple_log(L"MyPureCallHandler");
 16         throw std::invalid_argument("");
 17     }
 18 
 19     void CDumpCatch::MyInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved)
 20     {
 21         //simple_log(L"MyPureCallHandler");
 22         //The parameters all have the value NULL unless a debug version of the CRT library is used.
 23         throw std::invalid_argument("");
 24     }
 25 
 26     void CDumpCatch::SetInvalidHandle()
 27     {
 28 #if _MSC_VER >= 1400  // MSVC 2005/8
 29         m_preIph = _set_invalid_parameter_handler(MyInvalidParameterHandler);
 30 #endif  // _MSC_VER >= 1400
 31         m_prePch = _set_purecall_handler(MyPureCallHandler);   //At application, this call can stop show the error message box.
 32     }
 33     void CDumpCatch::UnSetInvalidHandle()
 34     {
 35 #if _MSC_VER >= 1400  // MSVC 2005/8
 36         _set_invalid_parameter_handler(m_preIph);
 37 #endif  // _MSC_VER >= 1400
 38         _set_purecall_handler(m_prePch); //At application this can stop show the error message box.
 39     }
 40 
 41     LPTOP_LEVEL_EXCEPTION_FILTER WINAPI CDumpCatch::TempSetUnhandledExceptionFilter( LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter )
 42     {
 43         return NULL;
 44     }
 45 
 46     BOOL CDumpCatch::AddExceptionHandle()
 47     {
 48         m_preFilter = ::SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
 49         PreventSetUnhandledExceptionFilter();
 50         return TRUE;
 51     }
 52 
 53     BOOL CDumpCatch::RemoveExceptionHandle()
 54     {
 55         if(m_preFilter != NULL)
 56         {
 57             ::SetUnhandledExceptionFilter(m_preFilter);
 58             m_preFilter = NULL;
 59         }
 60         return TRUE;
 61     }
 62 
 63     CDumpCatch::CDumpCatch()
 64     {
 65         SetInvalidHandle();
 66         AddExceptionHandle();
 67     }
 68 
 69     CDumpCatch::~CDumpCatch()
 70     {
 71         UnSetInvalidHandle();
 72         RemoveExceptionHandle();
 73     }
 74 
 75     BOOL CDumpCatch::ReleaseDumpFile(const std::wstring& strPath, EXCEPTION_POINTERS *pException)
 76     {
 77         HANDLE hDumpFile = ::CreateFile(strPath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  
 78         if (hDumpFile == INVALID_HANDLE_VALUE)
 79         {
 80             return FALSE;
 81         }
 82         MINIDUMP_EXCEPTION_INFORMATION dumpInfo;  
 83         dumpInfo.ExceptionPointers = pException;  
 84         dumpInfo.ThreadId = ::GetCurrentThreadId();  
 85         dumpInfo.ClientPointers = TRUE;  
 86     //    ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);  
 87         BOOL bRet = ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hDumpFile, MiniDumpWithFullMemory, &dumpInfo, NULL, NULL);  
 88         ::CloseHandle(hDumpFile);  
 89         return bRet;
 90     }
 91 
 92     LONG WINAPI CDumpCatch::UnhandledExceptionFilterEx( struct _EXCEPTION_POINTERS *pException )
 93     {
 94         //simple_log(L"UnhandledExceptionFilterEx");
 95         wchar_t szPath[MAX_PATH] = { 0 };
 96         ::GetModuleFileName(NULL, szPath, MAX_PATH);
 97         ::PathRemoveFileSpec(szPath);
 98         std::wstring strPath = szPath;
 99         strPath += L"\\CrashDumpFile.dmp";
100         BOOL bRelease = ReleaseDumpFile(strPath.c_str(), pException);
101         //::FatalAppExit(0,  L"Error");
102         if (bRelease)
103         {
104             return EXCEPTION_EXECUTE_HANDLER;
105         }
106         return EXCEPTION_CONTINUE_SEARCH;
107     }
108 
109     BOOL CDumpCatch::PreventSetUnhandledExceptionFilter()
110     {
111         HMODULE hKernel32 = LoadLibrary(L"kernel32.dll");
112         if (hKernel32 ==   NULL)
113         {
114             return FALSE;
115         }
116         void *pOrgEntry = ::GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
117         if(pOrgEntry == NULL)
118         {
119             return FALSE;
120         }
121 
122         unsigned char newJump[5];
123         DWORD dwOrgEntryAddr = (DWORD)pOrgEntry;
124         dwOrgEntryAddr += 5; //jump instruction has 5 byte space.
125 
126         void *pNewFunc = &TempSetUnhandledExceptionFilter;
127         DWORD dwNewEntryAddr = (DWORD)pNewFunc;
128         DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
129 
130         newJump[0] = 0xE9;  //jump
131         memcpy(&newJump[1], &dwRelativeAddr, sizeof(DWORD));
132         SIZE_T bytesWritten;
133         DWORD dwOldFlag, dwTempFlag;
134         ::VirtualProtect(pOrgEntry, 5, PAGE_READWRITE, &dwOldFlag);
135         BOOL bRet = ::WriteProcessMemory(::GetCurrentProcess(), pOrgEntry, newJump, 5, &bytesWritten);
136         ::VirtualProtect(pOrgEntry, 5, dwOldFlag, &dwTempFlag);
137         return bRet;
138     }
139 
140 }
   能引發pure function called 錯誤的代碼
class IPureCall
{
public:
    virtual ~IPureCall(){};
    IPureCall()
    {
        //indirect call the virtual function, the compiler would not treat as "static binding", it is "dynamic binding".
        //At this time, the CPureCall class hasn't been constructed, the virtual table didn't has the pure_call function's point, so it cause "pure virtual function called exception".
        call_by_constructor();
    };
    virtual void pure_call() = 0;
    void call_by_constructor()
    {
        pure_call();
    }
};

class CPureCall : public IPureCall
{
public:
    CPureCall()
    {
    }
    void pure_call()
    {
    }
};
  pure virtual function called在之前的文章里介紹過(http://www.cnblogs.com/cswuyg/archive/2012/08/22/2650610.html)。
  
  進程外捕獲崩潰的做法是使用進程間通信(IPC,內存映射文件或者管道都行),把 EXCEPTION_POINTERS指針數據等信息通知捕獲進程,讓捕獲進程去寫dump(windows下捕獲dump之Google breakpad_client的理解)。進程外捕獲dump是比較推薦的做法,chromium的breakpad文檔解釋說,“一般認為在崩潰進程內部寫minidump是不安全的:關鍵的進程數據結構可能會被破壞掉,或者異常處理程序獲取到的堆棧可能是被覆蓋了的”(原文: http://code.google.com/p/google-breakpad/wiki/GettingStartedWithBreakpad )。
 
     可復用源碼分享:https://github.com/cswuyg/simple_win/tree/master/dump_catch/dump_catch
 
     多模塊dump處理相關補充
1、如果CRT是/MD,那么CRT錯誤捕獲EXE、DLL共用;dump捕獲多EXE、DLL共用,只需要在EXE里加上處理就ok;
2、如果CRT是/MT,那么CRT錯誤捕獲各PE文件獨立, EXE、DLL必須有自己的處理;dump捕獲多EXE、DLL共用。
這方面的知識MSDN也稍有提及:
http://technet.microsoft.com/zh-cn/library/t296ys27(v=vs.71)
http://msdn.microsoft.com/en-us/library/windows/desktop/ms680634(v=vs.85).aspx
 
 
不錯的編程資料:
 
不錯的抓dump工具介紹:
 
breakpad相關代碼:
 


免責聲明!

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



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