Windows下Qt使用dump定位崩潰位置(1)

mahuifa發表於2024-05-30

軟體除錯

Windows下Qt使用dump定位崩潰位置(1)

目錄
  • 軟體除錯
  • Windows下Qt使用dump定位崩潰位置(1)
    • 1、Qt崩潰定位方法
    • 2、 什麼是dump檔案
    • 3、使用vs除錯dmp
    • 4、下載Windows符號表
    • 5、下載Qt符號表
    • 6、主要程式碼
    • 7、原始碼

更多精彩內容
👉個人內容分類彙總 👈
👉C++軟體除錯、異常定位 👈

本文說的方法只適合Windows下MSVC編譯器(不支援MinGW),如果需要跨平臺可以👉看這一篇

1、Qt崩潰定位方法

方法1: 透過日誌系統儲存程式執行日誌資訊,在程式崩潰後透過日誌資訊可分析出程式進行了哪些操作;
方法2:

  • 日誌資訊並不是萬能的,有些情況下日誌資訊不一定能分析出崩潰問題,這時就需要藉助dump檔案進行分析;
  • 當程式遇到未處理的異常導致程式崩潰,如果在異常發生之前呼叫了SetUnhandledExceptionFilter() 函式,異常交給函式處理。因而,在程式開始處增加SetUnhandledExceptionFilter()函式,並在函式中利用適當的方法生成Dump檔案,儲存崩潰位置資訊。

2、 什麼是dump檔案

  • Dump檔案通常是指程式或系統崩潰時,將記憶體中的資料轉儲並儲存到磁碟檔案中的一種檔案格式。Dump檔案包含了程式崩潰時的記憶體狀態、暫存器狀態、執行緒狀態等資訊,可以幫助開發人員診斷和解決故障。
  • Dump檔案一般是以二進位制格式儲存在磁碟上,其內容包含了程式或系統崩潰時記憶體中的所有資料,包括程式碼、資料、棧、堆等。Dump檔案的大小通常很大,可能達到幾百MB或幾GB,因此在進行分析和除錯時需要使用專門的工具和技術。
  • 在Windows系統中,dump檔案一般使用Windows的除錯工具WinDbg來分析。WinDbg可以讀取dump檔案,並提供除錯命令和分析工具,幫助開發人員進行故障診斷和除錯。除了WinDbg,還有其他的除錯工具和分析工具可以讀取和分析dump檔案,例如Visual Studio的偵錯程式、QtCreator和第三方工具如IDA Pro等。

3、使用vs除錯dmp

  1. 開啟vs,選擇繼續但無需程式碼
    img

  2. 將dmp檔案直接拖如vs中

    img

  3. 點選1️⃣【設定符號路徑】,進行如下設定;

    1. 如果是第一次除錯需要勾選Microsoft符號伺服器2️⃣:會從網路下載除錯使用的符號檔案,然後將符號檔案下載到5️⃣位置,以後就不需要勾選符號伺服器了;
    2. 點選“+”號3️⃣新增【TestCrashHandler.pdb】6️⃣檔案所在路徑4️⃣

    img

  4. 點選 使用僅限本機進行除錯

    img

  5. 成功定位到崩潰位置

    img

4、下載Windows符號表

如果勾選了Microsoft符號伺服器或者NuGet.org伺服器則可將window符號表下載到下列路徑中,在離線環境中也可進行除錯。

img

5、下載Qt符號表

這種方法可以選擇下載部分除錯符號表,體積比較小。

  1. 選擇系統環境https://download.qt.io/online/qtsdkrepository/

    img

  2. 選擇desktop

    img

  3. 選擇安裝的qt版本,我的時5.14.2

    img

  4. 選擇除錯編譯器版本,我的是msvc2017-64

    img

  5. 下載需要除錯的模組的符號表

    img

