zoukankan      html  css  js  c++  java
  • Windows 程序 dump 崩溃调试

    Windows 程序捕获崩溃异常 生成dump

    概述

    事情的起因是,有个同事开发的程序,交付的版本程序,会偶尔随机崩溃了。

    悲催的是没有输出log,也没有输出dump文件。

    我建议他给程序代码加个异常捕获,在崩溃时生成dump,方便找出问题点。

    隔了一天之后,短暂交流,发现他没有这个开发经验,我只好披挂上阵了。

    开动

    查阅MSDN文档,和stackoverlfow.com的相关文章,可知

    SetUnhandledExceptionFilter 可以捕获触发系统崩溃的异常

    风风火火开始写代码

        void exceptionHandler(PEXCEPTION_POINTERS excpInfo)
        {
            // your code to handle the exception. Ideally it should
            // marshal the exception for processing to some other
            // thread and wait for the thread to complete the job
            std::unique_lock<std::mutex> lk(g_handlerLock);
            generateMiniDump(nullptr, excpInfo);
        }
    
        LONG WINAPI unhandledException(PEXCEPTION_POINTERS excpInfo = nullptr)
        {
            DebugBreak();
    
            if (excpInfo == nullptr)
            {
                __try // Generate exception to get proper context in dump
                {
                    RaiseException(EXCEPTION_BREAKPOINT, 0, 0, nullptr);
                }
                __except (exceptionHandler(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
                {
                }
            }
            else
            {
                exceptionHandler(excpInfo);
            }
    
            return 0;
        }
    
        SetUnhandledExceptionFilter(unhandledException);

    测试

    在main函数入口,设置异常处理函数SetUnhandledExceptionFilter。

    异常处理函数负责捕获异常,调用MiniDumpWriteDump生成dump文件,供开发者使用Windbg调试

    编译运行

    Access Volation C000005错误可以顺利捕获

    令人费解的是,

    abort,数组越界,虚函数调用异常等均无法捕获

    系统把这些异常给拦截了,并给出了程序崩溃的提示窗口

    改进

    为了捕获这些异常并生成dump文件,必须要把系统拦截的那一层给禁止掉

    1. 禁止系统弹出崩溃窗口,该窗口提示非常渣,对开发者和用户都不友好

      SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);

    可以模仿腾讯的QQ程序,专门开发一个对用户界面友好Bug Report的程序,
    在程序崩溃时转存储dump文件时,运行该程序提示用户有用的信息。

    2. 注册异常捕获函数

      SetUnhandledExceptionFilter(unhandledException);

    当异常发生时,系统会跳进我们的unhandleException回调中

    在该回调函数中,我们可以弹出Bug Report这样的子进程,并存储异常dump文件

    3. 拦截C Runtime的异常处理

      _set_invalid_parameter_handler(invalidParameter);
      _set_purecall_handler(pureVirtualCall);
      signal(SIGABRT, sigAbortHandler);
      _set_abort_behavior(0, 0);

    这些异常处理只是简单的调用unhandleException函数

    4. 开启系统的程序崩溃请求

    Vista之后,微软加了一个特性

    程序崩溃时,默认不交给程序崩溃处理

    而是使用一个莫名其妙的机制,不让程序进入崩溃环节

    搞得用户懵逼,开发者也让代码无法进入崩溃异常处理

        void EnableCrashingOnCrashes()
        {
            typedef BOOL(WINAPI *tGetPolicy)(LPDWORD lpFlags);
            typedef BOOL(WINAPI *tSetPolicy)(DWORD dwFlags);
            const DWORD EXCEPTION_SWALLOWING = 0x1;
    
            HMODULE kernel32 = LoadLibraryA("kernel32.dll");
            tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32,
                "GetProcessUserModeExceptionPolicy");
            tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32,
                "SetProcessUserModeExceptionPolicy");
            if (pGetPolicy && pSetPolicy)
            {
                DWORD dwFlags;
                if (pGetPolicy(&dwFlags))
                {
                    // Turn off the filter 
                    pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
                }
            }
        }

    5. 断了系统SetUnhandledExceptionFilter的后路

    C Runtime等异常,运行时库会调用SetUnhandledExceptionFilter向系统注册一个NULL

    从而使得我们之前注册的回调失效

    真是无语(ˉ▽ˉ;)...

    在这里,需要hook掉SetUnhandledExceptionFilter,在我们注册完回调之后,让它默认不做任何处理

    用到Windows核心编程这本书里面,Jeffrey Richter开发的CAPIHook这个模块

        void PreventSetUnhandledExceptionFilter()
        {
            CAPIHook apiHook("kernel32.dll",
                "SetUnhandledExceptionFilter",
                (PROC)ExceptionFilterHookProc);
        }

    其中ExceptionFilterHookProc这个函数是个空函数,无需做多余操作,直接renturn null即可

    6. 完整流程

        void setExceptionHandlers()
        {
            if (!IsDebuggerPresent() && !g_isHandlerSet)
            {
                g_isHandlerSet = true;
    
                SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
                SetUnhandledExceptionFilter(unhandledException);
                _set_invalid_parameter_handler(invalidParameter);
                _set_purecall_handler(pureVirtualCall);
                signal(SIGABRT, sigAbortHandler);
                _set_abort_behavior(0, 0);
    
                EnableCrashingOnCrashes();
                PreventSetUnhandledExceptionFilter();
            }
        }

    在VS或者Windbg中调试时,我们就没有必要生成dump文件了

    IsDebuggerPresent这个系统API会帮助我们判断我们是否在调试环境中

    总结

    即使看起来这么简单的一个功能,也是需要挺多细节处理的。

  • 相关阅读:
    轻重搭配
    EF的优缺点
    使用bootstrap-select有时显示“Nothing selected”
    IIS发布 HTTP 错误 500.21
    js添加的元素无法触发click事件
    sql server查看表是否死锁
    sql server把一个库表的某个字段更新到另一张表的相同字段
    SQLSERVER排查CPU占用高的情况
    SQL server中如何按照某一字段中的分割符将记录拆成多条
    LINQ to Entities does not recognize the method 'System.DateTime AddDays(Double)' method, and this method cannot be translated into a store expression.
  • 原文地址:https://www.cnblogs.com/jojodru/p/9618416.html
Copyright © 2011-2022 走看看