Windows下Qt使用dump定位崩溃位置(1)
软件调试
Windows下Qt使用dump定位崩溃位置(1)
更多精彩内容 |
---|
👉个人内容分类汇总 👈 |
👉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
-
打开vs,选择继续但无需代码
-
将dmp文件直接拖如vs中
-
点击1️⃣【设置符号路径】,进行如下设置;
- 如果是第一次调试需要勾选Microsoft符号服务器2️⃣:会从网络下载调试使用的符号文件,然后将符号文件下载到5️⃣位置,以后就不需要勾选符号服务器了;
- 点击“+”号3️⃣添加【TestCrashHandler.pdb】6️⃣文件所在路径4️⃣
-
点击 使用仅限本机进行调试
-
成功定位到崩溃位置
4、下载Windows符号表
如果勾选了Microsoft符号服务器或者NuGet.org服务器则可将window符号表下载到下列路径中,在离线环境中也可进行调试。
5、下载Qt符号表
这种方法可以选择下载部分调试符号表,体积比较小。
-
选择系统环境https://download.qt.io/online/qtsdkrepository/
-
选择desktop
-
选择安装的qt版本,我的时5.14.2
-
选择调试编译器版本,我的是msvc2017-64
-
下载需要调试的模块的符号表
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 }