全面理解和分析 iOS 的崩潰日志

felf50t6125 8年前發布 | 31K 次閱讀 iOS開發 移動開發

寫在前面:本文會在最開頭將蘋果官方的文檔 Understanding and Analyzing Application Crash Reports 進行翻譯,但這不僅僅是一篇翻譯的文章,本文會讓大家更加全面的了解ios的崩潰報告的獲取、分析、用途。翻譯的時候我會結合自己以往的使用經驗來進行翻譯。

理解和分析應用程序崩潰報告

重要提示:本文檔包含有關開發中的一個接口或技術的初步信息。此信息將被更改,并根據本文檔實現的軟件應該用最終的操作系統軟件進行測試。

當應用程序崩潰時,創建了一個崩潰報告,這是非常有用的了解什么造成的崩潰。本文件包含重要的信息,如何symbolicate,理解和解釋的崩潰報告。

簡介

當應用程序崩潰時,將創建一個崩潰報告并存儲在設備上。崩潰報告描述的情況下,應用程序終止,在大多數情況下,包括每個執行線程的一個完整的回溯,并通常對于調試應用中的問題是非常有用。你應該看看這些崩潰報告,了解你的應用程序有什么崩潰,然后嘗試修復它們。

有回溯的崩潰報告需要被符號化了才可以分析。符號化(symbolication)用人們類可讀的函數名稱和行號來取代內存地址。如果你通過Xcode的設備窗口來獲取設備的崩潰日志,它們將在幾秒鐘后自動被符號化。否則,你將需要自己將崩潰日志符號化,通過自己導入崩潰文件到Xcode設備窗口。看到符號化(symbolicating)后的崩潰報告。

低內存報告不同于其他的崩潰報告,沒有回朔在這種類型的報告。當低內存崩潰發生時,你必須調查你的內存使用模式和對低內存警告的響應。此文檔指向了您可能會發現有用的多個內存管理引用。

獲取崩潰和低內存報告

調試部署iOS應用程序 討論如何獲取碰撞和直接從iOS設備內存不足的報道。

在 應用程序分發指南 中的 分析崩潰報告 討論如何從TestFlight beta測試已經那些從App Store下載應用程序的用戶中收集崩潰報告。

回到頂部

symbolicating崩潰報告

symbolication是解決回溯地址的源代碼的方法或函數名的過程,稱為符號化。沒有一symbolicating崩潰報告難以確定事故發生。

注:低內存報告不需要symbolicated。
注:來自MacOS崩潰報告通常是已經符號化的,或部分symbolicated,當時他們正在生成。
本節重點symbolicating從iOS,WatchOS,和TVOS的崩潰報告,但整體過程類似MacOS。
圖1概述了崩潰報告和symbolication過程。

獲取崩潰報告以及符號化過程

1.當編譯器將源代碼翻譯成機器代碼時,它也會生成調試符號,它將編譯后的二進制中的每一個機器指令映射到源代碼的行源代碼中。根據調試信息格式(debug_information_format)編譯設置,這些調試符號存儲在二進制或在同伴的調試符號文件(dsym)。默認情況下,調試版本的應用程序的調試符號存儲在編譯后的二進制中,而發布版本的應用程序的調試符號存儲在相應的dsym文中件以減少二進制大小。

調試符號文件和應用程序二進制文件與每一個build生成的UUID捆綁在一起。一個新的UUID生成是由build一個應用產生的,它應用程序每次build(編譯打包)的唯一標識。即使一個功能相同的可執行文件是從相同的源代碼重構,具有相同的編譯器設置,也會有不同的生成的UUID。調試符號文件的后續版本,甚至來自同一個源文件,不會與其他版本的二進制文件相混淆。

2.當你的存檔應用程序分配,Xcode會收集應用程序二進制隨著.dsym文件,并且存放在home文件夾里。你可以在Xcode的組織者在“歸檔”部分,找到所有你歸檔后(archived)的應用。

重要:為了符號化(symbolicate)從測試人員,審查程序,和客戶得到的崩潰報告,你必須保留每個存檔您的應用程序。

3.如果你是通過App Store發布的應用程序,或使test flight進行測試,你將選擇包括dsym文件到iTunes Connect。在提交對話框中,選擇“您的應用程序的應用程序符號……”。為了接收來test flight收集的 以及 那些選擇了分享的診斷數據的客戶的崩潰報告,上傳你的dsym文件是必要的 。有關崩潰報告服務的更多信息,可以參考應用程序分發指南。

重要:從app review收到的崩潰報告將是unsymbolicated,即使你有上傳dsym文件到iTunes Connect。
你需要使用Xcode 來symbolicate任何從app review得到的崩潰報告。
看下午中"到symbolicating崩潰報告Xcode"。

