博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
windows下捕获dump
阅读量:5984 次
发布时间:2019-06-20

本文共 8442 字,大约阅读时间需要 28 分钟。

一般要捕获异常只需要两个函数:
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也有人提到这个问题: )。
     
进程内捕获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;  //jump131         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在之前的文章里介绍过( )。
  
  
进程外捕获崩溃的做法是使用进程间通信(IPC,内存映射文件或者管道都行),把EXCEPTION_POINTERS指针数据等信息通知捕获进程,让捕获进程去写dump( )。
进程外捕获dump是比较推荐的做法,chromium的breakpad文档解释说,“一般认为在崩溃进程内部写minidump是不安全的:关键的进程数据结构可能会被破坏掉,或者异常处理程序获取到的堆栈可能是被覆盖了的”(原文: )。
 
     可复用源码分享: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相关代码:

转载地址:http://twgox.baihongyu.com/

你可能感兴趣的文章
关于authlib集成windows ad失败的分析并解决[草稿]
查看>>
python中单元测试的常用语句
查看>>
阿里Java面试题剖析:为什么使用消息队列?消息队列有什么优点和缺点?
查看>>
3.2.4 Shell脚本--函数的用法
查看>>
ssh-keygen -t rsa -f cloud.key ssh -i cloud.key <username>@<instance_ip>
查看>>
培训机构管理系统帮助机构解决管理问题
查看>>
我的友情链接
查看>>
DISCUZ官方论坛模仿开发日志(二)
查看>>
Java设计模式系列之策略模式
查看>>
Sql异常①
查看>>
Jquery 校验文本框只能输入负数、小数、整数
查看>>
PHP 命名空间
查看>>
层次分析法
查看>>
ASP.NET一些常用的东西
查看>>
2015百度之星 单调区间
查看>>
Ubuntu安装Chrome的方法
查看>>
用批处理来操纵你的光驱
查看>>
SQL 问题记录
查看>>
vim修改时自动备份配置文件小脚本
查看>>
我的友情链接
查看>>