carsh日志及解析

14年记录于有道笔记的老文章,有时间再修改。


什么是崩溃日志?

一个App启动之后,用着用着就突然被iOS系统关闭,或者干脆就起不来,在打开的一瞬间关闭,这就是Crash,俗称“闪退”“崩溃”。

iOS设备上的应用闪退时,操作系统会生成一个扩展名为crash的日志文件,这个文件会保存的特定系统目录下。

崩溃日志上有很多有用的信息,上面有每个正在执行线程的完整堆栈跟踪信息,所以你能从中了解到闪退发生时各线程都在做什么,并分辨出闪退发生在哪个线程上和相关的原因。

从哪里能得到它?

  • iTunes

设备与电脑上的iTunes Store同步后,会将崩溃日志保存在电脑上。根据电脑操作系统的不同,崩溃日志将保存在以下位置:

  1. Mac OSX:~/Library/Logs/CrashReporter/MobileDevice/

  2. Windows XP: C:Documents and SettingsApplication DataApple ComputerLogsCrashReporterMobileDevice

  3. Windows Vista or 7: C:UsersAppDataRoamingApple ComputerLogsCrashReporterMobileDevice

当用户抱怨闪退时,你可以要求他让设备与iTunes同步,并根据操作系统的不同,到上述位置把崩溃日志下载下来,然后通过电子邮件发送给你。

你必需尽量获取用户设备生成的所有崩溃日志。因为崩溃日志越多,就越容易诊断问题所在!

  • Xcode

将iOS设备连接到电脑上,然后打开Xcode。从菜单栏上选择 Window 菜单, 然后选择 Devices ,在左侧的导航面板上,点击View Device Logs查看设备的日志,然后查看你对应的appname的日志。

  • App Store

应用提交到App Store后,你也能从 iTunes Connect 获取到用户的崩溃日志. 登录到 iTunes Connect 上, 选择Manage Your Applications, 点击相应的应用, 点击应用图标下面的View Details 按钮, 然后点击右栏Links部分的Crash Reports

如果应用还卖得不多,或者刚上架不久,或者用户关闭了上传日志的选项,iTunes Connect账号上也可能还没有任何崩溃日志。

什么情况下会产生崩溃日志?

应用违反操作系统规则
  • 在启动、恢复、挂起、退出时watchdog超时

从iOS 4.x开始,退出应用时,应用不会立即终止,而是退到后台。但是,如果你的应用响应不够快,操作系统有可能会终止你的应用,并产生一个崩溃日志。这些事件与下列UIApplicationDelegate方法相对应:
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationDidBecomeActive:
applicationWillTerminate:

上面所有这些方法,应用只有有限的时间去完成处理。如果花费时间太长,操作系统将终止应用。

注意: 如果你没有把需要花费时间比较长的操作(如网络访问)放在后台线程上就很容易发生这种情况。

  • 用户强制退出

iOS 4.x开始支持多任务。如果应用阻塞界面并停止响应,用户可以通过在主屏幕上双击Home按钮来终止应用。此时,操作应用将生成一个崩溃日志。

注意: 双击Home按钮后,你将看到运行过的所有应用。那些应用不一定是正在运行,也不一定是被挂起。通常,用户点击Home按钮时,应用将在后台保留约10分钟,然后操作系统自动将其终止。 所以双击Home按钮显示的应用列表只是表明那是一系列过去打开过的应用。删除那些应用的图标不会产生任何崩溃日志。

  • 低内存终止

在前台运行的应用拥有访问和使用内存的最高优化级。然而,这并不意味着该应用能使用设备的所有可用内存 ——每个应用只能使用一部分可用内存。

当内存使用达到一定程度时,操作系统将发出一个 UIApplicationDidReceiveMemoryWarningNotification 通知。同时,调用 didReceiveMemoryWarning 方法。

此时,为了让应用继续正常运行,操作系统开始终止在后台的其他应用以释放一些内存。所有后台应用被终止后,如果你的应用还需要更多内存,操作系统会将你的应用也终止掉,并产生一个崩溃日志。而在这种情况下被终止的其他后台应用,不会产生崩溃日志。

应用中有Bug

大多数闪退都是由于应用中有Bug,比如调用用了Objective-C对象根本不支持的方法(发送消息),非法内存访问,数组越界,参数不符合要求等。

这些问题在调试阶段,我们都可以很容易的通过断点和console中提供的信息快速定位并解决。

但对于已发布的App,如果想重现并利用上述办法来解决,恐怕会比较费时费事。

