Qt Mingw 创建dump 并查找crash 出错行

前言:

客户现场跑的程序有可能存在长时间运行挂掉的情况,而无法复现的话无法更好的查找问题原因。所以研究了一下window下程序程序调试crash的方法。该本是介绍qt+ mingw 的。

1. 在PRO文件中加入调试信息(可能导致exe增大)

LIBS += -lDbgHelp
QMAKE_CXXFLAGS_RELEASE += -g

QMAKE_CFLAGS_RELEASE += -g

QMAKE_LFLAGS_RELEASE = -mthreads -Wl, #其中最后的逗号注意添加,如果不添加可能编译不过

#禁止优化

QMAKE_CFLAGS_RELEASE -= -O2

QMAKE_CXXFLAGS_RELEASE -= -O2

2、dump生成类的.h文件

/**
* @projectName
* @file          ccrashstack
* @brief         记录崩溃日志
* @date          2022-08-08
*/

#ifndef CCRASHSTACK_H
#define CCRASHSTACK_H

#include <windows.h>
#include <QString>

class CCrashStack
{
private:
    PEXCEPTION_POINTERS m_pException;

private:
    QString GetModuleByRetAddr(PBYTE Ret_Addr, PBYTE & Module_Addr);
    QString GetCallStack(PEXCEPTION_POINTERS pException);
    QString GetVersionStr();
    bool GetHardwareInaformation(QString &graphics_card, QString &sound_deivce);

public:
    CCrashStack(PEXCEPTION_POINTERS pException);

    QString GetExceptionInfo();
};

#endif // CCRASHSTACK_H

3、记录崩溃日志类的.cpp文件

#include "ccrashstack.h"
#include <tlhelp32.h>
#include <stdio.h>

#define _WIN32_DCOM
#include <comdef.h>
#include <wbemidl.h>

//#include<base/constants.h>
#include "qdebug.h"

CCrashStack::CCrashStack(PEXCEPTION_POINTERS pException)
{
    m_pException = pException;
}


QString CCrashStack::GetModuleByRetAddr(PBYTE Ret_Addr, PBYTE & Module_Addr)
{
    MODULEENTRY32   M = {sizeof(M)};
    HANDLE  hSnapshot;

    wchar_t Module_Name[MAX_PATH] = {0};

    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);

    if ((hSnapshot != INVALID_HANDLE_VALUE) &&
            Module32First(hSnapshot, &M))
    {
            do
            {
                    if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
                    {
                            lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
                            Module_Addr = M.modBaseAddr;
                            break;
                    }
            } while (Module32Next(hSnapshot, &M));
    }

    CloseHandle(hSnapshot);

    QString sRet = QString::fromWCharArray(Module_Name);
    return sRet;
}

QString CCrashStack::GetCallStack(PEXCEPTION_POINTERS pException)
{
        PBYTE   Module_Addr_1;
        char bufer[256]={0};
        QString sRet;

        typedef struct STACK
        {
                STACK *Ebp;
                PBYTE  Ret_Addr;
                DWORD  Param[0];
        } STACK, *PSTACK;

        STACK   Stack = {0, 0};
        PSTACK  Ebp;

        if (pException)     //fake frame for exception address
        {
                Stack.Ebp = (PSTACK)pException->ContextRecord->Rbp;
                Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;
                Ebp = &Stack;
        }
        else
        {
                Ebp = (PSTACK)&pException - 1;  //frame addr of Get_Call_Stack()

                // Skip frame of Get_Call_Stack().
                if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
                        Ebp = Ebp->Ebp;     //caller ebp
        }

        // Break trace on wrong stack frame.
        for (; !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
                 Ebp = Ebp->Ebp)
        {
                // If module with Ebp->Ret_Addr found.
                memset(bufer,0, sizeof(0));
                sprintf(bufer, "\n%08X ", (Ebp->Ret_Addr));
                sRet.append(bufer);

                QString moduleName = this->GetModuleByRetAddr(Ebp->Ret_Addr, Module_Addr_1) ;
                if (moduleName.length() > 0)
                {
                    sRet.append(moduleName);
                }
        }

        return sRet;
} //Get_Call_Stack

QString CCrashStack::GetVersionStr()
{
        OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)};  //EX for NT 5.0 and later

        if (!GetVersionEx((POSVERSIONINFO)&V))
        {
                ZeroMemory(&V, sizeof(V));
                V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
                GetVersionEx((POSVERSIONINFO)&V);
        }

        if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
                V.dwBuildNumber = LOWORD(V.dwBuildNumber);  //for 9x HIWORD(dwBuildNumber) = 0x04xx

        QString sRet;
        sRet.append(QString("Windows:  %1.%2.%3, SP %4.%5, Product Type %6\n")
                .arg(V.dwMajorVersion).arg(V.dwMinorVersion).arg(V.dwBuildNumber)
                .arg(V.wServicePackMajor).arg(V.wServicePackMinor).arg(V.wProductType));


        return sRet;
}

