在研发的时候,通过NSLog,断点等,都能够很好的发现和定位bug。但是如果已经提交到AppStore,或者在安装到设备上后运行崩溃,而且无法再次重现时,只能通过日志来发现和定位bug。现在说的就是怎么通过日志来定位bug。
1.日志的产生官方文档
两种主要情况会产生崩溃日志:
1.应用违反操作系统规则。
2.应用中有Bug。
违反iOS规则包括在启动、恢复、挂起、退出时watchdog超时、用户强制退出和低内存终止。让我们详细了解一下吧。
Watchdog 超时机制
从iOS 4.x开始,退出应用时,应用不会立即终止,而是退到后台。但是,如果你的应用响应不够快,操作系统有可能会终止你的应用,并产生一个崩溃日志。这些事件与下列UIApplicationDelegate方法相对应:
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationDidBecomeActive:
applicationWillTerminate:
上面所有这些方法,应用只有有限的时间去完成处理。如果花费时间太长,操作系统将终止应用。
注意: 如果你没有把需要花费时间比较长的操作(如网络访问)放在后台线程上就很容易发生这种情况。关于如果避免这种情况的信息,请参考我们的另外两篇教程: Grand Central Dispatch 和 NSOperations。
用户强制退出
iOS 4.x开始支持多任务。如果应用阻塞界面并停止响应, 用户可以通过在主屏幕上双击Home按钮来终止应用。此时,操作应用将生成一个崩溃日志。
注意: 双击Home按钮后,你将看到运行过的所有应用。那些应用不一定是正在运行,也不一定是被挂起。
通常,用户点击Home按钮时,应用将在后台保留约10分钟,然后操作系统自动将其终止。 所以双击Home按钮显示的应用列表只是表明那是一系列过去打开过的应用。删除那些应用的图标不会产生任何崩溃日志。
低内存终止
子类化UIViewController时,你或许已经注意到didReceiveMemoryWarning方法。
在前台运行的应用拥有访问和使用内存的最高优化级。然而,这并不意味着该应用能使用设备的所有可用内存 ——每个应用只能使用一部分可用内存。
当内存使用达到一定程度时,操作系统将发出一个 UIApplicationDidReceiveMemoryWarningNotification 通知。同时,调用 didReceiveMemoryWarning 方法。
此时,为了让应用继续正常运行,操作系统开始终止在后台的其他应用以释放一些内存。所有后台应用被终止后,如果你的应用还需要更多内存,操作系统会将你的应用也终止掉,并产生一个崩溃日志。而在这种情况下被终止的后台应用,不会产生崩溃日志。
注意: 根据 苹果文档, Xcode不会自动添加低内存日志。你必需手动获取日志。 然而,根据我的个人经验,使用 Xcode 4.5.2, 低内存日志也会自动导入,只是”Process”和”Type”属性都被标为Unknown(未知)。
另外,值得一提的是在极短时间内分配一大块内存将给系统内存带来巨大负担。这样,也会产生内存警告的通知。
应用中有Bug
如你所想,大多数闪退都是由于应用中有Bug,因此大多数崩溃日志的产生都是因为应用中的Bug。Bug的种类的有很多。
在本教程的后半部分,你将通过调试一个会产生崩溃日志的含有Bug的应用,学习如何找到问题所在并进行修复!
2.日志的获取
如果能够拿到崩溃的设备,Xcode》Window》Devices》选中设备》View Device Logs》选中,可以导出日志也可以不导出点击查看,不过如果没有导出,拔掉设备日志就没有了。
如果不能拿到设备,还可以用友盟也有收集查看崩溃日志的功能,但是友盟日志只保存15天,所以就需要新版本发布后及时的监视公司应用的数据情况,及时分析修改。
iTunes Connect 里面没有日志,只可以看到崩溃的统计。可以通过xcode获取到线上的崩溃日志xcode》window》organizer》(左侧选择)应用》crashes。organizer统计到的bug,已经指定了应用中对应的函数,比较方便分析。
3.分析日志
|
|
(1) 进程信息
ncident Identifier是崩溃报告的唯一标识符。
CrashReporter Key 是与设备标识相对应的唯一键值。虽然它不是真正的设备标识符,但也是一个非常有用的情报:如果你看到100个崩溃日志的CrashReporter Key值都是相同的,或者只有少数几个不同的CrashReport值,说明这不是一个普遍的问题,只发生在一个或少数几个设备上。
Hardware Model 标识设备类型。 如果很多崩溃日志都是来自相同的设备类型,说明应用只在某特定类型的设备上有问题。上面的日志里,崩溃日志产生的设备是iPhone6,2。
Process 是应用名称。中括号里面的数字是闪退时应用的进程ID。
(2) 基本信息
这部分给出了一些基本信息,包括闪退发生的日期和时间,设备的iOS版本。如果有很多崩溃日志都来自iOS 6.0,说明问题只发生在iOS 6.0上。
(3) 异常
在这部分,你可以看到闪退发生时抛出的异常类型。还能看到异常编码和抛出异常的线程。根据崩溃报告类型的不同,在这部分你还能看到一些另外的信息。
Exception Type:异常类型的信息
|
|
Exception Codes:异常出错的代码
|
|
Triggered by Thread:判断几号线程闪退,
(4) 线程回溯
这部分提供应用中所有线程的回溯日志。 回溯是闪退发生时所有活动帧清单。它包含闪退发生时调用函数的清单,一般根据这个代码就能找到具体的crash问题。看下面这行日志:
|
|
它包括四列:
帧编号—— 此处是0。
二进制库的名称 ——此处是 CoreFoundation.
调用方法的地址 ——此处是 0x181112db0.
第四列分为两个子列,一个基本地址和一个偏移量。此处是__exceptionPreprocess + 124, 第一个指向文件,第二个数字指向文件中的代码行。
(5) 线程状态
这部分是闪退时寄存器中的值。一般不需要这部分的信息,因为回溯部分的信息已经足够让你找出问题所在。
(6) 二进制映像
这部分列出了闪退时已经加载的二进制文件。
符号化
第一次看到崩溃日志上的回溯时,你或许会觉得它没什么意义。我们习惯使用方法名和行数,而非像这样的神秘位置:
4 HomeHelper 0x100118874 0x1000b4000 + 411764
Xcode符号化崩溃日志时,需要访问与App Store上对应的应用二进制文件以及生成二进制文件时产生的 .dSYM 文件。必需完全匹配才行。否则,日志将无法被完全符号化。
所以,保留每个分发给用户的编译版本非常重要。提交应用前进行归档时,Xcode将保存应用的二进制文件。可以在Xcode Organizer的Archives标签栏下找到所有已归档的应用文件。
友盟日志
xcode可以自动寻找.dSYM文件,然后自动对日志符号化。但是有事情其他日志,无法实现自动符号化,这个时候就需要自己找到.dSYM文件,把日志中的地址手动符号化。
.dSYM文件地址:xcode》window》Organizer》选择对应的应用,对应的版本号。》show in finder》show package contents》dSYMs文件夹》找到应用对应的.app.dSYM。
1.查看 xx.app 文件的 UUID,terminal 中输入命令 :
dwarfdump –uuid xx.app/xx (xx代表你的项目名)
2.查看 xx.app.dSYM 文件的 UUID ,在 terminal 中输入命令:
dwarfdump –uuid xx.app.dSYM
3.crash 文件内第一行 Incident Identifier 就是该 crash 文件的 UUID。
友盟日志的最后一部分的dSYM UUID,要和 xx.app 文件的 UUID相匹配,不然无法转化。
4.执行dwarfdump –arch=arm64 –lookup 0x100038a4c “$dSYMPath”,0x100038a4c日志中函数的地址,把”$dSYMPath”替换成你xx.app.dSYM文件的路径,不带双引号。
执行结果:
可以分析道地址所代表的哪个文件中的哪一行代码。
低内存崩溃
低内存崩溃日志上没有应用线程的堆栈回溯。当应用发生低内存闪退时,你必需看看应用中内存使用的方式,以及是如何处理低内存警告的。你可以使用Instruments工具中使用Allocations 和 Leaks来发现内存分配问题和内存泄漏问题。如果你不知道如何利用 Instruments 检查内存问题,那就自己好好找找资料学习一下吧。
本文主要借鉴iOS应用崩溃日志分析