背景
对于经常使用C/C++的伙伴来说,程序有问题动不动就罢工崩溃的问题简直不能太熟悉了。比如本地测试通过打包发布的release版本Qt程序,在客户环境下仍可能出现异常崩溃的问题。一般通过客户反馈以及分析系统运行日志,问题基本都能够得到快速解决。但总会有些bug很难定位,这种情况下通过生成dump文件,结合winDbg程序定位问题将是一个很好的解决方式。
内容新增
-------------------------------------------------------------------分割线----------------------------------------------------------
VS作为宇宙第一IDE,名声在外自然要利用起来。对于编写Qt程序,QtCreator还是最香的。但VS的强大的调试能力还是无出其右。对于疑难问题,将QT程序移植到VS环境运行调试,是可以直接跳转到问题代码行的,这也是不错的可行方法。
-------------------------------------------------------------------分割线----------------------------------------------------------
具体操作
一、生成dump文件
在QtCreator中默认不支持生成dump文件,且发行版release模式不含调试信息,因此这里需要进行以下两步设置。
1.在.pro文件中添加如下语句,来产生调试信息以及pdb文件
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
QMAKE_LFLAGS_RELEASE = $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
LIBS += -lDbgHelp
2.程序文件中添加可以产生dump文件的代码。代码直接复制可用,不再讲解含义
LONG crashHandler(EXCEPTION_POINTERS *pException)
{
QString curDataTime = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
QString dumpName = curDataTime + ".dmp";
HANDLE dumpFile = CreateFile((LPCWSTR)QString("./" + dumpName).utf16(),GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(dumpFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ExceptionPointers = pException;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ClientPointers = TRUE;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),dumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
CloseHandle(dumpFile);
}
else
{
qDebug() << "dumpFile not vaild";
}
return EXCEPTION_EXECUTE_HANDLER;
}
//防止CRT(C runtime)函数报错可能捕捉不到
void DisableSetUnhandledExceptionFilter()
{
void* addr = (void*)GetProcAddress(LoadLibrary(L"kernel32.dll"), "SetUnhandledExceptionFilter");
if(addr)
{
unsigned char code[16];
int size = 0;
code[size++] = 0x33;
code[size++] = 0xC0;
code[size++] = 0xC2;
code[size++] = 0x04;
code[size++] = 0x00;
DWORD dwOldFlag, dwTempFlag;
VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
}
}
int main(int argc, char *argv[])
{
//注冊异常捕获函数
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)crashHandler);
DisableSetUnhandledExceptionFilter();
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
3.清除并重新qmake运行,产生pdb文件以及运行程序。此种情况下应用程序崩溃将会预定位置产生dump文件。
二、使用dump文件
1.假设此时程序崩溃得到dump文件,打开winDbg软件(需要自行下载,安装很简单),分别设置源代码路径、pdb文件路径、dump路径,确定后软件跳转至分析界面。
2.点击自动分析(!anasyle -v),产生堆栈信息,之后可以查看到出错的源代码行数
注意事项
以上提供了一个解决异常崩溃的思路,不是终极解决方案,虽然无法保证100%解决问题,但是可能会有奇效,个人建议还是逻辑分析为主,问题基本都能解决,还能锻炼分析能力,避免再写类似bug。
注意点如下:
1.应保证打包的exe,pdb,源代码版本一致,否则调试可能有偏差。
2.dump文件不保证一定产生,可能有些错误捕捉不到。
3.!anasyle -v自动分析后,大部分时候可以直接定位到出错行数,很直观。但也存在找不到行数的情况,但是有清晰的堆栈信息可以帮助分析问题。