使用 Google Breakpad 来助力解决程序崩溃
背景
作为一名程序,最头疼的莫过于项目上线后收到程序崩溃的通知,若能够在手头重现出该问题,那相对来说项目能够及时的修复并更新;如果无法重现外网崩溃的问题,那就十分的"头疼"了。要是能够实时的采集到项目的崩溃信息,那该多好啊!这并不是一种什么奢望,目前就有现成的技术解决方案。这段时间,我一直在帮项目开发程序崩溃的采集功能,其中用到的技术方案就是 Google 开发的 Breakpad。
Google Breakpad 是什么?
Google Breakpad 是 Google 开发的一个跨平台的崩溃采集库。利用这个库可以在 Windows, Mac, Linux, iOS 和 Android 平台上对程序的崩溃进行捕获,并生成 dump 文件供后期分析。也是目前最成熟运用最广的开源库,并且这个库现在依然在更新和维护。
它主要包括三个部分:
dumpSyms 负责读取用户开发应用中的 debug 信息,并生成特定的符号文件。
client 在崩溃系统中负责抓取当前线程和当前载入的库生成 minidump 文件。
processor 通过 minidump_stackwalk 读取 minidump 文件 找到合适的符号文件产生一个可读的 c/c++ 调用栈。
breakpad 原理图
在默认情况下,当程序崩溃时 breakpad 会生成一个 minidump 文件,它在不同平台上的实现机制不一样,解释如下:
在 Windows 平台上,使用微软提供的 SetUnhandledExceptionFilter() 方法来实现。
在 OS X 平台上,通过创建一个线程来监听 Mach Exception port 来实现。
在 Linux 平台上,通过设置一个信号处理器来监听 SIGILL SIGSEGV 等异常信号。
Breakpad 在所有的平台上都使用 minidump 文件格式,minidump 文件格式是由微软开发的用于崩溃上传,它包括:
当 dump 生成时进程中一系列 executable 和 shared libraries, 包括这些文件的文件名和版本号。
进程中的线程列表,对于每个线程,minidump 包含它在寄存器中的状态,线程的 stack memory 内容。这些数据都是未解析的字节流,Breakpad client 通常没有调试信息能生成函数名,行号,甚至无法确定 stack frame 的边界。
其他收集关于系统的信息,如处理器,操作系统高版本,dump 的原因等等。
breakpad 的使用
在 Github 上将 Breakpad 源码下载到本地:https://github.com/google/breakpad。
编译
打开终端,cd 到源码路径,编译源码:
./configure && make
编译之前请先在电脑上通过 homebrew 安装 cmake 工具,否则会报错。
生成 dump_syms
找到路径 /breakpad-main/src/tools/mac/dump_syms/dump_syms.xcodeproj,然后通过 Xcode 编译生成 dump_syms。
生成 libBreakpad.a
本文通过在 iOS 平台上集成 Breakpad 来演示崩溃采集,所以这里我们只会去编译供 iOS 应用使用的 .a 库。
找到路径 /breakpad-main/src/client/ios/Breakpad.xcodeproj,然后通过 Xcode 打开它,然后编译成 libBreakpad.a。
好了,到这里我们的准备工作就做好了,接下来就来看看如何去解析崩溃吧!
项目集成
首先创建一个 iOS App 的测试工程,然后在工程中依赖我们上面编译生成的 libBreakpad.a 库。
然后在 didFinishLaunchingWithOptions 方法里加入
[[BreakpadController sharedInstance] start: YES];
在 applicationWillTerminate 方法时加入
[[BreakpadController sharedInstance] stop];
代码如下:
除了依赖库之外,我们还得在工程中加入一些配置,这也是 Breakpad 所要求的,否则 Breakpad 在初始化的时候就会初始化失败。
打开我们的测试工程的 plist,然后为其加上以下内容:
这些配置做完了以后,接下来我们就要来模拟一次崩溃啦!这还不容易吗?直接写一个数组越界的逻辑就可以模拟 Crash 啦!
代码如下:
当我们点击这个按钮事件的时候,就会触发这次崩溃。
Breakpad 在捕获到这次崩溃时,会在我们 App 的 Library/Caches 路径下创建 Breakpad 文件夹,并将生成的 dmp 文件保存在里面,如图:
现在有了 dmp 文件,我们暂时还无法去解析它,为什么?因为我们还缺少符号文件去符号化这个 dmp。
那我们去哪里找这个符号文件呢? 打包时,iOS App 的符号文件默认情况下都存放在 xcarchive 文件中。
打开 Xcode 的 Window -> Organizer, 然后找到对应的 archive 包,右键 Show in Finder, 文件夹 dSYMs 里即为我们的符号文件。
分析崩溃文件
在上面我们已经编译好了 dump_syms 和 minidump_stackwalk ,接下来用这两个工具生成 symbols 文件和堆栈文件。
在你合适的目录中新建一个文件夹,名称按照个人喜好即可,然后将 dump_syms,minidump_stackwalk,dmp 和 .dSYM 文件拖进来。
生成 Symbols 文件:
cd 到该目录,执行如下命令:
$ ./dump_syms -a arm64 TTTT.app.dSYM > TTTT.sym
解释下,命令中的架构可以是 armv7, armv7s 等等,主要还是看你的应用是支持的什么架构,像我的这个测试工程,它就只支持 arm64,所以我这边就只生成 arm64 的 Symbols 文件。另外,TTTT.app.dSYM 是我这边打包生成的,需要替换你自己的 .dSYM 文件,然后生成的 .sym 文件,文件名必须与之前的 TTTT 保持一致,否则 dmp 文件就不能符号化。
查看 TTTT.sym 文件内容,执行如下:
head -n1 TTTT.sym
头部会有
MODULE mac arm64 6DD6178D3EF53F21A61BE8B5D9E294D00 TTTT
这样的字符串。
继续执行命令
mkdir -p symbols/TTTT/6DD6178D3EF53F21A61BE8B5D9E294D00 /
mv TTTT.sym symbols/TTTT/6DD6178D3EF53F21A61BE8B5D9E294D00 /
./minidump_stackwalk xxxx-xxxx-xxxx-xxx.dmp symbols > crashed.log
这样我们就把 dmp 给分析出来了,符号化的数据都保存在 crashed.log 中。
根据图上的内容,我们可以发现程序的崩溃发生在 ViewController.m 的第 24 行。回到我们的测试工程中,发现确实在 24 行,这里发生了数组越界。
由此说明,Breakpad 帮助我们找到了程序崩溃的地方,本次实验就成功啦!
最后
好了,本篇教程到此就结束了。总体来说 Breakpad 使用起来并不麻烦,崩溃采集的结果也很准确,相信对很多想把产品做好的公司来说是一把利器。本篇仅是简单的讲解了一下 Google Breakpad 的使用以及 dump 解析,如果真正想把这一块做好的话还需要下一点功夫,譬如说崩溃文件压缩上传,以及服务器崩溃日志解析等工作都需要自动化完成,本篇就不再赘述了,如果你有什么好的 idea,欢迎与我交流。
版权声明: 本文为 InfoQ 作者【HelloWorld杰少】的原创文章。
原文链接:【http://xie.infoq.cn/article/872f3e61146544f185e9941e7】。文章转载请联系作者。
评论