ios開(kāi)發(fā)-崩潰分析

崩潰日志

如何得到crash report

當(dāng)一個(gè)iOS應(yīng)用程序崩潰時(shí), 系統(tǒng)會(huì)創(chuàng)建一份crash日志保存在設(shè)備上. 這份crash日志記錄著應(yīng)用程序崩潰時(shí)的信息, 通常包含著每個(gè)執(zhí)行線程的棧調(diào)用信息(低內(nèi)存閃退日志例外). 如果設(shè)備就在身邊, 可以連接設(shè)備, 打開(kāi)Xcode - Window - Organizer, 在左側(cè)面板中選擇Device Logs(可以選擇具體設(shè)備的Device Logs或者Library下所有設(shè)備的Device Logs), 然后根據(jù)時(shí)間排序查看設(shè)備上的crash日志. 這是開(kāi)發(fā), 測(cè)試階段最經(jīng)常采用的方式.

如果應(yīng)用程序已經(jīng)提交到App Store發(fā)布, 用戶已經(jīng)安裝使用了, 那么開(kāi)發(fā)者可以通過(guò)iTunes Connect(Manage Your Applications - View Details - Crash Reports)獲取用戶的crash日志. 不過(guò)這并不是100%有效的, 因?yàn)檫@需要用戶設(shè)備同意上傳相關(guān)信息.

線上app的崩潰日志會(huì)被app store收集并符號(hào)化分組. 類似的崩潰報(bào)告的集合被稱為崩潰點(diǎn), (如果用戶選擇了與蘋(píng)果共享診斷數(shù)據(jù), 這些崩潰日志才會(huì)被收集并被符號(hào)化). 打開(kāi)Xcode - Window - Organizer, 在點(diǎn)擊相應(yīng)應(yīng)用后, 會(huì)顯示此應(yīng)用的崩潰集合. 可以看到每一個(gè)集合中都會(huì)有很多個(gè)設(shè)備, 如果右鍵進(jìn)去查看的話, 會(huì)看到很多文件. 右鍵顯示包內(nèi)容, 會(huì)看到最終的詳細(xì)日志, 當(dāng)選中了一個(gè)崩潰集合后, 如果選擇在項(xiàng)目中打開(kāi), 會(huì)在項(xiàng)目代碼中找到具體出問(wèn)題的代碼. 選中Open in Project的話, 會(huì)直接在工程中打開(kāi).

如果用戶反饋應(yīng)用曾虧, 也可以通過(guò)讓用戶設(shè)備與iTunes同步, 設(shè)備與電腦上的iTunes Store同步后, 會(huì)將崩潰日志保存在電腦上(路徑:Mac OS X:~/Library/Logs/CrashReporter/MobileDevice/)到上述位置把崩潰日志下載下來(lái), 然后通過(guò)電子郵件發(fā)送給你.

通過(guò)第三方工具來(lái)獲取崩潰信息.

如何得到.dSYM

我們?cè)贏rchive的時(shí)候會(huì)生成.xcarchive文件, 顯示包內(nèi)容就能夠在里面找到.dSYM文件和.app文件. .dSYM文件位于 /Users/<用戶名>/Library/Developer/Xcode/Archives 目錄下, 在目錄中包含了一個(gè)16進(jìn)制的保存函數(shù)地址映射信息的中轉(zhuǎn)文件, 所有Debug文件的symbols都在這個(gè)文件中(包含文件名, 函數(shù)名, 行號(hào)等), 也稱之為調(diào)試符號(hào)信息文件.

當(dāng)我們軟件 release 模式打包或上線后, 不會(huì)像我們?cè)?Xcode 中那樣直觀的看到用崩潰的錯(cuò)誤, 這個(gè)時(shí)候我們就需要分析 crash report 文件了, iOS 設(shè)備中會(huì)有日志文件保存我們每個(gè)應(yīng)用出錯(cuò)的函數(shù)內(nèi)存地址, 通過(guò) Xcode 的 Organizer 可以將 iOS 設(shè)備中的 DeviceLog 導(dǎo)出成 crash 文件, 這個(gè)時(shí)候我們就可以通過(guò)出錯(cuò)的函數(shù)地址去查詢 dSYM 文件中程序?qū)?yīng)的函數(shù)名和文件名. 大前提是我們需要有軟件版本對(duì)應(yīng)的 dSYM 文件, 這也是為什么我們很有必要保存每個(gè)發(fā)布版本的 Archives 文件了.

