14年记录于有道笔记的老文章,有时间再修改。
什么是崩溃日志?
一个App启动之后,用着用着就突然被iOS系统关闭,或者干脆就起不来,在打开的一瞬间关闭,这就是Crash,俗称“闪退”“崩溃”。
iOS设备上的应用闪退时,操作系统会生成一个扩展名为crash的日志文件,这个文件会保存的特定系统目录下。
崩溃日志上有很多有用的信息,上面有每个正在执行线程的完整堆栈跟踪信息,所以你能从中了解到闪退发生时各线程都在做什么,并分辨出闪退发生在哪个线程上和相关的原因。
从哪里能得到它?
设备与电脑上的iTunes Store同步后,会将崩溃日志保存在电脑上。根据电脑操作系统的不同,崩溃日志将保存在以下位置:
Mac OSX:~/Library/Logs/CrashReporter/MobileDevice/
Windows XP: C:Documents and Settings
Application DataApple ComputerLogsCrashReporterMobileDevice Windows Vista or 7: C:Users
AppDataRoamingApple ComputerLogsCrashReporterMobileDevice 
当用户抱怨闪退时,你可以要求他让设备与iTunes同步,并根据操作系统的不同,到上述位置把崩溃日志下载下来,然后通过电子邮件发送给你。
你必需尽量获取用户设备生成的所有崩溃日志。因为崩溃日志越多,就越容易诊断问题所在!
将iOS设备连接到电脑上,然后打开Xcode。从菜单栏上选择 Window  菜单, 然后选择 Devices ,在左侧的导航面板上,点击View Device Logs查看设备的日志,然后查看你对应的appname的日志。
应用提交到App Store后,你也能从 iTunes Connect 获取到用户的崩溃日志. 登录到 iTunes Connect 上, 选择Manage Your Applications, 点击相应的应用, 点击应用图标下面的View Details 按钮, 然后点击右栏Links部分的Crash Reports。
如果应用还卖得不多,或者刚上架不久,或者用户关闭了上传日志的选项,iTunes Connect账号上也可能还没有任何崩溃日志。
什么情况下会产生崩溃日志?
应用违反操作系统规则
从iOS 4.x开始,退出应用时,应用不会立即终止,而是退到后台。但是,如果你的应用响应不够快,操作系统有可能会终止你的应用,并产生一个崩溃日志。这些事件与下列UIApplicationDelegate方法相对应:
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationDidBecomeActive:
applicationWillTerminate:
上面所有这些方法,应用只有有限的时间去完成处理。如果花费时间太长,操作系统将终止应用。
注意: 如果你没有把需要花费时间比较长的操作(如网络访问)放在后台线程上就很容易发生这种情况。
iOS 4.x开始支持多任务。如果应用阻塞界面并停止响应,用户可以通过在主屏幕上双击Home按钮来终止应用。此时,操作应用将生成一个崩溃日志。
注意: 双击Home按钮后,你将看到运行过的所有应用。那些应用不一定是正在运行,也不一定是被挂起。通常,用户点击Home按钮时,应用将在后台保留约10分钟,然后操作系统自动将其终止。 所以双击Home按钮显示的应用列表只是表明那是一系列过去打开过的应用。删除那些应用的图标不会产生任何崩溃日志。
在前台运行的应用拥有访问和使用内存的最高优化级。然而,这并不意味着该应用能使用设备的所有可用内存 ——每个应用只能使用一部分可用内存。
当内存使用达到一定程度时,操作系统将发出一个 UIApplicationDidReceiveMemoryWarningNotification 通知。同时,调用 didReceiveMemoryWarning 方法。
此时,为了让应用继续正常运行,操作系统开始终止在后台的其他应用以释放一些内存。所有后台应用被终止后,如果你的应用还需要更多内存,操作系统会将你的应用也终止掉,并产生一个崩溃日志。而在这种情况下被终止的其他后台应用,不会产生崩溃日志。
应用中有Bug
大多数闪退都是由于应用中有Bug,比如调用用了Objective-C对象根本不支持的方法(发送消息),非法内存访问,数组越界,参数不符合要求等。
这些问题在调试阶段,我们都可以很容易的通过断点和console中提供的信息快速定位并解决。
但对于已发布的App,如果想重现并利用上述办法来解决,恐怕会比较费时费事。
最有帮助最直接的办法就是根据出现问题时的闪退日志,分析和判断crash的原因,快速准确的定位和解决。
崩溃日志示例
  | 
  | 
