閱讀1016 返回首頁    go 技術社區[雲棲]


iOS應用的crash日誌的分析基礎

Outline
如何獲得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日誌的討論:https://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事件比較抽象,那麼用代碼來直接描述的話,對應的就是(創建一個工程時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”等。需要稍微注意點的是多線程問題,當一時找不到解決思路時,不妨往多線程方麵考慮下。


看到這裏的移動開發者(iOS/Android),如果有興趣到阿裏巴巴(杭州)工作,可以聯係我,或者直接把簡曆發到 siqin.ljp(at)taobao.com。

最後更新:2017-04-03 12:55:01

  上一篇:go HDU 1010 Tempter of the Bone(搜索經典題)
  下一篇:go JAVA中URL鏈接中文參數亂碼處理方法