每一個(gè) xx.app 和 xx.app.dSYM 文件都有對(duì)應(yīng)的 UUID, crash 文件也有自己的 UUID, 只要這三個(gè)文件的 UUID 一致, 我們就可以通過(guò)他們解析出正確的錯(cuò)誤函數(shù)信息了.

通過(guò)dwarfdump --uuid xx.app/xx (xx代表你的項(xiàng)目名)查看xx.app文件的UUID

通過(guò)dwarfdump --uuid xx.app.dSYM查看xx.dSYM的UUID

crash 文件內(nèi)第一行 Incident Identifier 就是該 crash 文件的 UUID.

結(jié)合分析crash文件

根據(jù)crash report, .dSYM分析崩潰函數(shù).

如果使用的是友盟的話, 友盟自帶的有一個(gè)分析工具. 但是要注意, 使用的時(shí)候要確保你的.xcarchive在 ~/Library/Developer/Xcode/或該路徑的子目錄下. .xcarchive里的.dsYM文件和.app文件是有對(duì)應(yīng)的UUID的. 然后你的crash report里也是有UUID, 只有當(dāng)UUID相等時(shí)才能分析對(duì). 如果是在別人電腦上archive, 那你需要把.dSYM文件copy過(guò)來(lái).

symbolicatecrash是xcode的一個(gè)符號(hào)化crash log的命令行工具. 使用方法也就是導(dǎo)出.crash文件(crash log)和找到.dsYM文件, 然后進(jìn)行分析.?查看這里

如果你有多個(gè)“.ipa”文件, 多個(gè)”.dSYMB”文件, 你并不太確定到底“dSYM”文件對(duì)應(yīng)哪個(gè)”.ipa”文件, 可以使用命令行工具atos.?查看這里

崩潰日志分析

Xcode->Window->Organizer->Crashes,?這里有關(guān)于崩潰日志的詳細(xì)分析.

盜圖一張, 關(guān)于崩潰日志的詳細(xì)信息

野指針?lè)治?/h2>

因?yàn)橐爸羔樀脑虬l(fā)生崩潰是常常出現(xiàn)的事, 而且比較隨機(jī). 所以我們要提高野指針的崩潰率好來(lái)幫我們快速找到有問(wèn)題的代碼. 對(duì)象釋放后只有出現(xiàn)被隨機(jī)填入的數(shù)據(jù)是不可訪問(wèn)的時(shí)候才會(huì)必現(xiàn)Crash.

這個(gè)地方我們可以做一下手腳, 把這一隨機(jī)的過(guò)程變成不隨機(jī)的過(guò)程. 對(duì)象釋放后在內(nèi)存上填上不可訪問(wèn)的數(shù)據(jù), 其實(shí)這種技術(shù)其實(shí)一直都有, Edit Scheme -> Diagnostics ->Enable Malloc Scribble 選中就可以實(shí)現(xiàn)這個(gè)功能

僵尸模式分析

啟用了NSZombieEnabled, 它會(huì)用一個(gè)僵尸來(lái)替換默認(rèn)的dealloc實(shí)現(xiàn), 也就是在引用計(jì)數(shù)降到0時(shí), 該僵尸實(shí)現(xiàn)會(huì)將該對(duì)象轉(zhuǎn)換成僵尸對(duì)象. 僵尸對(duì)象的作用是在你向它發(fā)送消息時(shí), 它會(huì)顯示一段日志并自動(dòng)跳入調(diào)試器. 當(dāng)啟用NSZombieEnabled時(shí), 一個(gè)錯(cuò)誤的內(nèi)存訪問(wèn)就會(huì)變成一條無(wú)法識(shí)別的消息發(fā)送給僵尸對(duì)象. 僵尸對(duì)象會(huì)顯示接受到得信息, 然后跳入調(diào)試器, 這樣你就可以查看到底是哪里出了問(wèn)題.