进程信息
Incident Identifier 是崩溃报告的唯一标识符。
CrashReporter Key 是与设备标识相对应的唯一键值。虽然它不是真正的设备标识符,但也是一个非常有用的情报:如果你看到100个崩溃日志的CrashReporter Key值都是相同的,或者只有少数几个不同的CrashReport值,说明这不是一个普遍的问题,只发生在一个或少数几个设备上。
Hardware Model 是标识设备类型。 如果很多崩溃日志都是来自相同的设备类型,说明应用只在某特定类型的设备上有问题。上面的日志里,崩溃日志产生的设备是iPhone 4s。
Process 是应用名称。中括号里面的数字是闪退时应用的进程ID。
Identifier 是应用标识符,即bundle identifier。
Version 是应用版本信息,前面是build code,后面是发布版本号。
基本信息
这部分给出了一些基本信息,包括闪退发生的日期和时间,设备的iOS版本。如果有很多崩溃日志都来自iOS 6.0,说明问题只发生在iOS 6.0上。
异常信息
在这部分,你可以看到闪退发生时抛出的异常类型。还能看到异常编码和抛出异常的线程。根据崩溃报告类型的不同,在这部分你还能看到一些另外的信息。
线程回溯
这部分提供应用中所有线程的回溯日志。 回溯是闪退发生时所有活动帧清单。它包含闪退发生时调用函数的清单。看下面这行日志:
  | 
  | 
它包括四列:
- 模块号:这里是13
 - 二进制库名:这里是LiuLianCustomer
 - 调用方法的地址:这里是0x000dbfe6
 - 第四部分分为两列,基地址和偏移地址。此处基地址为0x7f000,偏移地址为380902。基地址指向crash的模块(也是模块的load地址)如UIKit。偏移地址指向crash代码的行数。
 
动态库信息
这些信息包括动态库名称、UUID、模块起始地址、模块结束地址、指令集种类、安装路径等信息。
符号化Symbolication
第一次看到崩溃日志上的回溯时,你或许会觉得它没什么意义。我们习惯使用方法名和行数,而非像这样的神秘位置:
  | 
  | 
将这些十六进制地址转化成方法名称和行数的过程称之为符号化。
从Xcode获取崩溃日志后过几秒钟,崩溃日志将被自动符号化。上面那行被符号化后的版本如下 :
  | 
  | 
Xcode符号化崩溃日志时,需要访问与App Store上对应的应用二进制文件以及生成二进制文件时产生的 .dSYM 文件,必需完全匹配才行,否则,日志将无法被完全符号化。所以,保留每个分发给用户的编译版本非常重要。
注意: 你必需同时保留应用二进制文件和.dSYM文件才能将崩溃日志完整符号化。每次提交到iTunes Connect的构建都必需归档。.dSYM文件和二进制文件是特定绑定于每一次构建和后续构建的,即使来自相同的源代码文件,每一次构建也与其他构建不同,不能相互替换。
atos就是address to symbol,把地址翻译成符号。是苹果提供的符号化工具,在Mac OS系统下默认安装。
终端下面执行以下命令
  | 
  | 
- dysm文件路径:可以在Xcode Organizer的Archives标签栏下找到所有已归档的应用文件。它保存了编译过程的详细信息,其中包括符号信息,路径为 appName.app.dSYM/Contents/Resources/DWARF/appName。
 - 模块load地址:模块加载的基地址,可以在日志的动态库信息中找到对应模块的基地址。
 - cpu指令集种类:可以为armv6 armv7 armv7s arm64。具体用哪个,可以参考对应模块的动态库信息中确定
 - 调用方法地址:想要符号化的地址
 
symbolicatecrash是Xcode自带的一个分析工具,可以通过机器上的崩溃日志和应用的.dSYM文件定位发生崩溃的位置,把crash日志中的地址替换成代码相应位置。
使用方法:
软链接symbollicatecrash到/usr/bin/中,就可以直接使用sybollicatecrash命令
  | 
  | 
设置xcode DEVELOPER_DIR,把下面的内容加入到.bash_profile中即可
  | 
  | 
符号化崩溃日志(不需要.app文件,符号文件和crash文件也可以不在同一目录)
  | 
  | 
使用dwarfdump验证xxx.crash、xxx.app和xxx.dSYM三者的uuid是否一致。
  | 
  | 
  | 
  | 
crash文件在动态链接库部分查看对应的UUID,,三者UUID一致才能符号化崩溃日志。
  | 
  |