最近看到一篇关于符号化 iOS 崩溃报告的文章挺好,决定翻一下,下方链接是原文地址:

https://possiblemobile.com/2015/03/symbolicating-your-ios-crash-reports/

开始

你已经递交你的应用程序的崩溃报告,但是栈回溯包含了难懂的内存地址。开发者该怎么办?简而言之,你需要调试符号来应用于堆栈追踪,从而使它人类可读,这个过程我们称作符号化。

在开始之前,我们依照使用 Crasher,它提供了一个简单崩溃报告,让你破译。

你应该有 .crash 文件。如果没有,你可以从 iTunes Connect 中攫取,或者直接通过 Xcode 从连接的设备中攫取(Windows > Devices),或在一个连接的设备(Settings > Privacy > Diagnostics & Usage),或使用 PLCrashReporter 框架。你可能已经使用了第三方的崩溃报告服务,在你配置正确后,它将符号化你的崩溃。

根据你的应用创建是如何配置的,你需要一个或者全部下面的东西:

0.导致崩溃的应用程序构建的 .app 文件。这个包包含了应用二进制文件,以及可能包含测试符号。(如果你有一个 .ipa 文件,你可以把它当做一个 .zip 文件,通过解压这个文件把它提取出来)

1.导致崩溃的应用程序构建的 .dSYM 文件。这是你的应用程序的副产品,包含调试符号化。

你需要哪一个? 在 Xcode 中,查找 Build Setting 里的 “Strip Debug Symbols During Copy”(COPY_PHASE_STRIP)。当允许时,调试符号将会从你的 .app 省去,把它放到了 .dSYM 文件。否则你的 .app 包含这些符号。(默认情况下, 由于某些不清楚的原因,调试符号从释放的创建中剥离,你可能不应该改变这个发布配置)

但是等等,什么是调试符号?

为了让调试符号变成人类可读的即程序员给方法命名的名称,编译器通过使用它自己的符号来减少这些命名的调试符号,从而使代码模糊。即使两次相同代码创建,你也不能依赖这些符号。

检查崩溃

如果通过 Xcode 的 Organizer 从设备中提交崩溃报告,你的崩溃报告可能将会对 UIKit 和其他 iOS 框架自动符号化。如果 Xcode 知道你的创建,他将自动符号化你的崩溃。

如果不是这种情况,你需要自己符号化。

使用”Symbolicatecrash”工具来符号化

幸运的是,苹果提供给了我们脚本来检索调试符号化,将他们应用于崩溃报告。

对 Xcode 6,你可以在下面目录里找到这个工具:

1
/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/Current/Resources/symbolicatecrash

或者如果你使用的是Xcode 5 从下方路径获得:

1
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/Current/Resources/symbolicatecrash

为了使用这个工具,你需要用安装Xcode恰当路径导出 DEVELOPER_DIR 环境变量:

1
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"

把你的.crash文件.app文件和.dSYM文件放在同一个目录下然后运行:

1
symbolicatecrash -v ScaryCrash.crash > Symbolicated.crash

你可能需要显示应用应用程序二进制:

1
symbolicatecrash -v ScaryCrash.crash ./Crasher.app/Crasher > Symbolicated.crash

如果你使用在 Crasher 例子中发现的便利脚本,需要冗长的标记:

1
sh symbolicate6.sh ScaryCrash.crash ./Crasher.app/Crasher > Symbolicated.crash

如果你使用在 Crasher 例子中发现的便利脚本,需要冗长的标记:

1
sh symbolicate6.sh ScaryCrash.crash ./Crasher.app/Crasher > Symbolicated.crash

验证符号化

如果符号化不能工作,仔细检查你攫取的 .dSYM 或者 .app 文件是否正确。你可以通过交叉引用崩溃的报告中的 UUID 和应用程序二进制中的 UUID:

1
dwarfdump –uuid Crasher.app/Crasher

结果:

1.png

在 dSYM 文件中:

1
dwarfdump –uuid Crasher.app.dSYM/Contents/Resources/DWARF/Crasher

结果:

2.png

和你的崩溃报告中的 UUID 进行比较:

3.png

从 symbolicatecrash 工具冗长的日志里,同样列出它找出的 UUID。

排查 “Symbolicatecrash” 工具

如果你还是困惑,请认真检查 symbolication 日志。symbolication 工具尝试找出你应用中与 UUID 相匹配的相应文件以及每个动态框架,寻找着你的应用程序名称或者 UUID 并看出是否匹配。

4.png

如果 Spotlight 不能找到你的 .dSYM 文件,你可能碰到这样一个日志样例:

5.png

或者有一个无效的 .dSYM:

6.png

或者有任何其他无效的输入:

7.png

在 Xcode 6 版本中的 symbolicatecrash 工具尝试解决一些再 Xcode 5 版本中面临的 Spotlight 问题。如果你遇到问题,他可能是 Spotlight 中一个文件的索引问题,尝试:

1
mdimport -g /Applications/Xcode.app/Contents/Library/Spotlight/uuid.mdimporter .

使用命令行工具链

我们可以深入 和使用开发者命令行工具链来一行行的符号化栈追踪的内存地址。如果你在过去使用这个方法中遇到问题,那可能是因为在最近几年.crash文件根式改变了。

首先,让我们再看看我们崩溃报考中的栈追踪:

8.png

最左边的十六进制值,0x000aeef6,是栈地址。第二个十六进制值,0xa8000,是应用程序的加载地址。以加号为开始的数字,28406,是栈地址和加载地址的差值。 0x00aeef6 = 0xa8000 + 0x6EF6
。你将会发现在我们的崩溃报告底部的“Binary Images”,它是动态库内存地址范围列表。我们的二进制文件的起始地址在堆栈跟踪加载地址相匹配。

9.png

下一步,为了更好的测量,我们将验证可执行文件包含蹦溃的架构,不论使用file或者lipo -info命令:

1
file Crasher.app/Crasher

结果:

10.png

现在我们全副武装。我们经查实atos命令来将我们的地址转换成调试符号。注意我们如何提供加载地址,它在我们指定崩溃的架构的栈地址后面:

1
atos -arch armv7 -o Crasher.app/Crasher -l 0xa8000 0x000aeef6

结果:

11.png

就是这样。如果你有兴趣继续钻研,研读 Mach-O 对象文件格式以及 Mach-O 命令行实用工具,也就是 otoollipo 命令。

更多阅读

更多全面的文档,请查看:
Technical Note TN2151: Understanding and Analyzing iOS Application Crash Reports
Technical Q&A QA1765: How to Match a Crash Report to a Build
Mach-O Programming Topics
Objc.io on Mach-O Executables