Xcode:崩潰堆疊符號化,定位崩潰
首先,進行常識“腦補”。
1. 符號表是什麼?
符號表就是指在Xcode專案編譯後,在編譯生成的二進位制檔案.app的同級目錄下生成的同名的.dSYM檔案。
.dSYM檔案其實是一個目錄,在子目錄中包含了一個16進位制的儲存函式地址對映資訊的中轉檔案,所有Debug的symbols都在這個檔案中(包括檔名、函式名、行號等),所以也稱之為除錯符號資訊檔案。
一般地,Xcode專案每次編譯後,都會生成一個新的.dSYM檔案。因此,App的每一個釋出版本,都需要備份一個對應的.dSYM檔案,以便後續除錯定位問題。
注意:
專案每一次編譯後,.app和.dSYM成對出現,並且二者有相同的UUID值,以標識是同一次編譯的產物。
UUID值可以使用dwarfdump —uuid來檢查:$ dwarfdump --uuid XX.app.dSYM $ dwarfdump --uuid XX.app/XX
那麼,問題就來了!
2. 符號表有什麼用?
在Xcode開發除錯App時,一旦遇到崩潰問題,開發者可以直接使用Xcode的偵錯程式定位分析。
但如果App釋出上線,開發者不可能進行除錯,只能通過分析系統記錄的崩潰日誌來定位問題,在這份崩潰日誌檔案中,會指出App出錯的函式記憶體地址,而這些函式地址是可以在.dSYM檔案中找到具體的檔名、函式名和行號資訊的,這正是符號表的重要作用所在。
實際上,使用Xcode的Organizer檢視崩潰日誌時,也自動根據本地儲存的.dSYM檔案進行了符號化的操作。
並且,崩潰日誌也有UUID資訊,這個UUID和對應的.dSYM檔案是一致的,即只有當三者的UUID一致時,才可以正確的把函式地址符號化。
3. 符號表怎麼生成?
一般地,Xcode專案預設的配置是會在編譯後生成.dSYM,開發者無需額外修改配置。
專案的Build Settings的相關配置如下:
Generate Debug Symbols = Yes Debug Information Format = DWARF with dSYM File
採用不同的編譯打包方式,產生的.dSYM檔案的路徑也不相同。
下面是幾種常用的編譯打包方式:
1、使用xcodebuild編譯打包
在Xcode中編譯專案後,會在工程目錄下的build/ConfigurationName-iphoneos目錄下生成.app和.app.dSYM檔案。
如果使用xcodebuild命令進行編譯打包,則可以指定編譯結果的儲存路徑,同樣會有.app和.app.dSYM生成。
一般地,我們推薦打包釋出時,使用xcodebuild編譯打包,方便.app和.app.dSYM的匹配儲存,避免.app.dSYM檔案丟失的情況。
2、使用Xcode的Archive匯出
如果開發者使用Xcode的Archive匯出功能打包,可以切換到Organizer的Projects檢視,檢視對應專案的Derived Data路徑,在其中可以找到當前匯出過程產生的.app和.app.dSYM檔案
3、使用make編譯打包
如果開發團隊不使用Xcode編譯打包,而是使用make編譯生成.o檔案,然後打包釋出。此時,編譯過程不會有.dSYM檔案生成。開發者可以使用dsymutil工具從.o檔案中提取符號資訊。
4. 符號表怎麼用?
在前面的內容可以知道,符號表的作用是把崩潰中的函式地址解析為函式名等資訊。
如果開發者能夠獲取到崩潰的函式地址資訊,就可以利用符號表分析出具體的出錯位置。
Xcode提供了幾個工具來幫助開發者執行函式地址符號化的操作。
例如,崩潰問題的函式地址堆疊如下:
錯誤地址堆疊
3 CoreFoundation 0x254b5949 0x253aa000 + 1096008
4 CoreFoundation 0x253e6b68 _CF_forwarding_prep_0 + 24
5 SuperSDKTest 0x0010143b 0x000ef000 + 74808
符號化堆疊
3 CoreFoundation 0x254b5949 <redacted> + 712
4 CoreFoundation 0x253e6b68 _CF_forwarding_prep_0 + 24
5 SuperSDKTest 0x0010143b -[ViewController didTriggerClick:] + 58
說明:
大部分情況下,開發者能獲取到的都是錯誤地址堆疊,需要利用符號表進一步符號化才能分析定位問題。
部分情況下,開發者也可以利用backtrace看到符號化堆疊,可以大概定位出錯的函式、但卻不知道具體的位置。通過利用符號表資訊,也是可以進一步得到具體的出錯位置的。
目前,許多崩潰監控服務都顯示backtrace符號化堆疊,增加了可讀性,但分析定位問題時,仍然要進一步符號化處理。
崩潰資訊的UUID
0xef000 - 0x17efff SuperSDKTest armv7 <38d66f9734ca3843a2bf628bb9015a8b> /var/mobile/.../SuperSDKTest.app/SuperSDKTest
下面,利用兩個工具來進行一下符號化的嘗試:
1、symbolicatecrash
symbolicatecrash是一個將堆疊地址符號化的指令碼,輸入引數是蘋果官方格式的崩潰日誌及本地的.dSYM檔案,執行方式如下:
$ symbolicatecrash XX.crash [XX.app.dSYM] > xx.sym.crash# 如果輸入.dSYM引數,將只解析系統庫對應的符號
使用symbolicatecrash工具的限制就在於只能分析官方格式的崩潰日誌,需要從具體的裝置中匯出,獲取和操作都不是很方便,而且,符號化的結果也是沒有具體的行號資訊的,也經常會出現符號化失敗的情況。
實際上Xcode的Organizer內建了symbolicatecrash工具,所以開發者才可以直接看到符號化的錯誤日誌。
2、atos
更普遍的情況是,開發者能獲取到錯誤堆疊資訊,而使用atos工具就是把地址對應的具體符號資訊找到。
atos實際是一個可以把地址轉換為函式名(包括行號)的工具,它的執行方式如下:
$ xcrun atos -o executable -arch architecture -l loadAddress
address ...
說明:
loadAddress 表示函式的動態載入地址,對應崩潰地址堆疊中 + 號前面的地址,即0x000ef000
address 表示執行時地址、對應崩潰地址堆疊中第一個地址,即0x0010143b
實際上,崩潰地址堆疊中+號前後的地址相加即是執行時地址,即0x000ef000 + 74808 = 0x0010143b
執行命令查詢地址的符號,可以看到如下結果:
$ xcrun atos -o SuperSDKTest.app.dSYM/Contents/Resources/DWARF/SuperSDKTest -arch armv7 -l 0x000ef000
0x0010143b
-[ViewController didTriggerClick:] (in SuperSDKTest) (ViewController.m:35)
開發者在具體的運用中,是可以通過編寫一個指令碼來實現符號化錯誤地址堆疊的。
5. 結語
在實際的專案開發中,崩潰問題的分析定位都不是採用這種方式,因為它依賴於系統記錄的崩潰日誌或錯誤堆疊,在本地開發除錯階段,是沒有問題的。
如果在釋出的線上版本出現崩潰問題,開發者是無法即時準確的取得錯誤堆疊。一般地,開發者都是接入第三方的崩潰監控服務(如騰訊Bugly),實現線上版本崩潰問題的記錄和跟蹤。
目前,國內外提供崩潰監控服務的產品有好幾個,在崩潰問題的統計上可能不分伯仲。但提供自動符號化功能的產品卻基本沒有,大部分崩潰問題的堆疊只是簡單符號化以增強可讀性,沒有可以快速定位問題的行號資訊。
而騰訊Bugly提供了地址堆疊符號化功能的崩潰監控服務,只要開發者配置了對應的符號表資訊,Bugly服務會自動對錯誤地址堆疊進行符號化,出錯位置清晰可見,分分鐘定位和解決崩潰問題。