最有帮助最直接的办法就是根据出现问题时的闪退日志,分析和判断crash的原因,快速准确的定位和解决。

崩溃日志示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**********进程信息**********/
Incident Identifier: 421FEFDC-E649-42E0-AC34-D3E2E3E5713D
CrashReporter Key: 963c64a27bb863f790c0e751f1991b29ab541923
Hardware Model: iPhone5,2
Process: LiuLianCustomer [6694]
Path: /private/var/mobile/Containers/Bundle/Application/ACFACAB0-E67F-48A6-BBF1-6BE0209D6F1D/LiuLianCustomer.app/LiuLianCustomer
Identifier: com.LLWF.Customer.RC
Version: 1936 (0.0.1)
Code Type: ARM (Native)
Parent Process: launchd [1]
/**********基本信息**********/
Date/Time: 2015-02-11 18:44:04.610 +0800
Launch Time: 2015-02-11 18:30:28.148 +0800
OS Version: iOS 8.1.2 (12B440)
Report Version: 105
/**********异常信息**********/
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread: 0
/**********线程回溯**********/
Thread 0 name: Dispatch queue: com.apple.main-thread
/**********堆栈信息**********/
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x36111dfc __pthread_kill + 8
1 libsystem_pthread.dylib 0x3618fd32 pthread_kill + 58
2 libsystem_c.dylib 0x360b1904 abort + 72
3 libc++abi.dylib 0x353ef9c4 abort_message + 84
4 libc++abi.dylib 0x3540966c default_terminate_handler() + 264
5 libobjc.A.dylib 0x35acbf22 _objc_terminate() + 190
6 libc++abi.dylib 0x35406de0 std::__terminate(void (*)()) + 76
7 libc++abi.dylib 0x354068aa __cxa_rethrow + 98
8 libobjc.A.dylib 0x35acbdce objc_exception_rethrow + 38
9 CoreFoundation 0x28227458 CFRunLoopRunSpecific + 628
10 CoreFoundation 0x282271ce CFRunLoopRunInMode + 102
11 GraphicsServices 0x2f6250a4 GSEventRunModal + 132
12 UIKit 0x2b836f9c UIApplicationMain + 1436
13 LiuLianCustomer 0x000dbfe6 0x7f000 + 380902
14 libdyld.dylib 0x3604baac start + 0
/**********动态库信息**********/
Binary Images:
0x7f000 - 0x2b6fff LiuLianCustomer armv7 <20c3e1f8927c3f008d675262a64594ba> /var/mobile/Containers/Bundle/Application/ACFACAB0-E67F-48A6-BBF1-6BE0209D6F1D/LiuLianCustomer.app/LiuLianCustomer
0x1fefb000 - 0x1ff1efff dyld armv7s <8ffd813a380c333bbd4a25e1dbe05715> /usr/lib/dyld
0x26955000 - 0x2695dfff AccessibilitySettingsLoader armv7s <eb1b9f48c19f37fc95ef2e98c0af1cb4> /System/Library/AccessibilityBundles/AccessibilitySettingsLoader.bundle/AccessibilitySettingsLoader
0x26dab000 - 0x26f17fff AVFoundation armv7s <23a20af23f733b39b5c87059c080f9f7> /System/Library/Frameworks/AVFoundation.framework/AVFoundation
0x26f18000 - 0x26f76fff libAVFAudio.dylib armv7s <e3f677adf77d37baa08716b5d334486f> /System/Library/Frameworks/AVFoundation.framework/libAVFAudio.dylib
0x26fb0000 - 0x26fb0fff Accelerate armv7s <3c10ee15d8363fa58b719f2abca91b06> /System/Library/Frameworks/Accelerate.framework/Accelerate
进程信息
  1. Incident Identifier 是崩溃报告的唯一标识符。

  2. CrashReporter Key 是与设备标识相对应的唯一键值。虽然它不是真正的设备标识符,但也是一个非常有用的情报:如果你看到100个崩溃日志的CrashReporter Key值都是相同的,或者只有少数几个不同的CrashReport值,说明这不是一个普遍的问题,只发生在一个或少数几个设备上。

  3. Hardware Model 是标识设备类型。 如果很多崩溃日志都是来自相同的设备类型,说明应用只在某特定类型的设备上有问题。上面的日志里,崩溃日志产生的设备是iPhone 4s。

  4. Process 是应用名称。中括号里面的数字是闪退时应用的进程ID。

  1. Identifier 是应用标识符,即bundle identifier。

  2. Version 是应用版本信息,前面是build code,后面是发布版本号。

