iOS开发Swift

Crash日志,分析专用

当一个应用程序在一台iOS 设备上崩溃时,一份“崩溃报告”将在该设备上次创建并存储起来。崩溃报告描述应用程序是在何种条件下崩溃的,大部分情况下包含一份当前正在运行线程的完整的堆栈跟踪。

产生崩溃日志的原因:

  • 应用违反操作系统规则,包括在启动、恢复、挂起、退出时watchdog超时、用户强制退出和低内存终止等。
  • 应用中有Bug

从多任务窗口中终止一个暂停的应用程序不会产生崩溃日志。一旦一个应用被暂停,它有资格被iOS在任何时间终止,因此不会产生崩溃日志。

  • 本机通过Xcode的Devices窗口获取某个设备的崩溃日志
  • 设备与电脑上的iTunes Store同步后,会将崩溃日志保存在电脑上
  • 应用提交到App Store后,可通过itunes connect后台获取到用户上报的Crash日志。
  • 有很多优秀的第三方Crash收集系统大大的方便了我们收集Crash,甚至还带了符号化Crash日志的功能。比较常用的有CrashlyticsFlurry等。

1.Process Information

Incident Identifier: 66AFBBF0-7ACB-4319-97C7-6F44E09FF9EB         //崩溃报告的唯一标识符
CrashReporter Key:   97aec51145730a778c0d1cfdfc17c1b8c86ba4c5     //设备标识相对应的唯一键值(并非真正的设备的UDID,为保护隐私iOS6以后已无法获取)
Hardware Model:      iPad5,3                                      //发生Crash的设备类型
Process:             XXXXClient [407]                     //Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的ID
Path:                /private/var/mobile/Containers/Bundle/Application/0380D606-3A40-4633-A1B2-7E1F3E3D4FCA/XXXXClient.app/XXXXClient   
            //可执行程序在手机上的存储位置,注意路径时到x.app/x,x.app其实是作为一个Bundle的,真正的可执行文件其实是Bundle里面的x
Identifier:          com.xxxx.myapp                               //App的Indentifier,通常为“com.xxx.yyy”
Version:             1 (1.0.0)                                    //App的版本号,由Info.plist中
CFBundleShortVersionString + 
CFBundleVersion

Code Type:           ARM-64 (Native)                   //App的CPU架构
Parent Process:      launchd [1]                                  //当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都launchd

2.Basic Information

Date/Time:           2016-02-19 00:34:43.449 -0800                //Crash发生的时间
Launch Time:         2016-02-19 00:34:43.399 -0800               
OS Version:          iOS 8.4 (12H143)                             //系统版本,括号内的数字代表的时Bulid号
Report Version:      105                                          //Crash日志的格式

3.Exception

Exception Type:    EXC_CRASH (SIGABRT)                             //异常类型
Exception Subtype:                                                  //v104
Exception Codes:   0x0000000000000000, 0x0000000000000000          //v105
Triggered by Thread: 0                                              //v105
Crashed Thread                                                      //v104

 4.Thread Backtrace

发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
//编号  二进制库名                        调用方法的地址       基本地址 + 偏移
0   libsystem_kernel.dylib        	0x0000000194b3b270 __pthread_kill + 8
1   libsystem_pthread.dylib       	0x0000000194bd916c pthread_kill + 108
2   libsystem_c.dylib             	0x0000000194ab2b14 abort + 108
3   ...g_rt.asan_ios_dynamic.dylib	0x00000001032756d0 0x103224000 + 333520
4   ...g_rt.asan_ios_dynamic.dylib	0x000000010326955c 0x103224000 + 283996
5   ...g_rt.asan_ios_dynamic.dylib	0x000000010326cf28 0x103224000 + 298792
6   ...g_rt.asan_ios_dynamic.dylib	0x0000000103269640 0x103224000 + 284224
7   ...g_rt.asan_ios_dynamic.dylib	0x000000010326d0e8 0x103224000 + 299240
8   ...g_rt.asan_ios_dynamic.dylib	0x000000010325ef50 0x103224000 + 241488
9   ...g_rt.asan_ios_dynamic.dylib	0x0000000103268d18 0x103224000 + 281880
10  dyld                          	0x00000001200b9234 ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 256
11  dyld                          	0x00000001200b93ec ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 32
12  dyld                          	0x00000001200b5688 ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 328
13  dyld                          	0x00000001200b561c ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 220
14  dyld                          	0x00000001200b54d8 ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 136
15  dyld                          	0x00000001200b57a0 ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 80
16  dyld                          	0x00000001200aa150 dyld::initializeMainExecutable() + 196
17  dyld                          	0x00000001200ad8bc dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 2664
18  dyld                          	0x00000001200a9040 _dyld_start + 64

