日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS 崩溃日志在线符号化实践

發(fā)布時(shí)間:2024/10/12 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS 崩溃日志在线符号化实践 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. 什么是符號(hào)化?

在日常開發(fā)中,應(yīng)用難免會(huì)發(fā)生崩潰。通常,我們直接從用戶導(dǎo)出來的崩潰日志都是未符號(hào)化或者部分符號(hào)化的,都是一堆十六進(jìn)制內(nèi)存地址的集合,可讀性較差。未符號(hào)化或者部分符號(hào)化的崩潰日志對(duì)閃退問題的解決幾乎毫無幫助,如下所示:

Last Exception Backtrace: 0 CoreFoundation 0x1ca4cd27c 0x1ca3b5000 + 1147516 1 libobjc.A.dylib 0x1c96a79f8 0x1c96a2000 + 23032 2 CoreFoundation 0x1ca3ded94 0x1ca3b5000 + 171412 3 TestBacktrace 0x102a47464 0x102a40000 + 29796 4 UIKitCore 0x1f6c86e30 0x1f63d3000 + 9125424

只有符號(hào)化后的崩潰日志才能顯示各個(gè)線程的函數(shù)調(diào)用,而不僅僅是毫無意義的虛擬內(nèi)存地址。符號(hào)化后的崩潰日志如下所示, 此時(shí),我們就能夠直接從堆棧信息中知道應(yīng)用 TestBacktrace 發(fā)生崩潰時(shí)的函數(shù)為 [AppDelegate Application:didFinishLaunchingWithOptions:],崩潰時(shí)函數(shù)所在文件為 AppDelegate.m,行號(hào)為23:

Last Exception Backtrace: 0 CoreFoundation 0x1ca4cd27c __exceptionPreprocess + 228 1 libobjc.A.dylib 0x1c96a79f8 objc_exception_throw + 55 2 CoreFoundation 0x1ca3ded94 -[__NSSingleObjectArrayI objectAtIndex:] + 127 3 TestBacktrace 0x102a47464 -[AppDelegate Application:didFinishLaunchingWithOptions:] + 29796 (AppDelegate.m:23) 4 UIKitCore 0x1f6c86e30 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 411

2. 符號(hào)化原理

丨2.1 什么是dSYM文件?

iOS 平臺(tái)中, dSYM 文件是指具有調(diào)試信息的目標(biāo)文件,文件名通常為:xxx.app.dSYM,其中 xxx 通常表示應(yīng)用程序的二進(jìn)制包名,如下圖所示:

通常我們可以在 Xcode 打包出來的文件 xcarchive 里面看到 dSYM 文件以及目錄架構(gòu):

dSYM 中存儲(chǔ)著文件名、方法名、行號(hào)等信息,是和可執(zhí)行文件的16進(jìn)制函數(shù)地址一一對(duì)應(yīng)的,通過分析崩潰的崩潰文件可以準(zhǔn)確知道具體的崩潰信息。DWARF(Debuging With Arbitrary Record Format) 是 ELF 和 Mach-O 等文件格式中用來存儲(chǔ)和處理調(diào)試信息的標(biāo)準(zhǔn)格式。DWARF 中的數(shù)據(jù)是高度壓縮的,可以通過 dwarfdump、otool 等命令提取其中的可讀信息。比如提取關(guān)鍵的調(diào)試信息 debug_info 、debug_line,可使用命令

dwarfdump --debug-line /Users/xxxx/Desktop/resource/TestBacktrace.app.dSYM > debug_line.txt

導(dǎo)出debug_line 的信息到文件 debug_line.txt 中,debug_info 也可以使用類似命令導(dǎo)出。

ELF、Mach-O 分別是 Linux 和 Mac OS 平臺(tái)用于存儲(chǔ)二進(jìn)制文件、可執(zhí)行文件、目標(biāo)代碼和共享庫的文件名稱。

丨2.2 如何生成dSYM文件

