理解和分析iOS App Crash报告(一)

2,125 阅读5分钟

原文来自于Application Crash Report 这篇是第一部分, 主要讲怎么获取和符号化crash report

当crash发生时, crash报告就被生成并保存在了设备里. Crash报告描述了应用终止时的情况, 大多数情况下包含了完整的线程调用堆栈, 这些信息在debug时是非常有用的. 可以通过分析crash报告来找应用发生crash的原因, 找到修复问题的方法.

crash报告的backtrace需要符号化才能用来分析. 符号化的过程就是将内存地址转成可读的方法名和行号. 如果你从Xcode中获取的crash log, xcode能自动将它符号化.其他情况下需要手动来符号化(Symbolicating Crash Reports).

Low Memory报告不同于其他的crash报告, 内存不足报告中没有详细的堆栈记录. 当因为内存不足引起crash时, 需要调查应用中的内存使用模式和低内存警告. 报告中会指出应用终止时的内存使用情况.

获取crash和内存不足报告

符号化crash报告

Low Memory Reports不需要符号化.

overview of the crash reporting and symbolication process

  1. 编译器将源码(OC文件或Swift文件)编译成机器语言的同时, 也生成了一个对应的debug符号表, 用于将二进制文件中机器指令的地址映射回原始源代码中的代码行号. 根据build setting里的__Debug Information Format__设置, 这个符号表文件保存在二进制文件中, 或相应的Debug Symbol(dSYM)符号表文件中. 默认情况下, debug build时, app的二进制文件中包含着符号表; release build时, 为了减小应用包的大小, 符号表被保存在了符号表文件中.

    每一次build时, 符号表文件和二进制文件根据编译的UUID绑定在一起, 每一次build时都会生成一个唯一的UUID. 即使同样的源码重新build之后也会生成一个新的UUID. 不对应的二进制文件和符号表文件无法交互操作.

  2. 如果用Xcode打包发布版本, Xcode会把二进制文件和.dSYM文件一起存放在本电脑中, 可以通过Archived找到.

    如果想符号化crash报告, 需要保存每一次打包后的Archive文件.

    1. 如果通过App Store发布了应用, 在上传到App Store时需要选择''Include app symbols for your application...", 并且上传dSYM文件. 这样就能通过App Review收集crash报告.

    2. 应用发生crash时, 未符号化的crash报告就被生成且保存在了设备中.

    3. 用户可以通过调试方法来从设备中获取crash报告. 用Ad-Hoc或企业版发布的应用, 只能用Xcode来获取crash报告.

    4. 通过Xcode从设备中获取到的crash报告是未符号化的, 需要符号化crash报告.

    5. 如果用户选择了向Apple共享诊断信息, 或者通过TestFlight安装了beta版本的应用, crash报告会上传到App Store.

    6. App Store会符号化crash report并且给crash report分类, 把类似的crash report集合到一起称作Crash聚类.

    7. 可以在Xcode的Crashes organizer中获取到符号化的crash reports.

Bitcode

Bitcode(位编码)是一个编译好的项目的中间表现形式, 当在允许bitcode编码的情况下archive时, 编译出的二进制文件包含的是bitcode而不是机器码. 二进制文件传到App Store时才会被编译成机器码.

Overview of the Bitcode compilation process.

因为最终的编译过程是在App Store进行的, 本地打包生成的dSYM文件对应的是bitcode文件而不是机器码文件, 如果需要分析App Store中的crash report, 在上传App Store后需要下载对应的dSYM文件用于分析用户crash.

  • 从Xcode下载dSYM文件: Archive organizer -> download dSYM
  • 从iTunes Connect下载dSYM文件: App Details -> Activity -> chose builds -> download dSYM

使用'hidden' symbol

如果不把.dSYM文件上传到App Store, Xcode会在上传到iTunes Connect之前, 将.dSYM文件中的符号换成"_hidden#109"这样的模糊码. 原始符号码和模糊码的映射文件.bcsymbolmap文件在archive中, 每一个.dSYM文件对应一个.bcsymbolmap文件.

解析模糊码:

xcrun dsymutil -symbol-map ~/Library/Developer/Xcode/Archives/2017-11-23/MyGreatApp\ 11-23-17\,\ 12.00\ PM.xcarchive/BCSymbolMaps ~/Downloads/dSYMs/3B15C133-88AA-35B0-B8BA-84AF76826CE0.dSYM

判断crash report的符号化程度

  • 未符号化的crash report: backtrace中不包含方法名或函数名, 而是可执行码在二进制文件中的十六进制的地址
  • 完全符号化: 每一行十六进制地址都转换成了相应的符号
  • 部分符号化: 有部分转换成功, 有部分没有转换成功. 如果crash的关键部分已经符号化出来了, 这时一个部分符号化的crash报告就已经足够用来分析crash原因了.

用atos来符号化crash reports

atos 命令行可以把numeric address转成二进制文件符号或进程.

crash report信息的意义

用atos符号化crash report中的某一条信息:

  • 找到需要符号化那一行的binary名, 地址
  • 找到架构名和加载地址
$ atos -arch arm64 -o TheElements.app.dSYM/Contents/Resources/DWARF/TheElements -l 0x1000e4000 0x00000001000effdc
-[AtomicElementViewController myTransitionDidStop:finished:context:]

符号化前需要准备和crash report对应的dSYM文件, 可以用mdfind命令查找电脑中是否有dSYM文件:

$ mdfind "com_apple_xcode_dsym_uuids == <UUID>"

dwarfdump命令来查看文件的UUID:

xcrun dwarfdump --uuid <Path to dSYM file>