Instruments 是 Apple 官方提供给开发者的性能分析、测试工具,常用于分析性能、稳定性相关的问题。
但在使用过程中可能会遇到地址没有被符号化的问题,如下系统库 CFNetwork 相关的堆栈地址并没有被符号化展示:
关于符号化相关的官方详细介绍,可以查看 WWDC2021 视频:Symbolication: Beyond the basics。
类型
符号化与调试信息息息相关,调试信息分为以下三种类型:
- Function starts
- Nlist symbol table
- DWARF
Function starts
Function starts 仅提供函数对应的起始地址:
Function starts 数据解析类似于 iOS 15 以前的 rebase & bind,通过压缩字节流解析。
通过 Function starts 信息,可以知道地址距离其所在函数的首地址的 offset(0x100003a68 是函数首地址,264 是 offset):
Nlist symbol table
Nlist symbol table 是一个结构体,内部定义了获取函数地址和名称的相关信息:
Nlist 包含了direct symbols 和 indirect symbols。fishhook 就是利用了 Nlist 信息获取到 indirect symbols 地址对应的符号名称进行匹配,进而替换函数指针完成 hook 逻辑的。
Nlist 信息不能提供函数文件名、行号以及编译时优化信息。这也是系统库符号化之后没有文件名、行号的原因:
DWARF
DWARF(debugging with attributed record formats)是一种调试信息的存储格式,dSYM 文件和 Mach-O 库都可以包含 DWARF 信息:
DWARF 除了支持函数地址、名称,还拥有文件名、行号以及编译优化(内联等)信息。Apple 推荐使用 dSYM 文件完成符号化,因为 dSYM 文件不仅拥有最丰富细节的调试信息,而且能够被 Xcode 以及 Instruments 良好支持。
那么如何解决 Instruments 使用过程中未能符号化的问题?
系统库
因为系统库并没有提供 dSYM 文件,所以需要使用系统库文件完成符号化。当 iPhone 设备连接到 Mac 时,系统会默认下载该设备对应的系统库文件,文件对应的目录:
$HOME/Library/Developer/Xcode/iOS DeviceSupport
如果没有对应的 iPhone 设备,也可以使用开源工具库 ipsw 下载。
非系统库
对应自己编译的库,可以将 Debug Information Format 设置为:DWARF with dSYM file:
可以通过 symbols -uuid 命令,校验库文件与 dSYM 文件一致性:
如果是非自己编译的库,尽量让其提供 dSYM 文件。
如果确定已经使用 dSYM 文件了,Instruments 堆栈信息仍未符号化,那么可以检查一下项目的 entitlements 和代码签名配置。
可以使用 codesign 命令行工具,验证是否拥有正确的代码签名配置:
同时还需要检查本地开发的 entitlement 中是否包含了 get-task-allow 项,确保 code signing inject base entitlemens 值为 YES:
配置
当我们已经拥有系统库文件以及 dSYM 文件时,可以在 Symbols 配置里指定路径:
如果此时依然未能符号化,可以通过 Instrument->File->Symbols-> Add Symbols 配置:
可以指定具体的 dSYMs 文件夹或者库文件,需要注意的是当指定系统库文件时,系统库文件没有可执行权限的情况下,默认不能够选中:
可以通过增加可执行文件解决:
chmod +x CFNetwork