0%

Google BreakPad接入与dump文件分析

为了收集程序崩溃时的dump信息,之前接入了Google的BreakPad,但一直没有起到太大的作用--因为定位不到具体的代码,这次仔细研究了一下这个问题,做一下记录。 首先BreakPad的代码已经迁移到Git上了(git clone https://chromium.googlesource.com/breakpad/breakpad),而且按Google的说法已经新出了一个CrashPad--但暂时还是用BreakPad吧。

然后BreakPad在Windows上的接入有两种方式:1. 直接将其代码放到你的项目里面,同项目一起编译; 2.  先将其编译为静态链接库.lib,然后在项目中使用其头文件和lib文件即可,这种方式的优点是编译你的工程时不用再编译BreakPad了。

先说第一种方式,直接参考 https://github.com/JPNaude/dev_notes/wiki/Using-Google-Breakpad-with-Qt 即可,从BreakPad的源码里面拷出代码来按文件夹组织好即可同项目一起编译。 第二种方式就复杂一点了,先安装好Python 2.7并设置好环境变量,然后:

  1. 把Chromium的项目管理工具 GYP Clone下来,把gyp.bat设置到环境变更Path中;
  2. 编辑breakpad\src\client\windows\breakpad_client.gyp,注释掉 targets : dependencies 列表中的最后三个和 test 有关的依赖引用;
  3. 在breakpad的目录下启动命令行,用 SET GYP_MSVS_VERSION=2013 设置目标 Visual Studio 工程的版本,后面的2013根据你的VS可设成2010、2015之类的;
  4. 运行 gyp.bat --no-circular-check src\client\windows\breakpad_client.gyp -Dwin_release_RuntimeLibrary=2 -Dwin_debug_RuntimeLibrary=3;最后两个参数设置的是 /MT(d) 还是 /MD(d)  (0:/MT,1:/MTd, 2:/MD, 3:/MDd),一般情况下是使用MD和MDd;
  5. 生成的项目文件在目录 src\client\windows 中,直接用VS打开编译即可;

编译完成后,还得从BreakPad中把头文件提取出来,目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
|--google_breakpad
|---processor
|---common
|--common
|---windows
|---scoped_ptr.h
|---client
|---windows
|---crash_generation
|---sender
|---handler
|---common
然后在你的项目中包含src/client/windows/handler/exception_handler.h,再把之前生成的几个lib链接进去(crash_report_sender.lib、crash_generation_client.lib、common.lib、exception_handler.lib)即可。

接入的示例代码如下:

1
2
3
4
5
6
7
8
9
google_breakpad::ExceptionHandler *pHandler = new google_breakpad::ExceptionHandler( 
L"dumps\\",
DmpFilter,
DmpCallback,
0,
google_breakpad::ExceptionHandler::HANDLER_ALL,
MiniDumpNormal,
L"",
0 );

接入BreakPad后,若程序发生崩溃,BreadPad会向dumps文件夹中写入dump文件,把dump文件和应用程序的pdb放在一起,用VS打开dump文件即可进行调试,一般情况下可定位到出现问题的代码处。  

再说遇到的几个问题,一是如果是用GCC(MingW),该如何生成pdb呢? 得加入“-g”让gcc生成调试信息,还需要关闭优化,再用一个叫cv2pdb的工具从最后编译出来的exe、dll中提取出pdb来,以Qt的工程文件为例,相关的编译器开关如下:

1
2
3
4
5
6
7
8
9
10
win32-g++ {
#加入调试信息
QMAKE_CFLAGS_RELEASE += -g
QMAKE_CXXFLAGS_RELEASE += -g
#禁止优化
QMAKE_CFLAGS_RELEASE -= -O2
QMAKE_CXXFLAGS_RELEASE -= -O2
#release在最后link时默认有"-s”参数,表示"Omit all symbol information from the output file",因此要去掉该参数
QMAKE_LFLAGS_RELEASE =
}

第二个问题就是定位不到正确的堆栈和代码了,在研究过程中发现了以下现象,如果把以下代码作为崩溃触发点,则debug和release模式下的程序都可以正常生成dump文件,并且VS都可以分析:

1
2
3
4
5
6
7
int buggyFunc()
{
int* p = NULL;
*p = 13;

return 0;
}
但如果用以下代码来触发崩溃,在VS工程下,debug模式的程序可以正常生成dump且VS可分析,而release模式的程序则会弹出Windows错误报告(Window Error Reporting)的对话框、不会生成dump文件,但是--如果是Qt工程,Release模式下可以生成dump文件--但是这个dump文件VS没法定位到正确的代码:
1
2
3
4
5
6
int buggyFunc()
{
delete reinterpret_cast<QString*>(0xFEE1DEAD);

return 0;
}
摸索了一阵子并查找了一些资料之后,只能推测VS默认的一些编译开关(如/GS等),会使发生这种越界内存读取时,跳过应用程序设置的异常处理,直接弹出Windows错误报告,而Qt生成的编译命令中不带这些指令。由于一番实验之后没有找到对应的编译开关,因此这个纯属推测。

那么还有一个问题,为什么生成的dump文件VS定位不到代码? 我先尝试了用 https://github.com/JPNaude/dev_notes/wiki/Using-Google-Breakpad-with-Qt 这篇文章里面提到的dump_syms.exe来转换pdb为sym,再用minidump_stackwalk.exe来进行分析,结果比VS好一点,但全定位到BreakPad的代码上去了...   最后终于从这里(http://stackoverflow.com/questions/33939009/useless-dumps-in-google-breakpad)找到了原因,那就是Windows下的minidump_stackwalk不好使,得在linux下自己编译一个来用! 经实验,在linux下用breakpad源码编译的minidump_stackwalk可以正常地分析这种情况下的dump文件。

最后,还推荐一下这几天找到的CrashRpt(http://crashrpt.sourceforge.net/),一个实现了UI和打包上传的崩溃处理类库,在发生崩溃时还能进行截屏,然后打包dump文件、日志等一起上传。经实验,对于上面的那个特例,在Qt的Release模式下CrashRpt也可以生成dump,而且VS对dump文件也无法正确定位,至于linux下的minidump_stackwalk能不能分析,暂时还没有实验过。  

参考资料: * http://zxstudio.org/blog/2014/10/28/integrating-google-breakpad/ * https://kingsamchen.github.io/2016/09/24/build-and-integrate-the-newest-version-of-breakpad-on-windows/