一 引言
我们写代码的时候经常会遇到一些Bug导致程序异常退出,比如访问了空指针。在大多数情况下,我们能够根据经验和IDE本身提供的调试功能来定位问题,并找到解决方案。但最近在工作中遇到了一个问题,一个提供后台服务的程序在运行不等时间后例如30分钟,一个小时或其它时间后会异常退出。一时,毫无头绪,因为代码里面嵌入了其它同事写的模块,虽然很怀疑是同事引起的,但是没有证据就没有底气啊。百度了很多解决办法后,最终选择下面的方案,最终证明是同事模块引起的。 以下内容是我整理网友的解决方案,加以自己的简单封装,下面就对这种利用Dump文件进行程序异常分析的方式进行简介。
二 Dump文件
来自百度百科:
Dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。Dump文件是用来给驱动程序编写人员调试驱动程序用的,这种文件必须用专用工具软件打开,比如使用WinDbg打开。
我的解释:dump文件可以保存程序的运行信息,Windows下程序奔溃退出后会调用一个被注册过的处理函数,我们就可以在这个函数里面生成dump文件,保存现场,然后利用WinDbg Preview进行分析,可以查看当时调用堆栈,确定程序最后调用了哪个函数导致了没有出来,引起程序异常崩溃。
三 如何在程序崩溃时生成Dump文件
SetUnhandledExceptionFilter函数是Win32API的异常捕获函数,在程式异常结束前。会调用该函数注冊的回调函数,这样就能在进程终止前运行指定的代码,达到比如保存数据的功能。其实在程序奔溃时我们也可以给予一个用户提示等,下面写出我简单封装的一个类WinDebug,使用的时候调用install即可,该类在程序崩溃时会在当前目录生成Debug.dmp,如果需要其它功能用户可自行更改handler函数。使用时不要忘记链接DbgHelp.lib!!!
WinDebug.h:
#pragma once
#include <Windows.h>
#include <DbgHelp.h>
class WinDebug
{
public:
static void install();
private:
static long handler(_EXCEPTION_POINTERS* pException);
};
WinDebug.cpp:
#include "WinDebug.h"
#include <QDebug>
void WinDebug::install()
{
SetUnhandledExceptionFilter(&WinDebug::handler);
}
long WinDebug::handler(_EXCEPTION_POINTERS* pException)
{
HANDLE hDumpFile = CreateFile(L"Debug.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDumpFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ExceptionPointers = pException;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ClientPointers = true;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
}
return EXCEPTION_EXECUTE_HANDLER;
}
main.cpp:
#include <QApplication>
#include "WinDebug.h"
#include "mainwindow.h"
#include <QPainter>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WinDebug::install();
QPainter* p;
p->drawLine(0, 0, 100, 100);
MainWindow w;
w.show();
return a.exec();
}
注意:p是空指针,程序会异常退出。
四 Pdb文件
PDB文件是在编译工程的时候产生的,它是和对应的模块(exe或dll)一起生成出来的。pdb文件可以使用一些特有的pdb阅读器打开。如果想把PDB文件转换成TXT文件查看,可以使用专用转换器转换。如果想根据dump文件分析程序是在哪一行崩溃的就必须要有pdb文件,debug下会自动生成pdb文件。那么Release程序如何生成pdb文件呢?Qt工程可以在pro文件中加上
QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO /DEBUG
VS工程做如下设置:
1.
2.
这样就能在Release下生成PDB文件了。
五 利用WinDbg Preview分析dump文件
WinDbg Preview可以在微软的应用商店和官网下载,这里不再赘述。请自行下载安装!
1.打开WinDbg Preview,界面如下
2.加载dump文件,点击文件,然后点击Open dump file,加载进去刚刚生成的Debug.dmp
3. 设置pdb和源码路径,点击文件->Settings,选择Debugging settings,设置好source path 和 symbol path(pdb文件路径)
4.此时主界面如下:
5. 输入 !analyze -v 或者点击上面的提示,此时会加载一些信息并打印出分析结果,查看STACK_TEXT即可查看程序异常退出前的堆栈信息。
6.我们可以看到上述函数调用过程,最后进去了QPainter::drawLines,这和我们预期的是一致的,这个函数导致了程序崩溃。
五 写在最后
以上只是简单的程序崩溃处理,如果有更加方便简洁的方式也请各位同仁不吝赐教,最后也是感谢网友的无私分享,方有我这篇拙作。
四时最好是三月,一去不回唯少年。