QString CCrashStack::GetExceptionInfo()
{
        WCHAR       Module_Name[MAX_PATH];
        PBYTE       Module_Addr;

        QString sRet;
        char buffer[512]={0};

        QString sTmp = GetVersionStr();
        sRet.append(sTmp);
        sRet.append("Process:  ");

        GetModuleFileName(NULL, Module_Name, MAX_PATH);
        sRet.append(QString::fromWCharArray(Module_Name));
        sRet.append("\n");

        // If exception occurred.
        if (m_pException)
        {
                EXCEPTION_RECORD &  E = *m_pException->ExceptionRecord;
                CONTEXT &           C = *m_pException->ContextRecord;

                memset(buffer, 0, sizeof(buffer));
                sprintf(buffer, "Exception Addr: %08X  ", E.ExceptionAddress);
                sRet.append(buffer);
                // If module with E.ExceptionAddress found - save its path and date.
                QString module = GetModuleByRetAddr((PBYTE)E.ExceptionAddress, Module_Addr);
                if (module.length() > 0)
                {
                    sRet.append(" Module: ");

                    sRet.append(module);
                }

                memset(buffer, 0, sizeof(buffer));
                sprintf(buffer, "\nException Code:  %08X\n", (int)E.ExceptionCode);
                sRet.append(buffer);

                if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
                {
                        // Access violation type - Write/Read.
                    memset(buffer, 0, sizeof(buffer));
                    sprintf(buffer,"%s Address:  %08X\n",
                            (E.ExceptionInformation[0]) ? "Write" : "Read", (int)E.ExceptionInformation[1]);
                    sRet.append(buffer);
                }


                sRet.append("Instruction: ");
                for (int i = 0; i < 16; i++)
                {
                    memset(buffer, 0, sizeof(buffer));
                    sprintf(buffer, " %02X",  PBYTE(E.ExceptionAddress)[i]);
                    sRet.append(buffer);
                }

                sRet.append("\nRegisters: ");

                memset(buffer, 0, sizeof(buffer));
                sprintf(buffer, "\nRAX: %08X  RBX: %08X  RCX: %08X  RDX: %08X",  (unsigned int)C.Rax,(unsigned int) C.Rbx, (unsigned int)C.Rcx, (unsigned int)C.Rdx);
                sRet.append(buffer);

                memset(buffer, 0, sizeof(buffer));
                sprintf(buffer, "\nRSI: %08X  RDI: %08X  RSP: %08X  RBP: %08X", (unsigned int)C.Rsi, (unsigned int)C.Rdi, (unsigned int)C.Rsp, (unsigned int)C.Rbp);
                sRet.append(buffer);

                memset(buffer, 0, sizeof(buffer));
                sprintf(buffer, "\nRIP: %08X  RFlags: %08X", (unsigned int)C.Rip,(unsigned int) C.EFlags);
                sRet.append(buffer);

        } //if (pException)

        sRet.append("\nCall Stack:");
        QString sCallstack = this->GetCallStack(m_pException);
        sRet.append(sCallstack);

        return sRet;
}

