iOS: Crash文件解析(一)
開發(fā)程序的過程中不管我們已經(jīng)如何小心烹玉,總是會(huì)在不經(jīng)意間遇到程序閃退。腦補(bǔ)一下當(dāng)你在一群人面前自信的拿著你的App做功能預(yù)演的時(shí)候仪召,流暢的操作被無情地Crash打斷。聯(lián)想起老羅在發(fā)布Smartisan OS的時(shí)候說了瞒窒,他準(zhǔn)備了10個(gè)手機(jī)啼止,如果一臺(tái)有問題浆劲,就換一臺(tái),如果10臺(tái)后掛了他就不做手機(jī)了烫扼。好了不閑扯了曙求,今天就跟大家一起聊聊iOSCrash文件的組成以及常用的分析工具。
有一個(gè)WWDC 2010的視頻推薦大家抽空看看映企,視頻名稱“Understanding Crash Reports on iPhone OS”悟狱,該視頻詳細(xì)講解了Crash文件的結(jié)構(gòu)。當(dāng)然如果你沒時(shí)間看的話堰氓,不妨閱讀以下這篇文章挤渐。
一、Crash文件結(jié)構(gòu)
當(dāng)程序運(yùn)行Crash的時(shí)候双絮,系統(tǒng)會(huì)把運(yùn)行的最后時(shí)刻的運(yùn)行信息記錄下來浴麻,存儲(chǔ)到一個(gè)文件中,也就是我們所說的Crash文件囤攀。iOS的Crash日志通常由以下6各部分組成软免。
1、Process Information(進(jìn)程信息)
Tables | Are |
---|---|
Incident Idnetifier | 崩潰報(bào)告的唯一標(biāo)識(shí)符焚挠,不同的Crash |
CrashReporter Key | 設(shè)備標(biāo)識(shí)相對(duì)應(yīng)的唯一鍵值(并非真正的設(shè)備的UDID膏萧,蘋果為了保護(hù)用戶隱私iOS6以后已經(jīng)無法獲取)。通常同一個(gè)設(shè)備上同一版本的App發(fā)生Crash時(shí),該值都是一樣的向抢。 |
Hardware Model | 代表發(fā)生Crash的設(shè)備類型认境,上圖中的“iPad4,4”代表iPad Air |
Process | 代表Crash的進(jìn)程名稱,通常都是我們的App的名字, []里面是當(dāng)時(shí)進(jìn)程的ID |
Path | 可執(zhí)行程序在手機(jī)上的存儲(chǔ)位置挟鸠,注意路徑時(shí)到XXX.app/XXX叉信,XXX.app其實(shí)是作為一個(gè)Bundle的,真正的可執(zhí)行文件其實(shí)是Bundle里面的XXX艘希,感興趣的可以自己查一下相關(guān)資料硼身,有機(jī)會(huì)我后面也會(huì)介紹到 |
Identifier | 你的App的Indentifier,通常為“com.xxx.yyy”覆享,xxx代表你們公司的域名佳遂,yyy代表某一個(gè)App |
Version | 當(dāng)前App的版本號(hào),由Info.plist中的兩個(gè)字段組成撒顿,CFBundleShortVersionString and CFBundleVersion
|
Code Type | 當(dāng)前App的CPU架構(gòu) |
Parent Process | 當(dāng)前進(jìn)程的父進(jìn)程丑罪,由于iOS中App通常都是單進(jìn)程的,一般父進(jìn)程都是launchd |
2凤壁、Basic Information
Tables | Are |
---|---|
Date/Time | Crash發(fā)生的時(shí)間吩屹,可讀的字符串 |
OS Version | 系統(tǒng)版本,()內(nèi)的數(shù)字代表的時(shí)Bulid號(hào) |
Report Version | Crash日志的格式拧抖,目前基本上都是104煤搜,不同的version里面包含的字段可能有不同 |
3、Exception(非常重要)
Tables | Are |
---|---|
Exception Type | 異常類型 |
Exception Subtype: | 異常子類型 |
Crashed Thread | 發(fā)生異常的線程號(hào) |
4唧席、Thread Backtrace
發(fā)生Crash的線程的Crash調(diào)用棧擦盾,從上到下分別代表調(diào)用順序,最上面的一個(gè)表示拋出異常的位置淌哟,依次往下可以看到API的調(diào)用順序迹卢。上圖的信息表明本次Crash出現(xiàn)xxxViewController的323行,出錯(cuò)的函數(shù)調(diào)用為orderCountLoadFailed绞绒。
5婶希、Thread State
Crash時(shí)發(fā)生時(shí)刻,線程的狀態(tài)蓬衡,通常我們根據(jù)Crash棧即可獲取到相關(guān)信息喻杈,這部分一般不用關(guān)心。
6狰晚、Binary Images
Crash時(shí)刻App加載的所有的庫筒饰,其中第一行是Crash發(fā)生時(shí)我們App可執(zhí)行文件的信息,可以看出為armv7壁晒,可執(zhí)行文件的包得uuid位c0f……cd65瓷们,解析Crash的時(shí)候dsym文件的uuid必須和這個(gè)一樣才能完成Crash的符號(hào)化解析。
二、常見的Crash類型
1谬晕、Watchdog timeout
Exception Code:0x8badf00d碘裕, 不太直觀,可以讀成“eat bad food”攒钳,意思是don‘t block main thread
緊接著下面會(huì)有一段描述:
Application Specific Information:
com.xxx.yyy failed to resume in time
對(duì)于此類Crash帮孔,我們應(yīng)該去審視自己App初始化時(shí)做的事情是否正確,是否在主線程請(qǐng)求了網(wǎng)絡(luò)不撑,或者其他耗時(shí)的事情卡住了正常初始化流程文兢。
通常系統(tǒng)允許一個(gè)App從啟動(dòng)到可以相應(yīng)用戶事件的時(shí)間最多為5S,如果超過了5S焕檬,App就會(huì)被系統(tǒng)終止掉姆坚。在Launch,resume实愚,suspend兼呵,quit時(shí)都會(huì)有相應(yīng)的時(shí)間要求。在Highlight Thread里面我們可以看到被終止時(shí)調(diào)用到的位置爆侣,xxxAppDelegate加上行號(hào)萍程。
PS. 在連接Xcode調(diào)試時(shí)為了便于調(diào)試,系統(tǒng)會(huì)暫時(shí)禁用掉Watchdog兔仰,所以此類問題的發(fā)現(xiàn)需要使用正常的啟動(dòng)模式。
2蕉鸳、User force-quit
Exception Codes: 0xdeadfa11, deadfall
這個(gè)強(qiáng)制退出跟我們平時(shí)所說的kill掉后臺(tái)任務(wù)操作還不太一樣乎赴,通常在程序bug造成系統(tǒng)無法響應(yīng)時(shí)可以采用長(zhǎng)按電源鍵,當(dāng)屏幕出現(xiàn)關(guān)機(jī)確認(rèn)畫面時(shí)按下Home鍵即可關(guān)閉當(dāng)前程序潮尝。
3榕吼、Low Memory termination
跟一般的Crash結(jié)構(gòu)不太一樣,通常有Free pages勉失,Wired Pages羹蚣,Purgeable pages,largest process 組成乱凿,同事會(huì)列出當(dāng)前時(shí)刻系統(tǒng)運(yùn)行所有進(jìn)程的信息顽素。
關(guān)于Memory warning可以參看我之前寫的一篇文章IOS 內(nèi)存警告 Memory warning level。
App在運(yùn)行過程中徒蟆,系統(tǒng)內(nèi)存緊張時(shí)通常會(huì)先發(fā)警告胁出,同時(shí)把后臺(tái)掛起的程序終止掉,最終如果還是內(nèi)存不夠的話就會(huì)終止掉當(dāng)前前臺(tái)的進(jìn)程段审。
當(dāng)接受到內(nèi)存警告的事后全蝶,我們應(yīng)該釋放盡可能多的內(nèi)存,Crash其實(shí)也可以看做是對(duì)App的一種保護(hù)。
4抑淫、Crash due to bugs
因?yàn)槌绦騜ug導(dǎo)致的Crash通常千奇百怪绷落,很難一概而論。大部分情況通過Crash日志就可以定位出問題始苇,當(dāng)然也不排除部分疑難雜癥看半天都不值問題出在哪兒砌烁。這個(gè)就只能看功底了,一點(diǎn)點(diǎn)找埂蕊,總是能發(fā)現(xiàn)蛛絲馬跡往弓。是在看不出來時(shí)還可以求助于Google大神,總有人遇到和你一樣的Bug
三蓄氧、常見的Exception Type & Exception Code
1函似、Exception Type
1)EXC_BAD_ACCESS
此類型的Excpetion是我們最長(zhǎng)碰到的Crash,通常用于訪問了不改訪問的內(nèi)存導(dǎo)致喉童。一般EXC_BAD_ACCESS后面的"()"還會(huì)帶有補(bǔ)充信息撇寞。
SIGSEGV: 通常由于重復(fù)釋放對(duì)象導(dǎo)致,這種類型在切換了ARC以后應(yīng)該已經(jīng)很少見到了堂氯。
SIGABRT: 收到Abort信號(hào)退出蔑担,通常Foundation庫中的容器為了保護(hù)狀態(tài)正常會(huì)做一些檢測(cè),例如插入nil到數(shù)組中等會(huì)遇到此類錯(cuò)誤咽白。
SEGV:(Segmentation Violation)啤握,代表無效內(nèi)存地址,比如空指針晶框,未初始化指針排抬,棧溢出等;
SIGBUS:總線錯(cuò)誤授段,與 SIGSEGV 不同的是蹲蒲,SIGSEGV 訪問的是無效地址,而 SIGBUS 訪問的是有效地址侵贵,但總線訪問異常(如地址對(duì)齊問題)
SIGILL:嘗試執(zhí)行非法的指令届搁,可能不被識(shí)別或者沒有權(quán)限
2)EXC_BAD_INSTRUCTION
此類異常通常由于線程執(zhí)行非法指令導(dǎo)致
3)EXC_ARITHMETIC
除零錯(cuò)誤會(huì)拋出此類異常
2、Exception Code
Tables | Are |
---|---|
0xbaaaaaad | 此種類型的log意味著該Crash log并非一個(gè)真正的Crash窍育,它僅僅只是包含了整個(gè)系統(tǒng)某一時(shí)刻的運(yùn)行狀態(tài)卡睦。通常可以通過同時(shí)按Home鍵和音量鍵蔫骂,可能由于用戶不小心觸發(fā) |
0xbad22222 | 當(dāng)VOIP程序在后臺(tái)太過頻繁的激活時(shí)么翰,系統(tǒng)可能會(huì)終止此類程序 |
0x8badf00d | 這個(gè)前面已經(jīng)介紹了,程序啟動(dòng)或者恢復(fù)時(shí)間過長(zhǎng)被watch dog終止 |
0xc00010ff | 程序執(zhí)行大量耗費(fèi)CPU和GPU的運(yùn)算辽旋,導(dǎo)致設(shè)備過熱浩嫌,觸發(fā)系統(tǒng)過熱保護(hù)被系統(tǒng)終止 |
0xdead10cc | 程序退到后臺(tái)時(shí)還占用系統(tǒng)資源檐迟,如通訊錄被系統(tǒng)終止 |
0xdeadfa11 |
前面也提到過,程序無響應(yīng)用戶強(qiáng)制關(guān)閉 |
三码耐、獲取Crash的途徑
1追迟、本機(jī)
通過xCode連接測(cè)試機(jī)器,直接在Device中即可讀取到該機(jī)器上發(fā)生的所有Crash log骚腥。
2敦间、itunes connect
通過itunes connect后臺(tái)獲取到用戶上報(bào)的Crash日志。
3束铭、第三方的Crash收集系統(tǒng)
有很多優(yōu)秀的第三方Crash收集系統(tǒng)大大的方便了我們收集Crash廓块,甚至還帶了符號(hào)化Crash日志的功能。比較常用的有Crashlytics契沫,Flurry等带猴。
四、附錄
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日志記錄的時(shí)候是將Crash發(fā)生時(shí)刻懈万,函數(shù)的調(diào)用棧拴清,以及線程等信息寫入文件。一般都是直接寫的16進(jìn)制地址会通,如果不經(jīng)過符號(hào)化的話口予,基本上很難獲取到有用信息,下一篇我們將聊一聊Crash日志的符號(hào)化涕侈,通俗點(diǎn)講就是讓Crash日志變成我們可讀的格式沪停。