4.當你的應用程序崩潰時,一個unsymbolicated崩潰報告會被創建并存儲在設備上。

5.用戶可以按照 調試部署iOS應用程序的步驟 ,直接從他們的設備中檢索崩潰報告。如果你的應用程序通過AdHoc or Enterprise distribution發布的,這是從你的用戶獲取崩潰報告的唯一途徑。

6.從設備檢索得到的未符號化的(unsymbolicated)崩潰報告需要使用Xcode來符號化(symbolicated)。Xcode使用您的應用程序的二進制在源代碼中的源位置的回溯替換每個地址相關聯的dsym文件。其結果是一個符號化的(symbolicated)崩潰報告。

7.如果用戶選擇了與蘋果共享診斷數據,或者如果用戶通過TestFlight安裝了你的應用程序,崩潰報告會被上傳到應用商店。

8.App Store 符號化崩潰報告 并且將類似的崩潰報告分組。類似的崩潰報告的集合被稱為崩潰點。

9.符號化的崩潰報告是在Xcode's Crashes organizer提供給你的。

Bitcode

Bitcode是一個編譯程序的中間表示。當你可以用bitcode來 archive an application,編譯器產生的二進制文件包含bitcode而不是機器代碼。一旦二進制已經上傳到App Store,這可以被編譯成機器碼。App Store在將來,在沒有任何行動的一部分的情況下,利用未來的改進的編譯器,再次編譯。

Bitcode compilation process

因為你的二進制最后的編譯出現在App Store,Mac將不包含符號化從應用程序審查或用戶發給你他們從設備中取得的崩潰報告所需要的調試符號文件(.dsym)。雖然dsym文件是您歸檔應用程序(archive your application)的時候產生的,它是為bitcode二進制并不能用來symbolicate崩潰報告。App Store在你從Xcode或從iTunes Connect網站,可以獲得編譯的bitcode并且可以下載的過程中,產生dsym文件。符號化從應用程序審查或用戶發給你他們從設備中取得的崩潰報告,你必須下載這些dsym文件。崩潰報告獲得通過崩潰報告服務將自動symbolicated。

重要:二進制應用程序商店會比最初二進制編譯,有不同的UUID

從Xcode獲取dSYMs文件

1.下載dsyms文件

在歸檔管理中選擇相應的歸檔并下載dsyms文件

QQ20161122-0@2x.png

QQ20161122-1@2x.png

2.在歸檔出的文件中找到dSYMs文件

QQ20161122.png

QQ20161122-3@2x.png

QQ20161122-4@2x.png

確定崩潰報告是否被符號化

tn2151_symbolication_levels.png

符號化崩潰報告

1.符號化用xcode編譯安裝軟件的設備上的崩潰報告

當應用在設備上運行,遇到崩潰的時候會產生崩潰日志。如果這個應用是用xcode直接在設備上編譯運行的,那么就可以將手機連接到編譯的時候所用的電腦上,打開xcode,在Window的Devices中去查看日志。找到日志的時候,xcode會自己去符號化崩潰日志。

選取設備

查看設備日志

xcode自己去符號化崩潰文件

2.符號化用安裝包安裝在測試設備上的應用所產生的崩潰日志

符號化的時候需要準備symbolicatecrash文件 .dSYM文件 以及.app文件

符號化前先檢查一下三者的uuid是不是一致的,只有是一致的才能符號化成功。

查看xx.app文件的uuid的方法:

dwarfdump --uuid xxx.app/xxx (xxx工程名)

查看xx.app.dSYM文件的uuid的方法令:

dwarfdump --uuid xxx.app.dSYM (xxx工程名)

而.crash的uuid位于,crash日志中的Binary Images:中的第一行尖括號內。如:armv7 <8bdeaf1a0b233ac199728c2a0ebb4165>

將三種文件拷貝到同一個目錄中,在終端中使用命令

./symbolicatecrash ./ .crash ./ .app.dSYM > xxx.crash來解析崩潰日志。

QQ20161209-5.png

如果想今后解析日志的時候更方便一點,特別是解析多個崩潰日志的時候,如果一個一個去解析的話,很花費時間的。我的話,會將這些寫到一個腳本中,解析的時候就只用執行腳本,就可以很方便快捷的獲取到崩潰日志。

3.獲取并符號化線上的崩潰報告

一.通過打包上線時的xcode來獲取線上的崩潰報告

線上app的崩潰日志會被app store收集并符號化分組。類似的崩潰報告的集合被稱為崩潰點。(如果用戶選擇了與蘋果共享診斷數據,這些崩潰日志才會被收集并被符號化)

QQ20161122-0@2x.png

