Qt dump崩溃调试简单整理

1 篇文章 0 订阅
1 篇文章 0 订阅

1. 生成dump文件

这边参考博客中的源码地址,breakpaddemo不同编译环境可直接编译运行使用,可得到dmp文件

2. dump调试(msvc)

2.1 vs2010编译调试

关键是原项目需要生成pdb文件,vs需要如下配置(偷图):

  • 项目->属性->C/C+±>General->Debug Information Format->Program Database

在这里插入图片描述

  • 项目->属性->C/C+±>Optimization->Optimization->Disabled(/Od),这一步貌似也可不设置
    在这里插入图片描述
  • 项目->属性->Linker->Debugging->Generate Debug Info->Yes(/DEBUG)
    在这里插入图片描述
    做完以上配置后,构建生成可执行文件,运行,生成dmp文件,dmp文件拷贝到执行文件目录(保证exe,pdb,dmp文件在一个目录即可),运行dmp文件,vs会自动打开,再点击右上角的调试,即可定位到崩溃的代码片段。

2.2 Qt + msvc编译调试

类似vs,要生成pdb文件,原项目pro文件中需添加代码:

CONFIG += force_debug_info
CONFIG += separate_debug_info

之后就是构建运行,与2.1之后的操作一样

3. Qt mingw编译调试

3.1 项目pro文件代码片段

QMAKE_CFLAGS_RELEASE += -g
QMAKE_CXXFLAGS_RELEASE += -g
QMAKE_CFLAGS_RELEASE -= -O2
QMAKE_CXXFLAGS_RELEASE -= -O2
QMAKE_LFLAGS_RELEASE = -mthreads -W

3.2 crash类与回调函数准备

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();
};

ccrashstack.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->Ebp;
                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  ", (unsigned int)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));

//        QString graphics_module = "GraphicsCard: ";
//        QString sound_module = "SoundDevice: ";
//        GetHardwareInaformation(graphics_module, sound_module);
//        sRet.append(graphics_module);
//        sRet.append(sound_module);

        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  ", (int)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, "\nEAX: %08X  EBX: %08X  ECX: %08X  EDX: %08X",  (unsigned int)C.Eax,(unsigned int) C.Ebx, (unsigned int)C.Ecx, (unsigned int)C.Edx);
                sRet.append(buffer);

                memset(buffer, 0, sizeof(buffer));
                sprintf(buffer, "\nESI: %08X  EDI: %08X  ESP: %08X  EBP: %08X", (unsigned int)C.Esi, (unsigned int)C.Edi, (unsigned int)C.Esp, (unsigned int)C.Ebp);
                sRet.append(buffer);

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

        } //if (pException)

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

        return sRet;
}

main.cpp

#include "ccrashstack.h"

long __stdcall   callback(_EXCEPTION_POINTERS*   excp)
{
    CCrashStack crashStack(excp);
    QString sCrashInfo = crashStack.GetExceptionInfo();

//    TCHAR my_documents[MAX_PATH];
//    SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, my_documents);
//    QString file_path = QString::fromWCharArray(my_documents);
    QDir dir;
    QString file_path = dir.currentPath();

    QDir *folder_path = new QDir;
    bool exist = folder_path->exists(file_path.append("\\MyApp"));
    if(!exist)
    {
        folder_path->mkdir(file_path);
    }
    delete folder_path;
    folder_path = nullptr;

    QString sFileName = file_path + "\\crash.log";

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

    return   EXCEPTION_EXECUTE_HANDLER;
}

int main() {
	SetUnhandledExceptionFilter(callback);
	...
	...
}

3.3 使用Qt工具,根据crash输出的错误地址查找错误位置

使用上述crashstack类在进程崩溃时会在执行文件目录下的MyApp目录内产生一个crash.log文件:

Windows: 6.1.7601, SP 1.0, Product Type 1
Process: …\BreakpadDemo.exe
Exception Addr: 00406621 Module: …\BreakpadDemo.exe
Exception Code: C0000005
Write Address: 00000000
Instruction: C7 00 01 00 00 00 90 C9 C3 55 89 E5 83 EC 04 89
Registers:
EAX: 00000000 EBX: 00000000 ECX: 0028FE1C EDX: 00000000
ESI: 00000029 EDI: 0280E968 ESP: 0028D3DC EBP: 0028D3EC
EIP: 00406621 EFlags: 00010202
Call Stack:
00406621 …\BreakpadDemo.exe
00406638 …\BreakpadDemo.exe
00408AED …\BreakpadDemo.exe
00408BE0 …\BreakpadDemo.exe
68C5E1FF …\Qt5Core.dll
00AB9ED0 …\Qt5Widgets.dll
00440064 …\BreakpadDemo.exe

(注意加粗的部分,在下个步骤会用到)

在Qt安装目录下找到对应32位的addr2line.exe程序,拷贝到应用程序的目录下,在cmd定位到执行文件目录再执行addr2line.exe -f -e [exe name] [addr name]
下面是我的运行结果

F:\...\release>addr2line.exe -f -e BreakpadDemo.exe 00406621
_Z5crashv
F:\.../../BreakpadDemo/src/ui/demodialog.cpp:16

[exe name]后面可跟多个addr地址,我只输入一个,即用了log文件中加粗的部分。最后输出的可以看到是demodialog.cpp:16出了问题,成功定位。

4. 参考链接

qt mingw 创建dump 并查找crash 出错行

Qt下MSVC/Mingw平台dump/crash log报告调试方法差异

C+±Dump文件的创建和调试

12.5-使用Qt实现跨平台C++崩溃捕获,看这一篇就足够了(Breakpad)

Linux下Qt生成dump文件并定位bug(基于qBreakpad)

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Windows调试是一种通过分析崩溃时生成的dump文件来定位错误和问题的过程。dump文件是操作系统在程序崩溃时自动创建的一种保存程序状态和信息的文件。 通过dump文件定位崩溃的过程可以分为以下几个步骤: 1. 获取dump文件:当程序崩溃时,操作系统会自动创建一个dump文件,通常保存在程序的运行目录下。可以通过查看程序崩溃时的错误提示信息来确定dump文件的位置。 2. 安装符号文件:符号文件是包含程序源代码和编译器产生的调试信息的文件。在调试过程中,需要将符号文件与dump文件关联起来,以便能够查看和分析源代码。可以从官方网站下载对应版本的符号文件。 3. 打开dump文件:可以使用调试工具(如Visual Studio、WinDbg等)打开dump文件。在打开文件后,工具会加载符号文件,显示出崩溃时的堆栈信息。 4. 分析堆栈信息:堆栈信息显示了程序在崩溃时的函数调用情况。从堆栈信息中,可以定位到导致崩溃的函数和代码所在的位置。通过查看源代码,可以进一步分析和解决错误。 5. 调试程序:在分析崩溃原因后,可以使用调试工具来逐步执行程序,观察变量的值和执行的过程,以便定位错误。可以设置断点、观察变量、修改变量的值等操作,帮助分析和解决问题。 通过以上步骤,我们可以利用dump文件进行Windows调试,快速定位程序崩溃的原因,并解决问题。调试过程需要了解一定的调试技巧和工具的使用方法,同时对于程序的结构和运行过程有一定的了解,这样才能更好地定位和解决问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值