5.Thread State

Crash时发生时刻,线程的状态(寄存器中的值)

Thread 0 crashed with ARM Thread State (64-bit):
    x0: 0x0000000000000000   x1: 0x0000000000000000   x2: 0x0000000000000000   x3: 0x0000000000010000
    x4: 0x000000000000027b   x5: 0x0000000000000000   x6: 0x0000000000000000   x7: 0x0000000000000250
    x8: 0x0000000008000000   x9: 0x0000000004000000  x10: 0x0000000000000000  x11: 0x0000000000000018
   x12: 0x0000000000000001  x13: 0x0000000000062aa8  x14: 0x0000000000000015  x15: 0x0000000000000000
   x16: 0x0000000000000148  x17: 0x0000000000000000  x18: 0x0000000000000000  x19: 0x0000000000000006
   x20: 0x0000000198ae4310  x21: 0x0000000103280963  x22: 0x0000000000000000  x23: 0x0000000000000000
   x24: 0x0000000000000093  x25: 0x000000016fd182d8  x26: 0x00000001200d8d11  x27: 0x000000010328c000
   x28: 0x00000001032243d0  fp: 0x000000016fd17920   lr: 0x0000000194bd9170
    sp: 0x000000016fd17900   pc: 0x0000000194b3b270 cpsr: 0x00000000

6.Binary Images

Crash时刻App加载的所有的库,其中第一行是Crash发生时我们App可执行文件的信息,可以看出为armv7,可执行文件的包得uuid位c0f……cd65,解析Crash的时候dsym文件的uuid必须和这个一样才能完成Crash的符号化解析。

Binary Images:
0x1000e4000 - 0x101fdffff XXXClient arm64  <aa8ef7e9f9c43c7c87f4f75cf266d479> /var/mobile/Containers/Bundle/Application/0380D606-3A40-4633-A1B2-7E1F3E3D4FCA/XXXXClient.app/XXXXClient
0x103224000 - 0x103287fff libclang_rt.asan_ios_dynamic.dylib arm64  <c51061e5b8443a8e9b6c2b76628b4b95> /var/mobile/Containers/Bundle/Application/0380D606-3A40-4633-A1B2-7E1F3E3D4FCA/XXXX.app/Frameworks/libclang_rt.asan_ios_dynamic.dylib
0x1200a8000 - 0x1200cffff dyld arm64  <de589e6153453237a6cf724cb236d83c> /usr/lib/dyld
0x1810ac000 - 0x181240fff AVFoundation arm64  <b9c4b32ba43a3a798c4adcaad3608f52> /System/Library/Frameworks/AVFoundation.framework/AVFoundation
0x181244000 - 0x1812a8fff libAVFAudio.dylib arm64  <6667f63f0f1635668dc941d6b79062e1> /System/Library/Frameworks/AVFoundation.framework/libAVFAudio.dylib
0x194bf0000 - 0x194bf5fff libunwind.dylib arm64  <8b87982b31ad3569a95e75457cadba3e> /usr/lib/system/libunwind.dylib
0x194bf8000 - 0x194c1bfff libxpc.dylib arm64  <c9f3c08a8a3b3849a905d24911240853> /usr/lib/system/libxpc.dylib

符号化

包含堆栈跟踪的崩溃报告需要先进行符号化(symbolicated)才可以进行分析。符号化的过程是将内存地址替换为便于人们阅读的函数名称和行号。假如你通过Xcode的Organizer窗口获取崩溃日志,那么该报告将在几秒钟后自动进行符号化。否则你需要将.crash文件导入到Xcode的Organizer进行符号化。

//符号化前
6  Rage Masters   0x0001625c        0x2a000 + 3003

//符号化后
6  Rage Masters   0x0001625c        -[RMAppDelegate application:didFinishLaunchingWithOptions:]  (RMAppDelegate.m:35)

一个完整的Sample

打开Crash Log 会看到如下的信息:
Incident Identifier: AF4F2C83-8F68-47EF-B5AA-F16B067B5DF4
CrashReporter Key:   5670de85ee1f0f3c904891536e81ec086ed4b35b
Hardware Model:      iPhone8,1
Process:             kidneyUser [896]
Path:                /private/var/containers/Bundle/Application/48C71AA1-EB99-49B1-ABD7-2903DBA8E394/kidneyUser.app/kidneyUser
Identifier:          kidneyDiseaseHospitalUser
Version:             1 (1.0)
Code Type:           ARM-64 (Native)
Parent Process:      launchd [1]

