實現(xiàn)方案
基本思路:日志捕獲采用 KSCrash钮科,捕獲的日志上傳服務(wù)器,然后在服務(wù)器對日志進行符號化婆赠。
KSCrash 的上傳日志需要注意啟動閃退的情況跺嗽,一般是應(yīng)用啟動如果存在日志,需要先 hold 主線程页藻,等上傳完再釋放桨嫁。
基本模型如下:
//防止在登錄前就必閃情況
__block BOOL finished = NO;
[self uploadIfExistWithCompleteHandler:^{
finished = YES;
}];
//防止上傳失敗
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
finished = YES;
});
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
while (!finished) {
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
符號化方案
以下主要討論的是服務(wù)端通過腳本自動符號化日志需求
方案一:通過修改配置,不需要自主符號化日志份帐,也能看到日志堆棧類和函數(shù)名
通過在 Xcode 中設(shè)置 Build Settings
-> Strip Style
-> Debugging Symbols
Strip Style
有三個選項如下
-
All Symbols
:剝離所有符號表和重定向信息 -
Non-Global Symbols
:剝離非全局的符號(包括調(diào)試符號)璃吧,保留外部符號 -
Debugging Symbols
:剝離調(diào)試符號,保留局部符號和全局符號
開啟設(shè)備符號化需要在最終版本中包含基本符號废境,所以要在 build settings
中設(shè)置 Strip Style
為 Debugging Symbols
畜挨。也會造成最終的二進制文件大小增加 5% 左右筒繁,這也是之前 PLCrashReporter
中提到的,不過當(dāng)時查到的數(shù)據(jù)是 30-50%巴元,確實測試后沒有如此大的差距毡咏,也算是解了疑惑,由于打包包含了基本符號表導(dǎo)致的二進制大小增加逮刨。
注意:系統(tǒng)庫無法符號化呕缭,需要 symbolicatecrash 來符號化
方案二:通過 symbolicatecrash 自主符號化日志
atos 是蘋果提供的符號化工具,在 Mac OS 系統(tǒng)下默認安裝修己,他的缺點是只能一個地址一個地址逐個翻譯恢总。
symbolicatecrash 是 Xcode 自帶的一個程序,他是對 atos 的封裝睬愤,可以翻譯整個 crash 文件片仿。
接下來通過使用 symbolicatecrash 進行符號化日志
第一步:獲取 symbolicatecrash 文件,通過使用以下命令搜索尤辱,找到之后拷到閃退日志目錄下
find /Applications/Xcode.App -name symbolicatecrash -type f
第二步:設(shè)置路徑
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
第三步:符號化日志砂豌,cd 到閃退日志目錄下轴猎,然后運行下面命令
./symbolicatecrash log.txt -d xxx.App.dSYM > result.txt
獲取符號文件
獲取項目符號文件
iOS 的打包一般有兩種方式驮吱,Xcode 打包或者腳本打包。
不過哪種方式都會生成 xxx.xcarchive 文件浸剩,打開就可以看到 dSYMs 符號文件可帽。
- Xcode 打包:在 Xcode 中工具欄上 Window -> Organizeer -> 選擇相應(yīng)的App -> Archives -> 選擇對應(yīng)的包進入就可以看到 xxx.xcarchive 文件
- 腳本打包:一般ipa包生成同時旁邊也會生成 xxx.xcarchive 文件
這里有個問題,閃退日志必須和符號化文件匹配才能解析窗怒,我們可以通過 UUID 來匹配
dSYM 的 UUID 獲取方式
命令:dwarfdump --uuid appName.app.dSYM
例如:dwarfdump --uuid /Users/xxx/Desktop/Demo.app.dSYM
結(jié)果:UUID: 35E34DE0-4C45-36CF-8F11-1F33BA40F3ED (arm64)
日志自動帶上了 UUID
此時符號化的日志如下
獲取系統(tǒng)符號文件
系統(tǒng)符號文件會根據(jù)不同 CPU 架構(gòu)(armv7,armv7s,arm64,arm64e)映跟,以及不同系統(tǒng)對應(yīng)的文件都不一樣。通過 symbolicatecrash 工具進行符號化時扬虚,工具會自動會在 /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport
目錄下進行匹配解析努隙。
方式一:從真機上獲取
當(dāng)你用 Xcode 第一次連接某臺設(shè)備進行真機調(diào)試時,會看到 Xcode 顯示 Processing symbol files 辜昵,這時候就是在拷貝真機上的符號文件到 Mac OS 系統(tǒng)的 /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport
目錄下荸镊。
14.6 (18F72) arm64e
就是對應(yīng)的系統(tǒng)符號文件
如果僅通過真機來獲取系統(tǒng)的符號化文件,比較局限堪置,把不同系統(tǒng)版本以及不同 CPU 架構(gòu)類型的手機準(zhǔn)備齊全比較難躬存。
方式二:從固件中提取符號文件
下面以獲取 iPhone12 系統(tǒng)為 iOS 14.6 為例。
第一步:下載對應(yīng)的固件舀锨,從中獲取 dyld_shared_cache_xxx
可執(zhí)行文件岭洲,其中 xxx 是 CPU 架構(gòu)類型。
在 www.theiphonewiki.com 下載坎匿。優(yōu)點整理的齊全盾剩,缺點下載速度有點慢雷激。
下載完解壓得到,獲取最大的 dmg 文件
運行 018-17771-088.dmg
獲取 dyld_shared_cache_arm64e
告私,路徑 System -> Library -> Caches -> com.apple.dyld
第二步:通過 dyld 生成 dsc_extractor
可執(zhí)行文件屎暇。
在蘋果的開源網(wǎng)站下載 dyld 源碼,例子中下載了 dyld-750.6.tar.gz
解壓
修改 dsc_extractor.cpp
文件驻粟,把 #if 0
改成 #if 1
根悼,屏蔽其他代碼
#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 != 3 ) {
fprintf(stderr, "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n");
return 1;
}
//void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY);
void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", 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[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } );
fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result);
return 0;
}
#endif
修改 dsc_iterator.cpp
文件,屏蔽頭文件 #include "SupportedArchs.h"
在終端上 cd 到 dyld 源碼目錄 launch-cache 下格嗅,在終端命令行編譯并生成 dsc_extractor 可執(zhí)行文件
clang++ -o dsc_extractor dsc_extractor.cpp dsc_iterator.cpp
第三步:通過 dyld_shared_cache_xxx
和 dsc_extractor
獲取系統(tǒng)符號文件
新建一個文件夾把 dsc_extractor
和 dyld_shared_cache_arm64e
兩個可執(zhí)行文件放入其中
通過以下命令就可以生成系統(tǒng)符號文件
dsc_extractor <path-to-cache-file> <path-to-device-dir>
比如:
./dsc_extractor ./dyld_shared_cache_arm64e ./Symbols
新建一個文件夾 14.6 (18F72) arm64e
番挺,把 Symbols
拖入其中,然后拖入 /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport
目錄下屯掖。
此時符號化的日志如下