#if 0
bool CCrashStack::GetHardwareInaformation(QString &graphics_card, QString &sound_deivce)
{

    HRESULT hres;

    // Initialize COM.
    hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
    if (FAILED(hres))
    {
        qDebug() << "Failed to initialize COM library. "
                 << "Error code = 0x"
                 << hex << hres << endl;
        return false;
    }

    // Initialize
    hres = CoInitializeSecurity(NULL,
                                -1,
                                NULL,
                                NULL,
                                RPC_C_AUTHN_LEVEL_DEFAULT,
                                RPC_C_IMP_LEVEL_IMPERSONATE,
                                NULL,
                                EOAC_NONE,
                                NULL);


    if ((hres != RPC_E_TOO_LATE) && FAILED(hres))
    {
        qDebug() << "Failed to initialize security. "
                 << "Error code = 0x"
                 << hex << hres << endl;
        CoUninitialize();
        return false;
    }

    // Obtain the initial locator to Windows Management
    // on a particular host computer.
    IWbemLocator *pLoc = 0;

    hres = CoCreateInstance(
                CLSID_WbemLocator,
                0,
                CLSCTX_INPROC_SERVER,
                IID_IWbemLocator, (LPVOID *)&pLoc);

    if (FAILED(hres))
    {
        qDebug() << "Failed to create IWbemLocator object. "
                 << "Error code = 0x"
                 << hex << hres << endl;
        CoUninitialize();
        return false;
    }

    IWbemServices *pSvc = 0;

    // Connect to the root\cimv2 namespace with the
    // current user and obtain pointer pSvc
    // to make IWbemServices calls.

    hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);

    if (FAILED(hres))
    {
        qDebug() << "Could not connect. Error code = 0x"
                 << hex << hres << endl;
        pLoc->Release();
        CoUninitialize();
        return false;
    }

    // Set the IWbemServices proxy so that impersonation
    // of the user (client) occurs.
    hres = CoSetProxyBlanket(
                pSvc,                         // the proxy to set
                RPC_C_AUTHN_WINNT,            // authentication service
                RPC_C_AUTHZ_NONE,             // authorization service
                NULL,                         // Server principal name
                RPC_C_AUTHN_LEVEL_CALL,       // authentication level
                RPC_C_IMP_LEVEL_IMPERSONATE,  // impersonation level
                NULL,                         // client identity
                EOAC_NONE                     // proxy capabilities
                );

    if (FAILED(hres))
    {
        qDebug() << "Could not set proxy blanket. Error code = 0x"
                 << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return false;
    }


    // Use the IWbemServices pointer to make requests of WMI.
    // Make requests here:

    IEnumWbemClassObject* pEnumerator_graphics = NULL;
    IEnumWbemClassObject* pEnumerator_sound = NULL;
    BSTR wql = SysAllocString(L"WQL");
    BSTR sql_graphics = SysAllocString(L"SELECT * FROM Win32_VideoController");
    BSTR sql_sound = SysAllocString(L"SELECT * FROM Win32_SoundDevice");

    HRESULT hres_graphics;
    HRESULT hres_sound;
    hres_graphics = pSvc->ExecQuery(wql,
                           sql_graphics,
                           WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
                           NULL,
                           &pEnumerator_graphics);
    hres_sound = pSvc->ExecQuery(wql,
                           sql_sound,
                           WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
                           NULL,
                           &pEnumerator_sound);

    if (FAILED(hres_graphics) || FAILED(hres_sound))
    {
        qDebug() << "Query for processes failed. "
                 << "Error code = 0x"
                 << hex << hres_graphics << endl;

        qDebug() << "Query for processes failed. "
                 << "Error code = 0x"
                 << hex << hres_sound << endl;

        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return "";
    }
    else
    {
        IWbemClassObject *pclsObj_graphics;
        ULONG uReturnGraphics = 0;

        IWbemClassObject *pclsObj_sound;
        ULONG uReturnSound = 0;

        while (pEnumerator_graphics)
        {
            hres_graphics = pEnumerator_graphics->Next(WBEM_INFINITE, 1, &pclsObj_graphics, &uReturnGraphics);

            if (0 == uReturnGraphics)
            {
                break;
            }

            VARIANT description_vtProp;
            VARIANT driver_version_vtProp;
            VARIANT status_vtProp;

            // Get the value of the Name property
            hres_graphics = pclsObj_graphics->Get(L"Description", 0, &description_vtProp, 0, 0);
            QString description((QChar*) description_vtProp.bstrVal, wcslen(description_vtProp.bstrVal));

            hres_graphics = pclsObj_graphics->Get(L"DriverVersion", 0, &driver_version_vtProp, 0, 0);
            QString driver_version((QChar*) driver_version_vtProp.bstrVal, wcslen(driver_version_vtProp.bstrVal));

            hres_graphics = pclsObj_graphics->Get(L"Status", 0, &status_vtProp, 0, 0);
            QString status((QChar*) status_vtProp.bstrVal, wcslen(status_vtProp.bstrVal));

            graphics_card.append(description).append(" ").append(driver_version).append(" ").append(status).append(" \n");

            VariantClear(&description_vtProp);
            VariantClear(&driver_version_vtProp);
            VariantClear(&status_vtProp);
        }

        while (pEnumerator_sound)
        {
            hres_sound = pEnumerator_sound->Next(WBEM_INFINITE, 1, &pclsObj_sound, &uReturnSound);

            if (0 == uReturnSound)
            {
                break;
            }

            VARIANT description_vtProp;
            VARIANT status_vtProp;

            // Get the value of the Name property
            hres_sound = pclsObj_sound->Get(L"Description", 0, &description_vtProp, 0, 0);
            QString description((QChar*) description_vtProp.bstrVal, wcslen(description_vtProp.bstrVal));

            hres_sound = pclsObj_sound->Get(L"Status", 0, &status_vtProp, 0, 0);
            QString status((QChar*) status_vtProp.bstrVal, wcslen(status_vtProp.bstrVal));

            sound_deivce.append(description).append(" ").append(status).append(" \n");

            VariantClear(&description_vtProp);
            VariantClear(&status_vtProp);
        }
    }

    // Cleanup
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();
    return true;
}
#endif

