如果你有過分析iOS崩潰日志的經(jīng)驗,一定經(jīng)??吹饺罩纠锍霈F(xiàn)很多<redacted>的字段。這篇文章就是幫助開發(fā)者將這些字段符號化為對應(yīng)的系統(tǒng)庫方法名。
如果你已經(jīng)掌握了這方面的知識,就直接去這里iOS-System-Symbols,下載我整理好的系統(tǒng)庫符號文件吧。
符號化的作用
當(dāng)獲取到app的crash日志時,第一步就是將其符號化。作用是把日志堆棧中的方法調(diào)用顯示出來,對于來自app內(nèi)部的方法,還能直接給出方法對應(yīng).m文件的所在行。
符號化前(這里項目的Build Settings里的Strip Style設(shè)為了Debugging Symbols,所以這里能直接看到MyApp的方法名。更多和symbol相關(guān)的設(shè)置,請看這里):
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
可以發(fā)現(xiàn),frame 3里libsystem_pthread.dylib的<redacted>變成了_pthread_body,frame 2里MyApp的ksmachexc_i_handleExceptions變成了更為完整的ksmachexc_i_handleExceptions (KSCrashSentry_MachException.c:233),直接給出了方法及其所在文件和行數(shù)。
詳細的符號化方法,請參考符號化iOS Crash文件的3種方法。這里就不重復(fù)了。
需要注意的是,很多時候,crash日志里并不會有MyApp的調(diào)用,而全都是系統(tǒng)庫的調(diào)用:
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)
這時候就必須符號化系統(tǒng)庫了。符號化后的日志:
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導(dǎo)致的問題,檢查一下就發(fā)現(xiàn)UISearchDisplayController的delegate是assign的,是由于點擊搜索條的同時pop了界面導(dǎo)致的crash。
從這里可以發(fā)現(xiàn),符號化系統(tǒng)庫是很有必要的。
如何符號化系統(tǒng)庫
符號化自己app的方法名,需要編譯ipa時生成的dySYM文件。而要將系統(tǒng)庫的<redacted>符號化為完整的方法名,也需要系統(tǒng)庫的符號文件。
1. 匹配對應(yīng)的符號文件版本
系統(tǒng)庫符號文件不是通用的,而是對應(yīng)crash所在設(shè)備的系統(tǒng)版本和CPU型號的。
crash日志中有這樣兩個信息:
Code Type: ARM-64
OS Version: iOS 10.2 (14C82)
Code Type表示此設(shè)備的CPU為armv7、armv7s還是arm64。
OS Version表示此設(shè)備的系統(tǒng)版本號,括號中的字符串代表了此系統(tǒng)的build號??梢栽谶@里查找build號:iOS SDK,iOS version history。
2. 將對應(yīng)版本的符號文件放到指定目錄
這時候,把獲取到的對應(yīng)版本的符號文件放到Mac的~/Library/Developer/Xcode/iOS DeviceSupport目錄下,再使用符號化iOS Crash文件的3種方法里提到的Xcode自帶的符號化工具symbolicatecrash進行符號化。這個工具會自動搜索系統(tǒng)庫符號文件。
獲取系統(tǒng)符號文件的4個方法
從真機上獲取
大部分系統(tǒng)庫符號文件只能從真機上獲取,蘋果也沒有提供下載。
當(dāng)你用Xcode第一次連接某臺設(shè)備進行真機調(diào)試時,會看到Xcode顯示Processing symbol files,這時候就是在拷貝真機上的符號文件到Mac系統(tǒng)的/Users/xxx/Library/Developer/Xcode/iOS DeviceSupport目錄下。
目錄下的10.2(14C82)這樣的文件夾就是對應(yīng)的符號文件,通常都有1-3GB的大小,很占用空間,動不動就累積成3、40GB。很多講清理Mac垃圾文件的教程都會說要刪除這個目錄下的文件,真是坑爹。正確做法是做成壓縮包保存到外部硬盤里,需要符號化的時候再重新解壓到此目錄。
尋找蘋果官方的下載地址
之前watch的調(diào)試出現(xiàn)bug時,有人找出過幾個watch的符號文件下載地址。見No symbols for paired Apple Watch。
但是我嘗試按照對應(yīng)的命名格式,并沒有找到iOS符號文件的下載地址。
從已解密的固件中提取符號文件
某些已經(jīng)被破解的固件可以直接提取系統(tǒng)文件,但是未破解的固件(較新的固件和arm64的固件),無法用這種方式獲取。
固件解密步驟:
1.下載對應(yīng)版本的.ipsw固件,直接解壓,解壓后里面有幾個.dmg格式的鏡像文件,最大的.dmg文件就是系統(tǒng)鏡像。
2.從Firmware_Keys找到對應(yīng)固件的解密key(頁面上Root Filesystem字段的key)。
3.用一個dmg工具進行解密,下載地址。使用方式:cd到解壓后的ipsw文件夾,執(zhí)行./dmg extract xxx-xxxx-xxx.dmg dec.dmg -k <key>。extract后面跟兩個參數(shù),分別是系統(tǒng)鏡像dmg的名字和解密后的文件名,-k 后面填寫第2步獲取到的key,如果key不對,解密會失敗。
4.等待。最終會生成一個dec.dmg文件,雙擊打開即可加載系統(tǒng)鏡像。
提取符號文件方法:
參考聊聊從iOS固件提取系統(tǒng)庫符號中的二、系統(tǒng)庫符號提取部分。
update:目前大多數(shù)固件都能解密了,而且iOS 10之后蘋果不再進行加密,因此之后都可以用這個方式獲取符號文件。
下載舊版本Xcode,提取SDK
舊版本的Xcode里包含了對應(yīng)的iPhoneSDK,可以從中獲得符號文件。
路徑是/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/。里面的System/Library里就可以看到framework,而且同時包含了armv7,armv7s,arm643個平臺的版本。
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/version.plist可以查看是哪個版本。把iPhoneOS.sdk文件夾的名字改成對應(yīng)的CFBundleVersion (ProductBuildVersion)格式,然后在里面加一層Symbols子文件夾,把System,Library,usr都放進Symbols里,就可以和其他符號文件一樣使用了。
但是當(dāng)iOS版本只包含了bug修復(fù),而沒有改變API,Xcode就不會有附帶對應(yīng)的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日志只給出了系統(tǒng)的調(diào)用棧,不能直接定位到自己app的代碼,因此需要符號化系統(tǒng)庫。
- 用戶的crash來自各種系統(tǒng)版本,需要對應(yīng)版本的系統(tǒng)符號文件才能符號化。
- 系統(tǒng)庫符號文件只能從真機上獲取,蘋果沒有提供下載。
- 從
iOS 7.0(11A465)到iOS 10.2(14C92)一共有50個build版本,公司的測試機是不會覆蓋到這么變態(tài)的完整度的。 - 同一個版本,有時候會給iPhone和iPad、甚至6和6s提供不同build的固件。
- 某些版本是某些機子的特供版,例如
10.0.3(14A551)是iPhone 7和 7 Plus獨有的,這就更加大了收集難度。 - 同一個iOS版本可能有多個build,例如
10.1(14B72)和10.1(14B72c),蘋果覺得更新幅度太小,就沒有提升版本號。 - 除了build號的區(qū)別,符號文件在不同CPU平臺上也有區(qū)別,意味著來自4s(
armv7)和6s(arm64)的符號文件,即便build號是一樣的,也無法通用。所以50個build號又要翻倍,達到了88個,所以精確來說我只是收集了(63/88)的進度。幸運的是,arm64機型的系統(tǒng)庫里附帶了armv7s。
規(guī)則好厲害的收集游戲啊。收集品其實還有稀有度的區(qū)別,其中最厲害的應(yīng)該是10.0,這是iPhone 7和7 Plus獨有的出廠系統(tǒng),而且沒有固件可以下載,因此即便有iPhone 7也不能通過刷機來得到10.0。
其實我一直很奇怪為什么很少見到開發(fā)者抱怨找不到系統(tǒng)符號文件,從而召集大家進行收集并分享,猜測可能的原因是:
- 懶。遇到無法符號化的問題,沒有去解決。
- 有些公司可能很早就開始對crash日志自動符號化了,因此很早就開始收集符號文件。只要一直跟著蘋果的每一個版本升級,收集起來還是挺簡單的。而這些資源,開發(fā)者并不會注意到可以共享出來。
- crash收集和符號化使用的是第三方服務(wù),第三方平臺會幫助符號化系統(tǒng)庫。
但是我找了一下,沒有找到一家明確聲明了能夠符號化所有系統(tǒng)庫的第三方平臺。從騰訊的Bugly論壇里也能發(fā)現(xiàn),有些系統(tǒng)方法并沒有符號化出來,系統(tǒng)庫是缺失的。(update:找到了一個國外的平臺,在stack overflow上說能符號化所有版本的系統(tǒng)庫,但是是收費服務(wù),我也沒有測試)
在公司小組里,大部分時候都是我來分析crash日志,所以當(dāng)遇到缺少系統(tǒng)符號文件的情況,就會十分無奈。很多時候,沒有符號化的crash日志根本無法提供有用信息。
系統(tǒng)符號文件下載地址
暴力收集
收集不全一直讓我很不爽。之前搜刮了組內(nèi)的測試機,只收集到了有限的幾個(感謝無私提供iPhone 7讓我刷機降級的同事)。終于,這周末特意跑去了一趟二手機市場尋找舊版本的設(shè)備來拷貝,總算是收集得完整了一點,但是也花了我121塊錢。
心情總算愉悅了。
其中大部分都是拷貝自arm64設(shè)備的,armv7的符號文件收集我是放棄了。有哪位大俠有興趣把這個游戲玩通關(guān)的嗎?還有 tvOS 和 watchOS的符號,我也放棄了。
(update:又花了些時間從Xcode的SDK和固件里提取了armv7s和armv7的固件,現(xiàn)在只剩下幾個arm64的版本了)。
整理了一下傳到了網(wǎng)盤,地址見下方,有需要的去下載吧。
目前還缺少的符號文件
通過各種方式,目前已經(jīng)收集得差不多了,只剩下最后一個10.0(14A346)版本沒有得到。如果哪位小伙伴有這個版本,可以分享一下。
分享可以直接貼下載地址,也可以提交到這個github項目iOS-System-Symbols。如果我得到了新的符號文件,也會在這個項目里更新。
| 缺失符號的版本 | 缺失的CPU版本 | 描述 |
|---|---|---|
| 10.0(14A346) | arm64 | iPhone 7 和 7 Plus獨占,出廠自帶系統(tǒng) |
網(wǎng)盤下載地址
下載地址請見這個項目:iOS-System-Symbols。如果我得到了新的符號文件,會在這個項目里更新。
我把里面的那幾個dyld_shared_cache_xxxx大文件單獨拿出來了,目的是減小壓縮包大小。如果只是符號化的話,用不到這幾個文件,只是在真機調(diào)試的時候才需要。
符號文件版本列表
這些是我已經(jīng)收集到的符號文件,包括了對應(yīng)的CPU信息。iOS10系統(tǒng)開始不支持armv7的機器。但是iOS9以下還是支持armv7的。
查看是否帶有對應(yīng)CPU架構(gòu)的符號文件的方式:到10.2 (14C92)/Symbols/System/Library/Caches/com.apple.dyld這樣的目錄下,會有對應(yīng)的dyld_shared_cache_arm64,dyld_shared_cache_armv7s,dyld_shared_cache_armv7文件,如果缺失了,就說明對應(yīng)的CPU架構(gòu)符號文件還不存在。(經(jīng)過試驗,這幾個大文件并不影響符號化,只是在真機調(diào)試的時候才有用,因此如果嫌太占用空間,可以刪去)。
| 版本 | 已收集到的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 | 蘋果在發(fā)布了15C114之后,又發(fā)布了一個低版本的15C113,之后又移除了15C113的下載地址,因此這個固件目前無法下載。應(yīng)該只是誤發(fā)布,可以忽略這個版本 |
| 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獨占,出廠自帶系統(tǒng) |
| 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寸獨占,修復(fù)了變磚的問題 |
| 9.3.2 (13F69) | arm64,armv7s,armv7 | |
| 9.3.1 (13E238) | arm64,armv7s,armv7 | |
| 9.3(13E237) | armv7s,armv7 | 5s和更舊機型獨占,修復(fù)了不能激活的問題 |
| 9.3(13E236) | armv7 | iPad2獨占,修復(fù)了不能激活的問題 |
| 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有導(dǎo)致變磚的bug,發(fā)布后很快就停止推送了 |
| 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 |
機型對應(yīng)CPU架構(gòu)
| 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 |
結(jié)束語
最后再次呼吁一下,如果誰有上面缺失的符號文件,就請共享一下吧。雖然只是做一點微小的工作,但是能夠有很大幫助。
不覺得填滿上面那個列表會很爽嗎?
update:現(xiàn)在基本上已經(jīng)收集完了。
額外提示
其實這些符號文件就是真機上的系統(tǒng)庫,包括了完整的系統(tǒng)庫二進制文件。有時候要反編譯某些系統(tǒng)framework,但是模擬器SDK里沒有對應(yīng)的framework(比如只有真機上才有的CoreMotion.framework),就可以在這些真機上的系統(tǒng)庫里找到了。