iOS堆栈日志线上符号化方案调研
从手机导出.ips或者.crash文件都可以很容易获取到符号化后的堆栈日志, 但是线上大量的的crash,总不可能让用户自己导出crash文件发给开发者的,这个就需要线上符号化的一个能力, 才能更有效的治理线上崩溃问题,未符号化的日志大概率是看不出问题所在的,堆栈日志符号化是势在必行,行之有效的途径~
比如bugly,友盟,看云等三方平台,他们都可以实现事后上传DSYM
文件进行翻译匹配对应的堆栈日志,其实都是同一套代码打出来的包,只是区分了CPU架构的不同,可能会有几种可能的堆栈,但是除此之外机型一样的话数据就存在冗余,因为执行脚本翻译堆栈这个过程,时间长则需要耗费几秒钟的时间,最好的做法就是把符号表做成缓存,拿到堆栈直接取地址去读表,表里没有的再去查,查了后把结果映射关系等再进行缓存~ .... 之前看过字节大佬的直播分享好像就是这么干的
找到虚拟内存的起始地址 vmaddr 运算会用到 也就是加上偏移量就是 符号表中的相对位置
otool -l DSYM/xxx/xxx二进制 | grep __TEXT -C 5
dwarfdump iOSTest.app.dSYM --lookup 0xxxxx //vmaddr + 偏移 可以查到符号中的信息
找出.app/二进制和dsym文件的uuid进行比对 看是否一致,避免做无用功
dwarfdump --uuid Project.app/Project
dwarfdump --uuid Project.app.dSYM
atos -arch cpu架构 -o dSYM文件 -l 基地址 运行时崩溃地址=(基地址+偏移) -i //-i是显示内联信息
示例
未符号化的样子
3 CoreFoundation 0x254b5949 0x253aa000 + 1096008
4 CoreFoundation 0x253e6b68 _CF_forwarding_prep_0 + 24
5 SuperSDKTest 0x0010143b 0x000ef000 + 74808 //且看这一条
符号化后的样子
3 CoreFoundation 0x254b5949 <redacted> + 712 //该类是缺少系统库符号 被系统优化了 实在需要解析需要找出对应库的符号表进行翻译
4 CoreFoundation 0x253e6b68 _CF_forwarding_prep_0 + 24
5 SuperSDKTest 0x0010143b -[ViewController didTriggerClick:] + 58
其中 0x0010143b
为函数崩溃时的地址 它的值会等于 ( 0x000ef000
为函数加载的基地址 + 74808
为偏移量)
所以翻译代码可以这么写 两个地址 第一个是未偏移前的, 第二个是加上偏移后的也就是崩溃的那个地址
$ xcrun atos -o SuperSDKTest.app.dSYM/Contents/Resources/DWARF/SuperSDKTest -arch arm64 -l 0x000ef000
0x0010143b
-[ViewController didTriggerClick:] (in SuperSDKTest) (ViewController.m:35)
附上获取堆栈代码
#include <execinfo.h>
#import <dlfcn.h>
#import <cxxabi.h>
//获取线程的堆栈
NSLog(@"[NSThread callStackSymbols] = %@",[NSThread callStackSymbols]);
- (NSArray *)backtrace {
void *callStack[128];//堆栈方法数组
int frames = backtrace(callStack, 128);//获取错误堆栈方法指针数组,返回数目
char **strs = backtrace_symbols(callStack, frames);//符号化
NSMutableArray *symbolsBackTrace = [NSMutableArray arrayWithCapacity:frames];
unsigned long count = frames > 20? 20 : frames;//限制取20条
for (int i = 0; i < count; i++) {
if (i > 0) {
//过滤当前这个方法的符号
[symbolsBackTrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
}
free(strs);
return symbolsBackTrace;
}
//通过Dl_info获取调用栈
- (NSArray<NSString *> *)symbolicateBacktrace {
void *callStack[128];
int frames = backtrace(callStack, sizeof(callStack)/sizeof(*callStack));
NSMutableArray *symbols = [NSMutableArray arrayWithCapacity:frames];
for (int i = 0; i < frames; i++) {
@autoreleasepool {
Dl_info info;
if (dladdr(callStack[i], &info) == 0) {
[symbols addObject:@"[Unknown]"];
continue;
}
// 计算符号偏移量(考虑地址可能小于基址的情况)
const uintptr_t address = (uintptr_t)callStack[i];
const uintptr_t baseAddress = (uintptr_t)info.dli_saddr;
const ptrdiff_t offset = address - baseAddress;
// 解析符号名称
char *demangled = NULL;
const char *symbolName = info.dli_sname;
if (symbolName) {
demangled = abi::__cxa_demangle(symbolName, NULL, 0, NULL);
}
// 安全处理模块名称
const char *fname = info.dli_fname ?: "";
NSString *module = [[[NSString stringWithUTF8String:fname] lastPathComponent] stringByDeletingPathExtension];
// 构建符号信息
NSString *symbol = [NSString stringWithFormat:@"%-3d %-20@ 0x%016lx %-40s + %td",
i,
module,
(uintptr_t)info.dli_fbase,
(demangled ?: symbolName ?: "???"),
offset];
[symbols addObject:symbol];
if (demangled) free(demangled);
}
}
return [symbols copy];
}
来自大厂们的精彩分享:
我看了大厂分享的文章,逼格都比较高,要图有图,要数据有数据的,就是有点不接地气,具体实操主要还是参考CoderStar
大佬的 iOS 符号化浅析 - CoderStar 文章来吧
本文来自博客园,作者:CoderWGB,转载请注明原文链接:https://www.cnblogs.com/wgb1234/articles/17944242