写点什么

使用 Google Breakpad 来助力解决程序崩溃

  • 2022 年 8 月 12 日
    江苏
  • 本文字数:2873 字

    阅读完需:约 9 分钟

使用 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];


代码如下:


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    // Override point for customization after application launch.        [[BreakpadController sharedInstance] start:YES];    return YES;}

- (void)applicationWillTerminate:(UIApplication *)application{ [[BreakpadController sharedInstance] stop];}
复制代码


除了依赖库之外,我们还得在工程中加入一些配置,这也是 Breakpad 所要求的,否则 Breakpad 在初始化的时候就会初始化失败。


打开我们的测试工程的 plist,然后为其加上以下内容:


 <key>BreakpadProduct</key>    <string>Google_Notifier_Mac</string>    <key>BreakpadProductDisplay</key>    <string>${PRODUCT_NAME}</string>    <key>BreakpadURL</key>    <string>这里请填入您自己要上传的服务器地址</string>    <key>BreakpadReportInterval</key>    <string>30</string>
复制代码


这些配置做完了以后,接下来我们就要来模拟一次崩溃啦!这还不容易吗?直接写一个数组越界的逻辑就可以模拟 Crash 啦!


代码如下:


- (IBAction)Triger:(id)sender {    NSArray *array = [[NSArray alloc] initWithObjects:@"1", @"2", @"3", nil];        NSString *value = [array objectAtIndex:8];}
复制代码


当我们点击这个按钮事件的时候,就会触发这次崩溃。


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,欢迎与我交流。

发布于: 刚刚阅读数: 3
用户头像

佛系编码 2019.05.13 加入

红鲤鱼与绿鲤鱼与驴。

评论

发布
暂无评论
使用 Google Breakpad 来助力解决程序崩溃_ios_HelloWorld杰少_InfoQ写作社区