一般這時(shí)崩潰的原因就是因?yàn)檎{(diào)用了已經(jīng)釋放的內(nèi)存空間,或者說(shuō)重復(fù)釋放了某個(gè)地址空間.

打開(kāi)NSZombieEnabled之后, 如果遇到對(duì)應(yīng)的崩潰類型既調(diào)用了已經(jīng)釋放的內(nèi)存空間, 或者說(shuō)重復(fù)釋放了某個(gè)地址空間, 那么就能在GDB中看到對(duì)應(yīng)的輸出信息.

如果崩潰是發(fā)生在當(dāng)前調(diào)用棧, 通過(guò)上面的做法, 系統(tǒng)就會(huì)把崩潰原因定位到具體代碼中. 但是, 如果崩潰不在當(dāng)前調(diào)用棧, 系統(tǒng)就僅僅只能把崩潰地址告訴我們, 而沒(méi)辦法定位到具體代碼, 這樣我們也沒(méi)法去修改錯(cuò)誤. 這時(shí)就可以修改scheme, 讓xcode記錄每個(gè)地址alloc的歷史, 這樣我們就可以用命令把這個(gè)地址還原出來(lái). Edit Scheme -> Environment Variables -> 加入MallocStackLoggingNoCompact, 并且設(shè)置為YES.

Enable Address Sanitizer

Edit Scheme -> Diagnostics ->Enable Address Sanitizer選中就可以實(shí)現(xiàn)這個(gè)功能, 設(shè)置這個(gè)參數(shù)后, 我們可以看到一些更詳細(xì)的錯(cuò)誤信息提示, 設(shè)置還有內(nèi)存使用情況的展示.

這類工具的理論依據(jù)是: 訪問(wèn)內(nèi)存時(shí), 通過(guò)比較訪問(wèn)的內(nèi)存和程序?qū)嶋H分配的內(nèi)存, 驗(yàn)證內(nèi)存訪問(wèn)的有效性, 從而在bug發(fā)生時(shí)就檢測(cè)到它們, 而不會(huì)等到副作用產(chǎn)生時(shí)才有所察覺(jué).

靜態(tài)分析

Static Analyzer是一個(gè)非常好的工具去發(fā)現(xiàn)編譯器警告不會(huì)提示的問(wèn)題和一些個(gè)人的內(nèi)錯(cuò)泄露和死存儲(chǔ)(不會(huì)用到的賦了值的變量)錯(cuò)誤. 這個(gè)方法可能大大的提高內(nèi)存使用和性能, 以及提升應(yīng)用的整體穩(wěn)定性和代碼質(zhì)量.

打開(kāi)方式: Xcode->Product-Analyze 然后我們就能看到如下藍(lán)色箭頭所示的一些有問(wèn)題的代碼.

unrecognized selector send to instancd 快速定位

在debug navigator的斷點(diǎn)欄里添加Create Symbolic Breakpoint

在Symbolic中填寫(xiě)如下方法簽名:?-[NSObject(NSObject) doesNotRecognizeSelector:]

常見(jiàn)崩潰信息類型

Exception Type異常類型

SIGABRT類型

在iOS中就是未被捕獲的Objective-C異常(NSException)導(dǎo)致程序向自身發(fā)送了SIGABRT信號(hào)而崩潰, 常見(jiàn)的如下:

