在iOS開發調試過程中以及上線之後,程序經常會出現崩潰的問題。簡單的崩潰還好說,復雜的崩潰就需要我們通過解析Crash文件來分析了,解析Crash文件在iOS開發中是比較常見的。
現在網上有很多關於解析崩潰信息的博客,但是大多質量參差不齊,或者有些細節沒有注意到。今天寫一篇博客總結一下我對崩潰調試的使用和技巧,如果有哪些錯誤或遺漏,還請指點,謝謝!
獲取崩潰信息
在iOS中獲取崩潰信息的方式有很多,比較常見的是使用友盟、百度等第三方分析工具,或者自己收集崩潰信息並上傳公司服務器。下面列舉一些我們常用的崩潰分析方式:
使用友盟、百度等第三方崩潰統計工具。
自己實現應用內崩潰收集,並上傳服務器。
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]; }
我們也可以通過下面方法獲取崩潰統計的函數指針:
NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
dSYM 符號集
進行崩潰分析,首先要弄懂一個概念,就是符號集。
符號集是我們對ipa文件進行打包之後,和.app文件同級的後綴名為.dSYM的文件,這個文件必須使用Xcode進行打包才有。
每一個.dSYM文件都有一個UUID,和.app文件中的UUID對應,代表著是一個應用。而.dSYM文件中每一條崩潰信息也有一個單獨的UUID,用來和程序的UUID進行校對。
我們如果不使用.dSYM文件獲取到的崩潰信息都是不准確的。
符號集中存儲著文件名、方法名、行號的信息,是和可執行文件的16進制函數地址對應的,通過分析崩潰的.Crash文件可以准確知道具體的崩潰信息。
我們每次Archive一個包之後,都會隨之生成一個dSYM文件。每次發布一個版本,我們都需要備份這個文件,以方便以後的調試。進行崩潰信息符號化的時候,必須使用當前應用打包的電腦所生成的dSYM文件,其他電腦生成的文件可能會導致分析不准確的問題。
Archive
當程序崩潰的時候,我們可以獲得到崩潰的錯誤堆棧,但是這個錯誤堆棧都是0x開頭的16進制地址,需要我們使用Xcode自帶的symbolicatecrash工具來將.Crash和.dSYM文件進行符號化,就可以得到詳細崩潰的信息。
崩潰分析
命令行解析Crash文件
通過Mac自帶的命令行工具解析Crash文件需要具備三個文件
symbolicatecrash,Xcode自帶的崩潰分析工具,使用這個工具可以更精確的定位崩潰所在的位置,將0x開頭的地址替換為響應的代碼和具體行數。
我們打包時產生的dSYM文件。
崩潰時產生的Crash文件。
我在解析崩潰信息的時候,首先在桌面上建立一個Crash文件夾,然後將.Crash、.dSYM、symbolicatecrash放在這個文件夾中,這樣進入這個文件夾下,直接一行命令就解決了。
symbolicatecrash我們可以在下面路徑下可以找到,我用的是Xcode7,其他版本Xcode路徑不一樣,請自行Google。
/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash
然後Window->Organizer->Archives中,選中archive的版本右擊,選擇Show in Finder就可以獲取dSYM文件了。
dSYM文件
將.Crash、.dSYM、symbolicatecrash三個文件都放在我們在桌面建立的Crash文件夾中。
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文件,這個文件中就是崩潰詳細信息。圖中紅色標注的部分就是我們代碼崩潰的部分。
解析完成的結果
注意,以下情況不會有崩潰信息產生:
內存訪問錯誤(不是野指針錯誤)
低內存,當程序內存使用過多會造成系統低內存的問題,系統會將程序內存回收
因為某種原因觸發看門狗機制
通過Xcode查看設備崩潰信息
除了上面的系統分析工具來進行分析,如果是我們自己直接使用手機連接崩潰或者崩潰之後連接手機,選擇window-> devices -> 選擇自己的手機 -> view device logs 就可以查看我們的崩潰信息了。
view device logs
只要手機上的應用是這台電腦安裝打包的,這樣的崩潰信息系統已經為我們符號化好了,我們只需要進去之後等一會就行(不要相信這裡面的進度刷新,並不准確),如果還是沒有符號化完畢 ,我們選擇文件,然後右擊選擇Re-Sysbomlicate就可以。
如果是使用其他電腦進行的打包,我們可以在這裡面將Crash文件導出,自己通過命令行的方式進行解析。
使用第三方崩潰分析工具
現在有很多第三方工具都可以進行崩潰統計分析,使用比較多的是友盟崩潰統計,友盟崩潰統計被集成在友盟SDK中,具體用法直接看官方文檔是最好的方法,下面列出友盟崩潰統計文檔地址。
友盟崩潰統計官方文檔
在這裡我並不推薦友盟這個第三方,而是推薦一個更好用的第三方—bugHD。這個第三方和友盟的最大區別就是可以直接將崩潰信息分析結合dSYM分析好,在web網頁上展現出來,而且還可以統計崩潰數、崩潰設備、系統版本等。
下面是我公司使用bugHD統計的一些崩潰情況
bugHD
在bugHD服務器已經幫我們使用dSYM將崩潰符號化完成。我們可以通過點擊某條崩潰,查看詳細崩潰堆棧,以及崩潰設備分布和系統分布。
詳細分布
蘋果自帶崩潰統計工具
蘋果在Xcode中為我們集成了崩潰統計功能,在Window->Organizer->Crashes中可以看到
Crashes
蘋果自帶的崩潰統計工具並不推薦用,如果想要使用這個功能,需要用戶在iPhone中進行設置
設置->隱私->診斷與用量->診斷與用量數據(iOS8以下在通用中設置)
選擇自動發送,並與開發者共享即可
第三方工具惡意覆蓋
崩潰收集統計函數應該只進行一次調用,如果用第三方的話也最好只用一個第三方,這樣我們獲取崩潰統計信息的途徑也是唯一的。
第三方統計工具並不是用的越多越好,使用多個崩潰收集第三方會導致NSSetUncaughtExceptionHandler()函數指針的惡意覆蓋,導致有些第三方不能收到崩潰信息。
現在很多第三方崩潰收集工具為了確保自己能最大可能的收集到崩潰信息,會對NSSetUncaughtExceptionHandler()函數指針的惡意覆蓋。因為這個函數是將函數地址當做參數傳遞,所以只要重復調用就會被覆蓋,這樣就不能保證崩潰收集的穩定性。
我們解析崩潰信息時,看到崩潰堆棧只有main.m文件中的崩潰,並且可以確定不是因為main.m文件中的bug導致的崩潰,就基本可以確定是NSSetUncaughtExceptionHandler()函數指針被惡意覆蓋。
被惡意覆蓋的崩潰堆棧