iOS crash 日誌堆棧解析
日常開發中,我們難免遇到一些 crash。大部分情況下,Xcode 可以幫助我們找到問題所在,但也有些情況,Xcode 給我們反饋的是一些看不懂的地址,大大增加了我們分析問題的難度。
下面,就來介紹幾種能讓看不懂的地址,變得看的懂的方式。
symbolicatecrash
.dSYM 文件
dSYM 是保存十六進位函數地址映射信息的中轉文件,我們調試的 symbols 都會包含在這個文件中。每次編譯項目的時候都會生成一個新的 dSYM 文件,我們應該保存每個正式發布版本的 dSYM 文件,以備我們更好的調試問題。一般是在我們 Archives 時保存對應的版本文件的,裡面也有對應的.dSYM和.app文件。路徑為:
.dSYM文件默認在 debug 模式下是不生成的,我們去 Build Settings -> Debug Information Format 下,將DWARF修改為DWARF with dSYM File,再重新編譯下就能生成.dSYM文件了,直接去項目工程的Products目錄下找就行。
symbols 又是什麼呢?
引用 《程序員的自我修養》中的解釋:
在鏈接中,我們將函數和變數統稱為符號(Symbol),函數名或變數名就是符號名(Symbol Name)。我們可以將符號看作是鏈接中的粘合劑,整個鏈接過程正是基於符號才能夠正確完成。
所以,所謂的 symbols 就是函數名或變數名。
找到 symbolicatecrash
symbolicatecrash是 Xcode 自帶的 crash 日誌分析工具,我們需要先找到它:
執行完後會返回幾個路徑,我的是:
我們到這個路徑下把symbolicatecrash拷貝出來,放到一個文件夾下。
拿到 crash 日誌文件
我們可以隨便寫段強制 crash 的代碼:
接著用真機打個包。打好包之後,不要用 Xcode build,直接用打好的包運行我們能導致 crash 的代碼,這樣就生成好.crash日誌文件了。
之後,我們去 Xcode -> Window -> Devices and Simulators 或者快捷鍵Command + Shift + 2
找到對應時間點的 .crash 文件,右擊 Export Log。
拿到 .app 文件
.app文件可以使用真機編譯下,去 項目Products目錄下獲取,也可以去 Archives 目錄下獲取。
符號解析
利用 dSYM
將.dSYM、.crash及symbolicatecrash放到同一個文件下,執行命令:
./symbolicatecrash .crash文件路徑 .dSYM文件路徑 > 名字.crash
利用 app
將.app、.crash及symbolicatecrash放到同一個文件下,執行命令:
./symbolicatecrash .crash文件路徑 .app/appName 路徑 > 名字.crash
可能會報錯誤:
執行下命令就行:
然後再重新生成下新的.crash文件就行。
我們可以對比下沒有符號化和符號化的文件,下面是我自己測試的例子,iPhone5 iOS 10.2, 可能會有所不同:
問題也能看出來,但是因為第三行(DreamDemo)並沒有符號化,導致我們並不確定具體調用位置。
再來看看符號化之後的:
可以發現,第三行被解析出來了,這樣我們就能很清晰的知道具體的頁面了。
使用命令行工具 atos
symbolicatecrash 可以幫助我們很好的分析 crash 日誌,但是有它的局限性 --- 不夠靈活。我們需要symbolicatecrash、.crash及.dSYM三個文件才能解析。
相對於 symbolicatecrash,atos命令更加靈活,特別是你需要對不同渠道的 crash 文件,寫一個自動化的分析腳本的時候,這個方法就極其有用。
但是這種方式也有個不方便的地方:一個線上的 App,用戶使用的版本存在差異,而每個版本所對應的.dSYM都是不同的。必須確保.crash和.dSYM文件是匹配的,才能正確符號化,匹配的條件就是它們的 UUID 一致。 在這之前,先介紹下 UUID:
什麼是 UUID ?
UUID 是由一組 32 位數的十六進位數字所構成。每一個可執行程序都有一個 build UUID 唯一標識。.crash日誌包含發生 crash 的這個應用的 build UUID 以及 crash 發生時,應用載入的所有庫文件的 build UUID。
獲取 UUID
.crash UUID
執行命令:
輸出:
看到上面的輸出d009f8671129397a8aab9cb2b8e506ff就是DreamDemo項目的 UUID。
.dSYM UUID
執行命令:
輸出:
執行命令:
輸出:
可以發現這兩個文件的 UUID 是相同的,也就是匹配的,只有滿足這種條件,才能正確的解析!
atos 解析
我們現回顧下未解析前的堆棧:
執行命令:
接著輸入0x0008088e地址,終端輸出如下:
可以發現,正確的解析出來了!
除了搭配.dSYM文件,我們也可以使用.app文件來解析:
執行命令:
xcrun atos -o DreamDemo.app/DreamDemo -arch armv7 -l 0x7c000
同樣輸入 0x0008088e 地址,效果是一樣的。
工具
直接操作atos命令畢竟是有點不方便,GitHub 上有個工具,可以輔助我們解析dSYMTools,這是個 Mac 客戶端,界面長這樣:
使用起來也很方便,我們只需要把對應的dSYM文件拖進去,它會自動識別 UUID。我們對應的輸入參數地址就行:
系統庫的符號化解析
細心的人可以發現,我們上面的解析都是針對DreamDemo,這個自己的項目的。其實很多系統方法的堆棧之所以能解析出來,是因為已經有了系統庫的符號化文件,存放目錄如下:
這些庫的版本都是和.crash文件中是對應的:
一旦我把這個10.2 (14C5077b)系統的符號化庫刪掉,.crash文件就會變成這樣:
Last Exception Backtrace:
可以明顯的發現,系統庫的堆棧變成了一堆地址。
新版本,每當我們手機連上 Xcode 時,都會把當前版本的系統符號庫自動導入到/用戶/用戶名稱xxx/資源庫/Developer/Xcode/iOS DeviceSupport目錄下。但是 iOS 版本那麼多,之前舊的系統符號庫該怎麼獲取呢?有人已經整理好了iOS-System-Symbols,我們只需要根據.crash文件的版本信息,下載對應的系統符號化文件到目錄下即可。
總結
利用symbolicatecrash解析,可以將整個.crash日誌堆棧解析,但是由於依賴symbolicatecrash、.crash以及.dSYM三個文件,或者.app、.crash及symbolicatecrash三個文件,導致不太靈活。
利用atos命令只需要.crash和.dSYM,或者.crash和.app,知道對應的堆棧地址,就能解析,方便自動化腳本分析,但是 crash 堆棧可能需要自己實現收集。
參考
※被遺棄的線程
※又要跳票!蘋果跨平台應用推遲到2019年問世
TAG:Cocoa開發者社區 |