前期调研准备可参考上一篇系列文章: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 - convert addresses into source code locations
- llvm-readelf - GNU-style LLVM Object Reader
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 | //crash的某一行信息,0x0000000195dd13a8 为 Stack address, 0x195da1000 是 Load address。 最后的十进制值其实就是 Stack address - Load address 的值了。 |
而通过 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。
- 又可以愉快的解析啦~