在點擊相應應用后,會顯示此應用的崩潰集合。可以看到每一個集合中都會有很多個設備,如果右鍵進去查看的話,會看到很多文件。

QQ20161122-0.png

QQ20161122-1.png

再次郵件進去查看的話,會最終看到詳細的崩潰日志

QQ20161122-7.png

當選中了一個崩潰集合后,如果選擇在項目中打開,會在項目代碼中找到具體出問題的代碼

QQ20161122-8.png

QQ20161122-2.png

QQ20161122-3.png

二、通過友盟等第三方工具來獲取崩潰日志

崩潰日志列表

QQ20161209-0.png

其中的一個崩潰日志

QQ20161209-1.png

崩潰的代碼的位置,這個是最關鍵的,可以通過這個來找到代碼中的出問題的地方

QQ20161209-2.png

export dSYMPath="$(find ~/Library/Developer/Xcode -iname '*.dSYM' -print0 | xargs -0 dwarfdump -u | grep C0349572-9622-3A00-81D0-4DDE0E00DC7A | sed -E 's/^{FNXX==XXFN}+//' | head -n 1)";是為了找到歸檔時候產生的dsym文件的路徑

dwarfdump --arch=armv7 --lookup 0x426031 "$dSYMPath"是符號化的關鍵,可以找出出問題的地方。

QQ20161209-4.png

分析符號化后的崩潰報告

1.頭部

每個崩潰報告都會以一個頭部開始

Listing 4  Excerpt of the header from a crash report.
Incident Identifier: B6FD1E8E-B39F-430B-ADDE-FC3A45ED368C
CrashReporter Key: f04e68ec62d3c66057628c9ba9839e30d55937dc
Hardware Model: iPad6,8
Process: TheElements [303]
Path: /private/var/containers/Bundle/Application/888C1FA2-3666-4AE2-9E8E-62E2F787DEC1/TheElements.app/TheElements
Identifier: com.example.apple-samplecode.TheElements
Version: 1.12
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd [1]
Coalition: com.example.apple-samplecode.TheElements [402]

Date/Time: 2016-08-22 10:43:07.5806 -0700
Launch Time: 2016-08-22 10:43:01.0293 -0700
OS Version: iPhone OS 10.0 (14A5345a)
Report Version: 104

以下的值是特別值得注意的:

Incident Identifier: 一個唯一的標識. 不會存在共用一個標識的崩潰報告.

CrashReporter Key:是與設備標識相對應的唯一鍵值。雖然它不是真正的設備標識符,但也是一個非常有用的情報:如果你看到100個崩潰日志的CrashReporter Key值都是相同的,或者只有少數幾個不同的CrashReport值,說明這不是一個普遍的問題,只發生在一個或少數幾個設備上。

Process: 是應用名稱。中括號里面的數字是閃退時應用的進程ID。

Version: 崩潰進程的版本號. 這個值包含在 CFBundleVersion and CFBundleVersionString中.

Code Type: 崩潰日志所在設備的架構. 會是ARM-64, ARM, x86-64, or x86中的一個.

OS Version: 崩潰發生時的系統版本

異常信息

異常信息會列出異常的類型、位置。

以下的內容是摘錄的一個崩潰報告的異常代碼段,該崩潰報告是一個進程由于一個未捕獲的異常而崩潰產生的。

Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0

以下的內容是摘錄于一個因為空指針的訪問而崩潰產生的崩潰報告的異常代碼段

Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [0]
Triggered by Thread: 0

異常信息中可能出現的字段的解釋如下:

Exception Codes: 處理器的具體信息有關的異常編碼成一個或多個64位進制數。通常情況下,這個區域不會被呈現,因為將異常代碼解析成人們可以看懂的描述是在其它區域進行的。

Exception Subtype: 供人們可讀的異常代碼的名字

Exception Message: 從異常代碼中提取的額外的可供人們閱讀的信息.

Exception Note: 不是特定于一個異常類型的額外信息.如果這個區域包含SIMULATED (這不是一個崩潰)然后進程沒有崩潰,但是被watchdog殺掉了

Termination Reason: 當一個進程被終止的時的原因。

Triggered by Thread: 異常所在的線程.

下面的小節將介紹一些最常見的異常類型。

