iOS: Crash檔案解析(一)

發表於2015-06-29

開發程式的過程中不管我們已經如何小心,總是會在不經意間遇到程式閃退。腦補一下當你在一群人面前自信的拿著你的App做功能預演的時候,流暢的操作被無情地Crash打斷。聯想起老羅在釋出Smartisan OS的時候說了,他準備了10個手機,如果一臺有問題,就換一臺,如果10臺後掛了他就不做手機了。好了不閒扯了,今天就跟大家一起聊聊iOSCrash檔案的組成以及常用的分析工具。

有一個WWDC 2010的視訊推薦大家抽空看看,視訊名稱“Understanding Crash Reports on iPhone OS”,該視訊詳細講解了Crash檔案的結構。當然如果你沒時間看的話,不妨閱讀以下這篇文章。

 

一、Crash檔案結構

當程式執行Crash的時候,系統會把執行的最後時刻的執行資訊記錄下來,儲存到一個檔案中,也就是我們所說的Crash檔案。iOS的Crash日誌通常由以下6各部分組成。

1、Process Information(程式資訊)

Incident Idnetifier 崩潰報告的唯一識別符號,不同的Crash
CrashReporter Key 裝置標識相對應的唯一鍵值(並非真正的裝置的UDID,蘋果為了保護使用者隱私iOS6以後已經無法獲取)。通常同一個裝置上同一版本的App發生Crash時,該值都是一樣的。
Hardware Model 代表發生Crash的裝置型別,上圖中的“iPad4,4”代表iPad Air
Process 代表Crash的程式名稱,通常都是我們的App的名字, []裡面是當時程式的ID
Path 可執行程式在手機上的儲存位置,注意路徑時到XXX.app/XXX,XXX.app其實是作為一個Bundle的,真正的可執行檔案其實是Bundle裡面的XXX,感興趣的可以自己查一下相關資料,有機會我後面也會介紹到
Identifier 你的App的Indentifier,通常為“com.xxx.yyy”,xxx代表你們公司的域名,yyy代表某一個App
Version 當前App的版本號,由Info.plist中的兩個欄位組成,CFBundleShortVersionString and CFBundleVersion
Code Type 當前App的CPU架構
Parent Process 當前程式的父程式,由於iOS中App通常都是單程式的,一般父程式都是launchd

 

2、Basic Information

Date/Time Crash發生的時間,可讀的字串
OS Version 系統版本,()內的數字代表的時Bulid號
Report Version Crash日誌的格式,目前基本上都是104,不同的version裡面包含的欄位可能有不同

 

3、Exception(非常重要)

Exception Type 異常型別
Exception Subtype: 異常子型別
Crashed Thread 發生異常的執行緒號

 

4、Thread Backtrace

發生Crash的執行緒的Crash呼叫棧,從上到下分別代表呼叫順序,最上面的一個表示丟擲異常的位置,依次往下可以看到API的呼叫順序。上圖的資訊表明本次Crash出現xxxViewController的323行,出錯的函式呼叫為orderCountLoadFailed。

5、Thread State

Crash時發生時刻,執行緒的狀態,通常我們根據Crash棧即可獲取到相關資訊,這部分一般不用關心。

6、Binary Images

Crash時刻App載入的所有的庫,其中第一行是Crash發生時我們App可執行檔案的資訊,可以看出為armv7,可執行檔案的包得uuid位c0f……cd65,解析Crash的時候dsym檔案的uuid必須和這個一樣才能完成Crash的符號化解析。

二、常見的Crash型別

1、Watchdog timeout

Exception Code:0x8badf00d, 不太直觀,可以讀成“eat bad food”,意思是don‘t block main thread

緊接著下面會有一段描述:

Application Specific Information:

com.xxx.yyy   failed to resume in time

對於此類Crash,我們應該去審視自己App初始化時做的事情是否正確,是否在主執行緒請求了網路,或者其他耗時的事情卡住了正常初始化流程。

通常系統允許一個App從啟動到可以相應使用者事件的時間最多為5S,如果超過了5S,App就會被系統終止掉。在Launch,resume,suspend,quit時都會有相應的時間要求。在Highlight Thread裡面我們可以看到被終止時呼叫到的位置,xxxAppDelegate加上行號。

PS. 在連線Xcode除錯時為了便於除錯,系統會暫時禁用掉Watchdog,所以此類問題的發現需要使用正常的啟動模式。

