如果你有过分析iOS崩溃日志的经验,一定经常看到日志里出现很多<redacted>
的字段。这篇文章就是帮助开发者将这些字段符号化为对应的系统库方法名。
如果你已经掌握了这方面的知识,就直接去这里iOS-System-Symbols,下载我整理好的系统库符号文件吧。
符号化的作用
当获取到app的crash日志时,第一步就是将其符号化。作用是把日志堆栈中的方法调用显示出来,对于来自app内部的方法,还能直接给出方法对应.m文件的所在行。
符号化前(这里项目的Build Settings
里的Strip Style
设为了Debugging Symbols
,所以这里能直接看到MyApp
的方法名。更多和symbol相关的设置,请看这里):
Thread 7:
0 libsystem_kernel.dylib 0x000000018efb416c 0x18efb3000 + 4460 (mach_msg_trap + 8)
1 libsystem_kernel.dylib 0x000000018efb3fdc 0x18efb3000 + 4060 (mach_msg + 72)
2 MyApp 0x000000010091e558 0x1000bc000 + 8791384 (ksmachexc_i_handleExceptions + 148)
3 libsystem_pthread.dylib 0x000000018f097860 0x18f094000 + 14432 (<redacted> + 240)
4 libsystem_pthread.dylib 0x000000018f097770 0x18f094000 + 14192 (_pthread_start + 284)
符号化后:
Thread 7:
0 libsystem_kernel.dylib 0x000000018efb416c mach_msg_trap + 8
1 libsystem_kernel.dylib 0x000000018efb3fdc mach_msg + 72
2 MyApp 0x000000010091e558 ksmachexc_i_handleExceptions (KSCrashSentry_MachException.c:233)
3 libsystem_pthread.dylib 0x000000018f097860 _pthread_body + 240
4 libsystem_pthread.dylib 0x000000018f097770 _pthread_body + 0
可以发现,frame 3里libsystem_pthread.dylib
的<redacted>
变成了_pthread_body
,frame 2里MyApp
的ksmachexc_i_handleExceptions
变成了更为完整的ksmachexc_i_handleExceptions (KSCrashSentry_MachException.c:233)
,直接给出了方法及其所在文件和行数。
详细的符号化方法,请参考符号化iOS Crash文件的3种方法。这里就不重复了。
需要注意的是,很多时候,crash日志里并不会有MyApp的调用,而全都是系统库的调用:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x000000018b816f30 0x18b7fc000 + 110384 (objc_msgSend + 16)
1 UIKit 0x0000000192e0a79c 0x192c05000 + 2119580 (<redacted> + 72)
2 UIKit 0x0000000192c4db48 0x192c05000 + 297800 (<redacted> + 312)
3 UIKit 0x0000000192c4d988 0x192c05000 + 297352 (<redacted> + 160)
4 QuartzCore 0x00000001900d6404 0x18ffc5000 + 1119236 (<redacted> + 260)
5 libdispatch.dylib 0x000000018bc551c0 0x18bc54000 + 4544 (<redacted> + 16)
6 libdispatch.dylib 0x000000018bc59d6c 0x18bc54000 + 23916 (_dispatch_main_queue_callback_4CF + 1000)
7 CoreFoundation 0x000000018cd79f2c 0x18cc9d000 + 905004 (<redacted> + 12)
8 CoreFoundation 0x000000018cd77b18 0x18cc9d000 + 895768 (<redacted> + 1660)
9 CoreFoundation 0x000000018cca6048 0x18cc9d000 + 36936 (CFRunLoopRunSpecific + 444)
10 GraphicsServices 0x000000018e729198 0x18e71d000 + 49560 (GSEventRunModal + 180)
11 UIKit 0x0000000192c80628 0x192c05000 + 505384 (<redacted> + 684)
12 UIKit 0x0000000192c7b360 0x192c05000 + 484192 (UIApplicationMain + 208)
13 MyApp 0x0000000100016e54 0x100004000 + 77396 (_mh_execute_header + 77396)
14 libdyld.dylib 0x000000018bc885b8 0x18bc84000 + 17848 (<redacted> + 4)
这时候就必须符号化系统库了。符号化后的日志:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x000000018b816f30 objc_msgSend + 16
1 UIKit 0x0000000192e0a79c -[UISearchDisplayController _sendDelegateDidBeginDidEndSearch] + 72
2 UIKit 0x0000000192c4db48 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 312
3 UIKit 0x0000000192c4d988 -[UIViewAnimationState animationDidStop:finished:] + 160
4 QuartzCore 0x00000001900d6404 CA::Layer::run_animation_callbacks(void*) + 260
5 libdispatch.dylib 0x000000018bc551c0 _dispatch_client_callout + 16
6 libdispatch.dylib 0x000000018bc59d6c _dispatch_main_queue_callback_4CF + 1000
7 CoreFoundation 0x000000018cd79f2c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
8 CoreFoundation 0x000000018cd77b18 __CFRunLoopRun + 1660
9 CoreFoundation 0x000000018cca6048 CFRunLoopRunSpecific + 444
10 GraphicsServices 0x000000018e729198 GSEventRunModal + 180
11 UIKit 0x0000000192c80628 -[UIApplication _run] + 684
12 UIKit 0x0000000192c7b360 UIApplicationMain + 208
13 MyApp 0x0000000100016e54 main (main.m:15)
14 libdyld.dylib 0x000000018bc885b8 start + 4
可以看出是UISearchController
的delegate
导致的问题,检查一下就发现UISearchDisplayController
的delegate
是assign
的,是由于点击搜索条的同时pop了界面导致的crash。
从这里可以发现,符号化系统库是很有必要的。
如何符号化系统库
符号化自己app的方法名,需要编译ipa时生成的dySYM文件。而要将系统库的<redacted>
符号化为完整的方法名,也需要系统库的符号文件。
1. 匹配对应的符号文件版本
系统库符号文件不是通用的,而是对应crash所在设备的系统版本和CPU型号的。
crash日志中有这样两个信息:
Code Type: ARM-64
OS Version: iOS 10.2 (14C82)
Code Type
表示此设备的CPU为armv7
、armv7s
还是arm64
。
OS Version
表示此设备的系统版本号,括号中的字符串代表了此系统的build号。可以在这里查找build号:iOS SDK,iOS version history。
2. 将对应版本的符号文件放到指定目录
这时候,把获取到的对应版本的符号文件放到Mac的~/Library/Developer/Xcode/iOS DeviceSupport
目录下,再使用符号化iOS Crash文件的3种方法里提到的Xcode自带的符号化工具symbolicatecrash
进行符号化。这个工具会自动搜索系统库符号文件。
获取系统符号文件的4个方法
从真机上获取
大部分系统库符号文件只能从真机上获取,苹果也没有提供下载。
当你用Xcode第一次连接某台设备进行真机调试时,会看到Xcode显示Processing symbol files
,这时候就是在拷贝真机上的符号文件到Mac系统的/Users/xxx/Library/Developer/Xcode/iOS DeviceSupport
目录下。
目录下的10.2(14C82)
这样的文件夹就是对应的符号文件,通常都有1-3GB的大小,很占用空间,动不动就累积成3、40GB。很多讲清理Mac垃圾文件的教程都会说要删除这个目录下的文件,真是坑爹。正确做法是做成压缩包保存到外部硬盘里,需要符号化的时候再重新解压到此目录。
寻找苹果官方的下载地址
之前watch的调试出现bug时,有人找出过几个watch的符号文件下载地址。见No symbols for paired Apple Watch。
但是我尝试按照对应的命名格式,并没有找到iOS符号文件的下载地址。
从已解密的固件中提取符号文件
某些已经被破解的固件可以直接提取系统文件,但是未破解的固件(较新的固件和arm64
的固件),无法用这种方式获取。
固件解密步骤:
1.下载对应版本的.ipsw固件,直接解压,解压后里面有几个.dmg
格式的镜像文件,最大的.dmg
文件就是系统镜像。
2.从Firmware_Keys找到对应固件的解密key(页面上Root Filesystem
字段的key)。
3.用一个dmg
工具进行解密,下载地址。使用方式:cd
到解压后的ipsw文件夹,执行./dmg extract xxx-xxxx-xxx.dmg dec.dmg -k <key>
。extract
后面跟两个参数,分别是系统镜像dmg的名字和解密后的文件名,-k 后面填写第2步获取到的key,如果key不对,解密会失败。
4.等待。最终会生成一个dec.dmg
文件,双击打开即可加载系统镜像。
提取符号文件方法:
参考聊聊从iOS固件提取系统库符号中的二、系统库符号提取
部分。
update:目前大多数固件都能解密了,而且iOS 10之后苹果不再进行加密,因此之后都可以用这个方式获取符号文件。
下载旧版本Xcode,提取SDK
旧版本的Xcode里包含了对应的iPhoneSDK,可以从中获得符号文件。
路径是/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/
。里面的System/Library
里就可以看到framework,而且同时包含了armv7
,armv7s
,arm64
3个平台的版本。
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/version.plist
可以查看是哪个版本。把iPhoneOS.sdk
文件夹的名字改成对应的CFBundleVersion (ProductBuildVersion)
格式,然后在里面加一层Symbols
子文件夹,把System
,Library
,usr
都放进Symbols
里,就可以和其他符号文件一样使用了。
但是当iOS版本只包含了bug修复,而没有改变API,Xcode就不会有附带对应的SDK,还是需要从真机上获取。而且从Xcode7开始,苹果用tbd
文件代替了真机符号文件,所以这个方法也失效了。
参考:Xcode software image for user iOS in order to symbolicate iOS calls, Missing iOS symbols at “~/Library/Developer/Xcode/iOS DeviceSupport”。
获取符号文件的难题
这个时候,游戏就开始了:
- 很多时候crash日志只给出了系统的调用栈,不能直接定位到自己app的代码,因此需要符号化系统库。
- 用户的crash来自各种系统版本,需要对应版本的系统符号文件才能符号化。
- 系统库符号文件只能从真机上获取,苹果没有提供下载。
- 从
iOS 7.0(11A465)
到iOS 10.2(14C92)
一共有50个build版本,公司的测试机是不会覆盖到这么变态的完整度的。 - 同一个版本,有时候会给iPhone和iPad、甚至6和6s提供不同build的固件。
- 某些版本是某些机子的特供版,例如
10.0.3(14A551)
是iPhone 7和 7 Plus独有的,这就更加大了收集难度。 - 同一个iOS版本可能有多个build,例如
10.1(14B72)
和10.1(14B72c)
,苹果觉得更新幅度太小,就没有提升版本号。 - 除了build号的区别,符号文件在不同CPU平台上也有区别,意味着来自4s(
armv7
)和6s(arm64
)的符号文件,即便build号是一样的,也无法通用。所以50个build号又要翻倍,达到了88个,所以精确来说我只是收集了(63/88)的进度。幸运的是,arm64
机型的系统库里附带了armv7s
。
规则好厉害的收集游戏啊。收集品其实还有稀有度的区别,其中最厉害的应该是10.0
,这是iPhone 7和7 Plus独有的出厂系统,而且没有固件可以下载,因此即便有iPhone 7也不能通过刷机来得到10.0
。
其实我一直很奇怪为什么很少见到开发者抱怨找不到系统符号文件,从而召集大家进行收集并分享,猜测可能的原因是:
- 懒。遇到无法符号化的问题,没有去解决。
- 有些公司可能很早就开始对crash日志自动符号化了,因此很早就开始收集符号文件。只要一直跟着苹果的每一个版本升级,收集起来还是挺简单的。而这些资源,开发者并不会注意到可以共享出来。
- crash收集和符号化使用的是第三方服务,第三方平台会帮助符号化系统库。
但是我找了一下,没有找到一家明确声明了能够符号化所有系统库的第三方平台。从腾讯的Bugly论坛里也能发现,有些系统方法并没有符号化出来,系统库是缺失的。(update:找到了一个国外的平台,在stack overflow上说能符号化所有版本的系统库,但是是收费服务,我也没有测试)
在公司小组里,大部分时候都是我来分析crash日志,所以当遇到缺少系统符号文件的情况,就会十分无奈。很多时候,没有符号化的crash日志根本无法提供有用信息。
系统符号文件下载地址
暴力收集
收集不全一直让我很不爽。之前搜刮了组内的测试机,只收集到了有限的几个(感谢无私提供iPhone 7让我刷机降级的同事)。终于,这周末特意跑去了一趟二手机市场寻找旧版本的设备来拷贝,总算是收集得完整了一点,但是也花了我121块钱。
心情总算愉悦了。
其中大部分都是拷贝自arm64
设备的,armv7
的符号文件收集我是放弃了。有哪位大侠有兴趣把这个游戏玩通关的吗?还有 tvOS 和 watchOS的符号,我也放弃了。
(update:又花了些时间从Xcode的SDK和固件里提取了armv7s
和armv7
的固件,现在只剩下几个arm64
的版本了)。
整理了一下传到了网盘,地址见下方,有需要的去下载吧。
目前还缺少的符号文件
通过各种方式,目前已经收集得差不多了,只剩下最后一个10.0(14A346)
版本没有得到。如果哪位小伙伴有这个版本,可以分享一下。
分享可以直接贴下载地址,也可以提交到这个github项目iOS-System-Symbols。如果我得到了新的符号文件,也会在这个项目里更新。
缺失符号的版本 | 缺失的CPU版本 | 描述 |
---|---|---|
10.0(14A346) | arm64 | iPhone 7 和 7 Plus独占,出厂自带系统 |
网盘下载地址
下载地址请见这个项目:iOS-System-Symbols。如果我得到了新的符号文件,会在这个项目里更新。
我把里面的那几个dyld_shared_cache_xxxx
大文件单独拿出来了,目的是减小压缩包大小。如果只是符号化的话,用不到这几个文件,只是在真机调试的时候才需要。
符号文件版本列表
这些是我已经收集到的符号文件,包括了对应的CPU信息。iOS10系统开始不支持armv7
的机器。但是iOS9以下还是支持armv7
的。
查看是否带有对应CPU架构的符号文件的方式:到10.2 (14C92)/Symbols/System/Library/Caches/com.apple.dyld
这样的目录下,会有对应的dyld_shared_cache_arm64
,dyld_shared_cache_armv7s
,dyld_shared_cache_armv7
文件,如果缺失了,就说明对应的CPU架构符号文件还不存在。(经过试验,这几个大文件并不影响符号化,只是在真机调试的时候才有用,因此如果嫌太占用空间,可以删去)。
版本 | 已收集到的CPU版本 | 描述 |
---|---|---|
11.2.6 (15D100) | arm64 | |
11.2.5 (15D60) | arm64 | |
11.2.2 (15C202) | arm64 | |
11.2.1 (15C153) | arm64 | |
11.2 (15C114) | arm64 | |
11.2 (15C113) | none | 苹果在发布了15C114之后,又发布了一个低版本的15C113,之后又移除了15C113的下载地址,因此这个固件目前无法下载。应该只是误发布,可以忽略这个版本 |
11.1.2 (15B202) | arm64 | |
11.1.1 (15B150) | arm64 | |
11.1 (15B101) | arm64 | iPad Pro2 12.9-inch and iPad Pro 10.5-inch only |
11.1 (15B93) | arm64 | |
11.0.3 (15A432) | arm64 | |
11.0.2 (15A421) | arm64 | |
11.0.1 (15A403) | arm64 | iPhone 8 and 8 Plus only |
11.0.1 (15A402) | arm64 | |
11.0 (15A372) | arm64 | |
10.3.3 (14G60) | arm64,armv7s | |
10.3.2 (14F91) | arm64,armv7s | iPad mini4(Cellular) only |
10.3.2 (14F90) | arm64,armv7s | iPad (5th gen) only |
10.3.2 (14F89) | arm64,armv7s | |
10.3.1 (14E304) | arm64,armv7s | |
10.3 (14E277) | arm64,armv7s | |
10.2.1 (14D27) | arm64,armv7s | |
10.2 (14C92) | arm64,armv7s | |
10.1.1 (14B150) | arm64,armv7s | |
10.1.1 (14B100) | arm64,armv7s | |
10.1 (14B72c) | arm64,armv7s | |
10.1 (14B72) | arm64,armv7s | |
10.0.3 (14A551) | arm64,armv7s | |
10.0.2 (14A456) | arm64,armv7s | |
10.0.1 (14A403) | arm64,armv7s | |
10.0(14A346) | none | iPhone 7 和 7 Plus独占,出厂自带系统 |
9.3.5 (13G36) | arm64,armv7s,armv7 | |
9.3.4 (13G35) | arm64,armv7s,armv7 | |
9.3.3 (13G34) | arm64,armv7s,armv7 | |
9.3.2(13F72) | arm64,armv7s | iPad Pro 9.7寸独占,修复了变砖的问题 |
9.3.2 (13F69) | arm64,armv7s,armv7 | |
9.3.1 (13E238) | arm64,armv7s,armv7 | |
9.3(13E237) | armv7s,armv7 | 5s和更旧机型独占,修复了不能激活的问题 |
9.3(13E236) | armv7 | iPad2独占,修复了不能激活的问题 |
9.3(13E234) | arm64,armv7s | 6s, 6s Plus and iPad Pro 9.7寸独占 |
9.3 (13E233) | arm64,armv7s,armv7 | |
9.2.1 (13D20) | arm64,armv7s | iPhone 6 和更新的机型独占 |
9.2.1 (13D15) | arm64,armv7s,armv7 | |
9.2 (13C75) | arm64,armv7s,armv7 | |
9.1 (13B143) | arm64,armv7s,armv7 | |
9.0.2(13A452) | arm64,armv7s,armv7 | |
9.0.1(13A404) | arm64,armv7s,armv7 | |
9.0 (13A344) | arm64,armv7s,armv7 | |
8.4.1 (12H321) | arm64,armv7s,armv7 | |
8.4 (12H143) | arm64,armv7s,armv7 | |
8.3 (12F70) | arm64,armv7s,armv7 | iPhone独占 |
8.3 (12F69) | arm64,armv7s,armv7 | iPad独占 |
8.2 (12D508) | arm64,armv7s,armv7 | |
8.1.3 (12B466) | arm64,armv7s,armv7 | |
8.1.2 (12B440) | arm64,armv7s,armv7 | |
8.1.1 (12B436) | arm64,armv7s | iPhone 6 和更新的机型独占 |
8.1.1 (12B435) | armv7s,armv7 | 5s和更旧机型独占 |
8.1 (12B411) | arm64,armv7s,armv7 | iPhone独占 |
8.1 (12B410) | arm64,armv7s,armv7 | iPad独占 |
8.0.2 (12A405) | arm64,armv7s,armv7 | |
8.0.1(12A402) | armv7s,armv7 |
8.0.1 有导致变砖的bug,发布后很快就停止推送了 |
8.0 (12A366) | arm64,armv7s | 6 Plus独占 |
8.0 (12A365) | arm64,armv7s,armv7 | |
7.1.2 (11D257) | armv7s,armv7 | |
7.1.1 (11D201) | arm64,armv7s,armv7 | |
7.1 (11D167) | arm64,armv7s,armv7 | |
7.0.6 (11B651) | arm64,armv7s,armv7 | |
7.0.4 (11B554a) | arm64,armv7s,armv7 | |
7.0.3 (11B511) | arm64,armv7s,armv7 | |
7.0.2(11A501) | armv7s,armv7 | |
7.0.1(11A470a) | armv7s | 5s 和 5c 独占 |
7.0(11A465) | arm64,armv7s,armv7 |
机型对应CPU架构
CPU | 机型 |
---|---|
armv6 | iPhone, iPhone2, iPhone3G, iPod Touch 1 and 2 |
armv7 | iPhone3GS, iPhone4, iPhone4S,iPad, iPad2, iPad3(The New iPad), iPad mini,iPod Touch 3G, iPod Touch4, iPod Touch5 |
armv7s | iPhone5, iPhone5C, iPad4(iPad with Retina Display) |
arm64 | iPhone5S, iPad Air, iPad mini2(iPad mini with Retina Display), iPhone6, iPhone6s, iPhone7, iPhone7s and any new device in the future |
结束语
最后再次呼吁一下,如果谁有上面缺失的符号文件,就请共享一下吧。虽然只是做一点微小的工作,但是能够有很大帮助。
不觉得填满上面那个列表会很爽吗?
update:现在基本上已经收集完了。
额外提示
其实这些符号文件就是真机上的系统库,包括了完整的系统库二进制文件。有时候要反编译某些系统framework,但是模拟器SDK里没有对应的framework(比如只有真机上才有的CoreMotion.framework
),就可以在这些真机上的系统库里找到了。