程序异常捕获-001-UncaughtException
程序的异常捕获:
Crash日志记录的时候是将Crash发生时刻,函数的调用栈,以及线程等信息写入文件。
01-Crash文件的解析
今天就跟大家一起聊聊 iOS Crash文件的组成以及常用的分析工具。
一、Crash文件结构
当程序运行Crash的时候,系统会把运行的最后时刻的运行信息记录下来,存储到一个文件中,也就是我们所说的
Crash文件。iOS的Crash日志通常由以下6各部分组成。
1、Process Information(进程信息)
Incident Idnetifier-崩溃报告的唯一标识符,不同的CrashCrashReporter Key-设备标识相对应的唯一键值(并非真正的设备的UDID,苹果为了保护用户隐私iOS6以后已经无法
获取)。通常同一个设备上同一版本的App发生Crash时,该值都是一样的。
Hardware Model - 代表发生Crash的设备类型,上图中的“iPad4,4”代表iPad Air
Process - 代表Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的ID
Path - 可执行程序在手机上的存储位置,注意路径时到XXX.app/XXX,XXX.app其实是作为一个Bundle的,真正的可 执行文件其实是Bundle里面的XXX,感兴趣的可以自己查一下相关资料,有机会我后面也会介绍到
Identifier-你的App的Indentifier,通常为“com.xxx.yyy”,xxx代表你们公司的域名,yyy代表某一个App
Version-当前App的版本号,由Info.plist中的两个字段组成,CFBundleShortVersionString and CFBundleVersion
Code Type-当前App的CPU架构
Parent Process-当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都是launchd
2、Basic Information
Date/Time-Crash发生的时间,可读的字符串
OS Version-系统版本,()内的数字代表的时Bulid号
Report Version-Crash日志的格式,目前基本上都是104,不同的version里面包含的字段可能有不同
3、Exception(非常重要)
Exception Type-异常类型
Exception Subtype-异常子类型
Crashed Thread-发生异常的线程号
4、Thread Backtrace
发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看 到API的调用顺序。上图的信息表明本次Crash出现xxxViewController的323行,出错的函数调用为 orderCountLoadFailed。
5、Thread State
Crash时发生时刻,线程的状态,通常我们根据Crash栈即可获取到相关信息,这部分一般不用关心。
6、Binary Images
Crash时刻App加载的所有的库,其中第一行是Crash发生时我们App可执行文件的信息,可以看出为armv7,可执行 文件的包得uuid位c0f......cd65,解析Crash的时候dsym文件的uuid必须和这个一样才能完成Crash的符号化解析。
二、分析Crash来源
通常来说,crash产生来源于两种问题:违反iOS系统规则导致的crash和App代码逻辑 BUG导致的crash,下面分别对他们进行分析。
违反iOS系统规则产生crash的三种类型:
1>内存报警闪退
当iOS检测到内存过低时,它的VM系统会发出低内存警告通知,尝试回收一些内存;如果情况没有得到足够的改善, iOS会终止后台应用以回收更多内存;最后,如果内存还是不足,那么正在运行的应用可能会被终止掉。在Debug模 式下,可以主动将客户端执行的动作逻辑写入一个log文件中,这样程序童鞋可以将内存预警的逻辑写入该log文件, 当发生如下截图中的内存报警时,就是提醒当前客户端性能内存吃紧,可以通过Instruments工具中的Allocations 和 Leaks模块库来发现内存分配问题和内存泄漏问题。
2>响应超时
当应用程序对一些特定的事件(比如启动、挂起、恢复、结束)响应不及时,苹果的Watchdog机制会把应用程序干 掉,并生成一份相应的crash日志。这些事件与下列UIApplicationDelegate方法相对应,当遇到Watchdog日志时,可 以检查上图中的几个方法是否有比较重的阻塞UI的动作。
application:didFinishLaunchingWithOptions: 2 applicationWillResignActive:3 applicationDidEnterBackground:4 applicationWillEnterForeground: 5 applicationDidBecomeActive: 6 applicationWillTerminate:
3>用户强制退出
一看到“用户强制退出”,首先可能想到的双击Home键,然后关闭应用程序。不过这种场景一般是不会产生crash日志 的,因为双击Home键后,所有的应用程序都处于后台状态,而iOS随时都有可能关闭后台进程,当应用阻塞界面并停 止响应时这种场景才会产生crash日志。这里指的“用户强制退出”场景,是稍微比较复杂点的操作:先按住电源键,直 到出现“滑动关机”的界面时,再按住Home键,这时候当前应用程序会被终止掉,并且产生一份相应事件的crash日 志。
应用逻辑的Bug
大多数闪退崩溃日志的产生都是因为应用中的Bug,这种Bug的错误种类有很多,比如:
SEGV:(Segmentation Violation,段违例),无效内存地址,比如空指针,未初始化指针,栈溢出等; SIGABRT:收到Abort信号,可能自身调用abort()或者收到外部发送过来的信号; SIGBUS:总线错误。与SIGSEGV不同的是,SIGSEGV访问的是无效地址(比如虚存映射不到物理内存),而 SIGBUS访问的是有效地址,但总线访问异常(比如地址对齐问题); SIGILL:尝试执行非法的指令,可能不被识别或者没有权限;
SIGFPE:Floating Point Error,数学计算相关问题(可能不限于浮点计算),比如除零操作; SIGPIPE:管道另一端没有进程接手数据;
常见的崩溃原因基本都是代码逻辑问题或资源问题,比如数组越界,访问野指针或者资源不存在,或资源大小写错误
等。
三、获取Crash的途径
1、本机
通过xCode连接测试机器,直接在Device中即可读取到该机器上发生的所有Crash log。
2、itunes connect通过itunes connect后台获取到用户上报的Crash日志。
3、第三方的Crash收集系统
有很多优秀的第三方Crash收集系统大大的方便了我们收集Crash,甚至还带了符号化Crash日志的功能。比较常用的 有Crashlytics,Flurry等。