美文网首页
windows下 c++ 崩溃时,生成dump

windows下 c++ 崩溃时,生成dump

作者: 正向反馈 | 来源:发表于2019-10-11 15:53 被阅读0次
    #include <DbgHelp.h>  
    
    #pragma comment(lib, "dbghelp.lib")  
    
    void CreateDumpFile(const TCHAR *lpstrDumpFilePathName, EXCEPTION_POINTERS *pException)
    {
        // 创建Dump文件
        HANDLE hDumpFile = CreateFile(lpstrDumpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    
        // Dump信息
        //
        MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
        dumpInfo.ExceptionPointers = pException;
        dumpInfo.ThreadId = GetCurrentThreadId();
        dumpInfo.ClientPointers = TRUE;
    
        // 写入Dump文件内容
        //
        MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
    
        CloseHandle(hDumpFile);
    }
    // 处理Unhandled Exception的回调函数
    //
    LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException)
    {
        // 在这里添加处理程序崩溃情况的代码
        // 现在很多软件都是弹出一个发送错误报告的对话框
    
        // 这里以弹出一个错误对话框并退出程序为例子
        CreateDumpFile(TEXT("test.dmp"), pException);
        FatalAppExit(-1, TEXT("*** Unhandled Exception! ***"));
    
        return EXCEPTION_EXECUTE_HANDLER;
    }
    

    在程序启动时注册处理函数

    SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ApplicationCrashHandler);
    

    注意 Release版本,需要配置:

    • c/c++>优化>优化 选择 已禁用/Od
    • c/c++>常规>调试信息格式 选择 程序数据库 (/Zi)
    • 链接器>调试>生成调试信息 选择 优化以便于调试 (/DEBUG)
    • 链接器>调试>生成程序数据库文件 要注意生成出来的pdb文件最好与dump文件在同一目录

    UCRT hook

    windows下,ucrt内部的异常不会走SetUnhandledExceptionFilter. 因为新版本的CRT实现在异常处理中强制删除所有应用程序先前设置的捕获函数

    
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <string>
    #include <stdlib.h>
    
    #include <windows.h>
    #include <DbgHelp.h>  
    
    #pragma comment(lib, "dbghelp.lib")  
    
    bool CreateDumpFile(const TCHAR *lpstrDumpFilePathName, EXCEPTION_POINTERS *pException)
    {
        printf("CreateDumpFile\n");
        // 创建Dump文件
        HANDLE hDumpFile = CreateFile(lpstrDumpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hDumpFile == INVALID_HANDLE_VALUE) {
    
            printf("CreateDumpFile fail\n");
            return false;
        }
        printf("CreateDumpFile succ\n");
        // Dump信息
        //
        MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
        dumpInfo.ExceptionPointers = pException;
        dumpInfo.ThreadId = GetCurrentThreadId();
        dumpInfo.ClientPointers = TRUE;
    
        printf("MiniDumpWriteDump...\n");
        // 写入Dump文件内容
        //
        MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
    
        printf("MiniDumpWriteDump\n");
        CloseHandle(hDumpFile);
        printf("CloseHandle\n");
        return true;
    }
    // 处理Unhandled Exception的回调函数
    //
    LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException)
    {
        printf("ApplicationCrashHandler\n"); 
        // 在这里添加处理程序崩溃情况的代码
        // 现在很多软件都是弹出一个发送错误报告的对话框
    
        // 这里以弹出一个错误对话框并退出程序为例子
        if (CreateDumpFile(TEXT("test.dmp"), pException)) {
            printf("dump succ\n");
            FatalAppExit(-1, TEXT("*** Unhandled Exception! dmp created ***"));
        }
        else {
            printf("dump fail\n");
            FatalAppExit(-1, TEXT("*** Unhandled Exception! dmp create fail ***"));
        }
    
        return EXCEPTION_EXECUTE_HANDLER;
    }
    class Hi {
    public:
        void say() {
            printf("age=%d\n", age);
        }
        int age = 10;
    };
    // 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
    bool DisableSetUnhandledExceptionFilter()
    {
        HMODULE hKernel32 = LoadLibraryW(L"kernel32.dll");
    
        if (hKernel32 == NULL) return FALSE;
        void *addr = (void*)::GetProcAddress(hKernel32,
            "SetUnhandledExceptionFilter");     // The function is ANSI version.
        
        if (addr)
        {
    
    #ifdef _M_IX86
    
            // Code for x86:
            // 33 C0                xor         eax,eax  
            // C2 04 00             ret         4 
            unsigned char szExecute[] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 };
    #elif _M_X64
            // 33 C0                xor         eax,eax 
            // C3                   ret  
            unsigned char szExecute[] = { 0x33, 0xC0, 0xC3 };
    #else
    #error "The following code only works for x86 and x64!"
    #endif
    
            SIZE_T bytesWritten = 0;
            DWORD dwOldFlag, dwTempFlag;
    
            VirtualProtect(addr, sizeof(szExecute), PAGE_EXECUTE_READWRITE, &dwOldFlag);
            BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
                addr, szExecute, sizeof(szExecute), &bytesWritten);
            VirtualProtect(addr, sizeof(szExecute), dwOldFlag, &dwTempFlag);
            return bRet;
        }
        return false;
    }
    void test() {
        Hi *hi = NULL;
        //hi->say();
        char data[10];
        //auto data = new char[10];
        char tmp[5500] = { 1 };
        tmp[5000] = 0;
        data[30] = 10;
        sprintf(data, "lasdjfjal;fjasdlfjalfjalsdfja;sdfjaslkfjasdklfjasdl;fjasdl;fjasdl;fjsdlf%s\n", tmp);
        printf("%s\n", data);
        printf("hi\n"); 
    }
    int main() {
        printf("main\n");
        SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ApplicationCrashHandler);
        bool succ = DisableSetUnhandledExceptionFilter();
        //printf("disable succ=%d\n", succ);
        printf("start test\n");
        test();
        printf("lala\n");
        system("pause");
        return 0;
    }
    

    内存越界

    内存越界,崩不崩溃都有可能,也不一定能走SetUnhandledExceptionFilter
    对于堆内存越界,可以使用gflags完全页堆方案找出。
    原理是分配内存后跟不可访问访问内存区,这样内存越界时,就会触发崩溃。
    但是对于栈内存越界,我没有找到好的方案。

    相关文章

      网友评论

          本文标题:windows下 c++ 崩溃时,生成dump

          本文链接:https://www.haomeiwen.com/subject/fgtxmctx.html