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會幫助我們判斷我們是否在調試環境中

總結

即使看起來這么簡單的一個功能,也是需要挺多細節處理的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM