1. 程式人生 > >crash日誌的分析

crash日誌的分析

二進制文件 解決 view -c form array 通過 網絡請求 voip

怎樣獲得crash日誌
怎樣解析crash日誌
怎樣分析crash日誌
1. iOS策略相關
2. 常見錯誤標識
3. 代碼bug


一、怎樣獲得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)。或者自己編寫一個進行自己主動化收集、解析和統計匯總。


二、怎樣解析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


三、怎樣分析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事件比較抽象。那麽用代碼來直接描寫敘述的話,相應的就是(創建一個project時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”等。

須要略微註意點的是多線程問題,當一時找不到解決思路時。最好還是往多線程方面考慮下。

crash日誌的分析