基本信息

这部分给出了一些基本信息,包括闪退发生的日期和时间,设备的iOS版本。如果有很多崩溃日志都来自iOS 6.0,说明问题只发生在iOS 6.0上。

异常信息

在这部分,你可以看到闪退发生时抛出的异常类型。还能看到异常编码和抛出异常的线程。根据崩溃报告类型的不同,在这部分你还能看到一些另外的信息。

线程回溯

这部分提供应用中所有线程的回溯日志。 回溯是闪退发生时所有活动帧清单。它包含闪退发生时调用函数的清单。看下面这行日志:

1
13 LiuLianCustomer 0x000dbfe6 0x7f000 + 380902

它包括四列:

  1. 模块号:这里是13
  2. 二进制库名:这里是LiuLianCustomer
  3. 调用方法的地址:这里是0x000dbfe6
  4. 第四部分分为两列,基地址和偏移地址。此处基地址为0x7f000,偏移地址为380902。基地址指向crash的模块(也是模块的load地址)如UIKit。偏移地址指向crash代码的行数。
动态库信息

这些信息包括动态库名称、UUID、模块起始地址、模块结束地址、指令集种类、安装路径等信息。

符号化Symbolication

第一次看到崩溃日志上的回溯时,你或许会觉得它没什么意义。我们习惯使用方法名和行数,而非像这样的神秘位置:

1
13 SFtest 0x000ce3d6 0xc7000 + 29654

将这些十六进制地址转化成方法名称和行数的过程称之为符号化。

  • Xcode

从Xcode获取崩溃日志后过几秒钟,崩溃日志将被自动符号化。上面那行被符号化后的版本如下 :

1
2
2 CoreFoundation 0x25dcec30 -[__NSArrayI objectAtIndex:] + 176
3 SFtest 0x000133c4 -[MainViewController viewDidLoad] (MainViewController.m:79)

Xcode符号化崩溃日志时,需要访问与App Store上对应的应用二进制文件以及生成二进制文件时产生的 .dSYM 文件,必需完全匹配才行,否则,日志将无法被完全符号化。所以,保留每个分发给用户的编译版本非常重要。

注意: 你必需同时保留应用二进制文件和.dSYM文件才能将崩溃日志完整符号化。每次提交到iTunes Connect的构建都必需归档。.dSYM文件和二进制文件是特定绑定于每一次构建和后续构建的,即使来自相同的源代码文件,每一次构建也与其他构建不同,不能相互替换。


  • atos

atos就是address to symbol,把地址翻译成符号。是苹果提供的符号化工具,在Mac OS系统下默认安装。

终端下面执行以下命令

1
$ atos -arch cpu指令集种类 -o dysm文件路径 -l 模块load地址 调用方法的地址
  • dysm文件路径:可以在Xcode Organizer的Archives标签栏下找到所有已归档的应用文件。它保存了编译过程的详细信息,其中包括符号信息,路径为 appName.app.dSYM/Contents/Resources/DWARF/appName。
  • 模块load地址:模块加载的基地址,可以在日志的动态库信息中找到对应模块的基地址。
  • cpu指令集种类:可以为armv6 armv7 armv7s arm64。具体用哪个,可以参考对应模块的动态库信息中确定
  • 调用方法地址:想要符号化的地址

  • symbolicatecrash

symbolicatecrash是Xcode自带的一个分析工具,可以通过机器上的崩溃日志和应用的.dSYM文件定位发生崩溃的位置,把crash日志中的地址替换成代码相应位置。

使用方法:

软链接symbollicatecrash到/usr/bin/中,就可以直接使用sybollicatecrash命令

1
sudo ln -s /Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash /usr/bin/symbolicatecrash

设置xcode DEVELOPER_DIR,把下面的内容加入到.bash_profile中即可

1
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"

符号化崩溃日志(不需要.app文件,符号文件和crash文件也可以不在同一目录)

1
symbolicatecrash xxx.crash xxx.dSYM > xxx.crash

  • dwarfdump

使用dwarfdump验证xxx.crash、xxx.app和xxx.dSYM三者的uuid是否一致。

1
$ dwarfdump --uuid appName.app/appName
1
$ dwarfdump --uuid appName.app.dSYM/Contents/Resources/DWARF/appName

crash文件在动态链接库部分查看对应的UUID,,三者UUID一致才能符号化崩溃日志。

1
dwarfdump --lookup=0x25dcc458 --arch armv7 SFtest.app.dSYM
坚持原创技术分享,您的支持将鼓励我继续创作!