SIGSEGV: 段錯(cuò)誤信息(SIGSEGV)是操作系統(tǒng)產(chǎn)生的一個(gè)更嚴(yán)重的問(wèn)題。當(dāng)硬件出現(xiàn)錯(cuò)誤、訪問(wèn)不可讀的內(nèi)存地址或向受保護(hù)的內(nèi)存地址寫(xiě)入數(shù)據(jù)時(shí),就會(huì)發(fā)生這個(gè)錯(cuò)誤。硬件錯(cuò)誤這一情況并不常見(jiàn)。當(dāng)要讀取保存在RAM中的數(shù)據(jù),而該位置的RAM硬件有問(wèn)題時(shí),你會(huì)收到SIGSEGV。SIGSEGV更多是出現(xiàn)在后兩種情況。默認(rèn)情況下,代碼頁(yè)不允許進(jìn)行寫(xiě)操作,而數(shù)據(jù)而不允許進(jìn)行執(zhí)行操作。當(dāng)應(yīng)用中的某個(gè)指針指向代碼頁(yè)并試圖修改指向位置的值時(shí),你會(huì)收到SIGSEGV。當(dāng)要讀取一個(gè)指針的值,而它被初始化成指向無(wú)效內(nèi)存地址的垃圾值時(shí),你也會(huì)收到SIGSEGV SIGSEGV錯(cuò)誤調(diào)試起來(lái)更困難,而導(dǎo)致SIGSEGV的最常見(jiàn)原因是不正確的類型轉(zhuǎn)換。要避免過(guò)度使用指針或嘗試手動(dòng)修改指針來(lái)讀取私有數(shù)據(jù)結(jié)構(gòu)。如果你那樣做了,而在修改指針時(shí)沒(méi)有注意內(nèi)存對(duì)齊和填充問(wèn)題,就會(huì)收到SIGSEGV。

SIGBUS: 總線錯(cuò)誤信號(hào)(SIGBUG)代表無(wú)效內(nèi)存訪問(wèn),即訪問(wèn)的內(nèi)存是一個(gè)無(wú)效的內(nèi)存地址。也就是說(shuō),那個(gè)地址指向的位置根本不是物理內(nèi)存地址(它可能是某個(gè)硬件芯片的地址)。SIGSEGV和SIGBUS都羽毛球EXC_BAD_ACCESS的子類型。

SIGTRAP: SIGTRAP代表陷阱信號(hào)。它并不是一個(gè)真正的崩潰信號(hào)。它會(huì)在處理器執(zhí)行trap指令發(fā)送。LLDB調(diào)試器通常會(huì)處理此信號(hào),并在指定的斷點(diǎn)處停止運(yùn)行。如果你收到了原因不明的SIGTRAP,先清除上次的輸出,然后重新進(jìn)行構(gòu)建通常能解決這個(gè)問(wèn)題。

EXC_ARITHETIC: 當(dāng)要除零時(shí),應(yīng)用會(huì)收到EXC_ARITHMETIC信號(hào)。這個(gè)錯(cuò)誤應(yīng)該很容易解決.

SIGILL: SIGILL代表signal illegal instruction(非法指令信號(hào))。當(dāng)在處理器上執(zhí)行非法指令時(shí),它就會(huì)發(fā)生。執(zhí)行非法指令是指,將函數(shù)指針會(huì)給另外一個(gè)函數(shù)時(shí),該函數(shù)指針由于某種原因是壞的,指向了一段已經(jīng)釋放的內(nèi)存或是一個(gè)數(shù)據(jù)段。有時(shí)你收到的是EXC_BADINSTRUCTION而不是SIGILL,雖然它們是一回事,不過(guò)EXC*等同于此信號(hào)不依賴體系結(jié)構(gòu)。

SIGABRT: SIGABRT代表SIGNAL ABORT(中止信號(hào))。當(dāng)操作系統(tǒng)發(fā)現(xiàn)不安全的情況時(shí),它能夠?qū)@種情況進(jìn)行更多的控制;必要的話,它能要求進(jìn)程進(jìn)行清理工作。在調(diào)試造成此信號(hào)的底層錯(cuò)誤時(shí),并沒(méi)有什么妙招。Cocos2d或UIKit等框架通常會(huì)在特定的前提條件沒(méi)有滿足或一些糟糕的情況出現(xiàn)時(shí)調(diào)用C函數(shù)abort(由它來(lái)發(fā)送此信號(hào))。當(dāng)SIGABRT出現(xiàn)時(shí),控制臺(tái)通常會(huì)輸出大量的信息,說(shuō)明具體哪里出錯(cuò)了。由于它是可控制的崩潰,所以可以在LLDB控制臺(tái)上鍵入bt命令打印出回溯信息