Bad Memory Access [EXC_BAD_ACCESS // SIGSEGV // SIGBUS]

進程試圖訪問無效的內存,或試圖以內存的保護級別所不允許的方式去訪問內存(例如,寫入到只讀存儲器)。異常類型字段(Exception Subtype)包含一個kern_return_t描述錯誤,和錯誤的訪問的內存地址。

Here are some tips for debugging a bad memory access crash:

If objc_msgSend or objc_release are near the top of the Backtraces for the crashed thread, the process may have attempted to message a deallocated object. You should profile the application with the Zombies instrument to better understand the conditions of this crash.

If gpus_ReturnNotPermittedKillClient is near the top of the Backtraces for the crashed thread, the process was killed because it attempted to do rendering with OpenGL ES or Metal while in the background. See QA1766: How to fix OpenGL ES application crashes when moving to the background.

Run your application with the Address Sanitizer enabled. The address sanitizer adds additional instrumentation around memory access in your compiled code. As your application runs, Xcode will alert you if memory is accessed in a way that could lead to a crash.

Abnormal Exit (異常退出)[EXC_CRASH // SIGABRT]

進程異常退出。該異常類型崩潰最常見的原因是未捕獲的Objective-C和C++異常和調用abort()。

如果他們需要太多的時間來初始化,程序將被終止,因為觸發了看門狗。如果是因為啟動的時候被掛起,所產生的崩潰報告異常類型(Exception Subtype)將是launch_hang。

Trace Trap (追蹤捕獲)[EXC_BREAKPOINT // SIGTRAP]

類似于異常退出,這個異常是為了給附加的調試器中斷的過程的機會在其執行一個特定的點。您可以從您自己的代碼引發此異常使用__builtin_trap()函數。如果沒有調試器連接,進程將被終止并生成崩潰報告。

Illegal Instruction(非法指令) [EXC_BAD_INSTRUCTION // SIGILL]

進程試圖執行非法或未定義指令。這個進程可能試圖通過一個配置錯誤的函數指針,跳到一個無效的地址。在英特爾處理器,ud2操作碼造成EXC_BAD_INSTRUCTION異常但通常用于調試的時候的追蹤。在英特爾處理器上的swift代碼因為這個異常類型而被終止,如果在運行時遇到意外情況。有關詳細信息,請參閱追蹤捕獲。

Resource Limit [EXC_RESOURCE]

這個進程超出了資源消耗的限制。這是一個從操作系統通知,進程是使用太多的資源。這雖然不是崩潰但也會生成崩潰日志。

其它的異常信息

0x8badf00d: 讀做 “ate bad food”! (把數字換成字母,是不是很像 :p)該編碼表示應用是因為發生watchdog超時而被iOS終止的。 通常是應用花費太多時間而無法啟動、終止或響應用系統事件。

0xbad22222: 該編碼表示 VoIP 應用因為過于頻繁重啟而被終止。

0xdead10cc: 讀做 “dead lock”!該代碼表明應用因為在后臺運行時占用系統資源,如通訊錄數據庫不釋放而被終止 。

0xdeadfa11: 讀做 “dead fall”! 該代碼表示應用是被用戶強制退出的。根據蘋果文檔, 強制退出發生在用戶長按開關按鈕直到出現 “滑動來關機”, 然后長按 Home按鈕。強制退出將產生 包含0xdeadfa11 異常編碼的崩潰日志, 因為大多數是強制退出是因為應用阻塞了界面。

其它捕捉和符號化崩潰日志的方法

1.通過代碼來捕捉崩潰信息,并發送到指定的地方

在項目的代碼中重新指定頂層的錯誤處理的handler,這樣可以在軟件崩潰前的最后一秒調用錯誤處理的函數來獲得崩潰的信息(這里的信息已經是被符號化的)。然后將崩潰信息發送到自己的服務器或者其它的地方,以便開發者鞏固自己的代碼的可靠性。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
    appDelegate = self;
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    _notification = notification;

    NSSetUncaughtExceptionHandler(&caughtExceptionHandler);
    /*Changes the top-level error handler.
    Sets the top-level error-handling function where you can perform last-minute logging before the program terminates
    */
    return YES;
}

void caughtExceptionHandler(NSException *exception){
    /**
     *  獲取異常崩潰信息
     */
    NSArray *callStack = [exception callStackSymbols];
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    NSString *content = [NSString stringWithFormat:@"========異常錯誤報告========\\nname:%@\\nreason:\\n%@\\ncallStackSymbols:\\n%@",name,reason,[callStack componentsJoinedByString:@"\\n"]];

    //把異常崩潰信息發送至開發者郵件
    NSMutableString *mailUrl = [NSMutableString string];
    [mailUrl appendString:@"mailto:xxx@qq.com"];
    [mailUrl appendString:@"?subject=程序異常崩潰信息,請配合發送異常報告,謝謝合作!"];
    [mailUrl appendFormat:@"&body=%@", content];
    // 打開地址
    NSString *mailPath = [mailUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];
}

 

來自:http://www.jianshu.com/p/5119f76d93d6

 

 本文由用戶 felf50t6125 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!