6、主要程式碼

  • 預設情況下只有Debug可用生成可用的dmp檔案,Release對程式的編譯進行了最佳化,並且不生成PDB符號檔案,所以無法進行除錯,如果想要Release可用除錯,需要在Pro檔案進行下列設定

    # 在Release生成用於除錯dump的資訊,包括【禁用release編譯最佳化】、【生成PDB符號表】,但是這些設定會降低程式效能,和debug差不多
    CONFIG(release, debug|release) {
        QMAKE_CXXFLAGS_RELEASE -= -O2
        QMAKE_CXXFLAGS_RELEASE += -O0
        QMAKE_CXXFLAGS_RELEASE += /Zi
        QMAKE_LFLAGS_RELEASE += /DEBUG /OPT:REF /OPT:ICF                # 生成 PDB符號檔案,功能和下一行一樣,但是最好用這一行,顯示指定編譯選項
    #    QMAKE_LFLAGS_RELEASE += $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
    
        # 列印變數引數值
        message(QMAKE_CXXFLAGS_RELEASE變數值:$$QMAKE_CXXFLAGS_RELEASE)
        message(QMAKE_LFLAGS_RELEASE變數值:$$QMAKE_LFLAGS_RELEASE)
    }
    
  • main.cpp

    #include "crashhandler.h"
    #include <QMessageBox>
    #include <QDateTime>
    #include <qglobal.h>
    
    #ifdef _MSC_VER
    #include <Windows.h>          // Windows.h必須放在DbgHelp.h前,否則編譯會報錯
    #include <DbgHelp.h>
    #endif
    
    //MSVC編譯器
    #ifdef _MSC_VER
    
    #if defined(_MSC_VER) && (_MSC_VER >= 1600)
    #pragma execution_character_set("utf-8")
    #endif
    /**
     * @brief              應用程式崩潰處理程式
     * @param pException
     * @return             EXCEPTION_EXECUTE_HANDLER equ 1 表示我已經處理了異常,可以優雅地結束了
     *                     EXCEPTION_CONTINUE_SEARCH equ 0 表示我不處理,其他人來吧,於是windows呼叫預設的處理程式顯示一個錯誤框,並結束(qt中會導致視窗卡死一段時間)
     *                     EXCEPTION_CONTINUE_EXECUTION equ -1 表示錯誤已經被修復,請從異常發生處繼續執行
     */
    LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException){//程式異常捕獲
        //建立 Dump 檔案
        QString strPath = QString("%1.dmp").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH-mm-ss"));
    #ifdef UNICODE
        LPCWSTR filePath = reinterpret_cast<LPCWSTR>(strPath.utf16());
    #else
        LPCSTR filePath = reinterpret_cast<LPCSTR>(strPath.toStdString().data());
    #endif // !UNICODE
        HANDLE hDumpFile = CreateFile(filePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if( hDumpFile != INVALID_HANDLE_VALUE){
            //Dump資訊
            MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
            dumpInfo.ExceptionPointers = pException;
            dumpInfo.ThreadId = GetCurrentThreadId();
            dumpInfo.ClientPointers = TRUE;
            //寫入Dump檔案內容
            MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
        }
        //這裡彈出一個錯誤對話方塊並退出程式
        EXCEPTION_RECORD* record = pException->ExceptionRecord;
        QString errCode(QString::number((quint64)record->ExceptionCode, 16));
        QString errAdr(QString::number((uint)record->ExceptionAddress, 16));
        QMessageBox::critical(nullptr, "程式崩潰","<FONT size=4><div><b>對於發生的錯誤,表示誠摯的歉意</b><br/></div>"+
                              QString("<div>錯誤程式碼:%1</div><div>錯誤地址:%2</div></FONT>").arg(errCode).arg(errAdr),
                              QMessageBox::Ok);
    
        return EXCEPTION_EXECUTE_HANDLER;
    }
    #endif
    
    void CrashHandler::initCrashHandler()
    {
    #ifdef _MSC_VER
        SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ApplicationCrashHandler);   // 使用win API註冊異常處理函式
    #endif
    }
    
    

7、原始碼

gitee
github

相關文章