<簡書 — 劉小壯> http://www.jianshu.com/p/77660e626874objective-c
在iOS開發調試過程當中以及上線以後,程序常常會出現崩潰的問題。簡單的崩潰還好說,複雜的崩潰就須要咱們經過解析Crash文件來分析了,解析Crash文件在iOS開發中是比較常見的。如今網上有不少關於解析崩潰信息的博客,可是大多質量良莠不齊,或者有些細節沒有注意到。今天寫一篇博客總結一下我對崩潰調試的使用和技巧,若是有哪些錯誤或遺漏,還請指點,謝謝!😁數組
在iOS
中獲取崩潰信息的方式有不少,比較常見的是使用友盟、Bugly等第三方分析工具,或者本身收集崩潰信息並上傳公司服務器。下面列舉一些咱們經常使用的崩潰分析方式:xcode
Xcode-Devices
中直接查看某個設備的崩潰信息。Crash
崩潰收集服務。蘋果給咱們提供了異常處理的類-NSException
。這個類能夠建立一個異常對象,也能夠經過這個類獲取一個異常對象。服務器
這個類中最經常使用的是一個獲取崩潰信息的C函數,能夠經過這個函數在程序發生異常的時候收集這個異常。微信
// 將系統提供的獲取崩潰信息函數寫在這個方法中,以保證在程序開始運行就具備獲取崩潰信息的功能 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 將下面C函數的函數地址當作參數 NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler); return YES; } // 設置一個C函數,用來接收崩潰信息 void UncaughtExceptionHandler(NSException *exception){ // 能夠經過exception對象獲取一些崩潰信息,咱們就是經過這些崩潰信息來進行解析的,例以下面的symbols數組就是咱們的崩潰堆棧。 NSArray *symbols = [exception callStackSymbols]; NSString *reason = [exception reason]; NSString *name = [exception name]; }
咱們也能夠經過下面方法獲取崩潰統計的函數指針:app
NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
進行崩潰分析,首先要弄懂一個概念,就是符號集。函數
ipa
文件進行打包以後,和.app
文件同級的後綴名爲.dSYM
的文件,這個文件必須使用Xcode進行打包纔有。dSYM
文件都有一個UUID
,和.app
文件中的UUID
對應,表明着是一個應用。而.dSYM
文件中每一條崩潰信息也有一個單獨的UUID
,用來和程序的UUID
進行校對。.dSYM
文件解析出的崩潰信息都不能保證準確。.Crash
文件能夠準確知道具體的崩潰信息。咱們每次Archive
一個包以後,都會隨之生成一個dSYM
文件。每次發佈一個版本,咱們都須要備份這個文件,以方便之後的調試。進行崩潰信息符號化的時候,必須使用當前應用打包的電腦所生成的dSYM
文件,其餘電腦生成的文件可能會致使分析不許確的問題。工具
當程序崩潰的時候,能夠得到到崩潰的錯誤堆棧,錯誤堆棧都是0x
開頭的16進制地址,須要使用Xcode自帶的symbolicatecrash
工具來將.Crash
和.dSYM
文件進行符號化,就能夠獲得詳細崩潰的信息。優化
在崩潰分析時,dSYM
文件是解析App堆棧的,若是是系統庫則須要對應的符號化文件。不少解析不出來系統堆棧的問題,就是由於沒有系統的符號化文件。符號化文件就在Xcode的資源庫裏,能夠從下面的目錄找到符號化文件。網站
/Users/liuxiaozhuang/Library/Developer/Xcode/iOS DeviceSupport
符號化文件對版本和Architectures
都有要求,例如崩潰的系統是8.4.1系統 arm64的指令集,就須要對應的系統符號化文件8.4.1 (12H321)
。不然仍是不能解析出系統崩潰信息,或者解析出來也是錯的。若是在iOS DeviceSupport
文件中沒有找到對應的符號化文件,須要找一個對應的才能夠解析。
符號化文件的指令集通常都是兼容低版本的,例如8.4.1 (12H321)
的指令集會有arm64
、armv7s
、armv7
三個版本,若是蘋果沒有明確說明某個iOS
版本不兼容32位處理器,那麼指令集都會兼容的。
蒐集系統符號化文件很是困難,我在國外也沒找到蒐集全的網站。可是國內有一個很是敬業的iOS
同行,蒐集總結了iOS7
- iOS10
的不少符號化文件,並且做者對文件作了優化,下載下來的文件也不會很大,很是感謝他!
網盤連接: https://pan.baidu.com/s/1nvfi4g5 密碼: 79m8
這裏的符號化文件也並不全,若是遇到沒有找到的符號化文件,到崩潰日誌中找到OS Version:iOS 8.4.1 (12H321)
,把後面的系統build
版本放在Google
搜一下試試,若是搜不到就不太好解決了。
經過Mac自帶的命令行工具解析Crash
文件須要具有四個文件
symbolicatecrash
,Xcode自帶的崩潰分析工具,使用這個工具能夠更精確的定位崩潰所在的位置,將0x開頭的地址替換爲響應的代碼和具體行數。dSYM
文件。Crash
文件。iOS
系統和指令集的符號化文件。我在解析崩潰信息的時候,通常會在桌面上單獨創建一個Crash
文件夾,而後將.Crash
、.dSYM
、symbolicatecrash
放在這個文件夾中,這樣進入這個文件夾下,直接一行命令就解決了。下次再作崩潰分析的時候,換一下對應的文件名就能夠解析。
symbolicatecrash
在Xcode8及以後是下面的路徑。
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
Xcode8以前symbolicatecrash
在下面的路徑中。
/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash
而後Window -> Organizer -> Archives
中,選中archive
的版本右擊,選擇Show in Finder
就能夠獲取dSYM
文件了。
將.Crash
、.dSYM
、symbolicatecrash
三個文件都放在咱們在桌面創建的Crash
文件夾中。
開啓命令行工具,進入崩潰文件夾中
cd /Users/username/Desktop/崩潰文件夾
使用命令解析Crash文件
./symbolicatecrash ./*.crash ./*.app.dSYM > symbol.crash
若是上面命令不成功,使用命令檢查一下環境變量
xcode-select -print-path
返回結果:
/Applications/Xcode.app/Contents/Developer/
若是不是上面的結果,須要使用下面命令設置一下導出的環境變量,而後重複上面解析的操做。
export DEVELOPER_DIR=/Applications/XCode.app/Contents/Developer
解析完成後會生成一個新的.Crash
文件,這個文件中就是崩潰詳細信息。圖中紅色標註的部分就是咱們代碼崩潰的部分。
注意,如下狀況不會有崩潰信息產生:
bad memory
錯誤)有時候經過symbolicatecrash
並不能解析出來崩潰信息,或者App自身的堆棧能解析出來,可是系統堆棧解析不出來,這在解析過程當中是常常遇到的。
能夠經過atos
命令進行逐行解析,經過這個命令能夠解析指定的某一行堆棧。而且這個命令是不須要dSYM
的,能夠在沒有dSYM
的狀況下使用。
在講atos
以前先對一些基本知識瞭解一下。
architecture
是指令集類型,例如arm64
、armv7s
之類的。能夠在Xcode的Build Settings
中查看,也能夠執行下面命令查看二進制文件支持的指令集。
lipo -info 二進制名
會輸出下面信息,根據崩潰設備機型選擇對應的指令集類型。
Architectures in the fat file: 二進制名 are: armv7 arm64
loadAddress
是Binary Images
中對應的二進制首地址,每一個動態庫和二進制都有本身的首地址, 對應崩潰堆棧的首地址。
在每條崩潰堆棧中,都有對應的首地址和地址偏移。例如Zeus這個二進制對應的就是Binary Images
的Zeus arm64
的首地址,根據這個首地址加上後面的地址偏移,就是函數所在的函數地址。
以上面崩潰堆棧爲例,atos
命令格式
atos -arch arm64 -o Zeus -l 0x10063000 0x00000001009795d4
經過這個命令能夠解析出具體的崩潰信息
-[FocusView updateImageViews] (in Zeus) (FocusView.m:119)
若是是系統堆棧,則須要把二進制名Zeus換成對應的framework
或dylib
名。拿Foundation
舉例來講,爲了命令看起來簡單點,我先把8.4.1 (12H321)
中的Foundation.framework
拷貝到和Zeus統計目錄下,而後執行下面命令便可,dylib
同理。
atos -arch arm64 Foundation.framework/Foundation -l 0x1830bc000 0x0000000183109f94
而後就輸出了Foundation
內部的堆棧信息。
-[NSURL(NSURLPathUtilities) URLByAppendingPathComponent:] (in Foundation) + 144
1.須要注意的是,文中提到的dSYM
、符號化文件、App
二進制,都須要和當前崩潰設備的App
版本、iOS
系統版本、Architectures
指令集對應,地址偏移也不要寫錯,不然會致使解析出來的堆棧不正確。
atos -arch arm64 -o Zeus -l 0x10063000 0x000000010092b52a
例如上面命令解析的堆棧以下
-[DetailHeaderView layoutSubviews] (in Zeus) (DetailHeaderView.m:34)
可是我用其餘版本的二進制解析,一樣的命令,解析結果可能就不同。
-[ShareManager manager] (in Zeus) (ShareManager.m:194)
因此崩潰信息中最上面的一些參數就很重要,包含了咱們須要的iOS
版本、App build
號、指令集類型等信息。
2.不管是App
的堆棧仍是系統堆棧,若是使用系統自帶的symbolicatecrash
和atos
兩種方式不能解析,都是由於沒有對應的dSYM
或二進制、符號化文件。
若是手裏有對應的設備,第一次插入電腦後鏈接Xcode,會顯示Processing symbol files
。這個過程就是拷貝符號化文件到iOS DeviceSupport
目錄的過程,拷貝完成後就能夠拿到符號化文件了。
除了上面的系統分析工具來進行分析,也能夠將發生崩潰的設備鏈接Xcode,選擇window-> devices -> 選擇本身的手機 -> view device logs
就能夠查看手機上全部的崩潰信息了。
只要手機上的應用是這臺電腦安裝打包的,這樣的崩潰信息系統已經爲咱們符號化好了,只須要進去以後等一會就行(不要相信這裏面的進度刷新,並不許確,親測….)。若是仍是沒有符號化完畢 ,能夠選擇文件,而後右擊選擇Re-Sysbomlicate
就能夠。
若是是使用其餘電腦進行的打包,能夠在這裏面將Crash
文件導出,本身經過命令行的方式進行解析。
如今有不少第三方工具均可以進行崩潰統計分析,使用比較多的是友盟崩潰統計,友盟崩潰統計被集成在友盟SDK中,具體用法能夠直接看官方文檔,這裏很少作敘述。
可是我並不推薦友盟,而是推薦一個很是強大的崩潰統計工具—Bugly,我公司項目也在使用Bugly。它最大的優點在於,能夠直接將崩潰信息分析出來而且作好分類和彙總,並且一些咱們本身分析不出來或很難分析出來的崩潰,Bugly都能分析出來。固然須要提早上傳dSYM
到Bugly後臺,不然可能會致使一些崩潰分析不出來。
這是Bugly統計的崩潰分佈,能夠選擇版本、時間之類的。並且Bugly不只限於崩潰統計,還有卡頓分析和錯誤分析,我感受這兩個也比較實用。相對友盟或者其餘崩潰分析工具,Bugly是一個輕量級的崩潰統計工具。
出錯堆棧Bugly會幫咱們解析好,而且會根據不一樣狀況給一些解決建議,這個我就不截圖了。
Bugly有一個頁面追蹤功能,這是我認爲很是有用的一個功能。這個功能會將用戶在不一樣頁面之間跳轉的流程記錄下來。這樣對於復現bug
是頗有用的,能夠根據用戶頁面跳轉推測出用戶大概操做流程,根據這個流程復現bug
。
Bugly還有日報功能,能夠天天彙總一篇日報,而且發到團隊每一個人的郵箱和微信號上。我認爲這個功能很是實用,天天大概八九點上班路上,掏出手機看一下微信上Bugly日報,想一下項目哪可能有問題,到公司正好解決bug
。
蘋果在Xcode中爲咱們集成了崩潰統計功能,在Window -> Organizer -> Crashes
中能夠看到。
蘋果自帶的崩潰統計工具並不推薦用,若是想要使用這個功能,須要用戶在iPhone中進行設置設置 -> 隱私 -> 診斷與用量 -> 診斷與用量數據(iOS8如下在通用中設置)
選擇自動發送,並與開發者共享。
然而不少人並不想和開發者共享數據,或者不設置這個選項,那這樣就收集不到這部分的崩潰。
崩潰收集統計函數應該只進行一次調用,若是用第三方的話也最好只用一個第三方,這樣獲取崩潰統計信息的途徑也是惟一的。第三方統計工具並非用的越多越好,使用多個崩潰收集第三方會致使NSSetUncaughtExceptionHandler()
函數指針的惡意覆蓋,致使有些第三方不能收到崩潰信息。
如今不少第三方崩潰收集工具爲了確保本身能最大可能的收集到崩潰信息,會對NSSetUncaughtExceptionHandler()
函數指針的惡意覆蓋。由於這個函數是將函數地址當作參數傳遞,因此只要重複調用就會被覆蓋,這樣就不能保證崩潰收集的穩定性。