崩溃日志
进程一死,崩溃报告就写到设备上了。把它取出来、变成人能读的东西,就是这个模块干的事。
- 连上崩溃的那台设备 —— 报告只存在于产生它的设备上。
- 打开崩溃日志。
- 按应用名和时间戳找到对应条目。
- 导出,或者就地阅读。
报告会不断堆积。处理完的删掉,列表才用得下去,这个模块也能删。
原始报告是一堆地址。符号化把地址还原成函数名和行号。
要做到这一点,符号表必须与崩溃的那个二进制严格对应 —— 同版本、同构建号。请为每一个发出去的包保留 dSYM;没有它,无论用什么工具,你自己的栈帧都只会是十六进制。系统栈帧由对应 iOS 版本的系统符号还原。
按这个顺序。
1. 异常类型。 信息量最大的一行。
| 类型 | 通常意味着 |
|---|---|
EXC_BAD_ACCESS(SIGSEGV) |
访问了不存在的东西 —— 已释放对象、野指针 |
EXC_CRASH (SIGABRT) |
未捕获异常、断言失败,或主动 abort() |
EXC_BREAKPOINT (SIGTRAP) |
Swift 运行时陷阱 —— 强解包 nil、数组越界、强制类型转换失败 |
EXC_RESOURCE |
超出资源限制,常见的是内存或长时间高 CPU |
0x8badf00d |
看门狗。启动或响应太慢,被 iOS 杀掉 |
0xdead10cc |
挂起状态下仍持有文件锁或共享资源 |
2. Terminating description。 有这一行的话,它会直接点名断言或异常。读它,再读栈。
3. 崩溃线程。 报告里有明确标注。这条栈才是重点。
4. 你自己的栈帧。 在崩溃线程里往下找属于你二进制的帧。上面的系统帧通常只是“怎么走进来的”;你代码里最靠上的那一帧才是起点。
有两类“崩溃”不是崩溃
Section titled “有两类“崩溃”不是崩溃”列表里有两种条目看着像崩溃,其实不是:
- Jetsam / 内存报告。 进程因为内存用太多被终止。栈顶没有 bug,bug 是此前那段一路上涨的内存曲线。用性能监控里的内存曲线交叉验证。
- 看门狗终止。 主线程被阻塞。栈同样只显示阻塞在哪,不显示是什么把它堵住的 —— 去找同步 I/O 或者锁。
