搭建 iOS-Crash 平台实践总结

前期调研准备可参考上一篇系列文章:https://www.xuyanlan.com/2019/02/20/iOS-crash-report/

客户端收集 Crash

客户端收集 Crash 使用的是 PLCrashReporter 这个开源的库,集成方法有很多成熟的文章可以参考,这里不再赘述。而且有需要的话你可以在 signal_handler_callback 方法中获取到崩溃信息然后追加崩溃瞬间的一些 App 信息,对于定位 Crash 有着重要的作用。

Crash 解析 - llvm8.0

实践中 Crash 解析方式并未用到上一篇文章中提到的自己实现的 macho 解析工具,但是前期的准备工作让后期搭建工作更加顺利。如果你的服务器不是 MacOS (是的话可以直接是使用 symbolicatecrash,只需要收集各个版本的固件即可)。我们所部署的 Crash 解析服务器是Linux。
发现了一个成熟的 Crash 解析工具 - llvm, 我所使用的版本是llvm8.0,部署非常简单,下载解压后就能使用。http://llvm.org/docs/CommandGuide/ 中列出了一些 Commands。 其中主要使用到的是:

llvm-symbolizer 用于定位代码位置,这个是解析 Crash 的重点,来看一个例子:

1
llvm-symbolizer --obj=XXX:arm64 0x100301A5C  0x1003014DC 0x1002FDE74 0x1002FDD3C  0x1002FBE60 0x100188B10

llvm-symbolizer 使用非常简单,--obj 指定 Binary Image 文件(如:xxx.dSym、libobjc.A.dylib)路径,后面可以跟同一个 Binary Image 中的多个文件地址(File Address)。但是从 Crash 文件中不能直接得到文件地址,上篇文章中提到了获取文件地址的方式,File Address = Stack address - Load address + Slide Value。

1
2
//crash的某一行信息,0x0000000195dd13a8 为 Stack address, 0x195da1000 是 Load address。 最后的十进制值其实就是 Stack address - Load address 的值了。
1 libdispatch.dylib 0x0000000195dd13a8 0x195da1000 + 197544

而通过 Load address 可以从 Binary Images 里面找到对应的库:

  • dSym 类型的 Binary Image 能够直接通过判断 arm 来获取 Slide Value,
    Slide Value(32位虚拟地址): 0x00004000
    Slide Value(64位虚拟地址): 0x0000000100000000 
    
  • 系统库获取则要依靠从库文件中读取出来,可以通过 llvm-readelf 命令来获取 Slide Value,其中 Segment 的 Text 段中的vmaddr 就是 Slide Value 了。如图: 可通过 llvm-dwarfdump -uuid 获取到的 UUID 和 Slide Value进行 Map,记录到对应系统库的一个文件中,真正解析的时候直接通过 Binary Images 定位到 UUID 后通过 Map 查找到 Slide Value 进行计算。

附上一张平时 debug 时没开断点,abort 住了只能拿到未解析的堆栈信息,利用llvm 查找崩溃的截图。

至此,想必你已经知道了如何通过 llvm 来解析 Crash 中的某行崩溃信息, 接下来的事情就是如何批量解析和平台化了。

批量解析 Crash 文件 & 平台聚类

我们已经收集到很多 Crash 文件了,上面说了如何解析一行崩溃信息。那么就可以进行批量解析了和聚类了。

其实很简单,我们做的只是利用了 llvm-symbolizer 命令同时能够解析多个崩溃地址的特性:

  • 整合多个收集上来的 Crash 文件,再统一解析后切分成单个解析好的 Crash 文件。只需要简单的脚本即可实现。
  • 制定聚类规则
    1:找到有 AppName 相关的 Crash 堆栈 Thread 信息,优先级: Last Exception Backtrace > Crashed Thread: XXX > 第一个包含自身 AppName 堆栈的 Thread > Thread 0
    2: 定位到的 Thread 提取出 AppName 相关的 Crash 堆栈信息,去除地址相关的信息,进行 md5,这就聚类好了。
  • 其他定制需求: 如某个 Crash 出现的系统版本、App 版本、新Crash告警,报表等。

??? 号 + unknown-arm64问题解决

有些机型收上来发现image是???,基地址是0x0,搜索发现是苹果新出了arm64e,参考以下文章:
arm64e符号翻译与PAC问题
知道原因后发现可以通过crash地址恢复image 和 基地址。

  • 通过 va = va & 0x0fffffffff; 将崩溃地址复原,遍历所有的image,得到基地址和对应的image。
  • 又可以愉快的解析啦~