zoukankan      html  css  js  c++  java
  • 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相关代码:
     
  • 相关阅读:
    第4月第1天 makefile automake
    第3月30天 UIImage imageWithContentsOfFile卡顿 Can't add self as subview MPMoviePlayerControlle rcrash
    第3月第27天 uitableviewcell复用
    learning uboot fstype command
    learning uboot part command
    linux command dialog
    linux command curl and sha256sum implement download verification package
    learning shell script prompt to run with superuser privileges (4)
    learning shell get script absolute path (3)
    learning shell args handing key=value example (2)
  • 原文地址:https://www.cnblogs.com/cswuyg/p/3207576.html
Copyright © 2011-2022 走看看