在日常开发中,我们难免遇到崩溃.如果是在开发过程中,我们可以直接通过 Xcode 来找到问题所在。如果是在测试的时候崩溃,我们也可以轻易的通过测试机导出 crash 进行问题定位。 可是一旦当产品上线之后,这一切就变得不那么容易了。庆幸的是市场上已经出现了一些比较好的第三方 crash 统计服务比如 bugly。 可以让我轻松了解到crash时的堆栈信息。 但是bugly也有符号化不到位的情况, 比如:

image
那么在这种情况下我们应该怎么办呢?

崩溃日志符号化

使用xcode符号化

条件: (需要崩溃手机)

如果发生崩溃的手机在手边.可以连上电脑 在 Xcode 中的 organizer>device 中直接查看符号化好的日志.
优点: 操作简单
缺点: 如果崩溃日志较多 还需要崩溃时间才能确定是哪个crash文件. crash不好统计

使用symbolicatecrash符号化

条件: crash文件 dsYM 文件 symbolicatecrash
symbolicatecrash 位置

1
(/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash)

方法: 将三个文件放在同级下 然后打开终端 进入文件夹目录下

  1. 设置环境变量
1
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
  1. 符号化
1
2
### symbol.crash 就是最后符号化好的文件 ###
../symbolicatecrash ../.crash ../.dsym > ../symbol.crash

优点:操作简单
缺点:适合针对单个crash 文件符号化

使用 atos 命令符号化

条件: dSYM 和堆栈信息
使用 grep 命令 获取该模块的 指令集和加载起始地址

