當iOS應用崩潰時,系統會創建壹個崩潰日誌並保存在設備上。這個崩潰日誌記錄了應用崩潰時的信息,通常包含每個執行線程的堆棧調用信息(低內存閃回日誌除外),對於開發者定位問題非常有幫助。
如果設備在附近,可以連接設備,打開Xcode-Window-Organizer,在左側面板選擇設備日誌(可以選擇特定設備的設備日誌,也可以選擇庫中所有設備的設備日誌),然後按時間順序查看設備上的崩潰日誌。這是開發和測試階段最常用的方法。
如果應用已經提交到App Store,用戶已經安裝,開發者可以通過iTunes Connect獲取用戶的崩潰日誌(管理妳的應用-查看詳情-崩潰報告)。但這並不是100%有效,大部分開發者並不依賴,因為這需要用戶設備同意上傳相關信息。詳情請參考iOS的總結:為蘋果提供診斷和使用信息。
考慮到並不是所有的iPhone用戶都被允許自動發送診斷報告(崩潰日誌),而對於壹些提交給蘋果的崩潰日誌,開發者需要手動拉取,然後找到相應的符號文件進行解析——這是壹件非常繁瑣的事情。所以在實際的項目開發中,我們通常會接入現有的崩潰收集工具(參考文獻1,參考文獻2),或者編寫壹個進行自動收集、分析、統計匯總。
第二,如何解析崩潰日誌
在獲取崩潰日誌時,我們需要將最初顯示的十六進制地址等原始信息映射到源代碼級別的方法名和代碼行號,以便開發人員能夠閱讀。這個過程被稱為符號解析。為了成功地象征性地解析崩潰日誌,我們需要相應的應用程序二進制文件和符號(。dSYM)文件。
如果是在開發調試階段,Xcode通常可以匹配崩潰日誌對應的二進制文件和符號文件,所以可以幫助我們自動分析。
如果測試人員在測試階段安裝了不同的版本(比如alpha和beta版本),那麽就需要保存相應版本的二進制文件和符號文件,以便在應用崩潰時解析崩潰日誌。對於在這個場景中生成的崩潰日誌,只需將。崩潰文件。應用程序文件和。dSYM文件,然後拖放。崩潰文件到Xcode-Window-Organizer的左面板庫下的設備日誌中,妳可以解析它。
如果我們想要提交壹個發布,我們通常首先清理它,然後構建它,最後通過Product-Archive打包。這樣Xcode會把二進制文件和符號文件壹起歸檔,可以在Organizer裏通過歸檔瀏覽。
這裏討論壹下如何解析崩潰日誌:/questions/1460892/symboling-iPhone-APP-crash-reports。
三、如何分析崩潰日誌
在分析崩潰日誌之前,如果開發人員對常見的錯誤類型有所了解就更好了。
崩潰日誌來源於兩個問題:違反iOS政策被殺和自身代碼bug。
1.iOS策略
1.1低內存閃回
如上所述,除了低內存閃回日誌之外,大多數崩潰日誌都包含執行線程的堆棧調用信息。我們先來看看低內存閃回日誌是什麽樣子的。
我們用Xcode 5和iOS 7設備模擬壹個低內存閃回,然後通過Organizer查看生成的崩潰日誌,可以發現進程和類型都是未知的:
具體日誌內容如下:
第壹部分是崩潰信息,包括標識、軟硬件信息和時間信息。
第二部分是內存頁面分配信息和當前占用內存最多的進程,就是上圖中的crashTypeDemo。
第三部分是具體的進程列表,描述了每個進程的內存使用情況和當前狀態。在更早的版本中,妳可以在壹些進程後看到“被拋棄”的字樣,表示這些進程由於內存使用過多而被終止,但是現在我們看到的是“VM-page荒”的字樣。
當iOS檢測到內存不足時,它(VM系統)會發出內存不足警告通知,並嘗試回收部分內存;如果情況沒有得到足夠的改善,iOS將終止後臺應用程序以回收更多內存;最後,如果內存仍然不足,正在運行的應用程序可能會被終止。
因此,我們的應用程序應該合理地響應系統拋出的低內存警告通知,釋放壹些緩存的數據和重新創建的對象,避免內存泄漏等問題。
低內存閃回由終止應用程序運行的iOS策略決定,也基於iOS策略、看門狗超時和用戶強制退出。
1.2看門狗超時
在蘋果的iOS開發者庫網站上,QA1693文檔描述了看門狗機制,包括有效的場景和性能。如果我們的應用程序沒有及時響應某些特定的UI事件(如開始、暫停、恢復和結束),Watchdog會終止我們的應用程序,並生成響應的崩潰報告。
這個崩潰報告有趣的地方是異常代碼:“0x8badf00d”,也就是“吃了變質的食物”。
如果壹個特定的UI事件是抽象的,那麽如果是直接用代碼描述的話,它對應的是UIApplicationDelegate(創建項目時由Xcode自動生成)的幾個方法:
所以在遇到看門狗日誌的時候,可以檢查壹下上面的方法是否有很重的阻塞UI的動作。
QA1693給出的例子是主線程中的同步網絡請求。如果我們在公司的Wifi環境下使用,壹切都會很順利,但是當應用發布給大範圍的用戶,在各種網絡環境下運行時,必然會出現看門狗超時報告。
另壹個可能的問題場景是數據量比較大時的數據庫版本遷移(也在主線程上),這也是促使我寫這篇總結的直接因素。
1.3用戶強制退出
壹看到“用戶強制退出”,妳可能想到的第壹件事就是雙擊Home鍵,然後關閉應用。但這種場景不會產生崩潰日誌,因為雙擊Home鍵後,所有應用都處於後臺狀態,iOS隨時可能關閉後臺進程,所以這種場景下沒有崩潰日誌。
另壹種場景是用戶同時按下電源鍵和Home鍵重啟iPhone。這種情況會生成壹個日誌(只驗證壹次),但它並不特定於特定的應用程序。
這裏的“用戶強制退出”場景是稍微復雜壹點的操作:按住電源鍵直到出現“滑動關機”界面,然後按住Home鍵,此時當前應用將被終止並生成相應事件的崩潰日誌。
通常情況下,用戶應該只有在應用卡頓,iOS響應受到影響的情況下才會這麽做——但是感覺這個操作太高級了,所以這樣的崩潰日誌應該很少。
2.常見錯誤識別
2.1異常代碼
上面“用戶強制退出”的崩潰日誌中的異常代碼為“0xdeadfa11”,上面“看門狗超時”的崩潰日誌中的異常代碼為“0x8badf00d”,是唯壹的異常代碼。
根據官方文檔描述,至少有以下具體的異常代碼:
0x8badf00d錯誤代碼:Watchdog超時,表示“吃了變質的食物”。
0xdeadfa11錯誤代碼:用戶強制退出,表示“死摔”。
0xbaaaaaad錯誤代碼:用戶按住Home鍵和volume鍵獲取當前內存狀態,並不意味著崩潰。
0xbad22222錯誤代碼:VoIP應用(因為太頻繁?)被iOS幹掉了。
0xc00010ff錯誤代碼:因為太熱而被殺,意思是“涼快壹下”。
0xdead10cc錯誤代碼:因為在後臺還占用系統資源(比如通訊錄),所以被殺死,意思是“死鎖”。
2.2異常類型
查看我們的崩潰分析報告郵件,我們會發現最常見的錯誤類型是SEGV(分段違規),這表明內存操作不當,例如訪問未經授權的內存地址。
當我們接收到SIGSEGV信號時,我們可以考慮以下幾個方面:
訪問無效內存地址,比如訪問僵屍對象;
試圖將數據寫入只讀區域;
取消引用空指針;
使用未初始化的指針;
堆棧溢出;
此外,還有其他常見的信號:
SIGABRT:收到abort信號後,可以自己調用Abort(),也可以接收外部發來的信號;
SIGBUS:總線錯誤。與SIGSEGV不同的是,SIGSEGV訪問的是無效地址(比如虛擬內存無法映射到物理內存),而SIGBUS訪問的是有效地址,但總線訪問異常(比如地址對齊);
SIGILL:試圖執行非法指令,可能無法識別或沒有權限;
SIGFPE:浮點誤差,數學計算相關的問題(可能不限於浮點計算),比如被零除;
SIGPIPE:在管道的另壹端沒有進程接管數據;
3.代碼錯誤
另外,常見的崩潰基本都是由代碼bug引起的,比如數組越界、空插入、多線程安全、訪問野指針、發送未實現的選擇器等。如果引入核心數據,還有其他常見問題,但這是另壹個話題。
遇到這些bug時,有明確的錯誤原因解釋,比如“空數組索引0越界”等等。需要稍微註意的是多線程。當妳壹時找不到解決方案的時候,不妨考慮多線程。