• 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相关代码:
     
  • 相关阅读:
    单文档程序结构
    如何从一个对话框弹出单文档视图
    MFC 窗口居中显示 VS2010
    在单文档中显示我的第一个对话框
    在MFC中添加用户自定义消息
    MFC动态创建控件及添加消息响应
    MFC的DLL
    MFC多线程
    MFC的UDP编程实现
    MFC下CSocket编程详解
  • 原文地址:https://www.cnblogs.com/cswuyg/p/3207576.html
Copyright © 2020-2023  润新知