iOS 崩溃分析
在阅读完本书《iOS Crash Dump Analysis》后,我们对崩溃分析技巧进行一定的总结。
准备工作
崩溃报告的收集
线上或者开发环境的崩溃报告都可以利用第三方工具进行崩溃报告收集。让我们的应用程序能够尽可能的处理发生的所有可能的信号和异常,一旦应用程序将无法实现,从而导致底层操作系统触发崩溃。通过三方工具,对记录的崩溃信息进行收集,并且上传到服务器上。
自己基于
plcrashreporter
开发并继集成到服务器的成本需要用户自己考量
符号化
符号化 是将机器地址映射成对拥有源代码的程序员有意义的符号地址的过程。在使用三方的崩溃分析工具或者自建崩溃分析服务时,无论是构建Debug
版本还是构建 Release
版本,我们都应该在Xcode
中设置 DWARF with dSYM File
,以便我们能针对我们的每次构建都有一个匹配的 DSYM 文件。然后针对我们获取的崩溃报告进行符号化。保存好我们的ipa
文件和与之匹配的 DSYM
文件,为了后续使用的方便。
崩溃报告工具基本上就是使用 atos
命令来符号化崩溃报告,以及提供其他与系统相关的信息。
逆向工程
有时在我们的项目中,包含了第三方的二进制框架,我们并没有源代码。我们使用 Hopper
处理我们的ipa
文件,然后 rebase 反汇编程序,获取崩溃时地址与程序的地址相同。找到崩溃的堆栈。
分析故障排除法
分析故障排除法 提供了一个分析的方法来发现问题,便于问题的复现。在实际情况下,对于不同客户进行优先级筛选,并且针对不同的产品和产品变形,来自不同客户的崩溃报告也很多。科学的分析和找到问题。
确定问题的优先级
对于不崩溃或者是问题,优先级是不一样的。我们将从三个方法来思考问题:严重性、紧迫性和成长性。
-
根据影响确定优先级
崩溃将导致客户无法在我们的应用程序上执行任何操作。对我们来说,最迫切需要解决的崩溃问题,是影响我们收入的问题。社交类软件的聊天功能不容有失,电商类软件的购买支付流程十分重要等等。在应用程序中记录日志 来确定客户执行了什么操作。将最多最广泛的崩溃用例确定为最需要修复的崩溃问题。
一个软件的核心功能就是该软件最经常使用的功能
-
根据截止日期确定优先级
为了判断错误修复的紧迫性,我们需要评估与该错误相关的 截止日期。对于 iPhone 设备来说,每年的新机出现的时间(例如,每年 9月份)。新机发布一般意味着新的系统,新的用户。所以在 WWDC 开发者大会结束到新机发布之间的两至三个月,任何会导致应用商店审核失败或应用首次使用的崩溃问题都变得越来越重要。
-
根据趋势确定优先级
当我们看到崩溃报告数量的增长令人震惊,需要通过分析 趋势 来进行评估。如果我们的应用程序在 iOS 新版本中的功能而崩溃,那么针对新版本我们需要更多的测试。对隐私敏感的应用程序需要更关注新的用户协议、使用了底层API 的应用程序需要更加关注新系统的
beta
版、而对绘制敏感的应用程序,那么我们则应该关注新的硬件设备、硬件规格的更新。
收集问题并找到问题
我们会有崩溃报告、客户日志、分析数据等被提供的崩溃信息。如何从这些信息中找到问题发生的原因,对开发人员来说至关重要。
根据下表分类归纳收集到信息,能够帮助开发或运营人员更加准备的表明问题出现的具体操作。
Item | IS | IS NOT |
---|---|---|
WHAT | Seen | Not Seen |
WHERE | Seen | Not Seen |
WHEN | Seen | Not Seen |
EXTENT | Seen | Not Seen |
具体参照分析故障排除法 一文,正确的提出你所关注的问题。
读懂崩溃报告
崩溃报告 使我们所获得的最直观的崩溃问题的描述。了解并读懂崩溃报告对开发人员来说至关重要。
注意我们分析的崩溃报告是指真实的物理设备上的崩溃报告。模拟器上调试设备时发生的崩溃可能有所不同。
iOS崩溃报告大致分成以下几个部分
Header 部分
崩溃报告的Header
部分,主要是对整个崩溃情况进行一个概览。我们通过崩溃报告的头部,快速了解崩溃发生的设备,硬件设备(iPad 或 iPhone)和崩溃是在前台还是后台发生崩溃的。
Entry | Meaning |
---|---|
Incident Identifier | 崩溃报告的唯一编号 |
CrashReporter Key | 崩溃设备的唯一标识符 |
Hardware Model | Apple 硬件模型(iPad,iPhone) |
Process | 崩溃的进程名称(或编号) |
Path | 设备文件系统上崩溃程序的完整路径名 |
Identifier | 来自Info.plist 的 Bundle identifier |
Version |
CFBundleVersion ;括号中有 CFBundleVersionString
|
Code Type | 崩溃进程的目标体系结构 |
Role | 进程 task_role 。如果我们在后台、前台或控制台应用程序中,都会显示一个指示器。 主要影响进程的调度优先级。 |
Parent Process | 崩溃进程的父级。launchd 是一个进程启动程序,通常是父进程。 |
Coalition | 任务分组合并,然后他们就可以可以把资源消耗集中起来。 |
CFBundleVersion
表示 bundle 构建迭代的版本号(发布与未发布) 而CFBundleVersionString
可能想说的是CFBundleShortVersionString
表示 bundle 发布版本号
Date 和 Version 部分
这部分我们主要关系的是 OS Version
部分的具体数据,用来判断崩溃发生的版本。有时候我们需要关心 Date/Time
和 Launch Time
之间的差值,发现那种客户用了很久或客户一打开 APP 立马崩溃。
Entry | Meaning |
---|---|
Date/Time | 崩溃发生的时间 |
Launch Time | 崩溃前最初启动该进程的时间 |
OS Version | 操作系统版本(内部版本号)。 |
Baseband Version | 蜂窝调制解调器固件版本号(用于电话呼叫)或 n/a (如果设备没有蜂窝调制解调器)(大多数 iPad,iPod Touch 等) |
Report Version | 生成报告的 ReportCrash 版本 |
异常部分
这一部分我们主要关注的是异常类型。在崩溃报告 一文中,我们详细介绍了各种异常类型,并根据异常代码定位出问题的类型。
Entry | Meaning |
---|---|
Exception Type | Mach OS中的异常类型 |
Exception Codes | 异常类型的编码,例如尝试访问无效的地址以及支持信息。 |
Exception Note | 如果进程被看门狗计时器杀死,会显示SIMULATED(这不是崩溃) ,进程崩溃则显示 EXC_CORPSE_NOTIFY
|
Termination Reason | 视情况而定,它给出一个命名空间(数字或子系统名称)和一个 magic number (通常是一个看起来像英语单词的十六进制数字)。有关每个终止代码的详细信息,请参见下文。 |
Triggered by Thread | 导致崩溃的进程中的线程 |
异常回溯部分
当我们的应用程序检测到问题并要求操作系统终止该应用程序时,我们将获得报告的异常回溯部分。这涵盖了自己主动或通过 Swift,Objective-C 或 C 运行时支持库间接调用了abort
,NSException
,_NSLockError
或objc_exception_throw
的情况。
我们并没有办法得到实际发生断言的部分。但我们可以假定已经过滤的系统日志的前一个部分应该完成了这部分。尽管我们通过Xcode 的 Window-> Devices and Simulators-> Open Console 来允许我们恢复该信息。
当我们在客户的崩溃报告中看到异常回溯时,我们应该更关注崩溃设备的控制台日志。
线程部分
接下来是线程回溯的部分。这部分会告诉我们具体哪个线程发生崩溃。我们应该将我们发部分的精力放在线程 0
上。
崩溃报告的格式 大概有下面至少4个部分组成
Column | Meaning |
---|---|
1 | 堆栈帧号,最近执行的是 0。 |
2 | 二进制文件执行。 |
3 | 执行位置(第 0 帧)或返回位置(第 1 帧以后) |
4+ | 符号化函数名称或函数中具有偏移量的地址 |
线程状态部分
然后就是类似描述 ARM 寄存器状态的信息。
Binary Images(二进制镜像) 部分
该部分是一串很长的列表,表示了应用程序在加载时动态加载的二进制文件
动态加载器在准备二进制文件以执行时执行许多任务。如果我们的二进制引用了其他库,它将加载它们。如果没有,则无法加载我们的应用程序。这就是为什么即使在调用 main.m
中的任何代码之前也可能发生崩溃的原因。
如果我们的应用程序在调用 main.m
代码之前就发生崩溃,我们应该关注这部分的具体代码。
规避崩溃
尽管我们知道完全不发生崩溃的概率是极低的。大多数软件开发人员都知道他们 “应该” 做什么;整洁的代码、适当的测试、代码评审等。
在考量我们的代码和发布我们的代码时,我们应当做到以下几点:
-
尽可能解耦、封装我们的代码:将代码分离出来并使用一个特定的外观来保持各种范式。尽量让各种操作分不到不同的方法中,这样便于我们快速的定位问题。
-
适当进行 Xcode 诊断设置: 利用 Xcode内置工具 进行诊断分析。对于大中型项目来说,这可能会导致编译过程变慢,但是对于预防规避崩溃来说很有用。一个有效的从减少应用程序发生崩溃的方法,是将代码分析纳入我们的软件开发过程中。
-
内存诊断:iOS 开发过程中的大部分不可预见的崩溃现象都是由于内存问题引起的。在合适的时机进行内存诊断的操作很有必要。
展望
本书的翻译已经结束,但是还没有完。作者目前已经完成本书第二版本的写作,大概还有一章节关于第一代 Apple Silicon 产品的崩溃分析。 在十一月中旬,本书应该可以完成第二版的写作,届时笔者也会随着新书的发布一起快速进行翻译。希望各位读者能够对本书点赞、转发,star,帮助笔者完成本书的翻译和校对。
笔者下一步的计划是仿照《Python 100 Days》的思维完成一版本《Swift 100 Days》的教程。毕竟笔者从Swift 1.0 开始一直入门到放弃。敬请期待。
交流
最后推荐个我的iOS交流群:789143298
'有一个共同的圈子很重要,结识人脉!里面都是iOS开发,全栈发展,欢迎入驻,共同进步!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
-
——点击加入:iOS开发交流群
以下资料在群文件可自行下载
作者:little_xia
链接:https://juejin.im/post/6888254448582262791