首发于iOS从业者

iOS崩溃解析&原理说明


iOS崩溃解析&原理说明


1. 为什么崩溃日志需要解析

如图所示是崩溃日志线程回溯信息,其中的调用堆栈都是二进制地址,而不是可读的函数名称因此需要对崩溃日志进行解析,解析成可以理解的函数调用堆栈。


2.生成dSYM符号文件

crashlog 解析需要调试符号表文件 dSYM(debugging symbols)dSYM 文件实际上是从Mach-O 文件抽取调试信息得到的文件目录。在编译工程时, debug 模式会默认选中生成dSYM文件, 该配置可在 Build Setting|Build Option 中更改。 dSYM文件生成比较耗时,如果不需要进行 crashlog 解析,可以选择不生成。


2.1 Debug下可以在DeriveData的目录下获取到dSYM文件



2.2 打包的时候可以在生成的.achive目录下找到对应的dSYM文件



2 解析方法

2.1 Xcode 解析

crashlogdSYM 文件和可执行文件放在同一目录下,然后将 crashlog 拖拽至 Devicelog中,右键 Re-symbolicate Log 就能解析。


2.2 使用 symbolicatecrash 命令行解析

  • 1.首先找到symbolicatecrash的路径

通常symbolicatecrash的路径为/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash

  • 2.命令行解析 首先将崩溃日志,dSYM以及symbolicatecrash复制出来放到同一个文件夹,然后cd到当前文件夹 ,运行如下命令解析./symbolicatecrash temp.crash testxcConfig.app.dSYM > result.log


  • 3.首次运行需要先运行命令export DEVELOPER_DIR="/Applications/XCode.app/Contents/Developer"

3.解析原理

dSYM 文件介绍


其中真正保存保存数据的是 DWARF 文件, DWARF(Debuging With Arbitrary Format)是ELF 和 Mach-O 等文件格式中用来存储和处理调试信息的标准格式。 DWARF 中的数据是高度 压 缩 的 , 可以通过dwarfdump命令提取可读信息,比如提取关键的调试信息.debug_info、.debug_line。 注释: ELFMach-O用于存储二进制文件、可执行文件、目标代码和共享库的格式文件。


解析流程

1) 计算崩溃地址对应符号表中的地址 以某 crashlog 文件为例,如下图所示。是一个Exception类型的异常,从下至上依次为该线程的调用堆栈,右边红色框第一列为运行时的堆栈地址,第二列为进程运行时的起始地址(testxcConfig 所有行起始地址都相同),第三列为运行时的偏移地址。

运行时堆栈地址=运行时起始地址+偏移地址,以第 4 行为例。0x1022cd990=0x1022c8000 + 0x5990(22928),以上地址均为 app 发生崩溃时的运行地址,根据虚拟内存偏移地址不变的原理,只要知道符号表 TEXT 段的起始地址,加上偏移量(0x5990)就能得到崩溃地址对应符号表中的地址, 符号表 TEXT 段的起始地址可通过以下命令获得。

那么崩溃地址(0x1022cd990)对应符号表中的地址为:0x100005990 =0x0000000100000000+0x5990 2) 地址重映射 获取符号表地址后,在 debug-info 章节中查找包含该地址的 DIE(Debug Information Entry)单元就能获知该符号地址对应的函数名称(name)、 函数所在的文件路径(decl file)和函数所在行数(decl line),如下图所示。

上述步骤解析出了函数相关信息, 下面进一步获取该地址对应的准确行数, 这需要借助debug_line章节, debug_line 章节以文件为单位,准确记录了文件中的每一行对应的符号表地址, 0x100005990 对应 AppDelegate.m 的第 20 行。

3) 手动解析 crashlog 当有完整的 crashlog 文件和对应的 dSYM 文件时,以上过程可以由 Xcode 自动完成。但对于用户反馈的 crash, 需要用户手动复制本地的crashlog 文件,而通常crashlog 文本较长,完整复制其实比较麻烦,那么此时可以只复制崩溃线程的 crash 信息,并通过手动解析。手动解析 crash 可以使用 dwarfdump、 atos 工具, 命令如下。 - 方法一

- 方法二

- 方法三

方法二、三都使用了atos解析,区别是方法三不需要获取符号表地址, 其后倒数第一个地址为运行时堆栈地址,倒数第二个地址为进程起始地址。 手动解析另一个应用场景是,若开发人员为了跟进某一偶现问题在日志中记录的是运行时的二进制地址,那么可以通过对应的 dSYM 文件手动解析出调用函数明文。

4.常见问题

1) 如何找到crashlog 对应的 dSYM 文件? 打开终端,使用以下命令获取 dSYM 文件对应的 uuid, 并与crashlog文件Binary Image后面的字符对比,如果字符完全相同,就说明 dSYM文件与crashlog对应。

另外可以使用mdfind命令去寻找指定uuid的dSYM文件,如下,uuid需大写并转化成格式,如下图 mdfind "com_apple_xcode_dsym_uuids == D5644244-F2C4-3C96-BD63-EF0F4DA518FA"


2) 如何手动生成 dSYM 文件? 如果在编译之前忘记在 buildsetting 中选中生成 dSYM文件,然而 app 又发生了崩溃,那么可以通过 app 的可执行文件再手动生成 dSYM 文件。 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/dsymutil /Users/ranjingfu/Desktop/testxcConfig/testxcConfig.app/testxcConfig -o out.dSYM

值得注意的是,只有可执行文件为 debug 模式产物时,才能使用上述方式手动抽取调试符号表文件(dSYM)release模式无法抽取。 因为debug产物会保存调试信息,而release产物不会, dSYM文件就是从调试信息中抽取出来的。

解析后的崩溃日志实例


发布于 2021-03-09 19:55