任何情况下,都不能保证自己写的代码不会发生崩溃,崩溃不可怕,可怕的是无法定位哪里崩溃,特别是客户那边崩溃,开发者这边不崩溃,问题陷入僵局。自从有了下面这个神奇的代码,再也不怕了。

以下代码亲自测试没问题。

1. 如果是在VC++中,那么只需要将下列2个文件直接添加到工程中.发布程序后,如果发生了崩溃,那么在崩溃的文件中就已经记录了“哪个文件的哪一行发生了崩溃”。

再也不用像以前那样要在项目属性中配置,要生成.map、.cod等等文件,然后计算偏移地址来分析了。

2. 如果是在Qt+MinGW,那么只需要将下列2个文件直接添加到工程中,然后在.pro工程文件中添加以下几行配置:

LIBS += -lDbgHelp#表示在release下可以生成调试信息
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_CFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO

这样,编译release后的文件就带有调试信息,当运行程序后,发生崩溃就会产生崩溃日志,拿到崩溃日志后就可以分析了,只需要执行如下命令:

addr2line.exe -f -e 你的应用程序 崩溃地址 32、64位的分别使用对应的addr2line.exe工具即可,工具在:D:\Qt\Qt5.12.3\Tools\mingw730_32\bin、D:\Qt\Qt5.12.3\Tools\mingw730_64\bin目录下,例如你的应用  TestDmp.exe,崩溃地址是0000000000402E20,则

addr2line.exe -f -e TestDmp.exe 0000000000402E20

3. 补充一些:

3.1)在Qt+MinGW情况下,虽然能够生成崩溃文件,记录了崩溃地址,但是使用addr2line.exe工具时,发现竟然无法知道是哪行发生崩溃,甚至无法知道是哪个文件,此时仍然可以有挽救的方式:

1. 首先在崩溃的记录日志中知道是哪个库崩溃,例如本例子中是在TestDmp.exe中发生了崩溃。
2. 打开Qt的命令行工具,注意32和64位的区别,并cd到崩溃库所在目录。
3. 在命令行中输入以下命令,表示将引起崩溃的库导出为汇编代码objdump -S TestDmp.exe > TestDmp.exe.asm4. 最后就可以看到了汇编代码了

3.2)在崩溃记录的日志中,拿到崩溃的地址,然后在刚生成的那个asm文件中搜索,就可以看到了,就比如上面截图中的36337行(这个截图和2里面的不一致,是因为3是后来补充的)

3.3)要想能够在导出的asm文件中显示汇编代码和源代码,需要把.pro中的配置信息更换为如下:

如果不更改的话,也能显示很详细(只不过看到的区别就是:void TestForm::on_btnTest_clicked()显示的不会这么友好,而是显示为:<zxxxTestFormzdexxxon_btnTest_clickedzzddz>)

#表示在release下可以生成调试信息
#QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO
#QMAKE_CFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
#QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFOQMAKE_CXXFLAGS_RELEASE += -g
QMAKE_CFLAGS_RELEASE += -g
#禁止优化
QMAKE_CFLAGS_RELEASE -= -O2
QMAKE_CXXFLAGS_RELEASE -= -O2
#release在最后link时默认有"-s”参数,表示"忽略输出文件中的所有符号信息",因此要去掉该参数
QMAKE_LFLAGS_RELEASE = -mthreads -W

4. 补充二:

还有一个更加方便的方式,就是直接生成dump文件,然后利用WinDbg工具去分析即可;WinDbg常用的一个命令就是:这样就可以直接分析dump文件

!analyze -v

生成dump的文件,只需要把 CMSExceptionHandler::HandleException改为如下,这样其他代码都可以删掉了:

LONG CMSExceptionHandler::HandleExceptionMinDump(LPEXCEPTION_POINTERS pExceptionInfo)
{HANDLE hDumpFile = CreateFile(m_szLogFileName,GENERIC_WRITE,0,0,OPEN_ALWAYS,FILE_FLAG_WRITE_THROUGH,0);if (hDumpFile != INVALID_HANDLE_VALUE){MINIDUMP_EXCEPTION_INFORMATION dumpInfo;dumpInfo.ThreadId = GetCurrentThreadId();dumpInfo.ExceptionPointers = pExceptionInfo;dumpInfo.ClientPointers = TRUE;// 创建Dump文件MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithFullMemory, pExceptionInfo ? &dumpInfo : nullptr, nullptr, nullptr);CloseHandle(hDumpFile);}return EXCEPTION_EXECUTE_HANDLER;
}

代码文件如下:

头文件 MSExceptionHandler.h

#pragma once#include <Windows.h>
#include <string>class CMSExceptionHandler
{
public:CMSExceptionHandler();~CMSExceptionHandler();private:static LONG ExceptionFilter(LPEXCEPTION_POINTERS e);// The main function to handle exceptionLONG HandleException(LPEXCEPTION_POINTERS pExceptionInfo);void GenerateExceptionReport(LPEXCEPTION_POINTERS pExceptionInfo);const char* GetExceptionString(DWORD dwCode);// Work through the stack upwards to get the entire call stackvoid TraceCallStack(CONTEXT* pContext);int PrintTraceLog(const char * format, ...);private:static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;// Machine type matters when trace the call stack (StackWalk64)DWORD m_dwMachineType;HANDLE m_hReportFile;TCHAR m_szLogFileName[MAX_PATH];
};

cpp文件MSExceptionHandler.cpp

#include "MSExceptionHandler.h"#include <tchar.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>#pragma warning(push)
#pragma warning(disable : 4091)
#include <DbgHelp.h>
#pragma warning(pop)#pragma comment(lib, "Dbghelp.lib")static CMSExceptionHandler g_MSExceptionHandler;LPTOP_LEVEL_EXCEPTION_FILTER CMSExceptionHandler::m_previousFilter;static void GetNowTime(struct tm& nowTime)
{memset(&nowTime, 0, sizeof(struct tm));time_t t = time(NULL);struct tm* pTime = localtime(&t);if (pTime){nowTime.tm_sec = pTime->tm_sec;nowTime.tm_min = pTime->tm_min;nowTime.tm_hour = pTime->tm_hour;nowTime.tm_mday = pTime->tm_mday;nowTime.tm_mon = pTime->tm_mon;nowTime.tm_year = pTime->tm_year;nowTime.tm_wday = pTime->tm_wday;nowTime.tm_yday = pTime->tm_yday;nowTime.tm_isdst = pTime->tm_isdst;}
}static char* TrimString(char* psz)
{char szTmp[512] = { 0 };char* pszDot = strrchr(psz, '\\');if (pszDot){pszDot++;   // Advance past the '\\'strcpy(szTmp, pszDot);ZeroMemory(psz, strlen(psz));strcpy(psz, szTmp);}return psz;
}CMSExceptionHandler::CMSExceptionHandler()
{m_hReportFile = NULL;m_previousFilter = SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ExceptionFilter);// Get machine typem_dwMachineType = 0;TCHAR wszProcessor[256] = { 0 };_tcscpy(wszProcessor, ::_tgetenv(_T("PROCESSOR_ARCHITECTURE")));if (_tcslen(wszProcessor)>0){if ((!_tcsicmp(_T("EM64T"), wszProcessor)) || !_tcsicmp(_T("AMD64"), wszProcessor)){m_dwMachineType = IMAGE_FILE_MACHINE_AMD64;}else if (!_tcsicmp(_T("x86"), wszProcessor)){m_dwMachineType = IMAGE_FILE_MACHINE_I386;}}// Figure out what the report file will be named, and store it awayGetModuleFileName(0, m_szLogFileName, MAX_PATH);TCHAR szLogFile[MAX_PATH] = _T("");// Look for the '.' before the "EXE" extension.  Replace the extension// with "RPT"PTSTR pszDot = _tcsrchr(m_szLogFileName, _T('\\'));if (pszDot){pszDot++;   // Advance past the '\\'*pszDot = 0;_tcscpy(szLogFile, m_szLogFileName);}TCHAR szTime[125] = _T("debug.rpt");struct tm nowTime;GetNowTime(nowTime);_stprintf(szTime, _T("debug-%04d%02d%02d%02d%02d%02d.RPT"), nowTime.tm_year + 1900,nowTime.tm_mon + 1, nowTime.tm_mday, nowTime.tm_hour,nowTime.tm_min, nowTime.tm_sec);_tcscat(szLogFile, szTime);_tcscpy(m_szLogFileName, szLogFile);
}CMSExceptionHandler::~CMSExceptionHandler()
{SetUnhandledExceptionFilter(m_previousFilter);
}LONG CMSExceptionHandler::ExceptionFilter(LPEXCEPTION_POINTERS e)
{return g_MSExceptionHandler.HandleException(e);
}LONG CMSExceptionHandler::HandleException(LPEXCEPTION_POINTERS pExceptionInfo)
{HANDLE hProcess = INVALID_HANDLE_VALUE;// Initializes the symbol handlerif (!SymInitialize(GetCurrentProcess(), NULL, TRUE)){SymCleanup(hProcess);return EXCEPTION_EXECUTE_HANDLER;}m_hReportFile = CreateFile(m_szLogFileName,GENERIC_WRITE,0,0,OPEN_ALWAYS,FILE_FLAG_WRITE_THROUGH,0);if (m_hReportFile != INVALID_HANDLE_VALUE && NULL != m_hReportFile){SetFilePointer(m_hReportFile, 0, 0, FILE_END);GenerateExceptionReport(pExceptionInfo);// Work through the call stack upwards.TraceCallStack(pExceptionInfo->ContextRecord);CloseHandle(m_hReportFile);m_hReportFile = 0;}SymCleanup(hProcess);return(EXCEPTION_EXECUTE_HANDLER);/*if (m_previousFilter)return m_previousFilter(pExceptionInfo);elsereturn EXCEPTION_CONTINUE_SEARCH;*/
}void CMSExceptionHandler::GenerateExceptionReport(LPEXCEPTION_POINTERS pExceptionInfo)
{// Start out with a bannerPrintTraceLog("//=====================================================\n");struct tm nowTime;GetNowTime(nowTime);PrintTraceLog("Crash Last Time: %04d-%02d-%02d %02d:%02d:%02d\n", nowTime.tm_year + 1900,nowTime.tm_mon + 1, nowTime.tm_mday, nowTime.tm_hour,nowTime.tm_min, nowTime.tm_sec);PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;// First print information about the type of faultPrintTraceLog("Exception code: %08X %s\n",pExceptionRecord->ExceptionCode,GetExceptionString(pExceptionRecord->ExceptionCode));
#if defined(_WIN64)PrintTraceLog("Fault address: %016llX\n",pExceptionRecord->ExceptionAddress);
#elsePrintTraceLog("Fault address: %08X\n",pExceptionRecord->ExceptionAddress);
#endif
}const char* CMSExceptionHandler::GetExceptionString(DWORD dwCode)
{
#define EXCEPTION( x ) case EXCEPTION_##x: return (#x);switch (dwCode){EXCEPTION(ACCESS_VIOLATION)EXCEPTION(DATATYPE_MISALIGNMENT)EXCEPTION(BREAKPOINT)EXCEPTION(SINGLE_STEP)EXCEPTION(ARRAY_BOUNDS_EXCEEDED)EXCEPTION(FLT_DENORMAL_OPERAND)EXCEPTION(FLT_DIVIDE_BY_ZERO)EXCEPTION(FLT_INEXACT_RESULT)EXCEPTION(FLT_INVALID_OPERATION)EXCEPTION(FLT_OVERFLOW)EXCEPTION(FLT_STACK_CHECK)EXCEPTION(FLT_UNDERFLOW)EXCEPTION(INT_DIVIDE_BY_ZERO)EXCEPTION(INT_OVERFLOW)EXCEPTION(PRIV_INSTRUCTION)EXCEPTION(IN_PAGE_ERROR)EXCEPTION(ILLEGAL_INSTRUCTION)EXCEPTION(NONCONTINUABLE_EXCEPTION)EXCEPTION(STACK_OVERFLOW)EXCEPTION(INVALID_DISPOSITION)EXCEPTION(GUARD_PAGE)EXCEPTION(INVALID_HANDLE)}// If not one of the "known" exceptions, try to get the string// from NTDLL.DLL's message table.static char szBuffer[512] = { 0 };FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,GetModuleHandle(_T("NTDLL.DLL")),dwCode, 0, szBuffer, sizeof(szBuffer), 0);return szBuffer;
}// Work through the stack to get the entire call stack
void CMSExceptionHandler::TraceCallStack(CONTEXT* pContext)
{// Initialize stack frameSTACKFRAME64 sf;memset(&sf, 0, sizeof(STACKFRAME));#if defined(_WIN64)sf.AddrPC.Offset = pContext->Rip;sf.AddrStack.Offset = pContext->Rsp;sf.AddrFrame.Offset = pContext->Rbp;
#elif defined(WIN32)sf.AddrPC.Offset = pContext->Eip;sf.AddrStack.Offset = pContext->Esp;sf.AddrFrame.Offset = pContext->Ebp;
#endifsf.AddrPC.Mode = AddrModeFlat;sf.AddrStack.Mode = AddrModeFlat;sf.AddrFrame.Mode = AddrModeFlat;PrintTraceLog("\nRegisters:\n");#if defined(_WIN64)PrintTraceLog("EAX:%016llX\nEBX:%016llX\nECX:%016llX\nEDX:%016llX\nESI:%016llX\nEDI:%016llX\n",pContext->Rax, pContext->Rbx, pContext->Rcx, pContext->Rdx, pContext->Rsi, pContext->Rdi);PrintTraceLog("CS:EIP:%04X:%016llX\n", pContext->SegCs, sf.AddrPC.Offset);PrintTraceLog("SS:ESP:%04X:%016llX  EBP:%016llX\n",pContext->SegSs, sf.AddrStack.Offset, sf.AddrFrame.Offset);
#elsePrintTraceLog("EAX:%08X\nEBX:%08X\nECX:%08X\nEDX:%08X\nESI:%08X\nEDI:%08X\n",pContext->Eax, pContext->Ebx, pContext->Ecx, pContext->Edx, pContext->Esi, pContext->Edi);PrintTraceLog("CS:EIP:%04X:%08llX\n", pContext->SegCs, sf.AddrPC.Offset);PrintTraceLog("SS:ESP:%04X:%08llX  EBP:%08llX\n",pContext->SegSs, sf.AddrStack.Offset, sf.AddrFrame.Offset);
#endifPrintTraceLog("DS:%04X  ES:%04X  FS:%04X  GS:%04X\n",pContext->SegDs, pContext->SegEs, pContext->SegFs, pContext->SegGs);PrintTraceLog("Flags:%08X\n", pContext->EFlags);if (0 == m_dwMachineType)return;PrintTraceLog("\nCall stack:\n");#if defined(_WIN64)PrintTraceLog("Address           Line    Function    File    Module\n");
#elsePrintTraceLog("Address   Line    Function    File    Module\n");
#endif// Walk through the stack frames.int CALLSTACK_DEPTH = 0;HANDLE hProcess = GetCurrentProcess();HANDLE hThread = GetCurrentThread();while (StackWalk64(m_dwMachineType, hProcess, hThread, &sf, pContext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)){if (sf.AddrFrame.Offset == 0 || CALLSTACK_DEPTH >= 10)break;CALLSTACK_DEPTH++;// 1. Get function name at the addressconst int nBuffSize = (sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64);ULONG64 symbolBuffer[nBuffSize];PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);pSymbol->MaxNameLen = MAX_SYM_NAME;DWORD64 dwAddress = sf.AddrPC.Offset;DWORD dwLineNumber = 0;char szFuncName[260] = { 0 };char szFileName[260] = { 0 };char szModuleName[260] = { 0 };DWORD64 moduleBase = SymGetModuleBase64(hProcess, sf.AddrPC.Offset);if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, szModuleName, 260)){TrimString(szModuleName);}if (strlen(szModuleName) <= 0) strcpy(szModuleName, "Unknow");DWORD64 dwSymDisplacement = 0;if (SymFromAddr(hProcess, sf.AddrPC.Offset, &dwSymDisplacement, pSymbol)){std::string str(pSymbol->Name);strcpy(szFuncName, str.c_str());}if (strlen(szFuncName) <= 0) strcpy(szFuncName, "Unknow");//2. get line and file name at the addressIMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };DWORD dwLineDisplacement = 0;if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo)){std::string str(lineInfo.FileName);strcpy(szFileName, str.c_str());TrimString(szFileName);dwLineNumber = lineInfo.LineNumber;}if (strlen(szFileName) <= 0) strcpy(szFileName, "Unknow");// Call stack stored
#if defined(_WIN64)PrintTraceLog("%016llX  %-8ld%s    %s    %s\n",dwAddress, dwLineNumber, szFuncName, szFileName, szModuleName);
#elsePrintTraceLog("%08llX  %-8d%s    %s    %s\n",dwAddress, dwLineNumber, szFuncName, szFileName, szModuleName);
#endif}
}int CMSExceptionHandler::PrintTraceLog(const char * format, ...)
{char szBuff[1024] = "";int retValue;DWORD cbWritten;va_list argptr;va_start(argptr, format);retValue = vsprintf(szBuff, format, argptr);va_end(argptr);WriteFile(m_hReportFile, szBuff, retValue * sizeof(char), &cbWritten, 0);return retValue;
}

windows上bug崩溃定位分析(Qt或者VS)相关推荐

  1. 测试过程线上问题的定位分析问题处理总结

    问题,俗称BUG.在测试工程师的职责范围内,要尽可能且尽早地发现程序上的问题,找到问题,然后暴露出来给开发修复,减少线上问题的发生,降低公司因线上问题产生的风险.在发现问题之后 ,也要关注定位问题.分 ...

  2. iOS线上APP崩溃(Crash)分析

    这两周一直在研究如何追踪线上的bug,如何快速分析出程序到底崩溃在什么地方,从底层了解Crash是如何产生的.如何传递的.以及是如何分析出来的.虽然项目组并没有对这些要求很严格,但是作为一个高级开发人 ...

  3. 线上BUG 处理并分析原因

    昨天下午大神把组内几十号人召集在一起开Online bug分析大会,主要是针对近期线上事故从事故原因和解决方案两个维度来分析 对金融软件来说,每一次的线上事故都有可能给公司带来重大的损失,少扣了用户的 ...

  4. 使用Windows上SourceInsight工具建立分析Linux下uboot源代码project

    SourceInsight软件能够说是分析查阅大型源代码project文件的神器!界面不错.功能强大! 第一步:安装好SourceInsight后打开软件 点击上面的Project--->new ...

  5. javacore分析工具_线上死锁定位分析

    " 记录一次线上死锁的定位分析."        昨晚睡觉前提了点代码到 jfoa(https://github.com/JavaFamilyClub/jfoa) 怎么也没想到导致 ...

  6. 麒麟系统开发笔记(十):在国产麒麟系统上使用gdb定位崩溃异常方法流程以及测试Demo

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/129858821 红胖子网络科技博文大全:开发技术集合( ...

  7. 走完线上 BUG 定位最后一公里

    简介:因为线上线下环境隔离的问题,线上的输入很多时候难以在日常环境中构造,定位 bug 效率低下.是否有简单快捷的办法呢? 一个小故事 周末12点的闹钟在回龙观均价3000的出租屋急促的响起,程序员小 ...

  8. 缺陷定位 | 分析推理定位BUG案例(三)

    往期关联文章: 缺陷定位 | 测试发现了Bug,还要分析定位Bug?(一) 缺陷定位 | 如何精准效率分析推测BUG定位(二) 运营反馈,生产环境,WEB端管理后台,岗位审核详情,视频无法正常播放 表 ...

  9. 遇到bug我们如何分析定位?

    前言:日常工作中,每天可能都会遇到不同的bug,有些刚入行的测试喜欢不加分析就直接甩给开发去解决.开发比较闲还好,如果手头工作比较多,就容易烦.甚至有可能是后端的问题,但是你却把问题丢给了前端,这种事 ...

最新文章

  1. 为什么ElasticSearch比MySQL更适合复杂条件搜索
  2. svn命令行 批量添加(add)所有新增文件
  3. 代码和产品发布的几种方式
  4. [js]设计模式小结对原型的修改
  5. [转]百万数据查询优化技巧三四则
  6. vue使用Echart跟随窗口大小改变而重新绘制时出现读取窗口大小不及时的问题
  7. dao.php,DAO.php · Dodd/Training Lab - Gitee.com
  8. mysqli扩展是mysql扩展的增强版_PHP学习笔记【22】--PHP数据库编程 mysql扩展库 和mysqli扩展库...
  9. 计算机开发运维测试优劣,铁路运维软件安全性测试方法的研究
  10. Cent Os下安装软件之---源码安装
  11. c语言合法的用户字符,在C语言中下列合法的字符常量是
  12. MATLAB多幅图片生成GIF动画
  13. 360全景的原理与展示
  14. C语言math.h库函数中atan与atan2的区别
  15. 大学android移动开发笔记,基于Android的移动模拟练习系统的设计与实现
  16. 【知识图谱】关系抽取与总结展望
  17. 【云原生】4.2 DevOps 精讲篇
  18. C++课程教学改革问卷调查报告
  19. FastDFS关键配置说明,包括文件同步和安全设置。
  20. 【Redis】hmdp关注取关、共同关注、关注推送功能实现

热门文章

  1. Pointnet++代码详解(三):query_ball_point函数
  2. VBScript参数处理
  3. java版远程桌面控制(简约版)
  4. flutter开发欠揍♎
  5. NTFS格式和FAT格式的区别
  6. BIOS追code之PEI phase
  7. 初三物理光学知识点总结_中考物理光学知识点总结
  8. 外企的那些所谓礼貌用语
  9. 转:hosts文件及修改hosts的作用
  10. 局域网内配置服务器方法--p2p技术、端口映射、网络域名