在編譯工程時(shí), Debug 模式會(huì)默認(rèn)選中不生成 dSYM 文件, 該配置可在 Build Setting|Build Option 中更改,Release 模式下 dSYM 是默認(rèn)生成的。另外,如果開啟了 bitcode 優(yōu)化的話,蘋果會(huì)做二次編譯優(yōu)化,所以最終的 dSYM 就需要在 Apple Connect 手動(dòng)下載了。每次編譯生成的 dSYM 都會(huì)有所差別,通常 dSYM 中會(huì)有一個(gè)唯一標(biāo)識(shí),稱作 UUID ,用以區(qū)分不同的 dSYM 文件。

丨2.3 如何通過崩潰日志中應(yīng)用的 UUID 找到匹配的 dSYM ?

還原崩潰堆棧時(shí),需要 dSYM 的 UUID 與崩潰時(shí)的應(yīng)用 UUID 一致。通常,每一個(gè) dSYM 文件都有一個(gè) UUID,和 App 文件中的 UUID 對(duì)應(yīng),代表著是一個(gè)應(yīng)用。而每一條崩潰信息都會(huì)記錄著應(yīng)用的 UUID,用來和 dSYM 的 UUID 進(jìn)行校對(duì)匹配。

  • 首先從崩潰日志的 Binary Images 后找到應(yīng)用的 UUID,如下可得到 TestBacktrace 的 UUID 為 6be881754f573769926b838490e39857。
  • Binary Images: 0x102a40000 - 0x102a6bfff TestBacktrace arm64 <6be881754f573769926b838490e39857> /var/containers/Bundle/Application/B44844E6-AFF4-491E-8168-F4ED93D644C2/TestBacktrace.App/TestBacktrace
  • 使用以下命令查看 dSYM 文件的 UUID,去掉 - 并且小寫之后,與第一步中的 UUID 是完全一致的,證明兩者是匹配的,否則是不匹配的。
  • xcrun dwarfdump --uuid < dSYM 文件>

  • 如果本地 dSYM 過多的話,一個(gè)個(gè)查看太麻煩,還可以使用mdfind命名根據(jù) UUID 在本機(jī)查找 dSYM。以上面的 UUID 為例,直接在終端輸入以下命令就可以了。
  • mdfind "com_apple_xcode_dsym_uuids == 6BE88175-4F57-3769-926B-838490E39857"

    丨2.4 符號(hào)化流程

    將崩潰日志中的 APP 二進(jìn)制地址轉(zhuǎn)化為函數(shù)流程如下所示:

    獲取到崩潰日志 App 關(guān)鍵行信息

    從上圖中可以看到 APP 的關(guān)建行為是:

    3 TestBacktrace 0x102a47464 0x102a40000 + 29796

    其中 TestBacktrace 為我們的二進(jìn)制包名名稱,其余行都是系統(tǒng)堆棧。

    獲取到偏移量、運(yùn)行時(shí)堆棧地址、運(yùn)行時(shí)APP起始地址
    由關(guān)鍵行信息獲取到 TestBacktrace 相對(duì)于起始地址的偏移量為 29796,運(yùn)行時(shí)堆棧地址為 0x102a47464,運(yùn)行時(shí)APP起始地址為 0x102a40000。

    獲取 dSYM 起始地址
    dSYM 文件中保存中符號(hào)表 TEXT 段的起始地址,起始地址可通過以下命令獲得:

    otool -l /Users/xxxxx/Desktop/TestBacktrace.app.dSYM/Contents/Resources/DWARF/TestBacktrace | grep __TEXT -C 5

    由上圖中可得到 dSYM 中代碼段起始地址為 0x10000000。

    計(jì)算崩潰地址對(duì)應(yīng) dSYM 符號(hào)表中的地址
    因?yàn)?iOS 加載 Mach-O 文件時(shí)為了安全使用了 ASLR(Address Space Layout Randomization) 機(jī)制,導(dǎo)致二進(jìn)制 Mach-O 文件每次加載到內(nèi)存的首地址都會(huì)不一樣,但是偏移量,加載地址,起始地址的計(jì)算規(guī)則是一樣的;從上面我們可以得到 0x102a47464 (運(yùn)行時(shí)地址) = 0x102a40000 (起始地址) + 29796(偏移量)這個(gè)公式。因此通過 dSYM 的起始地址和偏移量就可以計(jì)算出 0x102a47464 對(duì)應(yīng)在 dSYM 中的地址為 0x100007464 = 0x0000000100000000 + 29296。

    獲取到具體的函數(shù)/行數(shù)/文件
    獲取到運(yùn)行堆棧地址在 dSYM 文件的對(duì)應(yīng)地址 0x100007464 之后,在 dSYM 文件的 debug-info 中就可以查找到包含該地址的 DIE(Debug Information Entry) 單元,Mac OS 下可使用命令

    dwarfdump TestBacktrace.app.dSYM --lookup 0x100007464

    獲取相應(yīng)信息,如圖所示:

    • DW_TAG_Subprogram 表示這個(gè)DIE單元表示的是函數(shù)方法。
    • DW_AT_low_pc 表示這個(gè)方法起始地址為 0x1000073b4 。
    • DW_AT_high_pc 表示這個(gè)方法結(jié)束地址為 0x1000074c4 。這就表示崩潰日志中 0x102a47464 轉(zhuǎn)化后的偏移地址0x100007464 正好位于這
    • DW_AT_low_p 和 DW_AT_high_pc 之間。
    • DW_AT_name 表示我們的函數(shù)名為[AppDelegateApplication:didFinishLaunchingWithOptions:]
    • DW_AT_decl_file表示函數(shù)所在文件路徑為 AppDelegate.m
    • DW_AT_decl_line 表示函數(shù)開始行數(shù)為 19。

    組裝并格式化
    最終經(jīng)過格式優(yōu)化,崩潰日志中 0x102a47464 符號(hào)化出來對(duì)應(yīng)的方法為:

    3 TestBacktrace 0x102a47464 -[AppDelegate Application:didFinishLaunchingWithOptions:] + 29796 (AppDelegate.m:23

    3. 本地符號(hào)化

    丨3.1 符號(hào)化方法

    Xcode 符號(hào)化
    將崩潰日志、 dSYM 文件和可執(zhí)行文件放在同一目錄下,然后將 崩潰日志拖拽至 Devicelog中,右鍵 symbolicate Log 或者 Re-symbolicate Log 就能符號(hào)化。

    使用 symbolicatecrash 命令行符號(hào)化

    • 定位 symbolicatecrash 腳本

    通常 symbolicatecrash 的路徑為 /Applications/Xcode.App/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash

    • 前置運(yùn)行命令

    運(yùn)行 symbolicatecrash 前一般需要先運(yùn)行:

    export DEVELOPER_DIR="/Applications/XCode.App/Contents/Developer"
    • 運(yùn)行symbolicatecrash命令

    首先將崩潰日志、 dSYM 以及 symbolicatecrash 復(fù)制出來放到同一個(gè)文件夾,然后 cd 到當(dāng)前文件夾,運(yùn)行如下命令符號(hào)化

    ./symbolicatecrash TestBacktrace-2021-07-30-135514.ips TestBacktrace.app.dSYM > symbol.log

    丨3.2 系統(tǒng)日志符號(hào)化

    值得注意的是,有些時(shí)候,崩潰日志里并不會(huì)有 App 的調(diào)用,而可能全都是系統(tǒng)庫的調(diào)用,如下:

    Thread 32 Crashed: 0 libobjc.A.dylib 0x19aaf6c10 0x19aad3000 + 146448 1 CFNetwork 0x187545a28 0x18737d000 + 1870376 2 Foundation 0x18808db4c 0x187f6c000 + 1186636 3 Foundation 0x187f8a908 0x187f6c000 + 125192 4 Foundation 0x18808fde8 0x187f6c000 + 1195496 5 Foundation 0x187f8a5c4 0x187f6c000 + 124356 6 Foundation 0x1880907e0 0x187f6c000 + 1198048 7 Foundation 0x1880902ac 0x187f6c000 + 1196716 8 libdispatch.dylib 0x1869863e4 0x186976000 + 66532 9 libdispatch.dylib 0x1869d7298 0x186976000 + 397976 10 libdispatch.dylib 0x18697c028 0x186976000 + 24616 11 libdispatch.dylib 0x18697b828 0x186976000 + 22568 12 libdispatch.dylib 0x186988bb8 0x186976000 + 76728 13 libdispatch.dylib 0x186989378 0x186976000 + 78712 14 libsystem_pthread.dylib 0x1cf2c5580 0x1cf2ba000 + 46464

    符號(hào)化后的日志為:

    Thread 32 Crashed: 0 libobjc.A.dylib _objc_release (in libobjc.A.dylib) 16 1 CFNetwork __CFNetworkHTTPConnectionCacheSetLimit (in CFNetwork) 154728 2 Foundation ___NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ (in Foundation) 16 3 Foundation -[NSBlockOperation main] (in Foundation) 100 4 Foundation ___NSOPERATION_IS_INVOKING_MAIN__ (in Foundation) 20 5 Foundation -[NSOperation start] (in Foundation) 784 6 Foundation ___NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ (in Foundation) 20 7 Foundation ___NSOQSchedule_f (in Foundation) 180 8 libdispatch.dylib __dispatch_block_async_invoke2 (in libdispatch.dylib) 104 9 libdispatch.dylib __dispatch_client_callout (in libdispatch.dylib) 16 10 libdispatch.dylib __dispatch_continuation_pop$VARIANT$mp (in libdispatch.dylib) 412 11 libdispatch.dylib __dispatch_async_redirect_invoke (in libdispatch.dylib) 784 12 libdispatch.dylib __dispatch_root_queue_drain (in libdispatch.dylib) 376 13 libdispatch.dylib __dispatch_worker_thread2 (in libdispatch.dylib) 120 14 libsystem_pthread.dylib __pthread_wqthread (in libsystem_pthread.dylib) 212

    可以看出是 CFNetwork 網(wǎng)絡(luò)請(qǐng)求時(shí)發(fā)生野指針導(dǎo)致的問題,那么我們就可以針對(duì)網(wǎng)絡(luò)相關(guān)的請(qǐng)求做進(jìn)一步排查。

    至此可以得出結(jié)論:符號(hào)化系統(tǒng)庫是很有必要的,特別是對(duì)一些 App 堆棧信息完全沒有的崩潰日志。

    如何符號(hào)化系統(tǒng)庫符號(hào)
    符號(hào)化自己 App 的方法名,需要編譯生成的 dSYM 文件。而要將系統(tǒng)庫的符號(hào)化為完整的方法名,也需要 iOS 各系統(tǒng)庫的符號(hào)文件。

    • 匹配對(duì)應(yīng)的符號(hào)文件版本

    用戶的崩潰日志來自各種系統(tǒng)版本,需要對(duì)應(yīng)版本的系統(tǒng)符號(hào)文件才能符號(hào)化。系統(tǒng)庫符號(hào)文件不是通用的,而是對(duì)應(yīng)崩潰所在設(shè)備的系統(tǒng)版本和 CPU 型號(hào)的。

    崩潰日志中有這樣幾個(gè)信息:

    Code Type: ARM-64 OS Version: iOS 10.2 (14C82) ......... Binary Images: 0x102a40000 - 0x102a6bfff TestBacktrace arm64 <6be881754f573769926b838490e39857> /var/containers/Bundle/Application/B44844E6-AFF4-491E-8168-F4ED93D644C2/TestBacktrace.App/TestBacktrace

    Code Type 表示此設(shè)備的 CPU 架構(gòu)為 armv7、armv7s、arm64 還是 arm64e。

    OS Version 表示此設(shè)備的系統(tǒng)版本號(hào),括號(hào)中的字符串代表了此系統(tǒng)的 build 號(hào)。

    Binary Images 中的<9c893b6aa3b13d9596326ef6952e7195> 里面的字符表示對(duì)應(yīng)的系統(tǒng)庫 dyld 的 UUID,只有 build + UUID 匹配的系統(tǒng)庫符號(hào)文件才能符號(hào)化系統(tǒng)符號(hào)。

    • 把符號(hào)文件放到指定位置

    把獲取到的對(duì)應(yīng)版本的符號(hào)文件放到 Mac OS 的 ~/Library/Developer/Xcode/iOS DeviceSupport 目錄下,就可以使用Xcode自帶的符號(hào)化工具 symbolicatecrash 進(jìn)行符號(hào)化了。這個(gè)工具會(huì)自動(dòng)根據(jù)崩潰日志中系統(tǒng)庫的 UUID 搜索本機(jī)系統(tǒng)庫的符號(hào)文件。

    丨3.3 獲取系統(tǒng)符號(hào)文件的2個(gè)方法

    從真機(jī)上獲取
    大部分系統(tǒng)庫符號(hào)文件只能從真機(jī)上獲取,蘋果也沒有提供直接的下載地址。但是當(dāng)你用 Xcode 第一次連接某臺(tái)設(shè)備進(jìn)行真機(jī)調(diào)試時(shí),會(huì)看到 Xcode 顯示 Processing symbol files ,這時(shí)候就是在拷貝真機(jī)上的符號(hào)文件到 Mac OS 系統(tǒng)的 /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport 目錄下。

    目錄下的 14.7.1 (18G82) 這樣的文件夾就是對(duì)應(yīng)的符號(hào)文件,通常都有 1-5GB 的大小。

    從固件中提取符號(hào)文件
    從固件 (iPSW) 中可以通過一些方式提取到系統(tǒng)庫符號(hào)文件。固件解密分為 下載并提取系統(tǒng)符號(hào) 和 系統(tǒng)庫符號(hào) 提取兩步。

    1. 下載并提取系統(tǒng)符號(hào)

    • iOS9 以及 iOS9 之前

    a. 下載對(duì)應(yīng)版本的 iPSW 固件,直接解壓,解壓后里面有幾個(gè) dmg 格式的鏡像文件,最大的 dmg 文件就是系統(tǒng)鏡像。

    b. 從 Firmware_Keys (見文末參考鏈接)找到對(duì)應(yīng)固件的解密 key (頁面上 Root Filesystem 字段的 key )

    c. 用 dmg 工具進(jìn)行解密。cd 到解壓后的 iPSW 文件夾,執(zhí)行 ./dmg extract xxx-xxxx-xxx.dmg dec.dmg -k 。extract 后面跟兩個(gè)參數(shù),分別是系統(tǒng)鏡像 dmg 的名字和解密后的文件名,-k 后面填寫第2步獲取到的 key 。如果 key 不對(duì),解密會(huì)失敗。解密成功后會(huì)生成一個(gè) dec.dmg 文件,雙擊打開即可加載系統(tǒng)鏡像。

    • iOS10 以及 iOS10 之后

    下載對(duì)應(yīng)版本的 iPSW 固件,直接解壓,解壓后里面有幾個(gè) dmg 格式的鏡像文件,最大的dmg 文件就是系統(tǒng)鏡像。

    2. 系統(tǒng)庫符號(hào)提取
    從 iPhone OS 3.1 開始,所有的系統(tǒng)庫都打包成一個(gè)文件:dyld_shared_cache_xxx ,其中 xxx 表示具體的架構(gòu),此文件位于:/System/Library/Caches/com.Apple.dyld 目錄。dyld_shared_cache_xxx 文件的解壓可以使用 dyld 中的 dsc_extractor.cpp 代碼,但做一定的改動(dòng)。

    a. 首先在 Apple 開源網(wǎng)站下載源碼dyld庫的源碼,注意,這里需要下載 dyld-7 的源碼。

    b. 下載之后,將文件 dsc_extractor.cpp,main 函數(shù)前后的代碼改為如下代碼:

    #if 1 // test program#include <stdio.h>#include <stddef.h>#include <dlfcn.h> typedef int (*extractor_proc)(const char* shared_cache_file_path,const char* extraction_root_path,void (^progress)(unsigned current,unsigned total)); int main(int argc, const char* argv[]){if ( argc != 4 ) {fprintf(stderr,"usage: dsc_extractor <dsc_extractor.bundle path> <path-to-cache-file> <path-to-device-dir>\n");return 1;}void* handle = dlopen(argv[1],RTLD_LAZY);if ( handle == NULL ) {fprintf(stderr,"dsc_extractor.bundle could not be loaded\n");return 1;}extractor_proc proc = (extractor_proc)dlsym(handle,"dyld_shared_cache_extract_dylibs_progress");if ( proc == NULL ) {fprintf(stderr,"dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");return 1;}int result = (*proc)(argv[2],argv[3],^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } );fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n",r esult);return 0; } #endif

    c. 在終端上 cd 到 dyld 源碼目錄 launch-cache 下,在終端命令行編譯并生成 dsc_extractor 工具。

    clang++ -o dsc_extractor dsc_extractor.cpp dsc_iterator.cpp

    d. 從Xcode的包中 /Applications/Xcode.App/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib 中提取出dsc_extractor.bundle 文件。dsc_extractor.bundle 和要提取的 iOS 系統(tǒng)強(qiáng)關(guān)聯(lián),比如 iOS14 的系統(tǒng)符號(hào)需要導(dǎo)出 Xcode12 里的 dsc_extractor.bundle,而 iOS15 的需要 Xcode 13 Beta 里的。如果不匹配的話,有可能不能提取出系統(tǒng)符號(hào)。

    e. 調(diào)用如下命令提取出系統(tǒng)符號(hào);如下,最終提取的系統(tǒng)庫在目錄 17C81 下,我們解析系統(tǒng)符號(hào)需要的文件基本為 dylib 和 framework。

    dsc_extractor dsc_extractor.bundle /System/Library/Caches/com.Apple.dyld/dyld_shared_cache_arm64 17C81

    4. 在線符號(hào)化

    丨4.1 為什么要實(shí)現(xiàn)在線符號(hào)化

    • 打包時(shí)候符號(hào)文件是由持續(xù)集成打包機(jī)產(chǎn)生,本地獲取有成本。
    • 方便研發(fā)人員快速符號(hào)化崩潰日志。很多時(shí)候,崩潰都是在非研發(fā)人員(產(chǎn)品,QA等)使用應(yīng)用的時(shí)候發(fā)生的;同步到研發(fā)人員之后,因?yàn)楸镜丨h(huán)境的差異,在沒有打包環(huán)境的情況下,研發(fā)人員也需要能迅速符號(hào)化崩潰堆棧
    • 線上用戶上傳的崩潰日志規(guī)模大。大多數(shù)崩潰都是發(fā)版之后用戶使用過程中發(fā)生的,如果大量線上日志未經(jīng)符號(hào)化就同步到研發(fā)人員,就會(huì)增加研發(fā)人員的負(fù)擔(dān),降低問題解決的效率。
    • 用戶系統(tǒng)多,收集難度大。用戶的系統(tǒng)從 iOS9 到 iOS14 都有,千奇百怪,靠研發(fā)人員本地想要解析所有的系統(tǒng)符號(hào)純屬臆想。

    丨4.2 在線 App /動(dòng)態(tài)庫符號(hào)化

    通常情況下,我們只需要符號(hào)化極少部分崩潰日志,這種情況下我們?cè)诒镜鼐涂梢苑?hào)化了。但當(dāng)我們的應(yīng)用上線發(fā)版后,崩潰日志日均收集量級(jí)可能超百萬以上,此時(shí)就不適合在 Mac OS 上使用腳本/工具符號(hào)化了( 在 Mac OS 上使用 symbolicatecrash 命令符號(hào)化單個(gè)日志時(shí),耗時(shí)基本 1 秒以上)。此時(shí),就需要更通用,快速的符號(hào)化方式了。
    為了能夠在 Linux 服務(wù)器上極速符號(hào)化 iOS 崩潰日志,我們深入調(diào)研了 iOS 本地符號(hào)化的原理,在和平臺(tái)方多次就技術(shù)方案進(jìn)行了調(diào)研磋商之后,最終采取了如下方案:

    生成mapping文件
    將 dSYM 文件通過腳本提取生成一個(gè) mapping 文件,格式如下:

    Format: Mach-O/64-Bit Arch: arm64 Symbols: 5 Tool Version:: 1.0.1 File Version: 1.0.0 UUID: e569d81abb2c372e89a2410edc3d368f Built Time: 2021-07-29 13:31:08 Symbol table: 6c64 6c78 -[ViewController viewDidLoad] (in TestBacktrace) (ViewController.m:17) 6c78 6c84 -[ViewController viewDidLoad] (in TestBacktrace) (ViewController.m:0)

    提取操作會(huì)涉及到 DWARF 中 debug_line 段數(shù)據(jù)的符號(hào)化,相關(guān)提取算法可以參考 DWARF 官方的資料。debug_line 段包含有詳細(xì)的代碼偏移量地址和文件名稱,按照 DWARF 的算法就可以解析出來,然后與 Symbol Table 的函數(shù)符號(hào)一一匹配,就能生成代碼地址偏移量與函數(shù)、文件、行數(shù)的映射關(guān)系。需要注意的是,蘋果的 Mach-O 現(xiàn)在大部分格式都是使用 DWARF2 和 DWARF4 版本,提取的時(shí)候需要重點(diǎn)關(guān)注這兩種格式的兼容和算法不同。最終,可以看到 Symbol table 每一行對(duì)應(yīng)一個(gè)符號(hào)的偏移量。可以發(fā)現(xiàn) 7464 剛好處于 7454 - 7478 之間,匹配出來的符號(hào)剛好是 -[AppDelegate Application:didFinishLaunchingWithOptions:] (in TestBacktrace) (AppDelegate.m:23) ,與 Mac OS 上使用 symbolicatecrash 腳本符號(hào)化的結(jié)果一致。

    根據(jù) mapping 文件符號(hào)化
    借助于腳本工具提取的符號(hào) mapping 文件,服務(wù)端就能夠脫離平臺(tái)限制,根據(jù)崩潰日志中的 UUID 去匹配映射文件,在 Linux 上極速符號(hào)化崩潰日志,提供高效實(shí)時(shí)的符號(hào)化服務(wù)。

    丨4.3 在線符號(hào)化 iOS 系統(tǒng)庫符號(hào)

    在 Mac OS 平臺(tái)上,我們可以直接使用系統(tǒng)庫的符號(hào)直接使用腳本去符號(hào)化符號(hào),但是一旦要符號(hào)化所有用戶上傳的崩潰日志,這一套機(jī)制就難免被速度和平臺(tái)限制。并且iOS 系統(tǒng)從 2.0 開始,一直到現(xiàn)在 iOS 14 ,發(fā)出的版本幾百個(gè),要手動(dòng)提取出系統(tǒng)庫符號(hào)幾乎是不可能的事情。為了解決這個(gè)問題,在借鑒了 dSYM 跨平臺(tái)符號(hào)化方案之后,我們做了一套系統(tǒng)符號(hào)自動(dòng)化符號(hào)化的方案,最終實(shí)現(xiàn)了在 Linux 平臺(tái)上高效實(shí)時(shí)的符號(hào)化系統(tǒng)堆棧。

  • 定時(shí)從 theiphonewiki 網(wǎng)站上導(dǎo)出各個(gè)系統(tǒng)以及最新發(fā)布系統(tǒng)的 iPSW 文件下載地址。

  • 解壓 iPSW 并加載系統(tǒng)鏡像 dmg 文件,找到 dyld_shared_cache_xxx 文件。

  • 使用工具 dsc_extractor 將系統(tǒng)庫符號(hào)文件導(dǎo)出,導(dǎo)出文件基本為后綴為 dylib 和 framework 的 Mach-O 類型文件。

  • 將所有的 dylib 和 framework 使用工具提取生成如下格式的 mapping 文件。這一步與 dSYM 提取操作會(huì)有一定差別,通常來說,系統(tǒng)庫只有符號(hào)表段,不需要對(duì) debug_line 段做提取,相對(duì)比較簡單。

  • 20b8 20fa +[ZoomServicesUI enableZoomServices] (in AccessibilitySettingsLoader) 20fa 2120 +[ZoomServicesUI disableZoomServices] (in AccessibilitySettingsLoader) 2120 21b6 -[ZoomServicesUI init] (in AccessibilitySettingsLoader) 21b6 2222 -[ZoomServicesUI dealloc] (in AccessibilitySettingsLoader)
  • 崩潰日志上傳到符號(hào)化服務(wù)器之后,服務(wù)器根據(jù)崩潰日志中系統(tǒng)庫的 UUID 和 mapping 文件中的 UUID 唯一確定 mapping 文件并符號(hào)化系統(tǒng)堆棧。
  • 5. 效果

  • 在線符號(hào)化系統(tǒng)上線之后,用戶的日志經(jīng)過崩潰組件自動(dòng)上傳到性能之后,符號(hào)化解析系統(tǒng)直接將崩潰日志符號(hào)化并聚類,最終符號(hào)化的崩潰日志詳情如下。應(yīng)用程序的地址最終表現(xiàn)為 函數(shù) + 文件 + 行數(shù),系統(tǒng)堆棧會(huì)具體顯示出崩潰的函數(shù),整個(gè)過程實(shí)時(shí)且高效。
  • Last Exception Backtrace: 0 CoreFoundation 0x1ca4cd27c __exceptionPreprocess + 228 1 libobjc.A.dylib 0x1c96a79f8 objc_exception_throw + 55 2 CoreFoundation 0x1ca3ded94 -[__NSSingleObjectArrayI objectAtIndex:] + 127 3 TestBacktrace 0x102a47464 -[AppDelegate Application:didFinishLaunchingWithOptions:] + 29796 (AppDelegate.m:23) 4 UIKitCore 0x1f6c86e30 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 411 5 UIKitCore 0x1f6c88594 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3351 6 UIKitCore 0x1f6c8dd20 -[UIApplication

    Thread 0 name: Dispatch queue: com.Apple.main-thread
    Thread 0 Crashed:
    0 libsystem_kernel.dylib 0x00000001ca06a0dc __pthread_kill + 8
    1 libsystem_pthread.dylib 0x00000001ca0e3094 pthread_killVARIANTVARIANTVARIANTmp + 380
    2 libsystem_c.dylib 0x00000001c9fc3ea8 abort + 140
    3 libc++abi.dylib 0x00000001c9690788 __cxa_bad_cast + 0
    ``
    2. 部分來自研發(fā)和測(cè)試的崩潰日志,平臺(tái)提供了在線符號(hào)化的入口,只需要手動(dòng)上傳崩潰日志到平臺(tái),立刻就能把符號(hào)化后的崩潰日志下載給相應(yīng)人員。

    6. 收益

  • 線上問題定位速度獲得極大提升,從線上發(fā)生新增卡頓/崩潰問題到具體研發(fā)響應(yīng)時(shí)間大大縮減,從發(fā)生崩潰到定位問題,基本都在 10 分鐘以內(nèi)。

  • 目前,性能平臺(tái)日均在線符號(hào)化崩潰/卡頓日志超百萬次,廠內(nèi)接入產(chǎn)品線超 30+,符號(hào)化功能做到了上傳即解析,整個(gè)過程無需研發(fā)人員干預(yù)。真正做到了自動(dòng)化、在線、實(shí)時(shí)的符號(hào)化崩潰、卡頓日志,并實(shí)時(shí)根據(jù)符號(hào)化的問題代碼定位到具體開發(fā)人員,高效的響應(yīng)并解決線上問題。

  • 參考資料

    [1] iOS Crash 分析必備:符號(hào)化系統(tǒng)庫方法https://zuikyo.github.io/2016/12/18/iOS%20Crash%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90%E5%BF%85%E5%A4%87%EF%BC%9A%E7%AC%A6%E5%8F%B7%E5%8C%96%E7%B3%BB%E7%BB%9F%E5%BA%93%E6%96%B9%E6%B3%95/
    [2] 聊聊從 iOS 固件提取系統(tǒng)庫符號(hào) http://crash.163.com/#news/!newsId=31
    [3] Xcode 中和 symbols 有關(guān)的幾個(gè)設(shè)置 https://www.jianshu.com/p/11710e7ab661
    [4] iOS_SDK https://en.wikipedia.org/wiki/IOS_SDK
    [5] IOS_version_history https://en.wikipedia.org/wiki/IOS_version_history#iOS_14
    [6] dyld 源碼下載地址 https://opensource.apple.com/tarballs/dyld/
    [7] The DWARF Debugging Standard http://www.dwarfstd.org/
    [8] iOS9 之前的Firmware_Keys https://www.theiphonewiki.com/wiki/Firmware_Keys
    [9] dmg 工具下載地址 https://github.com/Zuikyo/iOS-System-Symbols/blob/master/tools/dmg
    [10] 系統(tǒng)符號(hào)下載地址索引 wiki https://www.theiphonewiki.com/wiki/Firmware

    點(diǎn)擊進(jìn)入了解更多技術(shù)資訊~~

    與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

    總結(jié)

    以上是生活随笔為你收集整理的iOS 崩溃日志在线符号化实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。