戴銘(iOS開發(fā)課)讀書筆記:12章節(jié)-崩潰監(jiān)控

原文鏈接:iOS 崩潰千奇百怪,如何全面監(jiān)控?


一、編碼常見崩潰

1 數(shù)組越界
2 多線程問題
3 程序無響應(yīng)
4 野指針

二、捕獲崩潰問題

1 可捕獲的崩潰信號(hào)

KVO、Notification線程問題、數(shù)組越界、野指針

收集這些崩潰日志的常用方法:
1 Xcode -> Archive
2 PLCrashReporter 獲取崩潰日志,上傳到自己的服務(wù)器查看
3 Fabric
4 Bugly

捕獲原理:

程序發(fā)生崩潰時(shí),我們經(jīng)常會(huì)看到這段代碼:

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)

它表示,EXC_BAD_ACCESS這個(gè)異常會(huì)通過SIGSEGV信號(hào)發(fā)現(xiàn)問題。

可以通過 signalHandler 來捕獲不同種類的信號(hào),代碼如下:

void registerSignalHandler(void) {
    signal(SIGSEGV, handleSignalException);
    signal(SIGFPE, handleSignalException);
    signal(SIGBUS, handleSignalException);
    signal(SIGPIPE, handleSignalException);
    signal(SIGHUP, handleSignalException);
    signal(SIGINT, handleSignalException);
    signal(SIGQUIT, handleSignalException);
    signal(SIGABRT, handleSignalException);
    signal(SIGILL, handleSignalException);
}

void handleSignalException(int signal) {
    NSMutableString *crashString = [[NSMutableString alloc]init];
    void* callstack[128];
    int i, frames = backtrace(callstack, 128);
    char** traceChar = backtrace_symbols(callstack, frames);
    for (i = 0; i <frames; ++i) {
        [crashString appendFormat:@"%s\n", traceChar[i]];
    }
    NSLog(crashString);
}

上面這段代碼對(duì)各種信號(hào)都進(jìn)行了注冊(cè),捕獲到異常信號(hào)后,在處理方法 handleSignalException 里通過 backtrace_symbols 方法就能獲取到當(dāng)前的堆棧信息。
在程序崩潰前,將錯(cuò)誤的堆棧信息保存在本地磁盤中,下次啟動(dòng)時(shí)再上傳到服務(wù)器。

2 不可捕獲的崩潰信號(hào)

后臺(tái)任務(wù)超時(shí)、內(nèi)存打爆、主線程卡頓超閾值

App 退到后臺(tái)中,即使代碼邏輯沒有問題也很容易出現(xiàn)崩潰,這些崩潰往往是因?yàn)橄到y(tǒng)強(qiáng)制殺掉了某些進(jìn)程導(dǎo)致的,導(dǎo)致強(qiáng)殺的信號(hào)還由于系統(tǒng)限制無法被捕獲到。

后臺(tái)容易崩潰的原因是什么?
iOS后臺(tái)保活的方式有5種:Background Mode、Background Fetch、Silent Push、PushKit、Background Task。

每種方式都有不同的使用場(chǎng)景,這里就不一一介紹了,感興趣的朋友可以自己去了解一下。
這5種方式中,Background Task最為常用,App退到后臺(tái)后,默認(rèn)都會(huì)使用這種方式。

通過UIApplication的beginBackgroundTaskWithExpirationHandler:保持程序在后臺(tái)運(yùn)行

我們必須知道的是,App進(jìn)入后臺(tái)后,你的任務(wù)最終執(zhí)行的時(shí)間是有限的。(這個(gè)時(shí)間具體是多少?zèng)]有被考證,戴老師原文中說是3分鐘,上面提供的截圖中說的是10分鐘,截圖來自《iOS高級(jí)編程》)
總之,在有限的時(shí)間內(nèi)沒有執(zhí)行完任務(wù),系統(tǒng)也會(huì)強(qiáng)制殺掉進(jìn)程。而后臺(tái)處理的任務(wù)分門別類,所以很容易出現(xiàn)崩潰。尤其是要控制后臺(tái)讀寫操作。

如何監(jiān)控內(nèi)存打爆、主線程卡頓超閾值?
內(nèi)存打爆和主線程卡頓超過閾值被 watchdog 殺掉這兩種情況也會(huì)造成程序崩潰。
監(jiān)控他們的思路和監(jiān)控后臺(tái)崩潰類似,我們要先找到它們的閾值,然后在臨近閾值時(shí)還在執(zhí)行的后臺(tái)程序判斷為將要崩潰,收集信息并上報(bào)。(關(guān)于內(nèi)存和卡頓閾值怎么獲取和RunLoop有關(guān),后面會(huì)介紹)

三、分析和解決崩潰問題

對(duì)于捕獲的系統(tǒng)的崩潰日志,主要包含的信息為:進(jìn)程信息、基本信息、異常信息、線程回溯。

  • 進(jìn)程信息:崩潰進(jìn)程的相關(guān)信息,比如崩潰報(bào)告唯一標(biāo)識(shí)符、唯一鍵值、設(shè)備標(biāo)識(shí)
  • 基本信息:崩潰發(fā)生的日期、iOS版本
  • 異常信息:異常類型、異常編碼、異常的線程
  • 線程回溯:崩潰時(shí)的方法調(diào)用棧

方法1: 查看方法調(diào)用棧
先通過異常信息分析出發(fā)生異常的線程,然后分析方法調(diào)用棧,符號(hào)化后的方法調(diào)用棧可以完整的看到方法調(diào)用的過程,從而知道問題發(fā)生在哪個(gè)方法的調(diào)用上。

棧頂?shù)姆椒ň褪亲詈髮?dǎo)致崩潰的方法調(diào)用。

方法2: 分析異常編碼
一些被系統(tǒng)殺掉的情況,我們可以通過異常編碼來分析。
這里舉例3種常見的異常編碼:

  • 0x8badf00d,表示 App 在一定時(shí)間內(nèi)無響應(yīng)二倍 watchdog 殺掉
  • 0xdeadfa11,表示 App 被用戶強(qiáng)制退出
  • 0xc00010ff,表示 App 因?yàn)檫\(yùn)行造成設(shè)備溫度太高而被殺掉

方法3: 分析第三方平臺(tái)采集的崩潰信息
之前我們提到的向 Bugly 平臺(tái),在監(jiān)控到崩潰信息后,它們會(huì)提供可視化的信息來輔助我們追溯問題。

最后

解決線上崩潰問題是一個(gè)非常復(fù)雜且繁瑣的工作,由于我個(gè)人在這方面也沒有很豐富的經(jīng)驗(yàn),所以此篇文章僅做學(xué)習(xí)和了解。

雖說現(xiàn)在可以使用的第三方監(jiān)控平臺(tái)很多,但是有些錯(cuò)誤發(fā)生后難以定位和復(fù)現(xiàn)。解決崩潰問題涉及的知識(shí)也遠(yuǎn)不止這些,除了上文的內(nèi)容,可能還涉及 RunLoop 的相關(guān)知識(shí)、App對(duì)設(shè)備性能及電量的影響、App 的全量日志信息等知識(shí)。

太多內(nèi)容導(dǎo)致我無法去一一實(shí)踐和測(cè)試,做技術(shù)就是要不斷的學(xué)習(xí),希望自己能堅(jiān)持。

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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