2、User force-quit

Exception Codes: 0xdeadfa11, deadfall

這個強制退出跟我們平時所說的kill掉後臺任務操作還不太一樣,通常在程式bug造成系統無法響應時可以採用長按電源鍵,當螢幕出現關機確認畫面時按下Home鍵即可關閉當前程式。

3、Low Memory termination

跟一般的Crash結構不太一樣,通常有Free pages,Wired Pages,Purgeable pages,largest process 組成,同事會列出當前時刻系統執行所有程式的資訊。

關於Memory warning可以參看我之前寫的一篇文章IOS 記憶體警告 Memory warning level

App在執行過程中,系統記憶體緊張時通常會先發警告,同時把後臺掛起的程式終止掉,最終如果還是記憶體不夠的話就會終止掉當前前臺的程式。

當接受到記憶體警告的事後,我們應該釋放盡可能多的記憶體,Crash其實也可以看做是對App的一種保護。

4、Crash due to bugs

因為程式bug導致的Crash通常千奇百怪,很難一概而論。大部分情況通過Crash日誌就可以定位出問題,當然也不排除部分疑難雜症看半天都不值問題出在哪兒。這個就只能看功底了,一點點找,總是能發現蛛絲馬跡。是在看不出來時還可以求助於Google大神,總有人遇到和你一樣的Bug

三、常見的Exception Type & Exception Code

1、Exception Type

1)EXC_BAD_ACCESS

此型別的Excpetion是我們最長碰到的Crash,通常用於訪問了不改訪問的記憶體導致。一般EXC_BAD_ACCESS後面的”()”還會帶有補充資訊。

SIGSEGV: 通常由於重複釋放物件導致,這種型別在切換了ARC以後應該已經很少見到了。

SIGABRT:  收到Abort訊號退出,通常Foundation庫中的容器為了保護狀態正常會做一些檢測,例如插入nil到陣列中等會遇到此類錯誤。

SEGV:(Segmentation  Violation),代表無效記憶體地址,比如空指標,未初始化指標,棧溢位等;

SIGBUS:匯流排錯誤,與 SIGSEGV 不同的是,SIGSEGV 訪問的是無效地址,而 SIGBUS 訪問的是有效地址,但匯流排訪問異常(如地址對齊問題)

SIGILL:嘗試執行非法的指令,可能不被識別或者沒有許可權

2)EXC_BAD_INSTRUCTION

此類異常通常由於執行緒執行非法指令導致

3)EXC_ARITHMETIC

除零錯誤會丟擲此類異常

2、Exception Code

0xbaaaaaad 此種型別的log意味著該Crash log並非一個真正的Crash,它僅僅只是包含了整個系統某一時刻的執行狀態。通常可以通過同時按Home鍵和音量鍵,可能由於使用者不小心觸發
0xbad22222 當VOIP程式在後臺太過頻繁的啟用時,系統可能會終止此類程式
0x8badf00d 這個前面已經介紹了,程式啟動或者恢復時間過長被watch dog終止
0xc00010ff 程式執行大量耗費CPU和GPU的運算,導致裝置過熱,觸發系統過熱保護被系統終止
0xdead10cc 程式退到後臺時還佔用系統資源,如通訊錄被系統終止
0xdeadfa11 前面也提到過,程式無響應使用者強制關閉

三、獲取Crash的途徑

1、本機

通過xCode連線測試機器,直接在Device中即可讀取到該機器上發生的所有Crash log。

2、itunes connect

通過itunes connect後臺獲取到使用者上報的Crash日誌。

3、第三方的Crash收集系統

有很多優秀的第三方Crash收集系統大大的方便了我們收集Crash,甚至還帶了符號化Crash日誌的功能。比較常用的有CrashlyticsFlurry等。

四、附錄

Apple官方文件:Understanding and Analyzing iOS Application Crash Reports

Technical Note TN2123 CrashReporter

https://developer.apple.com/library/ios/qa/qa1592/_index.html

WWDC視訊:  Understanding Crash Reports on iPhone OS

Crash日誌記錄的時候是將Crash發生時刻,函式的呼叫棧,以及執行緒等資訊寫入檔案。一般都是直接寫的16進位制地址,如果不經過符號化的話,基本上很難獲取到有用資訊,下一篇我們將聊一聊Crash日誌的符號化,通俗點講就是讓Crash日誌變成我們可讀的格式。

相關文章