如果你有過分析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
),就可以在這些真機上的系統庫裡找到了。