qt生成dump文件并定位异常

📅 2026/8/7 19:09:44
qt生成dump文件并定位异常
一、什么是Dump文件对于程序崩溃最快的解决方式是生成dump文件通过生成dump文件使用调试工具进行调试还原程序崩溃时的状态能够起到快速定位排查问题的作用。dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。Dump文件是用来给驱动程序编写人员调试驱动程序用的这种文件必须用专用工具软件打开。二、Dump文件的类型2.1 内核模式DumpDump文件分为两大类内核模式Dump和用户模式Dump。内核模式Dump是操作系统创建的崩溃转储最经典的就是系统蓝屏这时候会自动创建内核模式的Dump。2.2 用户模式Dump用户模式Dump进一步可以分为完整Full Dump和Mini dump。Full Dump包含了某个进程完整的地址空间数据以及许多用于调试的信息而Mini dump则有许多类型根据需要可以包含不同的信息有的可能只包含某个线程和部分模块的信息。三、Dump文件的生成Dump文件能够保存程序内部的内存、堆栈、句柄、线程等程序运行相关的信息非常具有重要性。3.1 通过调试工具生成通过调试工具创建。调试工具如Visual StudioWindbg以及微软提供的ADplus都可以创建DUMP在Windbg中通过.dump命令来生成。3.2 通过使用任务管理器生成该方式可以生成.DMP文件通过打开任务管理器找到插件服务对应的进程右击选择创建转储文件3.3 通过编程自动生成当程序遇到未处理异常(主要指非指针造成)导致程序崩溃死如果在异常发生之前调用了SetUnhandledExceptionFilter()函数异常交给函数处理。因而在程序开始处增加SetUnhandledExceptionFilter()函数并在函数中利用适当的方法生成Dump文件即可实现需要的功能。在编程过程中可以预期的异常都通过结构化异常try/catch进行了处理。此时如果发生了未预期的异常这些异常处理代码无法处理则转由Windows提供的默认异常处理器来进行处理这个特殊的异常处理函数为UnhandledExceptionFilter。该函数会显示一个消息框提示发生了未处理的异常同时让用户选择结束或调试该进程。也就是如下界面因此为了更友好的处理未预期的异常主要是创建内存转储可以覆盖默认的异常处理操作。这是通过函数SetUnhandledExceptionFilter完成的函数原型如下LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter( _In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilterlpTopLevelExceptionFilter即异常处理函数指针如果设置为NULL则默认使用UnhandledExceptionFilter。因此我们可以对照lpTopLevelExceptionFilter自定义一个异常处理函数。我们需要创建内存转储。这通过函数MiniDumpWriteDump来实现。下述代码是一个通过MiniDumpWriteDump函数来实现转储文件创建LONG WINAPI MyUnhandledExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) { HANDLE hFile CreateFile(mini.dmp, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if( hFile INVALID_HANDLE_VALUE ) return EXCEPTION_EXECUTE_HANDLER; MINIDUMP_EXCEPTION_INFORMATION mdei; mdei.ThreadId GetCurrentThreadId(); mdei.ExceptionPointers ExceptionInfo; mdei.ClientPointers NULL; MINIDUMP_CALLBACK_INFORMATION mci; mci.CallbackRoutine NULL; mci.CallbackParam 0; MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, mdei, NULL, mci); CloseHandle(hFile); AfxMessageBox(已成功创建崩溃转储); return EXCEPTION_EXECUTE_HANDLER; }原文链接https://blog.csdn.net/qq_39543984/article/details/1239688343.4.代码实现3.4.1声明函数DumpHelper.h//#ifndef DUMPHELPER_H //#define DUMPHELPER_H #pragma once #include QSystemSemaphore #include QDir #include QDateTime #include QDebug #include tchar.h #include Windows.h #include DbgHelp.h #pragma comment(lib, user32.lib) #pragma comment(lib, DbgHelp.Lib) int GenerateMiniDump(PEXCEPTION_POINTERS pExceptionPointers) { // 定义函数指针 typedef BOOL(WINAPI * MiniDumpWriteDumpT)( HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION, PMINIDUMP_USER_STREAM_INFORMATION, PMINIDUMP_CALLBACK_INFORMATION ); // 从 DbgHelp.dll 库中获取 MiniDumpWriteDump 函数 MiniDumpWriteDumpT pfnMiniDumpWriteDump NULL; HMODULE hDbgHelp LoadLibrary(_T(DbgHelp.dll)); if (NULL hDbgHelp) { return EXCEPTION_CONTINUE_EXECUTION; } pfnMiniDumpWriteDump (MiniDumpWriteDumpT)GetProcAddress(hDbgHelp, MiniDumpWriteDump); if (NULL pfnMiniDumpWriteDump) { FreeLibrary(hDbgHelp); return EXCEPTION_CONTINUE_EXECUTION; } // 创建 dmp 文件件 TCHAR szFileName[MAX_PATH] { 0 }; TCHAR szVersion[] LDumpFile; SYSTEMTIME stLocalTime; GetLocalTime(stLocalTime); wsprintf(szFileName, L%s-%04d%02d%02d-%02d%02d%02d.dmp, szVersion, stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond); HANDLE hDumpFile CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0); if (INVALID_HANDLE_VALUE hDumpFile) { FreeLibrary(hDbgHelp); return EXCEPTION_CONTINUE_EXECUTION; } // 写入 dmp 文件 MINIDUMP_EXCEPTION_INFORMATION expParam; expParam.ThreadId GetCurrentThreadId(); expParam.ExceptionPointers pExceptionPointers; expParam.ClientPointers FALSE; pfnMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithDataSegs, (pExceptionPointers ? expParam : NULL), NULL, NULL); // 释放文件 CloseHandle(hDumpFile); FreeLibrary(hDbgHelp); return EXCEPTION_EXECUTE_HANDLER; } LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS lpExceptionInfo) { // 这里做一些异常的过滤或提示 if (IsDebuggerPresent()) { return EXCEPTION_CONTINUE_SEARCH; } return GenerateMiniDump(lpExceptionInfo); } long __stdcall errCallback(_EXCEPTION_POINTERS* pException) { // //用于崩溃重启 // // 信号量的意义把操作共享内存的代码锁住。因为有可能同时启动, 防止并发 // QSystemSemaphore sema(DyError, 1, QSystemSemaphore::Open); // sema.acquire(); QDir dir; dir.mkdir(./dumps); dir.cd(./dumps); /* ***保存数据代码*** */ QString fileName dir.path() / QDateTime::currentDateTime().toString(yyyyMMdd_HHmmss.zzz) .dmp; LPCWSTR pFileName (LPCWSTR)fileName.unicode(); //创建 Dump 文件 HANDLE hDumpFile CreateFile(pFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); qDebug() create dumpFile: hDumpFile INVALID_HANDLE_VALUE; if(hDumpFile ! INVALID_HANDLE_VALUE) { //Dump信息 MINIDUMP_EXCEPTION_INFORMATION dumpInfo; dumpInfo.ExceptionPointers pException; dumpInfo.ThreadId GetCurrentThreadId(); dumpInfo.ClientPointers TRUE; // ::全局作用域符号 // 写入Dump文件内容 // DumpType这里仅仅保存普通信息。假如需要保存变量值可以加上【MiniDumpWithFullMemory】 // 参考https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ne-minidumpapiset-minidump_type ::MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, dumpInfo, NULL, NULL); // ::MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal | MiniDumpWithFullMemory, dumpInfo, NULL, NULL); } // delete unimem; // qDebug() start application: QProcess::startDetached(qApp-applicationFilePath(), QStringList());//重启 // qApp-quit(); qApp-exit(-1); return EXCEPTION_EXECUTE_HANDLER; } //#endif // DUMPHELPER_H3.4.2.调用函数在main函数中注册main.cpp#include mainwindow.h #include QApplication #include DumpHelper.h int main(int argc, char *argv[]) { // SetUnhandledExceptionFilter(ExceptionFilter); // dmp文件比较大 SetUnhandledExceptionFilter(errCallback); // dmp文件比较小 QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }2、版本发布方式1选择release编译模式勾选Generate separate debug info复选框生成符号文件pdb。方式2选择Profile编译模式生成符号文件pdb。注发布版本后需要将生成的pdb文件、exe文件以及源码备份需补充为什么要使用Profile编译模式有什么优点以上两种方式有没有区别3、使用VS分析dump文件定位崩溃位置可参考Qt下生成pdb文件并在exe崩溃时生成dmp文件且由dmp查询崩溃原因_qt生成pdb文件-CSDN博客3.1程序崩溃后使用vs将生成的.dmp文件打开。3.2手动设置符号路径1、点击窗口右上侧的“设置符号路径”2、选择QT源码路径3、选择备份的pdb和exe所在路径4、选择缓存符号路径3.3手动设置源码路径1、选择“视图”-“解决方案资源管理器”打开“解决方案资源管理器”窗口1、2、右击“解决方案solution10项目”选择“属性”打开“解决方案Solution1属性页”窗口3、在“调试源文件”页面添加源码所在路径3.4进行调试1、点击窗口右上侧的“使用仅限本机进行调试”在VS加载系统pdb的时候点击“取消”按钮如下图VS会自动把源代码打开并指出异常崩溃的地方。防止VS将异常定位到系统模块的符号而非源代码上2、VS分析完后会自动将异常定位到源代码位置如下图3.5不取消加载系统pdb的结果如下图。出现未加载 wntdll.pdb 报错大概率是你的指针使用错误 比如使用野指针、越界访问、或者堆区空间释放方式错误等。Qt开发 之 抓取崩溃信息读这一篇就够了_wx635f8a025188b的技术博客_51CTO博客4、使用windbg分析dmp文件定位崩溃位置4.1打开windbg添加符号路径(1)、源码路径(2)、加载dmp文件(3)4.2 加载符号路径符号路径下文件内容4.3加载源码路径多路径使用分号该项目使用插件化架构如下图4.4 使用命令“!analyze -v”查看堆栈信息1、获取堆栈信息如图图中编号1处为有效的代码错误信息4.5 使用命令“u BaseProject!CModuleString::createException0x1a”查看源码错误行号代码结构如下图所示baseProject为项目中的一个插件CommonIO为调用该插件的子项目。注意事项windbg版本应该与生成项目的版本一致如下图所示生成项目使用的为msvc 2015 32bit则windgb使用的为x86版本。总结1、使用windgb分析dmp文件比VS分析 更有优势windgb总能定位到具体的源码位置VS成功概率并非100%。5.windbg常用命令5.1 基础调试命令1. 寄存器操作1. r显示/修改寄存器和标志位状态22. rm控制寄存器显示掩码筛选特定寄存器22. 执行控制1. p/t单步执行p跳过函数调用t进入函数2. .excr重置当前线程上下文5.2内存分析命令堆内存分析!heap显示堆内存分配详情对象列表、类型统计等4!address -summary查看进程内存分布含托管/非托管堆5内存泄漏检测!analyze -v自动分析崩溃转储文件优先执行此命令获取错误上下文导出自动分析结果##导出自动分析结果到D盘的Analysis_Report.txt文件中 .logopen D:\Analysis_Report.txt !analyze -v .logcloseWinDbg中!analyze -v命令分析DMP文件详解一、核心字段解析1. BUG_CHECK_CODE表示系统崩溃时的停止代码Stop Code例如 0x124硬件错误、 0x3B系统服务异常等。代码对应具体错误类型是诊断的起点1。2. PROCESS_NAME崩溃时正在运行的进程名称如svchost.exe或第三方软件进程。若进程属于用户程序可能指向应用程序兼容性问题2。3. MODULE_NAME导致崩溃的驱动或模块名称例如nvlddmkm.sysNVIDIA显卡驱动。此字段直接关联故障源需优先检查对应驱动或软件的更新/兼容性1。4. TRAP_FRAME包含崩溃时的寄存器状态和堆栈信息例如rax00000000表示寄存器值。通过分析寄存器可定位异常触发点如空指针访问或内存越界3。5. STACK_TEXT堆栈调用跟踪显示崩溃前函数调用链。5.2.1.windbg STACK_TEXT(堆栈信息)含义WinDbg 显示的堆栈信息通常由多列表示每列都有特定的意义。以下是对常见几列含义的具体解释:1.地址(Address)第一列为当前帧在内存中的起始地址。每个地址代表了一个函数调用的位置或者是某个返回指令前的状态快照。点击某一行即可将该处设为活动环境方便进一步检查局部变量等内容。2.模块名(Module Name)这是包含此地址代码段所属动态链接库(.dll)或可执行文件exe的名字标识符。了解哪部分程序正在被执行有助于快速锁定问题来源范围。3.函数原型(Function Prototype)它给出了准确无误的功能入口描述形式——即包括完整命名空间、类别成员资格以及参数清单在内的规范表达式。对于识别递归深度或者追踪复杂控制流而言至关重要。4.源码位置(Source Location)[如有]若已加载适当符号并且路径指向有效的原始文本则会附加显示实际所在脚本里的确切地方如行数。这对于从机器级回溯至高级语言层非常有价值。5.偏移量Offset指的是相对于函数头部的实际位移数值可以帮助确定究竟是在哪一个内部步骤产生了故障等情况。5.2.2如何进一步利用windbg显示堆栈信息里的偏移量分析问题如获取源码位置在WinDbg调试过程中堆栈信息中的偏移量可以帮助我们定位程序崩溃的位置、函数调用顺序以及更深入地理解代码运行状态。以下是关于如何分析堆栈信息中的偏移量的一些关键点1.偏移量的基本含义堆栈信息通常会包含类似这样的内容ntdll!RtlAllocateHeap0x1234ntd1l!RtlAllocateHeap表示当前指令所在的模块ntdll.dl1和具体函数名称RtlAllocateHeap )。0x1234则表示从该函数入口地址开始计算的偏移量即当前执行位置距离函数起始地址的具体字节数。分析步骤1.确定模块与函数名模块如 ntdll)提供了上下文环境。函数名指出了代码的大致作用区域。2.查看反汇编代码使用命令.disasm /f addressi或直接输入 u module!functionoffs et查看对应地址处的汇编代码片段。例如:u ntdll!RtlAllocateHeap0x1234这样可以清楚看到当前偏移量指向的具体机器码及其解释。3.检查源文件及行号如果有符号支持如果加载了正确的PDB符号表则除了偏移量外还会有对应的源文件路径和行数提示。这极大地方便了对高层语言代码的理解。4.结合其他调试工具进一步探索当前断点附近的寄存器值、内存布局等也可能影响最终结论因此需要综合考虑所有可用数据来完成全面诊断。示例解析假设你遇到了下面这条堆栈记录eax00000000 ebx7ffdf000 ecxObadf00 eip77b9abc6 esp0018f9bc ebp0018fac CS001b SS0023 ds0023 es0023 fs00ntdll!RtlAllocateHeap0x1a2:77b9abc6 c706feffffff mov dwor通过上面提到的技术手段逐步展开剖析过程…..首牛阳确这早发生在 RtlAllocateHean 内部的一个动作然后利用disassembly功能获取确切的行为模式并且评估是否合理合法——比如这里尝试向某个特定位置写入固定数值FFFFFFFE。如果发现异常则需回溯到更高层呼叫者那里去挖掘根本原因。5.3 断点管理1. 断点设置1. bp 地址在指定地址设置软件断点32. bm 模式通过通配符批量设置断点如bm module!func*32. 断点列表1. bl列出所有活动断点及状态5.4 系统级操作模块管理.reload强制重新加载符号文件.chain列出所有已加载扩展模块转储文件.dump /ma 文件名生成包含完整内存信息的转储文件5.5 线程控制• ~*k显示所有线程调用栈• ~线程号s切换到指定线程上下文25、windbg常用内存分析指令堆内存分析!heap显示堆内存分配详情对象列表、类型统计等4!address -summary查看进程内存分布含托管/非托管堆5内存泄漏检测!analyze -v自动分析崩溃转储文件优先执行此命令获取错误上下文5.1.ecxr命令在Windbg中.ecxr命令用于显示当前线程的异常上下文信息常用于分析崩溃转储文件如.dmp文件时定位异常发生时的处理器状态。以下是.ecxr输出信息的详细解释5.1.1.寄存器状态输出会显示异常发生时CPU寄存器的值例如eax00000000通用寄存器可能存储函数返回值或临时数据。eip77f0b6c2指令指针指向触发异常的指令地址。esp0019f4b4堆栈指针指向当前线程的堆栈顶部。ebp0019f4d0基址指针用于函数调用堆栈的展开。段寄存器cs001b,ds0023,es0023,fs003b,gs0000用于内存分段现代系统中fs通常指向线程环境块TEB。5.1.2. 异常记录Exception Record描述异常类型和详细信息ExceptionCode: c0000005异常代码如0xC0000005表示访问违规Access Violation。ExceptionFlags: 00000000异常标志通常为0。ExceptionAddress: 77f0b6c2触发异常的指令地址与eip一致。Parameters附加参数例如访问违规时参数10表示读操作参数200420000表示访问的无效内存地址。5.1.3. 上下文记录Context Record包含完整的线程上下文如ContextFlags标识哪些寄存器/状态被捕获如CONTEXT_INTEGER表示通用寄存器有效。SegGs/SegFs段寄存器值与线程执行环境相关。Dr0-Dr7调试寄存器用于硬件断点。5.1.4. 关键调试步骤定位异常代码通过ExceptionCode确定异常类型如除零、非法指令。分析eip地址使用u eip反汇编触发异常的指令。检查内存访问若为访问违规用dc 参数2查看目标内存是否有效。展开堆栈输入k查看调用堆栈结合符号路径分析函数调用链。0:000 .ecxr eax00000000 ebx00000000 eip77f0b6c2 esp0019f4b4 ... ExceptionCode: c0000005 (Access violation) ExceptionAddress: 77f0b6c2 (ntdll!RtlFreeHeap0x00000052) Parameters: 00000000 (Read) 00420000 (Invalid address) 0:000 u eip ntdll!RtlFreeHeap0x52: 77f0b6c2 8b39 mov edi,dword ptr [ecx] ; 尝试读取ecx指向的内存 0:000 dc 00420000 00420000 ???????? ???????? ... ; 无效地址说明ecx未正确初始化