在程序开发的过程中,难免遇到各种崩溃情况。目前我了解到的崩溃信息收集有如下几种途径:参考
- iTunes Connect导出手机上传日志、
- 拿到用户手机使用 Xcode 导出、
- 使用第三方崩溃收集服务(如 Bugly、友盟等),
- 手动收集崩溃信息。
这篇文章主要介绍crash日志的分析,为下一篇手动收集和分析崩溃日志打下一些基础。
我们先看看最原始取到的崩溃日志是什么样子的
Incident Identifier: FD86E6F8-FBF4-4D70-97FF-144CFA95286B
CrashReporter Key: 406e631e757d2782d57d0283f97edd069a4b5974
Hardware Model: iPhone8,1
Process: tesApp [1058]
Path: /private/var/containers/Bundle/Application/320EF082-7158-447F-9927-F0D52C16570F/tesApp.app/tesApp
Identifier: com.test.tesApp
Version: 1 (2.0.0)
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.test.tesApp [925]
Date/Time: 2017-11-23 16:06:29.6264 +0800
Launch Time: 2017-11-23 16:05:25.2049 +0800
OS Version: iPhone OS 11.1.2 (15B202)
Baseband Version: 4.00.01
Report Version: 104
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
Application Specific Information:
abort() called
Filtered syslog:
None found
Last Exception Backtrace:
0 CoreFoundation 0x1835bdd04 __exceptionPreprocess + 124
1 libobjc.A.dylib 0x18280c528 objc_exception_throw + 55
2 CoreFoundation 0x183556bd4 _CFThrowFormattedException + 111
3 CoreFoundation 0x1836058a8 -[__NSArrayI objectAtIndexedSubscript:] + 131
4 weidasheng_dev 0x102d478b8 0x102d14000 + 211128
5 weidasheng_dev 0x102d463f0 0x102d14000 + 205808
6 UIKit 0x18cd2f93c 0x18c9e5000 + 3451196
7 UIKit 0x18cd2fea0 0x18c9e5000 + 3452576
8 UIKit 0x18cd0f9e4 0x18c9e5000 + 3320292
9 UIKit 0x18cac54fc 0x18c9e5000 + 918780
10 UIKit 0x18c9f22f8 0x18c9e5000 + 54008
11 QuartzCore 0x1875abec8 -[CALayer layoutSublayers] + 183
12 QuartzCore 0x1875affa8 CA::Layer::layout_if_needed+ 1187752 (CA::Transaction*) + 331
13 QuartzCore 0x18751ea98 CA::Context::commit_transaction+ 592536 (CA::Transaction*) + 335
14 QuartzCore 0x187544eb4 CA::Transaction::commit+ 749236 () + 539
15 QuartzCore 0x187545cf4 CA::Transaction::observer_callback+ 752884 (__CFRunLoopObserver*, unsigned long, void*) + 91
16 CoreFoundation 0x183565848 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 31
17 CoreFoundation 0x183563200 __CFRunLoopDoObservers + 411
18 CoreFoundation 0x1835637bc __CFRunLoopRun + 1291
19 CoreFoundation 0x183483fb8 CFRunLoopRunSpecific + 435
20 GraphicsServices 0x18531bf84 GSEventRunModal + 99
21 UIKit 0x18ca582e8 0x18c9e5000 + 471784
22 weidasheng_dev 0x102d22360 0x102d14000 + 58208
23 libdyld.dylib 0x182fa656c start + 3
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001830d5348 __pthread_kill + 8
1 libsystem_pthread.dylib 0x00000001831e9344 pthread_kill$VARIANT$mp + 396
2 libsystem_c.dylib 0x0000000183044fb8 abort + 140
3 libc++abi.dylib 0x00000001827e4068 __cxa_bad_cast + 0
4 libc++abi.dylib 0x00000001827fc568 std::__terminate(void (*)+ 107880 ()) + 44
5 libc++abi.dylib 0x00000001827fc158 __cxa_rethrow + 144
6 libobjc.A.dylib 0x000000018280c6e8 objc_exception_rethrow + 44
7 CoreFoundation 0x0000000183484024 CFRunLoopRunSpecific + 544
8 GraphicsServices 0x000000018531bf84 GSEventRunModal + 100
9 UIKit 0x000000018ca582e8 0x18c9e5000 + 471784
10 weidasheng_dev 0x0000000102d22360 0x102d14000 + 58208
11 libdyld.dylib 0x0000000182fa656c start + 4
Thread 1 name: com.apple.uikit.eventfetch-thread
Thread 1:
0 libsystem_kernel.dylib 0x00000001830b4bc4 mach_msg_trap + 8
1 libsystem_kernel.dylib 0x00000001830b4a3c mach_msg + 72
2 CoreFoundation 0x0000000183565c74 __CFRunLoopServiceMachPort + 196
3 CoreFoundation 0x0000000183563840 __CFRunLoopRun + 1424
4 CoreFoundation 0x0000000183483fb8 CFRunLoopRunSpecific + 436
5 Foundation 0x0000000183ead6e4 -[NSRunLoop+ 50916 (NSRunLoop) runMode:beforeDate:] + 304
6 Foundation 0x0000000183eccafc -[NSRunLoop+ 178940 (NSRunLoop) runUntilDate:] + 96
7 UIKit 0x000000018d5b72f4 0x18c9e5000 + 12395252
8 Foundation 0x0000000183fae860 __NSThread__start__ + 996
9 libsystem_pthread.dylib 0x00000001831e831c _pthread_body + 308
10 libsystem_pthread.dylib 0x00000001831e81e8 _pthread_body + 0
11 libsystem_pthread.dylib 0x00000001831e6c28 thread_start + 4
Thread 2 name: com.apple.NSURLConnectionLoader
Thread 2:
0 libsystem_kernel.dylib 0x00000001830b4bc4 mach_msg_trap + 8
1 libsystem_kernel.dylib 0x00000001830b4a3c mach_msg + 72
2 CoreFoundation 0x0000000183565c74 __CFRunLoopServiceMachPort + 196
3 CoreFoundation 0x0000000183563840 __CFRunLoopRun + 1424
4 CoreFoundation 0x0000000183483fb8 CFRunLoopRunSpecific + 436
5 CFNetwork 0x0000000183bee264 +[NSURLConnection+ 717412 (Loader) _resourceLoadLoop:] + 404
6 Foundation 0x0000000183fae860 __NSThread__start__ + 996
7 libsystem_pthread.dylib 0x00000001831e831c _pthread_body + 308
8 libsystem_pthread.dylib 0x00000001831e81e8 _pthread_body + 0
9 libsystem_pthread.dylib 0x00000001831e6c28 thread_start + 4
...
Thread 0 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x00000001c02e7fb1
x4: 0x00000001828023de x5: 0x000000016d0eb920 x6: 0x000000000000006e x7: 0x00000001c4112480
x8: 0x0000000008000000 x9: 0x0000000004000000 x10: 0x00000001831ed62c x11: 0x0000000000000001
x12: 0xffffffffffffffff x13: 0x0000000000000001 x14: 0x00003b0000003b00 x15: 0x0000000000000010
x16: 0x0000000000000148 x17: 0x00000000ffffffff x18: 0x0000000000000000 x19: 0x0000000000000006
x20: 0x00000001b4168b80 x21: 0x000000016d0eb920 x22: 0x0000000000000303 x23: 0x00000001b4168c60
x24: 0x00000001c000a830 x25: 0x0000000000000000 x26: 0x0000000000000001 x27: 0x0000000000000000
x28: 0x000000016d0ebb18 fp: 0x000000016d0eb880 lr: 0x00000001831e9344
sp: 0x000000016d0eb850 pc: 0x00000001830d5348 cpsr: 0x00000000
Binary Images:
0x102d14000 - 0x10368ffff weidasheng_dev arm64 <9599d010fc953cdcacf0298f33f54d20> /var/containers/Bundle/Application/320EF082-7158-447F-9927-F0D52C16570F/weidasheng_dev.app/weidasheng_dev
0x104230000 - 0x10426ffff dyld arm64 <92368d6f78863cc88239f2e3ec79bba8> /usr/lib/dyld
….
EOF
分析日志
分析log日志参考,我这里稍微总结下。
我这里直接把我目前一般找crash原因的模块展示出来,其他的就留待各位自己去研究了,分别是设备和crash信息、异常信息、线程信息
1、首先是设备和crash信息
Incident Identifier: F3573A...E2F244A //crash的id
CrashReporter Key: cc2298...es77eeb //crash的设备id
Hardware Model: iPhone7,2 //手机型号
Process: [AppName] [1816] //APP的名字[进程的id]
Path: /private/.../Application... //APP的位置
Identifier: com.... //bundle ID
Version: 14 (2.3.5) //版本号
Code Type: ARM-64 (Native) //app的CPU类型
Parent Process: launchd [1]
Date/Time: 2015-10-26 15:03:29.29 +0800 //crash发生时间
Launch Time: 2015-10-26 14:58:28.28 +0800 //进入应用时间
OS Version: iOS 9.1 (13B143) //iOS版本
Report Version: 105
当你有大量的crash文件的时候,你就可以对crash文件里面的 Hardware Model,Version , OS Version等进行分类,就可以获知到很多信息。进行图像化展示
2、其次是异常信息
Exception Type: EXC_BAD_ACCESS (SIGABRT) //异常的类型
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000118 //异常子类型
Triggered by Thread: 0 //异常发生的线程(0为主线程,其他为子线程)
3、线程信息
Last Exception Backtrace:
0 CoreFoundation 0x182780f48 __exceptionPreprocess + 124
1 libobjc.A.dylib 0x197333f80 objc_exception_throw + 56
2 CoreFoundation 0x182780e90 +[NSException raise:format:] + 120
3 [AppName] 0x100c42a40 UmengSignalHandler + 144
4 libsystem_platform.dylib 0x197d6193c _sigtramp + 52
5 [AppName] 0x1005d9f38 CScopePtr::operator IAVGAudioLogic*() (xprefc.h:165)
6 [AppName] 0x1005d3b8c tencent::av::AVRoomMultiImpl::GetAudioLogic() (av_room_multi_impl.h:119)
7 [AppName] 0x10057076c tencent::av::AVAudioCtrlImpl::SetAudioOutputMode(int) (av_audio_ctrl_impl.cpp:443)
8 [AppName] 0x10044dc3c -[AVBasicManager changeSpeakerMode:] (AVManager.mm:525)
9 [AppName] 0x100296e1c -[KTQAVRoom enableSpeakerMode:] (KTQAVRoom.m:345)
10 [AppName] 0x1002970d0 -[KTQAVRoom settingSpeaker:] (KTQAVRoom.m:362)
11 [AppName] 0x1003d5464 -[KTChatView onAudioNotificationReceived:] (KTChatView.m:685)
什么情况下会产生崩溃日志?
两种主要情况会产生崩溃日志:
- 应用违反操作系统规则。
- 应用中有Bug。
1.违反iOS规则
违反iOS规则包括在启动、恢复、挂起、退出时watchdog超时、用户强制退出和低内存终止。
Watchdog 超时机制
退出应用时,应用不会立即终止,而是退到后台。但是,如果你的应用响应不够快,操作系统有可能会终止你的应用,并产生一个崩溃日志。这些事件与下列UIApplicationDelegate方法相对应:
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationDidBecomeActive:
applicationWillTerminate:
上面所有这些方法,应用只有有限的时间去完成处理。如果花费时间太长,操作系统将终止应用
用户强制退出
iOS 4.x开始支持多任务。如果应用阻塞界面并停止响应, 用户可以通过在主屏幕上双击Home按钮,并上滑来终止应用。此时,操作应用将生成一个崩溃日志。
注意: 双击Home按钮后,你将看到运行过的所有应用。那些应用不一定是正在运行,也不一定是被挂起。
通常,用户点击Home按钮时,应用将在后台保留约10分钟,然后操作系统自动将其终止。 所以双击Home按钮显示的应用列表只是表明那是一系列过去打开过的应用。删除那些应用的图标不会产生任何崩溃日志。
低内存终止
在前台运行的应用拥有访问和使用内存的最高优化级。然而,这并不意味着该应用能使用设备的所有可用内存 ——每个应用只能使用一部分可用内存。
当内存使用达到一定程度时,操作系统将发出一个 UIApplicationDidReceiveMemoryWarningNotification 通知。同时,调用 didReceiveMemoryWarning 方法。
此时,为了让应用继续正常运行,操作系统开始终止在后台的其他应用以释放一些内存。所有后台应用被终止后,如果你的应用还需要更多内存,操作系统会将你的应用也终止掉,并产生一个崩溃日志。而在这种情况下被终止的后台应用,不会产生崩溃日志。另外,在极短时间内分配一大块内存将给系统内存带来巨大负担。也会产生内存警告的通知。
2.应用中有Bug
但是大多数情况我们会更关心由于代码原因而导致的崩溃。常见的错误type和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、常见错误码
0x8badf00d: 读做 “ate bad food”! (把数字换成字母,是不是很像 :p)该编码表示应用是因为发生watchdog超时而被iOS终止的。 通常是应用花费太多时间而无法启动、终止或响应用系统事件。
0xbad22222: 该编码表示 VoIP 应用因为过于频繁重启而被终止。
0xdead10cc: 读做 “dead lock”!该代码表明应用因为在后台运行时占用系统资源,如通讯录数据库不释放而被终止 。
0xdeadfa11: 读做 “dead fall”! 该代码表示应用是被用户强制退出的。根据苹果文档, 强制退出发生在用户长按开关按钮直到出现 “滑动来关机”, 然后长按 Home按钮。强制退出将产生 包含0xdeadfa11 异常编码的崩溃日志, 因为大多数是强制退出是因为应用阻塞了界面。
(注意: 在后台任务列表中关闭已挂起的应用不会产生崩溃日志。 一旦应用被挂起,它何时被终止都是合理的。所以不会产生崩溃日志。)
低内存崩溃日志与其他类型的崩溃日志很不一样,它们不指向特定的文件和代码行。相反,它们画出了闪退时设备上的内存使用情况的图表。
符号化Symbolication
第一次看到崩溃日志上的回溯时,你或许会觉得它没什么意义。把它转化成可读性更强的样式。这个过程称之为符号化
我们习惯使用方法名和行数,而非像这样的神秘位置:
当前代码行地址 = 当前地址 + 地址偏移量,地址偏移量为十进制数值
如果相同的crash,相同位置后面的地址偏移量相同,前面两个数值可能会有变动
符号化前
4 weidasheng_dev 0x102d478b8 0x102d14000 + 211128
符号化后
符号化的常用方法
1.symbolicatecrash
symbolicatecrash是xcode的一个符号化crash log的命令行工具。使用方法也就是导出.crash文件(crash log)和找到.dsYM文件,然后进行分析。
如何使用查看[iOS]使用symbolicatecrash分析crash文件
2.还有命令行工具atos
如果你有多个“.ipa”文件,多个".dSYMB"文件,你并不太确定到底“dSYM”文件对应哪个".ipa"文件,那么,这个方法就非常适合你。
特别当你的应用发布到多个渠道的时候,你需要对不同渠道的crash文件,写一个自动化的分析脚本的时候,这个方法就极其有用。
简单使用方法命令行工具atos
3.第三方小工具.dsYM分析。(我平时就是用这个方法,比较简单)
具体可参考文章dSYM 文件分析工具