Outline
如何得到crash日誌
如何解析crash日誌
如何分析crash日誌
1. iOS策略相關
2. 常見錯誤標識
3. 代碼bug
1、如何得到crash日誌
當一個iOS應用程序崩潰時,系統會建立一份crash日誌保存在設備上。這份crash日誌記錄着應用程序崩潰時的信息,一般包含着每一個執行線程的棧調用信息(低內存閃退日誌例外),對於開發人員定位問題頗有幫助。
若是設備就在身邊,能夠鏈接設備,打開Xcode - Window - Organizer,在左側面板中選擇Device Logs(能夠選擇具體設備的Device Logs或者Library下全部設備的Device Logs),而後根據時間排序查看設備上的crash日誌。這是開發、測試階段最常常採用的方式。
若是應用程序已經提交到App Store發佈,用戶已經安裝使用了,那麼開發者能夠經過iTunes Connect(Manage Your Applications - View Details - Crash Reports)獲取用戶的crash日誌。不過這並非100%有效的,並且大多數開發者並不依賴於此,由於這須要用戶設備贊成上傳相關信息,詳情可參見iOS: Providing Apple with diagnostics and usage information摘要。
考慮到並非全部iPhone用戶都容許自動發送診斷報告(crash日誌),並且對於部分提交到Apple得crash日誌,開發者還須要手動去拉取,而後找到對應的符號文件進行解析——這是一件很繁瑣的事情。因此實際項目開發中,一般接入現有的crash收集工具(參考1,參考2),或者本身編寫一個進行自動化收集、解析和統計彙總。
2、如何解析crash日誌
當得到一份crash日誌時,咱們須要將初始展現的十六進制地址等原始信息映射爲源代碼級別的方法名稱和代碼行數,使其對開發人員可讀。這個過程稱爲符號化解析。要成功地符號化解析一份crash日誌,咱們須要有對應的應用程序二進制文件以及符號(.dSYM)文件。
若是處於開發調試階段,一般Xcode都能匹配到crash日誌對應的二進制文件和符號文件,因此可以幫咱們自動解析。
若是處於測試階段,測試人員已經安裝了不一樣的版本(好比alpha、beta版本),那麼須要保存好對應版本的二進制文件和符號文件,以便在應用程序崩潰時對crash日誌進行解析。對於這種場景下產生的crash日誌,只須要將.crash文件、.app文件和.dSYM文件三者放在同一個目錄下,而後將.crash文件拖放到Xcode - Window - Organizer中左側面板Library下的Device Logs中,便可進行解析。
若是要提交發布,那麼咱們一般會先執行Clean,再Build,最後經過Product - Archive來打包。這樣,Xcode會將二進制文件和符號文件歸檔在一塊兒,能夠經過Organizer中的Archives進行瀏覽。
這裏是一份關於如何解析crash日誌的討論:http://stackoverflow.com/questions/1460892/symbolicating-iphone-app-crash-reports 。
3、如何分析crash日誌
在分析一份crash日誌以前,若是開發人員對於常見的錯誤類型有所瞭解,那定是極好的。
crash日誌的產生來源於兩種問題:違反iOS策略被幹掉,以及自身的代碼bug。
1. iOS策略
1.1 低內存閃退
前面提到大多數crash日誌都包含着執行線程的棧調用信息,可是低內存閃退日誌除外,這裏就先看看低內存閃退日誌是什麼樣的。
咱們使用Xcode 5和iOS 7的設備模擬一次低內存閃退,而後經過Organizer查看產生的crash日誌,能夠發現Process和Type都爲Unknown:
而具體的日誌內容以下:
第一部分是崩潰信息,包括識別標識、軟硬件信息和時間信息等。
第二部分是內存頁分配信息,以及當前佔用內存最多的進程,上圖中爲crashTypeDemo。
第三部分是具體的進程列表,描述着每一個進程使用內存的狀況以及當前狀態。在較早的版本中能夠在某些進程後面看到「jettisoned」字樣,代表這些進程使用過多內存被終止了,而如今咱們看到的是「vm-pageshortage」字樣。
當iOS檢測到內存太低時,它(的VM系統)會發出低內存警告通知,嘗試回收一些內存;若是狀況沒有獲得足夠的改善,iOS會終止後臺應用以回收更多內存;最後,若是內存仍是不足,那麼正在運行的應用可能會被終止掉。
因此,咱們的應用應該合理地響應系統拋出來的低內存警告通知,對一些緩存數據和可從新建立的對象進行釋放,同時要避免出現內存泄露等問題。
低內存閃退是由iOS策略決定終止應用程序運行的,一樣基於iOS策略的還有Watchdog超時和用戶強制退出。
1.2 Watchdog超時
Apple的iOS Developer Library網站上,QA1693文檔中描述了Watchdog機制,包括生效場景和表現。若是咱們的應用程序對一些特定的UI事件(好比啓動、掛起、恢復、結束)響應不及時,Watchdog會把咱們的應用程序幹掉,並生成一份響應的crash報告。
這份crash報告的有趣之處在於異常代碼:「0x8badf00d」,即「ate bad food」。
若是說特定的UI事件比較抽象,那麼用代碼來直接描述的話,對應的就是(建立一個工程時Xcode自動生成的)UIApplicationDelegate的幾個方法:
因此當遇到Watchdog日誌時,能夠檢查下上圖幾個方法是否有比較重的阻塞UI的動做。
QA1693舉的例子是在主線程進行同步網絡請求。若是咱們是在公司的Wifi環境下使用則一切順利,但當應用程序發佈出去面向很大範圍的用戶,在各類網絡環境下運行,則不可避免地會出現一片Watchdog超時報告。
另外一種可能出現問題的場景就是數據量比較大的狀況下進行的數據庫版本遷移(一樣是在主線程上),這也是促使我寫這篇總結的一個直接因素。
1.3 用戶強制退出
一看到「用戶強制退出」,首先可能想到的雙擊Home鍵,而後關閉應用程序。不過這種場景是不會產生crash日誌的,由於雙擊Home鍵後,全部的應用程序都處於後臺狀態,而iOS隨時都有可能關閉後臺進程,因此這種場景沒有crash日誌。
另外一種場景是用戶同時按住電源鍵和Home鍵,讓iPhone重啓。這種場景會產生日誌(僅驗證過一次),但並不針對特定應用程序。
這裏指的「用戶強制退出」場景,是稍微比較複雜點的操做:先按住電源鍵,直到出現「滑動關機」的界面時,再按住Home鍵,這時候當前應用程序會被終止掉,而且產生一份相應事件的crash日誌。
一般,用戶應該是遇到應用程序卡死,而且影響到了iOS響應,纔會進行這樣的操做——不過感受這操做好高級,因此這樣的crash日誌應該比較少見。
2. 常見錯誤標識
2.1 Exception codes
上面「用戶強制退出」的crash日誌中的Exception Codes是「0xdeadfa11」,再上面「Watchdog超時」的crash日誌中的Exception Codes是「0x8badf00d」,這些都是特有的Exception codes。
根據官方文檔描述,至少有如下幾種特定異常代碼:
0x8badf00d錯誤碼:Watchdog超時,意爲「ate bad food」。
0xdeadfa11錯誤碼:用戶強制退出,意爲「dead fall」。
0xbaaaaaad錯誤碼:用戶按住Home鍵和音量鍵,獲取當前內存狀態,不表明崩潰。
0xbad22222錯誤碼:VoIP應用(由於太頻繁?)被iOS幹掉。
0xc00010ff錯誤碼:由於太燙了被幹掉,意爲「cool off」。
0xdead10cc錯誤碼:由於在後臺時仍然佔據系統資源(好比通信錄)被幹掉,意爲「dead lock」。
2.2 Exception types
查看咱們的crash分析報告郵件,會發現最常常遇到的錯誤類型是SEGV(Segmentation Violation,段違例),代表內存操做不當,好比訪問一個沒有權限的內存地址。
當咱們收到SIGSEGV信號時,能夠往如下幾個方面考慮:
訪問無效內存地址,好比訪問Zombie對象;
嘗試往只讀區域寫數據;
解引用空指針;
使用未初始化的指針;
棧溢出;
此外,還有其它常見信號:
SIGABRT:收到Abort信號,可能自身調用abort()或者收到外部發送過來的信號;
SIGBUS:總線錯誤。與SIGSEGV不一樣的是,SIGSEGV訪問的是無效地址(好比虛存映射不到物理內存),而SIGBUS訪問的是有效地址,但總線訪問異常(好比地址對齊問題);
SIGILL:嘗試執行非法的指令,可能不被識別或者沒有權限;
SIGFPE:Floating Point Error,數學計算相關問題(可能不限於浮點計算),好比除零操做;
SIGPIPE:管道另外一端沒有進程接手數據;
3. 代碼bug
此外,比較常見的崩潰基本都源於代碼bug,好比數組越界、插空、多線程安全性、訪問野指針、發送未實現的selector等。若是引入Core Data,則又有另一些常見問題,不過這是另外一個話題了。
數據庫
遇到這些bug時,都有比較清楚的錯誤緣由說明,好比「index 0 beyond bounds for empty array」等。須要稍微注意點的是多線程問題,當一時找不到解決思路時,不妨往多線程方面考慮下數組