1
grep "name armv" ../*.crash

然后用获得的指令集(armv7) 和起始地址(0x4000) 来符号化

1
2
3
4
5
xcrun atos -o ../.dsYM/../name -l 0x4000(起始地址) -arch -armv7(指令集)
//输入目标地址
0x00352aee
//得到结果
-[UIScrollView(UITouch) touchesEnded:withEvent:] (in appName) (UIScrollView+UITouch.h:26)

注意 模块只能定位到本模块的位置 不能越层 如:
AFN.framework.dSYM 只能定位到AFN内的目标地址位置
appName.app.dSYM 只能定位到app内代码的地址位置

优点: 只需要堆栈信息 和符号表就可以. 可以在获取不到Crash文件 但在bugly等第三方统计中获得了堆栈信息的情况下使用.
缺点: 一次只能符号化一行,比较繁琐.(可以作为bugly符号化不完全情况下的补充.)

有兴趣的可以实际动手操作下:
Xcode 自带 atos 脚本: symbolicatecrash 下载
crash 文件 : demoAPP.crash 下载
符号表: demoAPP.app.dSYM 下载
第三方模块符号表 RBPlayer.framework.dSYM 下载

crash文件组成分析

让我们看一下一个实际的 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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
### 1.进程信息 ###
Incident Identifier: CDDADF33-6F8A-44B3-90D1-52D9095ADA0C
CrashReporter Key: ed6929203307e230288ce2a758bd7099d3a52e15
Hardware Model: iPhone5,2
Process: demoApp [9806]
Path: /private/var/containers/Bundle/Application/0C5CCF0C-6A7F-4864-BD5A-0765725E0CCB/demoApp.app/demoApp
Identifier: com.yimiao100.demoApp
Version: 6 (1.6)
Code Type: ARM (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.yimiao100.demoApp [3588]
### 2.基本信息 ###
Date/Time: 2017-05-10 10:37:51.2003 +0800
Launch Time: 2017-05-10 10:36:26.0000 +0800
OS Version: iPhone OS 10.3.1 (14E304)
Report Version: 104
### 3.异常信息 ###
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:
(0x1ceefb38 0x1c177062 0x1ceefa80 0x1fd6f826 0x1fd6f9de 0x1fd70044 0x148a244 0x14813e4 0x1ce9bdb4 0x1ce9b6f4 0x1ce9b4dc 0x1cef6304 0x1cdff030 0x1d74c0a8 0x1d750b26 0x147f440 0x237bff76 0x237c0622 0x237c1644 0x1c5bd792 0x1c5bd77e 0x1c5c1d00 0x1ceabd64 0x1cea9e14 0x1cdfd0ea 0x1cdfcf0c 0x1e5a7b3c 0x22181e7e 0x10b2a8 0x1c5ea4e6)
### 4.线程回溯 ###
Thread 0 name: Dispatch queue: com.apple.main-thread
### Crash调用堆栈 ###
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x1c6bdacc 0x1c6a8000 + 88780
1 libsystem_pthread.dylib 0x1c7760f6 0x1c771000 + 20726
2 libsystem_c.dylib 0x1c65295a 0x1c608000 + 305498
3 libc++abi.dylib 0x1c157708 0x1c156000 + 5896
4 libc++abi.dylib 0x1c16e552 0x1c156000 + 99666
5 libobjc.A.dylib 0x1c17731e 0x1c170000 + 29470
6 demoApp 0x005b6e78 0x44000 + 5713528
7 libc++abi.dylib 0x1c16b98e 0x1c156000 + 88462
8 libc++abi.dylib 0x1c16b1a2 0x1c156000 + 86434
9 libobjc.A.dylib 0x1c177138 0x1c170000 + 28984
10 CoreFoundation 0x1ceefa84 0x1cdf5000 + 1026692
11 QuartzCore 0x1fd6f82a 0x1fc7c000 + 997418
12 QuartzCore 0x1fd6f9e2 0x1fc7c000 + 997858
13 QuartzCore 0x1fd70048 0x1fc7c000 + 999496
14 RBPlayer 0x0148a248 0x1477000 + 78408
15 RBPlayer 0x014813e8 0x1477000 + 41960
16 CoreFoundation 0x1ce9bdb8 0x1cdf5000 + 683448
17 CoreFoundation 0x1ce9b6f8 0x1cdf5000 + 681720
18 CoreFoundation 0x1ce9b4e0 0x1cdf5000 + 681184
19 CoreFoundation 0x1cef6308 0x1cdf5000 + 1053448
20 CoreFoundation 0x1cdff034 0x1cdf5000 + 41012
21 Foundation 0x1d74c0ac 0x1d746000 + 24748
22 Foundation 0x1d750b2a 0x1d746000 + 43818
23 RBPlayer 0x0147f444 0x1477000 + 33860
24 AVFoundation 0x237bff7a 0x23759000 + 421754
25 AVFoundation 0x237c0626 0x23759000 + 423462
26 AVFoundation 0x237c1648 0x23759000 + 427592
27 libdispatch.dylib 0x1c5bd796 0x1c5bc000 + 6038
28 libdispatch.dylib 0x1c5bd782 0x1c5bc000 + 6018
29 libdispatch.dylib 0x1c5c1d04 0x1c5bc000 + 23812
30 CoreFoundation 0x1ceabd68 0x1cdf5000 + 748904
31 CoreFoundation 0x1cea9e18 0x1cdf5000 + 740888
32 CoreFoundation 0x1cdfd0ee 0x1cdf5000 + 33006
33 CoreFoundation 0x1cdfcf10 0x1cdf5000 + 32528
34 GraphicsServices 0x1e5a7b40 0x1e59e000 + 39744
35 UIKit 0x22181e82 0x22110000 + 466562
36 demoApp 0x0010b2ac 0x44000 + 815788
37 libdyld.dylib 0x1c5ea4ea 0x1c5e7000 + 13546
### 其他线程 ###
Thread 1 name: Dispatch queue: com.apple.root.default-qos
Thread 1:
0 libsystem_kernel.dylib 0x1c6bde7c 0x1c6a8000 + 89724
1 libsystem_c.dylib 0x1c6120e8 0x1c608000 + 41192
2 Foundation 0x1d834c36 0x1d746000 + 977974
3 demoApp 0x00228310 0x44000 + 1983248
4 libdispatch.dylib 0x1c5bd796 0x1c5bc000 + 6038
5 libdispatch.dylib 0x1c5cab1c 0x1c5bc000 + 60188
6 libdispatch.dylib 0x1c5cc1b4 0x1c5bc000 + 65972
7 libdispatch.dylib 0x1c5cc00e 0x1c5bc000 + 65550
8 libsystem_pthread.dylib 0x1c7728ec 0x1c771000 + 6380
9 libsystem_pthread.dylib 0x1c7724cc 0x1c771000 + 5324
Thread 2 name: com.apple.uikit.eventfetch-thread
Thread 2:
0 libsystem_kernel.dylib 0x1c6a8900 0x1c6a8000 + 2304
1 libsystem_kernel.dylib 0x1c6a86e0 0x1c6a8000 + 1760
2 CoreFoundation 0x1ceabbe2 0x1cdf5000 + 748514
3 CoreFoundation 0x1ceaa064 0x1cdf5000 + 741476
...
Thread 3 name: com.apple.NSURLConnectionLoader
Thread 3:
0 libsystem_kernel.dylib 0x1c6a8900 0x1c6a8000 + 2304
1 libsystem_kernel.dylib 0x1c6a86e0 0x1c6a8000 + 1760
2 CoreFoundation 0x1ceabbe2 0x1cdf5000 + 748514
3 CoreFoundation 0x1ceaa064 0x1cdf5000 + 741476
4 CoreFoundation 0x1cdfd0ee 0x1cdf5000 + 33006
...
... ...
Thread 0 crashed with ARM Thread State (32-bit):
r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x0000006e
r4: 0x00000006 r5: 0x3a697e40 r6: 0x397d430c r7: 0x00c88044
r8: 0x00000002 r9: 0x00000000 r10: 0x40000000 r11: 0x158ee214
ip: 0x00000148 sp: 0x00c88038 lr: 0x1c7760f7 pc: 0x1c6bdacc
cpsr: 0x00000010
### 5.动态库信息 ###
Binary Images:
0x44000 - 0x85ffff demoApp armv7 <4d8140e3978f35d29c938c2659aa766d> /var/containers/Bundle/Application/0C5CCF0C-6A7F-4864-BD5A-0765725E0CCB/demoApp.app/demoApp
0xa4d000 - 0xa78fff AFNetworking armv7 <f0d55a034b7f3eb7b90a1214a8639f1a> /var/containers/Bundle/Application/0C5CCF0C-6A7F-4864-BD5A-0765725E0CCB/demoApp.app/Frameworks/AFNetworking.framework/AFNetworking
0x1477000 - 0x1496fff RBPlayer armv7 <f03427c9cf4e32ddae455c67d3a157ac> /var/containers/Bundle/Application/0C5CCF0C-6A7F-4864-BD5A-0765725E0CCB/demoApp.app/Frameworks/RBPlayer.framework/RBPlayer
EOF

进程信息

第一部分是闪退进程的相关信息。

Incident Identifier
是崩溃报告的唯一标识符。

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

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

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

基本信息

Version
APP的版本号

OS Version
iOS操作系统版本号 iPhone OS 10.3.1 (14E304)
10.3.1:系统版本
14E304:build 号
这里要所以下 build 号。每个系统版本号有可能会对应多个 build 号。如苹果发布的 10.3.1 会有几个版本,如:电信版本、联通版本等。build 号我们后面对日志符号化的时候会用到。

异常信息

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

Crashed Thread
crash 线程号。可以根据这个编号找到对应的 crash 调用堆栈,当前crash 线程的编号为 0,所以我们可以直接找到 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
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x1c6bdacc 0x1c6a8000 + 88780
1 libsystem_pthread.dylib 0x1c7760f6 0x1c771000 + 20726
2 libsystem_c.dylib 0x1c65295a 0x1c608000 + 305498
3 libc++abi.dylib 0x1c157708 0x1c156000 + 5896
4 libc++abi.dylib 0x1c16e552 0x1c156000 + 99666
5 libobjc.A.dylib 0x1c17731e 0x1c170000 + 29470
6 demoApp 0x005b6e78 0x44000 + 5713528
7 libc++abi.dylib 0x1c16b98e 0x1c156000 + 88462
8 libc++abi.dylib 0x1c16b1a2 0x1c156000 + 86434
9 libobjc.A.dylib 0x1c177138 0x1c170000 + 28984
10 CoreFoundation 0x1ceefa84 0x1cdf5000 + 1026692
11 QuartzCore 0x1fd6f82a 0x1fc7c000 + 997418
12 QuartzCore 0x1fd6f9e2 0x1fc7c000 + 997858
13 QuartzCore 0x1fd70048 0x1fc7c000 + 999496
14 RBPlayer 0x0148a248 0x1477000 + 78408
15 RBPlayer 0x014813e8 0x1477000 + 41960
16 CoreFoundation 0x1ce9bdb8 0x1cdf5000 + 683448
17 CoreFoundation 0x1ce9b6f8 0x1cdf5000 + 681720
18 CoreFoundation 0x1ce9b4e0 0x1cdf5000 + 681184
19 CoreFoundation 0x1cef6308 0x1cdf5000 + 1053448
20 CoreFoundation 0x1cdff034 0x1cdf5000 + 41012
21 Foundation 0x1d74c0ac 0x1d746000 + 24748
22 Foundation 0x1d750b2a 0x1d746000 + 43818
23 RBPlayer 0x0147f444 0x1477000 + 33860
24 AVFoundation 0x237bff7a 0x23759000 + 421754
25 AVFoundation 0x237c0626 0x23759000 + 423462
26 AVFoundation 0x237c1648 0x23759000 + 427592
27 libdispatch.dylib 0x1c5bd796 0x1c5bc000 + 6038
28 libdispatch.dylib 0x1c5bd782 0x1c5bc000 + 6018
29 libdispatch.dylib 0x1c5c1d04 0x1c5bc000 + 23812
30 CoreFoundation 0x1ceabd68 0x1cdf5000 + 748904
31 CoreFoundation 0x1cea9e18 0x1cdf5000 + 740888
32 CoreFoundation 0x1cdfd0ee 0x1cdf5000 + 33006
33 CoreFoundation 0x1cdfcf10 0x1cdf5000 + 32528
34 GraphicsServices 0x1e5a7b40 0x1e59e000 + 39744
35 UIKit 0x22181e82 0x22110000 + 466562
36 demoApp 0x0010b2ac 0x44000 + 815788
37 libdyld.dylib 0x1c5ea4ea 0x1c5e7000 + 13546

线程回溯

线程回溯这部分提供应用中所有线程的回溯日志。 回溯是闪退发生时所有活动帧清单。它包含闪退发生时调用函数的清单。
Crash 调用堆栈
这一部分是我们分析 crash 最重要的信息。一般我们会把焦点放在 crash 线程的堆栈上。因为这样可以帮我最快的找到 crash 的原因。

看下面这行日志:

1
6 demoApp 0x005b6e78 0x44000 + 5713528

这条调用栈包括下面四部分:

  1. 模块号:这里是 6
  2. 二进制库名:这里是 demoApp
  3. 调用方法的地址:这里是 0x005b6e78
  4. 第四部分分为两列,基地址和偏移地址。此处基地址为 0x66000,偏移地址为 19244367。基地址指向 crash 的模块(也是模块的 load 地址)如 UIKit。偏移地址指向 crash 代码的行数。如何转换我们后面讨论。这些信息都保存在 dsym 文件中。

动态库信息

这些信息包括动态库名称、UUID、模块起始地址、模块结束地址、指令集种类、安装路径等信息。
这些信息都是在符号化堆栈用到的。后面我们讨论怎么用。

特别注意

注意: 你必需同时保留应用二进制文件和 .dSYM 文件才能将崩溃日志完整符号化。每次提交到 iTunes Connect 的构建都必需归档保存。
.dSYM 文件和二进制文件是特定绑定于每一次构建和后续构建的,即使来自相同的源代码文件,每一次构建也与其他构建不同,不能相互替换。
如果你使用 Build 和 Archive 命令,这些文件会自动放在适当位置。 如果不是使用 Build 和 Archive 命令,最好放到单独的文件夹保存。

附录

符号化好的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
Last Exception Backtrace:
0 CoreFoundation 0x1ceefb38 __exceptionPreprocess + 124
1 libobjc.A.dylib 0x1c177062 objc_exception_throw + 34
2 CoreFoundation 0x1ceefa80 +[NSException raise:format:] + 104
3 QuartzCore 0x1fd6f826 CA::Layer::set_position(CA::Vec2<double> const&, bool) + 234
4 QuartzCore 0x1fd6f9de -[CALayer setPosition:] + 50
5 QuartzCore 0x1fd70044 -[CALayer setFrame:] + 484
6 RBPlayer 0x01483244 -[RBPlayerSlider setValue:] (RBPlayerSlider.m:194)
7 RBPlayer 0x0147a3e4 -[RBPlayerBottomMask playerUpdateCurrentSeconds] (RBPlayerBottomMask.m:111)
8 CoreFoundation 0x1ce9bdb4 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 6
9 CoreFoundation 0x1ce9b6f4 _CFXRegistrationPost + 378
10 CoreFoundation 0x1ce9b4dc ___CFXNotificationPost_block_invoke + 36
11 CoreFoundation 0x1cef6304 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1238
12 CoreFoundation 0x1cdff030 _CFXNotificationPost + 536
13 Foundation 0x1d74c0a8 -[NSNotificationCenter postNotificationName:object:userInfo:] + 62
14 Foundation 0x1d750b26 -[NSNotificationCenter postNotificationName:object:] + 26
15 RBPlayer 0x01478440 __32-[RBCorePlayer addTimerObserver]_block_invoke (RBCorePlayer.m:243)
16 AVFoundation 0x237bff76 -[AVPeriodicTimebaseObserver _fireBlockForTime:] + 64
17 AVFoundation 0x237c0622 -[AVPeriodicTimebaseObserver _handleTimeDiscontinuity] + 234
18 AVFoundation 0x237c1644 __AVTimebaseObserver_timebaseNotificationCallback_block_invoke + 122
19 libdispatch.dylib 0x1c5bd792 _dispatch_call_block_and_release + 6
20 libdispatch.dylib 0x1c5bd77e _dispatch_client_callout + 18
21 libdispatch.dylib 0x1c5c1d00 _dispatch_main_queue_callback_4CF + 898
22 CoreFoundation 0x1ceabd64 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 4
23 CoreFoundation 0x1cea9e14 __CFRunLoopRun + 844
24 CoreFoundation 0x1cdfd0ea CFRunLoopRunSpecific + 466
25 CoreFoundation 0x1cdfcf0c CFRunLoopRunInMode + 100
26 GraphicsServices 0x1e5a7b3c GSEventRunModal + 76
27 UIKit 0x22181e7e UIApplicationMain + 146
28 VaccineSale 0x001042a8 main (main.m:15)
29 libdyld.dylib 0x1c5ea4e6 _dyld_process_info_notify_release + 26

从符号化完的堆栈信息可以清楚的看出 崩溃是由 RBPlayer 类 RBPlayerSlider 文件 setValue: 方法中 第 194 行引起的. 该行对 layer 的 frame 和 position 进行赋值操作时引起了崩溃.

–原创所有,转载请注明出处。