紧接着上一篇文章
你可以直接用系统的方法获取当前线程堆栈,也可以使用 PLCrashRepoter 获取所有线程堆栈,也可以参考 BSBacktraceLogger 自己写一套轻量级的堆栈采集框架。
堆栈符号化还原有四种常见的方法:
symbolicatecrash
mac 下的 atos 工具
linux 下的 atos 的替代品 atosl
通过 dSYM 文件提取地址和符号的对应关系,进行符号还原
以上方案都有对应的应用场景,对于线上的 Crash 堆栈符号还原,主要采用的还是后三种方案。atos 和 atosl 的使用方法很类似,以下是 atos 的一个示例。
但是 atos 是Mac上一个工具,需要使用 Mac 或者黑苹果来进行解析工作,如果由后台来做解析工作,往往需要一套基于 Linux 的解析方案,这个时候可以选择 atosl,但是这个库已经有多年没有更新了,同时基于我司的尝试, atosl 好像不太支持 arm64 架构,所以我们放弃了该方案。
最终使用了第四个方案,提取 dSYM 的符号表,可以自己研发工具,也可以直接使用 bugly 和 网易云捕提供的工具,下面是提取出来的符号表。第一列是起始内存地址,第二列是结束地址,第三列是对应的函数名、文件名以及行号。
因为程序每次启动基地址都会变化,所以上面提到的地址是相对偏移地址,在我们获取到崩溃堆栈地址后,可以根据堆栈中的偏移地址来与符号表中的地址来做匹配,进而找到堆栈所对应的函数符号。比如下面的第四行,偏移为 43072 转换为十六进制就是 a840,用 a840 去上面的符号表中找对应关系,会发现对应着 -[GYRootViewController tableView:cellForRowAtIndexPath:]
,基于这种方式,就可以将堆栈地址完全还原为函数符号啦。
我们的应用存在多个版本,并且支持多种不同的架构,那么如何找到与崩溃日志对应的符号表呢?就是依靠 UUID,只有当崩溃日志的 UUID 与 dSYM 的 UUID 一致时,才能得到正确的解析结果。
dSYM 的 UUID 获取方法:
应用内获取 UUID 的方法:
上面只是提取到了我们应用中 dSYM 中的符号表,对于系统库还是无能为力的,比如 UIKit 就没有办法将其地址符号化,想要将动态库符号化,需要先获取系统库的符号文件。提取系统符号文件可以从 iOS 固件中获取,也可以从 Github 上开源项目中找到对应系统的符号文件。