iOS Crash日誌分析必備:符號化系統庫方法

黑超熊貓zuik發表於2018-03-25

如果你有過分析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裡MyAppksmachexc_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
複製程式碼

可以看出是UISearchControllerdelegate導致的問題,檢查一下就發現UISearchDisplayControllerdelegateassign的,是由於點選搜尋條的同時pop了介面導致的crash。

從這裡可以發現,符號化系統庫是很有必要的。

##如何符號化系統庫

符號化自己app的方法名,需要編譯ipa時生成的dySYM檔案。而要將系統庫的<redacted>符號化為完整的方法名,也需要系統庫的符號檔案。

1. 匹配對應的符號檔案版本

系統庫符號檔案不是通用的,而是對應crash所在裝置的系統版本和CPU型號的。

crash日誌中有這樣兩個資訊:

Code Type:       ARM-64
OS Version:      iOS 10.2 (14C82)
複製程式碼

Code Type表示此裝置的CPU為armv7armv7s還是arm64

OS Version表示此裝置的系統版本號,括號中的字串代表了此係統的build號。可以在這裡查詢build號:iOS SDKiOS 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,arm643個平臺的版本。

/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和韌體裡提取了armv7sarmv7的韌體,現在只剩下幾個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_arm64dyld_shared_cache_armv7sdyld_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),就可以在這些真機上的系統庫裡找到了。

參考

相關文章