Date/Time:           2016-05-05 10:45:43.43 +0800
Launch Time:         2016-05-05 10:42:07.07 +0800
OS Version:          iOS 9.3.1 (13E238)
Report Version:      105

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Filtered syslog:
None found

Last Exception Backtrace:
0   CoreFoundation                    0x181aeee38 __exceptionPreprocess + 124
1   libobjc.A.dylib                   0x181153f80 objc_exception_throw + 56
2   CoreData                          0x18393ab44 -[NSManagedObjectModel initWithContentsOfURL:] + 856
3   kidneyUser                        0x1002b81d8 0x1000d8000 + 1966552
4   kidneyUser                        0x1002b82dc 0x1000d8000 + 1966812
5   kidneyUser                        0x1002b86a0 0x1000d8000 + 1967776
6   kidneyUser                        0x1002b87cc 0x1000d8000 + 1968076
7   kidneyUser                        0x1002b8024 0x1000d8000 + 1966116
8   UIKit                             0x186cc9128 -[UIApplication _terminateWithStatus:] + 280
9   UIKit                             0x186ee7f08 __102-[UIApplication _handleApplicationDeactivationWithScene:shouldForceExit:transitionContext:completion:]_block_invoke2017 + 796
10  UIKit                             0x186eeafd8 _runAfterCACommitDeferredBlocks + 292
11  UIKit                             0x186ef8990 _cleanUpAfterCAFlushAndRunDeferredBlocks + 92
12  UIKit                             0x186c2a4a4 _afterCACommitHandler + 96
13  CoreFoundation                    0x181aa47b0 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
14  CoreFoundation                    0x181aa2554 __CFRunLoopDoObservers + 372
15  CoreFoundation                    0x181aa2984 __CFRunLoopRun + 928
16  CoreFoundation                    0x1819ccd10 CFRunLoopRunSpecific + 384
17  GraphicsServices                  0x1832b4088 GSEventRunModal + 180
18  UIKit                             0x186ca1f70 UIApplicationMain + 204
19  kidneyUser                        0x1002c71e8 0x1000d8000 + 2028008
20  libdyld.dylib                     0x18156a8b8 start + 4


Global Trace Buffer (reverse chronological seconds):
2.434148     AppleJPEG                     0x000000018354ea88 [0x12e203600] Releasing session



Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib            0x000000018168811c __pthread_kill + 8
1   libsystem_pthread.dylib           0x0000000181754ef8 pthread_kill + 112
2   libsystem_c.dylib                 0x00000001815f9dac abort + 140
3   libc++abi.dylib                   0x000000018112d3f4 __cxa_bad_cast + 0
4   libc++abi.dylib                   0x0000000181149e98 default_unexpected_handler() + 0
5   libobjc.A.dylib                   0x0000000181154248 _objc_terminate() + 124
6   libc++abi.dylib                   0x0000000181146f44 std::__terminate(void (*)()) + 16
7   libc++abi.dylib                   0x0000000181146b10 __cxa_rethrow + 144
8   libobjc.A.dylib                   0x0000000181154120 objc_exception_rethrow + 44
9   CoreFoundation                    0x00000001819ccdb8 CFRunLoopRunSpecific + 552
10  GraphicsServices                  0x00000001832b4088 GSEventRunModal + 180
11  UIKit                             0x0000000186ca1f70 UIApplicationMain + 204
12  kidneyUser                        0x00000001002c71e8 0x1000d8000 + 2028008

13  libdyld.dylib                     0x000000018156a8b8 start + 4

以上就是Crash Log 文件的信息(设备的信息, crash信息,异常信息, 线程信息)


1. 设备信息
Incident Identifier: AF4F2C83-8F68-47EF-B5AA-F16B067B5DF4   // crash的ID
CrashReporter Key:   5670de85ee1f0f3c904891536e81ec086ed4b35b   // crash 的设备ID
Hardware Model:      iPhone8,1   // 手机的型号 (iPhone8,1代表iPhone6s  8,2 代表iPhone6s Plus)
Process:             kidneyUser [896]   // App的名称 (该App的进程ID)
Path:                /private/var/containers/Bundle/Application/48C71AA1-EB99-49B1-ABD7-2903DBA8E394/kidneyUser.app/kidneyUser         // APP 的位置 路径
Identifier:          kidneyDiseaseHospitalUser // bundle ID
Version:             1 (1.0)   // APP的版本号
Code Type:           ARM-64 (Native) // app的应用架构
Parent Process:      launchd [1]

Date/Time:           2016-05-05 10:45:43.43 +0800      // crash发生的时间
Launch Time:         2016-05-05 10:42:07.07 +0800    // 进入应用的时间
OS Version:          iOS 9.3.1 (13E238)    // iOS系统的版本
Report Version:      105