4、引用记录崩溃类的main.cpp

#include "mainwindow.h"

#include <QApplication>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>

#include <windows.h>
#include <signal.h>
#include <dbghelp.h>
#include <QDateTime>
#include <QDir>
#include "ccrashstack.h"

#ifdef Q_OS_WIN
long __stdcall   callback(_EXCEPTION_POINTERS*   excp)
{
    CCrashStack crashStack(excp);
    QString sCrashInfo = crashStack.GetExceptionInfo();
    QString file_path = QApplication::applicationDirPath();

    QDir *folder_path = new QDir();
    bool exist = folder_path->exists(file_path.append("/Dumplog" ));
    if(!exist)
    {
        folder_path->mkdir(file_path);
    }
    delete folder_path;
    folder_path = nullptr;
    QDateTime dateTime = QDateTime::currentDateTime();
    QString baseFileName = "/crash"+ dateTime.toString("yyyyMMddhhmmss") + ".log";
    QString sFileName = file_path + baseFileName;

    QFile file(sFileName);
    if (file.open(QIODevice::WriteOnly|QIODevice::Truncate))
    {
        file.write(sCrashInfo.toUtf8());
        file.close();
    }

    return   EXCEPTION_EXECUTE_HANDLER;
}
#endif


int main(int argc, char *argv[])
{
    // 设置异常处理函数
#ifdef Q_OS_WIN
    SetUnhandledExceptionFilter(callback);
#endif

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    qDebug() << "Before crash.";
    //模拟崩溃
    int *p = nullptr;
    *p = 0;
    qDebug() << "After crash.";

    return a.exec();
}

5、查看产生的崩溃log例如:

Windows:  6.2.9200, SP 0.0, Product Type 1
Process:  E:\build-testwe-Desktop_Qt_5_12_12_MinGW_64_bit-Debug\debug\testwe.exe
Exception Addr: 00402776   Module: E:\build-testwe-Desktop_Qt_5_12_12_MinGW_64_bit-Debug\debug\testwe.exe
Exception Code:  C0000005
Write Address:  00000000
Instruction:  C7 00 00 00 00 00 48 8D 45 30 4C 8D 0D 19 9B 00
Registers: 
RAX: 00000000  RBX: 1F606AE0  RCX: 1F600000  RDX: 1F600000
RSI: 00000001  RDI: 1F606B00  RSP: 0079FC90  RBP: 0079FD10
RIP: 00402776  RFlags: 00010202
Call Stack:
00402776 E:\build-testwe-Desktop_Qt_5_12_12_MinGW_64_bit-Debug\debug\testwe.exe
0040C268 E:\build-testwe-Desktop_Qt_5_12_12_MinGW_64_bit-Debug\debug\testwe.exe

在QT安装路径下找到分析工具addr2line.exe

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
QT MinGW 是一个使用 MinGW 编译器的 QT 版本,它允许开发人员在 Windows 平台上使用 QT 框架进应用程序开发。在使用 QT MinGW开发之前,需要进一些配置和设置。 首先,打开 QT Creator。通过导航到 "工具 -> 选项" 打开选项窗口。在 "Kits" 选项卡中,找到 "Qt Versions" 选项,并点击 "添加"。在对话框中,找到并选择 MinGW 的 qmake.exe 文件。这样 QT Creator 就知道在构建项目时要使用 MinGW 编译器。 接下来,你可以使用 QT MinGW 开始开发项目。CEF MinGW Qt 是一个简单的 Web 客户端,它使用 QTMinGW构建。它使用 QT Application 对象来处理 Cef 事件,并使用单独的可执文件生成子进程。 如果你想自定义 QT MinGW 的配置,可以使用 configure.bat 脚本进设置。通过在命令中运 configure.bat 脚本,并提供一系列选项来进配置。例如,你可以使用 -confirm-license 和 -opensource 来确认 QT 的许可证,并开放源代码。你还可以使用 -platform 选项指定平台为 win32-g,使用 -prefix 选项指定安装路径,使用 -static 选项指定静态编译,使用 -qt-sqlite、-qt-zlib、-qt-libpng、-qt-libjpeg 来配置 QT 的库。还有其他选项可以根据你的需求进设置。 总而言之,QT MinGW 是一个允许开发人员在 Windows 平台上使用 MinGW 编译器的 QT 版本。你可以通过配置 QT Creator 和使用相应的工具和选项来开始使用 QT MinGW应用程序开发

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值