更多的SIGABRT信號(hào)類型看這里.

EXC_BAD_ACCESS

EXC_BAD_ACCESS是一個(gè)比較難處理的crash, 當(dāng)一個(gè)app進(jìn)入一種毀壞的狀態(tài), 通常是由于內(nèi)存管理問(wèn)題而引起的時(shí), 就會(huì)出現(xiàn)出現(xiàn)這樣的crash. 通常Signal信號(hào)錯(cuò)誤都會(huì)提醒EXC_BAD_ACCESS, 我們可以通過(guò)僵尸模式來(lái)捕獲這種異常.

看門(mén)狗超時(shí)

這種崩潰通常比較容易分辨, 因?yàn)殄e(cuò)誤碼是固定的0x8badf00d. 在iOS上, 它經(jīng)常出現(xiàn)在執(zhí)行一個(gè)同步網(wǎng)絡(luò)調(diào)用而阻塞主線程的情況. 因此, 永遠(yuǎn)不要進(jìn)行同步網(wǎng)絡(luò)調(diào)用.

Exception Codes異常編碼

0x8badf00d: 讀做 “ate bad food”! (把數(shù)字換成字母,是不是很像 :p)該編碼表示應(yīng)用是因?yàn)榘l(fā)生watchdog超時(shí)而被iOS終止的。 通常是應(yīng)用花費(fèi)太多時(shí)間而無(wú)法啟動(dòng)、終止或響應(yīng)用系統(tǒng)事件。

0xbad22222: 該編碼表示 VoIP 應(yīng)用因?yàn)檫^(guò)于頻繁重啟而被終止。

0xdead10cc: 讀做 “dead lock”!該代碼表明應(yīng)用因?yàn)樵诤笈_(tái)運(yùn)行時(shí)占用系統(tǒng)資源,如通訊錄數(shù)據(jù)庫(kù)不釋放而被終止 。

0xdeadfa11: 讀做 “dead fall”! 該代碼表示應(yīng)用是被用戶強(qiáng)制退出的。根據(jù)蘋(píng)果文檔, 強(qiáng)制退出發(fā)生在用戶長(zhǎng)按開(kāi)關(guān)按鈕直到出現(xiàn) “滑動(dòng)來(lái)關(guān)機(jī)”, 然后長(zhǎng)按 Home按鈕。強(qiáng)制退出將產(chǎn)生 包含0xdeadfa11 異常編碼的崩潰日志, 因?yàn)榇蠖鄶?shù)是強(qiáng)制退出是因?yàn)閼?yīng)用阻塞了界面

#常見(jiàn)的崩潰類型收集

新老操作系統(tǒng)兼容

在新 iOS 上正常的應(yīng)用, 到了老版本 iOS 上秒退最常見(jiàn)原因是系統(tǒng)動(dòng)態(tài)鏈接庫(kù)或Framework無(wú)法找到. 這種情況通常是由于 App 引用了一個(gè)新版操作系統(tǒng)里的動(dòng)態(tài)庫(kù)(或者某動(dòng)態(tài)庫(kù)的新版本)或只有新 iOS 支持的 Framework, 而又沒(méi)有對(duì)老系統(tǒng)進(jìn)行測(cè)試, 于是當(dāng) App 運(yùn)行在老系統(tǒng)上時(shí)便由于找不到而秒退.

還有就是有些方法是新版操作系統(tǒng)才支持的, 而又沒(méi)有對(duì)該方法是否存在于老系統(tǒng)中做出判斷.

本地存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)變化

程序在升級(jí)時(shí), 修改了本地存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu), 但是對(duì)用戶既存的舊數(shù)據(jù)沒(méi)有做好升級(jí), 結(jié)果導(dǎo)致初始化時(shí)因?yàn)闊o(wú)法正確讀取用戶數(shù)據(jù)而秒退.

訪問(wèn)的數(shù)據(jù)為空或者數(shù)據(jù)類型不對(duì)

