crash日志的分析

怎样获得crash日志
怎样解析crash日志
怎样分析crash日志
     1. iOS策略相关
     2. 常见错误标识
     3. 代码bug


一、怎样获得crash日志

当一个iOS应用程序崩溃时,系统会创建一份crash日志保存在设备上。

这份crash日志记录着应用程序崩溃时的信息,通常包括着每一个运行线程的栈调用信息(低内存闪退日志例外),对于开发者定位问题非常有帮助。

假设设备就在身边,能够连接设备,打开Xcode - Window - Organizer,在左側面板中选择Device Logs(能够选择详细设备的Device Logs或者Library下全部设备的Device Logs),然后依据时间排序查看设备上的crash日志。这是开发、測试阶段最常常採用的方式。

假设应用程序已经提交到App Store公布,用户已经安装使用了,那么开发人员能够通过iTunes Connect(Manage Your Applications - View Details - Crash Reports)获取用户的crash日志。只是这并非100%有效的,并且大多数开发人员并不依赖于此,由于这须要用户设备允许上传相关信息。详情可參见iOS: Providing Apple with diagnostics and usage information摘要。

考虑到并非全部iPhone用户都同意自己主动发送诊断报告(crash日志),并且对于部分提交到Apple得crash日志,开发人员还须要手动去拉取,然后找到相应的符号文件进行解析——这是一件非常繁琐的事情。所以实际项目开发中。通常接入现有的crash收集工具(參考1參考2)。或者自己编写一个进行自己主动化收集、解析和统计汇总。


二、怎样解析crash日志

当获得一份crash日志时,我们须要将初始展示的十六进制地址等原始信息映射为源码级别的方法名称和代码行数,使其对开发者可读。

这个过程称为符号化解析。要成功地符号化解析一份crash日志,我们须要有相应的应用程序二进制文件以及符号(.dSYM)文件。

假设处于开发调试阶段。通常Xcode都能匹配到crash日志相应的二进制文件和符号文件,所以可以帮我们自己主动解析。

假设处于測试阶段,測试人员已经安装了不同的版本号(比方alpha、beta版本号),那么须要保存好相应版本号的二进制文件和符号文件。以便在应用程序崩溃时对crash日志进行解析。对于这样的场景下产生的crash日志。仅仅须要将.crash文件、.app文件和.dSYM文件三者放在同一个文件夹下,然后将.crash文件拖放到Xcode - Window - Organizer中左側面板Library下的Device Logs中,就可以进行解析。

假设要提交公布,那么我们一般会先运行Clean。再Build。最后通过Product - Archive来打包。这样。Xcode会将二进制文件和符号文件归档在一起。能够通过Organizer中的Archives进行浏览。



这里是一份关于怎样解析crash日志的讨论:http://stackoverflow.com/questions/1460892/symbolicating-iphone-app-crash-reports 。


三、怎样分析crash日志

在分析一份crash日志之前,假设开发者对于常见的错误类型有所了解,那定是极好的。


crash日志的产生来源于两种问题:违反iOS策略被干掉,以及自身的代码bug。

1. iOS策略

1.1 低内存闪退
前面提到大多数crash日志都包括着运行线程的栈调用信息,可是低内存闪退日志除外,这里就先看看低内存闪退日志是什么样的。
我们使用Xcode 5和iOS 7的设备模拟一次低内存闪退。然后通过Organizer查看产生的crash日志,能够发现Process和Type都为Unknown:



而详细的日志内容例如以下:



第一部分是崩溃信息。包含识别标识、软硬件信息和时间信息等。
第二部分是内存页分配信息,以及当前占用内存最多的进程。上图中为crashTypeDemo。
第三部分是详细的进程列表,描写叙述着每一个进程使用内存的情况以及当前状态。在较早的版本号中能够在某些进程后面看到“jettisoned”字样,表明这些进程使用过多内存被终止了。而如今我们看到的是“vm-pageshortage”字样。

当iOS检測到内存过低时,它(的VM系统)会发出低内存警告通知,尝试回收一些内存。假设情况没有得到足够的改善,iOS会终止后台应用以回收很多其它内存;最后。假设内存还是不足,那么正在执行的应用可能会被终止掉。
所以,我们的应用应该合理地响应系统抛出来的低内存警告通知,对一些缓存数据和可又一次创建的对象进行释放。同一时候要避免出现内存泄露等问题。

低内存闪退是由iOS策略决定终止应用程序执行的,相同基于iOS策略的还有Watchdog超时和用户强制退出。

