一、Crash文件结构
当程序运行Crash的时候,系统会把运行的最后时刻的运行信息记录下来,存储到一个文件中,也就是我们所说的Crash文件。iOS的Crash日志通常由以下6个部分组成;
1、Process Information(进程信息)
- (1)、Incident Identifier:崩溃报告的唯一标识符,不同的Crash;
- (2)、CrashReporter Key :是与设备标识相对应的唯一键值。虽然它不是真正的设备标识符,但也是一个非常有用的情报:如果你看到100个崩溃日志的CrashReporter Key值都是相同的,或者只有少数几个不同的CrashReport值,说明这不是一个普遍的问题,只发生在一个或少数几个设备上。
- (3)、Hardware Model :标识设备类型。 如果很多崩溃日志都是来自相同的设备类型,说明应用只在某特定类型的设备上有问题。上面的日志里,崩溃日志产生的设备是iPhone 7,1。对照表参考:https://www.theiphonewiki.com/wiki/Models
- (4)、Process :是应用包名称。中括号里面的数字是闪退时应用的进程ID。
- (5)、Path:可执行程序再手机上的存储位置;
- (6)、Identifier:你的App的Identifier,通常为『com.xxx.yyy』,一般情况下xxx代表你们公司的域名,yyy代表某一个App;
- (7)、Version:当前App的版本号,由Info.plist中的两个字段组成,CFBundleShortVersionString and CFBundleVersion;
- (8)、Code Type:当前App的CPU框架;
- (9)、Parent Process:当前进行的父进程,由于iOS中App通常是单进程的,一般父进程都是launchd;
2、Basic Information
- (1)、Date/Time:Crash发生的时间,可读的字符串;
- (2)、OS Version:系统版本,()内的数字代表的是Build号;
- (3)、Report Version:Crash日志的格式;
3、Exception (非常重要)
- (1)、Exception Type:异常类型;
- (2)、Exception Codes:异常编号;
- (3)、Crashed Thread:发生异常的线程号;可以根据这个编号找到对应的crash调用堆栈;
4、Thread Backtrace
发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。
5、Thread State
Crash时发生的时刻,线程的状态,通常我们根据Crash栈即可获取到相关信息;
6、Binary Image
Crash时刻App加载的所有的库,其中第一行是Crash发生时我们的App可执行文件的信息;
二、常见的Crash类型
1、Watchdog timeout
Exception Code:0x8badf00d, 不太直观,可以读成“eat bad food”,意思是don‘t block main thread
紧接着下面会有一段描述:
Application Specific Information:
com.xxx.yyy failed to resume in time
对于此类Crash,我们应该去审视自己App初始化时做的事情是否正确,是否在主线程请求了网络,或者其他耗时的事情卡住了正常初始化流程。
通常系统允许一个App从启动到可以相应用户事件的时间最多为5S,如果超过了5S,App就会被系统终止掉。在Launch,resume,suspend,quit时都会有相应的时间要求。在Highlight Thread里面我们可以看到被终止时调用到的位置,xxxAppDelegate加上行号。
PS. 在连接Xcode调试时为了便于调试,系统会暂时禁用掉Watchdog,所以此类问题的发现需要使用正常的启动模式。
2、User force-quit
Exception Codes: 0xdeadfa11, deadfall
这个强制退出跟我们平时所说的kill掉后台任务操作还不太一样,通常在程序bug造成系统无法响应时可以采用长按电源键,当屏幕出现关机确认画面时按下Home键即可关闭当前程序。
3、Low Memory termination
跟一般的Crash结构不太一样,通常有Free pages,Wired Pages,Purgeable pages,largest process 组成,同事会列出当前时刻系统运行所有进程的信息。
App在运行过程中,系统内存紧张时通常会先发警告,同时把后台挂起的程序终止掉,最终如果还是内存不够的话就会终止掉当前前台的进程。
当接受到内存警告的事后,我们应该释放尽可能多的内存,Crash其实也可以看做是对App的一种保护。
4、Crash due to bugs
因为程序bug导致的Crash通常千奇百怪,很难一概而论。大部分情况通过Crash日志就可以定位出问题,当然也不排除部分疑难杂症看半天都不值问题出在哪儿。这个就只能看功底了,一点点找,总是能发现蛛丝马迹。是在看不出来时还可以求助于Google大神,总有人遇到和你一样的Bug
三、常见的Exception Type & Exception Code
1、Exception Type
(1)、EXC_BAD_ACCESS
此类型的Excpetion是我们最长碰到的Crash,通常用于访问了不改访问的内存导致。一般EXC_BAD_ACCESS后面的”()”还会带有补充信息。- SIGSEGV: 通常由于重复释放对象导致,这种类型在切换了ARC以后应该已经很少见到了。
- SIGABRT: 收到Abort信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。
- SEGV:(Segmentation Violation),代表无效内存地址,比如空指针,未初始化指针,栈溢出等;
- SIGBUS:总线错误,与 SIGSEGV 不同的是,SIGSEGV 访问的是无效地址,而 SIGBUS 访问的是有效地址,但总线访问异常(如地址对齐问题)
- SIGILL:尝试执行非法的指令,可能不被识别或者没有权限
(2)、EXC_BAD_INSTRUCTION
此类异常通常由于线程执行非法指令导致- (3)、EXC_ARITHMETIC
除零错误会抛出此类异常
2、Exception Code
- (1)、0xbaaaaaad
此种类型的log意味着该Crash log并非一个真正的Crash,它仅仅只是包含了整个系统某一时刻的运行状态。通常可以通过同时按Home键和音量键,可能由于用户不小心触发 - (2)、0xbad22222
当VOIP程序在后台太过频繁的激活时,系统可能会终止此类程序 - (3)、0x8badf00d
程序启动或者恢复时间过长被watch dog终止 - (4)、0xc00010ff
程序执行大量耗费CPU和GPU的运算,导致设备过热,触发系统过热保护被系统终止 - (5)、0xdead10cc
程序退到后台时还占用系统资源,如通讯录被系统终止 - (6)、0xdeadfa11的
程序无响应用户强制关闭
四、获取Crash的途径
1、模拟器崩溃
模拟器崩溃后可以在“~/Library/Logs/DiagnosticReports/”下找到crash日志。
2、真机
手机和mac连接后,打开Xcode选择window进入Devices(快捷方式是 Shift-CMD-2),选中View Device Logs可展示该设备的crash日志;
3、itunes connect
应用提交到App Store后,你也能从 iTunes Connect 获取到用户的崩溃日志. 登录到 iTunes Connect 上, 选择App分析, 点击相应的应用, 点击应用图标下面的指标按钮, 即可查看到崩溃选项
4、第三方的Crash手机系统
友盟等
5、手动获取日志
设备与电脑上的iTunes Store同步后,会将崩溃日志保存在电脑上。根据电脑操作系统的不同,崩溃日志将保存在以下位置:
- ①、Mac OS X:~/Library/Logs/CrashReporter/MobileDevice/
- ②、Windows XP: C:Documents and SettingsApplication DataApple ComputerLogsCrashReporterMobileDevice
- ③、Windows Vista or 7: C:UsersAppDataRoamingApple ComputerLogsCrashReporterMobileDevice