2.异常信息
Exception Type:  EXC_CRASH (SIGABRT)   // 异常的类型
Exception Codes: 0x0000000000000000, 0x0000000000000000  // 异常出错的代码
Exception Note:  EXC_CORPSE_NOTIFY  // 异常通知
Triggered by Thread:  0 // 异常发生的线程(0代表主线程, 其他为主线程)

补充常见的Exception Codes代码类型

Exception Codes:   常见代码有以下几种
                             0x8badf00d错误码:Watchdog超时,意为“ate bad food”。
                             0xdeadfa11错误码:用户强制退出,意为“dead fall”。
                             0xbaaaaaad错误码:用户按住Home键和音量键,获取当前内存状态,不代表崩溃。
                             0xbad22222错误码:VoIP应用(因为太频繁?)被iOS干掉。
                             0xc00010ff错误码:因为太烫了被干掉,意为“cool off”。
                             0xdead10cc错误码:因为在后台时仍然占据系统资源(比如通讯录)被干掉,意为“dead lock”

补充常见的Exception Type异常类型的信息:

1.EXC_BAD_ACCESS:此类型是最常见的crash, 通常用于访问了不该访问的内存导致的,一般     EXC_BAD_ACCESS后面的()还会带有补充信息

SIGSEGV:通常由于重复释放对象导致, 一般在ARC以后很少见到

SIGABRT: 收到Abort信号退出, 通常Foundtion库中的容器为了保护状态正常会做一些检测, 例如插入nil到数据中等会遇到此类错误.

野指针错误形式在Xcode中通常表现为:Thread 1:EXC_BAD_ACCESS(code=EXC_I386_GPFLT)错误。因为你访问了一块已经不属于你的内存。

SEGV(Segmentation Violation): 代表无效内存地址, 比如空指针, 未初始化指针, 栈溢出等.

SIGBUS:总栈错误, 与SIGSEGV不同的是, SIGSEGV访问的是无效的地址, 而SIGBUS访问的是有效的地址, 但是总栈访问异常(如地址对齐问题)

SIGILL: 尝试执行非法的指令, 可能不被识别或者没有权限

SIGFPE: 数学计算相关问题, 比如除零操作

SIGIPIPE: 管道另一端没有进程接手数据

2. EXC_BAD_INSTRUCTION:此类异常通常由于线程执行非法指令导致
3. EXC_ARITHMETIC:除零错误会抛出此类异常
                                                   

Last Exception Backtrace: 最后异常回溯, 一般根据这个代码就能找到具体的crash问题

下面截取的是微信的crash blog

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   libsystem_kernel.dylib            0x000000018223ff24 __psynch_cvwait + 8
1   libsystem_pthread.dylib           0x000000018230ad20 _pthread_cond_wait + 704
2   Foundation                        0x0000000182f9fdf0 -[NSCondition waitUntilDate:] + 344
3   Foundation                        0x0000000182f9ce34 -[NSConditionLock lockWhenCondition:beforeDate:] + 256
4   UIKit                             0x000000018781dbc4 -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] + 196
5   UIKit                             0x0000000187c05878 -[UIKeyboardImpl setKeyboardInputMode:userInitiated:] + 112
6   UIKit                             0x0000000187c0de44 -[UIKeyboardImpl recomputeActiveInputModesWithExtensions:] + 336
7   UIKit                             0x000000018781e8f0 -[UIKeyboardImpl setDelegate:force:] + 2292
8   UIKit                             0x0000000187817eb0 -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:] + 1180
9   UIKit                             0x00000001878179e4 -[UIResponder(UIResponderInputViewAdditions) reloadInputViews] + 80
10  UIKit                             0x0000000187879670 -[UIResponder becomeFirstResponder] + 600
11  UIKit                             0x0000000187879a1c -[UIView(Hierarchy) becomeFirstResponder] + 148
12  UIKit                             0x0000000187900b34 -[UITextField becomeFirstResponder] + 64
13  UIKit                             0x00000001879b1fe4 -[UITextInteractionAssistant(UITextInteractionAssistant_Internal) setFirstResponderIfNecessary] + 256
14  UIKit                             0x00000001879b1498 -

我们可以看到发生Crash的线程的Crash调用栈, 从上到下分别代表调用顺序, 最上面的一个表示抛出异常的位置, 一次往下可以看到API调用顺序, 上图的信息表明本次Crash出现在[NSCondition waitUntilDate:]这个方法中(后面加的数值 我猜应该是地址偏移量O(∩_∩)O) 大概可以找到crash的具体原因(某个文件中的某个方法), 这样问题就浮出水面了, 方便产品上线后版本迭代, 修改BUG.