這類情況是比較常見(jiàn)的, 后端傳回了空數(shù)據(jù), 客戶端沒(méi)有做對(duì)應(yīng)的判斷繼續(xù)執(zhí)行下去了, 這樣就產(chǎn)生了crash. 或者自己本地的某個(gè)數(shù)據(jù)為空數(shù)據(jù)而去使用了. 還有就是訪問(wèn)的數(shù)據(jù)類型不是期望的數(shù)據(jù)類型而產(chǎn)生崩潰.

操作了不該操作的對(duì)象

這里有可能是空指針或者野指針.

空指針是沒(méi)有存儲(chǔ)任何內(nèi)存地址的指針, 一般指nil, 但是在iOS中向nil發(fā)消息是不會(huì)崩潰的.

野指針是指指向一個(gè)已刪除的對(duì)象(“垃圾”內(nèi)存既不可用內(nèi)存)或未申請(qǐng)?jiān)L問(wèn)受限內(nèi)存區(qū)域的指針. 野指針是比較危險(xiǎn)的, 因?yàn)橐爸羔樦赶虻膶?duì)象已經(jīng)被釋放了, 不能用了, 你再給被釋放的對(duì)象發(fā)送消息就是違法的, 所以會(huì)崩潰.

內(nèi)存處理不當(dāng)

內(nèi)存管理是軟件開(kāi)發(fā)中一個(gè)重要的課題, iOS自從引入ARC機(jī)制后, 對(duì)于內(nèi)存的管理開(kāi)發(fā)者好像輕松了很多, 但是還會(huì)發(fā)生一些內(nèi)存泄露之類的問(wèn)題. Facebook工程師們開(kāi)源了一些自動(dòng)化工具來(lái)解決監(jiān)測(cè)內(nèi)存泄露問(wèn)題:FBRetainCycleDetector、FBAllocationTracker、FBMemoryProfiler.

主線程UI長(zhǎng)時(shí)間卡死, 被系統(tǒng)殺掉

主線程被卡住是非常常見(jiàn)的場(chǎng)景, 具體表現(xiàn)就是程序不響應(yīng)任何的UI交互. 這時(shí)按下調(diào)試的暫停按鈕, 查看堆棧, 就可以看到是到底是死鎖, 死循環(huán)等, 導(dǎo)致UI線程被卡住.

多線程之間切換訪問(wèn)引起的crash

多線程引起的崩潰大部分是因?yàn)槭褂脭?shù)據(jù)庫(kù)的時(shí)候多線程同時(shí)讀寫(xiě)數(shù)據(jù)庫(kù)而造成了crash.?這里關(guān)于多線程crash的調(diào)試.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Apple Develop官方原文:Understanding and Analyzing Application...
    iDeveloper閱讀 5,881評(píng)論 1 9
  • 前言 iOS崩潰是讓iOS開(kāi)發(fā)人員比較頭痛的事情,app崩潰了,說(shuō)明代碼寫(xiě)的有問(wèn)題,這時(shí)如何快速定位到崩潰的地方很...
    齊滇大圣閱讀 65,888評(píng)論 29 443
  • 寫(xiě)在前面:本文會(huì)在最開(kāi)頭將蘋(píng)果官方的文檔Understanding and Analyzing Applicati...
    小小外星人閱讀 26,578評(píng)論 23 130
  • 當(dāng)一個(gè)應(yīng)用崩潰時(shí),會(huì)產(chǎn)生一個(gè)崩潰報(bào)告。這個(gè)報(bào)告對(duì)分析崩潰問(wèn)題是非常有用的。這篇文章主要講述了如何符號(hào)化、了解和分析...
    alvin_wang閱讀 3,981評(píng)論 1 6
  • 崩潰分析 崩潰日志(crash log) 根據(jù)符號(hào)表來(lái)監(jiān)測(cè)崩潰位置 什么是符號(hào)表符號(hào)表就是指在Xcode項(xiàng)目編譯后...
    紙簡(jiǎn)書(shū)生閱讀 5,921評(píng)論 0 17

友情鏈接更多精彩內(nèi)容