美文网首页
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