1.2 Watchdog超时
Apple的iOS Developer Library站点上,QA1693文档中描写叙述了Watchdog机制,包含生效场景和表现。假设我们的应用程序对一些特定的UI事件(比方启动、挂起、恢复、结束)响应不及时,Watchdog会把我们的应用程序干掉,并生成一份响应的crash报告。



这份crash报告的有趣之处在于异常代码:“0x8badf00d”。即“ate bad food”。
假设说特定的UI事件比較抽象。那么用代码来直接描写叙述的话,相应的就是(创建一个project时Xcode自己主动生成的)UIApplicationDelegate的几个方法:



所以当遇到Watchdog日志时。能够检查下上图几个方法是否有比較重的堵塞UI的动作。



QA1693举的样例是在主线程进行同步网络请求。

假设我们是在公司的Wifi环境下使用则一切顺利。但当应用程序公布出去面向非常大范围的用户。在各种网络环境下执行。则不可避免地会出现一片Watchdog超时报告。


还有一种可能出现故障的场景就是数据量比較大的情况下进行的数据库版本号迁移(相同是在主线程上),这也是促使我写这篇总结的一个直接因素。

1.3 用户强制退出

一看到“用户强制退出”。首先可能想到的双击Home键。然后关闭应用程序。只是这样的场景是不会产生crash日志的,由于双击Home键后,全部的应用程序都处于后台状态。而iOS随时都有可能关闭后台进程,所以这样的场景没有crash日志。



还有一种场景是用户同一时候按住电源键和Home键。让iPhone重新启动。

这样的场景会产生日志(仅验证过一次),但并不针对特定应用程序。

这里指的“用户强制退出”场景,是略微比較复杂点的操作:先按住电源键。直到出现“滑动关机”的界面时,再按住Home键,这时候当前应用程序会被终止掉,而且产生一份对应事件的crash日志。





通常。用户应该是遇到应用程序卡死,而且影响到了iOS响应,才会进行这种操作——只是感觉这操作好高级,所以这种crash日志应该比較少见。

2. 常见错误标识

2.1 Exception codes
上面“用户强制退出”的crash日志中的Exception Codes是“0xdeadfa11”,再上面“Watchdog超时”的crash日志中的Exception Codes是“0x8badf00d”,这些都是特有的Exception codes。
依据官方文档描写叙述,至少有下面几种特定异常代码:

0x8badf00d错误码:Watchdog超时。意为“ate bad food”。

0xdeadfa11错误码:用户强制退出,意为“dead fall”。



0xbaaaaaad错误码:用户按住Home键和音量键。获取当前内存状态,不代表崩溃。



0xbad22222错误码:VoIP应用(由于太频繁?)被iOS干掉。

0xc00010ff错误码:由于太烫了被干掉。意为“cool off”。

0xdead10cc错误码:由于在后台时仍然占领系统资源(比方通讯录)被干掉,意为“dead lock”。



2.2 Exception types
查看我们的crash分析报告邮件,会发现最常常遇到的错误类型是SEGV(Segmentation Violation。段违例)。表明内存操作不当,比方訪问一个没有权限的内存地址。
当我们收到SIGSEGV信号时。能够往下面几个方面考虑:
訪问无效内存地址,比方訪问Zombie对象;
尝试往仅仅读区域写数据;
解引用空指针。
使用未初始化的指针。
栈溢出;
此外,还有其他常见信号:
SIGABRT:收到Abort信号,可能自身调用abort()或者收到外部发送过来的信号;
SIGBUS:总线错误。

与SIGSEGV不同的是,SIGSEGV訪问的是无效地址(比方虚存映射不到物理内存),而SIGBUS訪问的是有效地址。但总线訪问异常(比方地址对齐问题)。
SIGILL:尝试运行非法的指令,可能不被识别或者没有权限;
SIGFPE:Floating Point Error。数学计算相关问题(可能不限于浮点计算)。比方除零操作;
SIGPIPE:管道还有一端没有进程接手数据;

3. 代码bug
此外,比較常见的崩溃基本都源于代码bug,比方数组越界、插空、多线程安全性、訪问野指针、发送未实现的selector等。

假设引入Core Data。则又有另外一些常见问题。只是这是还有一个话题了。

遇到这些bug时,都有比較清楚的错误原因说明,比方“index 0 beyond bounds for empty array”等。

须要略微注意点的是多线程问题,当一时找不到解决思路时。最好还是往多线程方面考虑下。

posted on 2017-07-01 16:54  ljbguanli  阅读(942)  评论(0编辑  收藏  举报