行情
HOME
行情
正文内容
nand 日志 崩溃 不到四分钟的视频记录不了我备考的N多次崩溃
发布时间 : 2024-11-25
作者 : 小编
访问数量 : 23
扫码分享至微信

不到四分钟的视频记录不了我备考的N多次崩溃

备考。

·在市立图书馆听课,基础阶段。在学校图书馆听课,我好喜欢波波。寝室是学习的第二主战场,我们宿舍学习氛围如此浓厚,自己一个人也要好好学习。在计划本上写好多碎碎念,我还会记录午睡质量。在学习上我真是个军人,考公人有自己的手帐,冰山一角的草稿纸也很多,真的好笨自己,没完没了的做公安。

·我喜欢给每一套都打分,考前背公安时政,硬背。感谢郭t推荐警考通公安搭子,学行测比学公安还要难,猛猛学数量(夸张),但是也没怎么学会,言语还是很好的(捡好听的说)。喜欢学申论,一遍遍做申论题(悟性好差)。喜欢读材料,最开始老也抓不到重点,崩溃好多次,还好我还算努力,逻辑就是我的噩梦。

·喜欢学数资,做了好多题,画对号能给我增添信心。做题不就为了画√嘛,错的多就多做,数资真是多做题之后效果显著。做做题跟自己对上话了(就是写上错误原因),又用没N只MVP结算画面。机考真难,天天做,絮叨死了还得做。真不喜欢图形推理,但是真的不会,一点天赋都没有。

·我好喜欢做思维导图,条条框框的感觉让我很安心。曹氏鸭脖吃多少才能够?姨说我天天三个大包子,中之杰家不仅包子好吃,桃酥也好吃。麻辣烫,真喜欢吃川锦汇新疆炒米粉,辣的我都爱。小裴家的菜,健康又好吃。特别鸣谢裴佳茵同学。

就今天咱俩这点,他们知道每一个法条,加气别们长自己用不想看有自示多天时,么如我是大下,你是硬和取可是还,你总是发一长段新开解我日就过去3剩下的日子咱们相对我五点醒的,见你在婚礼上幸福绝说我愿意,那个时候的你会一定感谢现在。

备考之路太难了。我知道我一到晚上发疯找睡不,昨天找真是疯了我睡不着我把老师留那套卷写了写到五点我总是听不见闹铃,你总是怕我睡过站,总去你家吃饭!那段都是外卖的日子里的救命稻草。

出成绩啦!实习来啦,省考去啦,没学,没进面,答辩来啦,政审给我忙活的。结局不算圆满,但是也是知足。谢谢观看,bye-bye!

“杀死”App 上的疑难崩溃

在移动应用性能方面,崩溃带来的影响是最为严重的,程序崩了可以打断用户正在进行的操作体验,造成关键业务中断、用户留存率下降、品牌口碑变差、生命周期价值下降等影响。很多公司将崩溃率作为优先级最高的技术指标,因此程序崩溃的监控与收集就成为了一项必不可少的工作。以58同城App为例,其采用了腾讯Bugly作为发布环境下App异常数据的收集工具。

一直以来,为降低App崩溃率,58同城App的每个版本都有专门负责监控线上崩溃以及解决问题的同学。根据汇总统计,Bugly上收集的崩溃大部分都是野指针崩溃和疑难崩溃。但是遗留的疑难崩溃优化手段比较有限,一个主要的原因是Bugly上的崩溃不能正常解析,定位不到真正原因。

问题与背景

举一个简单的例子来说明:

RN的HOOK函数问题

0 CoreFoundation 0x00000001804f504c 0x000000018045c000+6267641 Foundation 0x0000000181dae6cc 0x0000000181c7e000+12469242 UIKit 0x0000000198e5cf30 0x0000000198e57000+243683 AppName 0xe622388106d79fcc RCTFBQuickPerformanceLoggerConfigureHooks+162444 CoreTelephony 0x0000000198e5e628 0x0000000198e57000+302485 CoreTelephony 0x0000000108f68fe4 0x0000000198e57000+784552606 CoreTelephony 0x00000001061ed870 0x0000000198e57000+307636247 CoreTelephony 0x0000000108f657ec 0x0000000198e57000+784409328 AppName 0x0000000108f67024 _ZN6tflite19AcquireFlexDelegateEv+784471329 Foundation 0x0000000108f67024 _NSGetUsingKeyValueGetter+88

在近几个版本中,我们发现Bugly上有大量的崩溃日志都会携带一个来自RN的函数调用栈:

RCTFBQuickPerformanceLoggerConfigureHooks

这是一个RN的HOOK函数。多条崩溃日志的堆栈都指向这个函数,且该函数是一个空函数,没有任何实现,这让我们比较困扰。用过Bugly的同学想必都知道,Bugly每条崩溃日志都有个跟踪数据,记录着这个崩溃发生之前页面的跟踪日志,通过页面的跟踪日志我们发现这些崩溃中用户浏览的页面大多数都不涉及RN业务,与RN没有任何关系,而且每条崩溃的页面跟踪日志也不相同。

既然程序崩溃之前浏览的业务不涉及RN,但Bugly上的堆栈确实指向了RN,因此我们怀疑这种崩溃不是始于RN的HOOK函数,而是由不同错误导致的。带着这种疑问,我们开始验证这个猜想,来看一看我们的怀疑是否准确。

如何验证Bugly解析错误

因为Bugly无法拿到应用崩溃后所产生的ips文件,无法利用symbolicatecrash等工具符号化日志。因此我们采用atos命令来验证我们的怀疑是否正确。

1. atos验证

atos工具会输出崩溃的代码语句和它所在的文件以及行数,前置条件是需要拿到dSYM文件,确定手机架构是ARM64还是ARMv7,还需要拿到atos需要的load-address与address,根据这些信息就能够找到问题所在。aots命令格式如下:

atos -o yourAppName.app.dSYM/Contents/Resources/DWARF/yourAppName -arch arm64/armv7 -l <load-address><address>

怎么获取dSYM文件与架构这里就不做详细介绍了,我们来看一下怎么在Bugly的崩溃日志中拿到load-address与address。

一般以App命名的地方就是崩溃的位置,例如:正常的一个崩溃日志格式为:0x0000000103ef69700x0000000102728000+30252,其中0x0000000103ef6970为运行地址,就是atos需要的address,0x0000000102728000为运行起始地址,就是atos需要的load address,302522为偏移量,一般来说,偏移量 + 运行起始地址 = 运行地址。

介绍完atos需要的load address(运行起始地址)与address(运行地址)之后,再来看一下RCTFBQuickPerformanceLoggerConfigureHooks这个函数的崩溃,根据图中示例我们看到这个崩溃的运行地址为0xe622388106d79fcc,但是这个崩溃地址是错误的,一般地址小于0xFFFFFFFFFF,示例中明显大很多。因此我需要将高位地址清洗,清洗后此地址为0x106d79fcc。因此address为0x106d79fcc。

接下来我们打开Bugly其他信息一栏,看到App base addr(基地址):0x0000000102604000,这个就是atos需要需要的load address。

通过上述信息,我们以RCTFBQuickPerformanceLoggerConfigureHooks这个函数为例验证一下Bugly的解析结果是否正确:

➜atos -o AppName.app.dSYM/Contents/Resources/DWARF/AppName -arch arm64 -l 0x00000001026040000x0000000106d79fcc-[NSMutableDictionary(YJKit)yjKit_setObject:forKey:] (in AppName) (YJKit.m:432)

结果发现atos符号化后的结果与Bugly给我们的结果确实不一致。再根据Bugly的页面跟踪数据我们确认atos符号化后的结果是正确的,这与我们的怀疑是一致的。

既然Bugly的堆栈错误的指向了这个RN的空函数,那么我们就来看一看源码中RCTFBQuickPerformanceLoggerConfigureHooks是怎样的存在。

2. 源码中的RCTFBQuickPerformanceLoggerConfigureHooks函数

RCTFBQuickPerformanceLoggerConfigureHooks函数在源码中的声明如下:

源码中,这个函数没有任何实现,完全是一个空函数。将RCT__EXTERN展开后为__attribute__((visibility("default"))),其作用为将RCTFBQuickPerformanceLoggerConfigureHooks向外界暴露,如果外界存在同名函数,那么RCTFBQuickPerformanceLoggerConfigureHooks会报符号冲突的错误。这里利用__attribute__((weak))将RCTFBQuickPerformanceLoggerConfigureHooks声明为弱符号,当外界有同名函数时,SDK内部调用外届的函数,否则调用内部空函数,这个弱符号在RN里起到了HOOK的作用,接下来我们就详细的了解一下弱符号。

3. 弱符号__attribute__ ((weak))

在一个程序中,无论是变量名,还是函数名,在编译器的眼里,就是一个符号而已,符号可以分为强符号和弱符号。对于C/C++来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量是弱符号,强符号和弱符号在程序编译连接过程中一般遵循下面三个规则:

不允许强符号被多次定义。如果有多个强符号,会报符号重定义错误;

如果有一个强符号,其他定义都是弱符号,则选择强符号;

如果一个符号在所有文件中都是弱符号,则选择其中一个占用空间最大的。(强弱符号规则定义摘选自:《强符号和弱符号,强引用和弱引用》https://www.cnblogs.com/fensi/p/13179344.html)

duplicate symbol '_OBJC_CLASS_$_XXX'这个错误大家应该都比较熟悉,通过错误的描述我们很容易就可以知道这是因为在链接的时候有重复的符号。在编译时,编译器向汇编器输出每个全局符号,若两个或两个以上全局符号(函数或变量名)名字一样,且都是强符号就会出现符号重定义错误,如果有一个是弱符号(weak symbol),则不会出现问题。

一个程序内同时存在强符号与弱符号时,链接器会忽略弱符号,去使用普通的全局符号来解析所有对这些符号的引用,但当普通的全局符号不可用时,链接器会使用弱符号。当有函数或变量名可能被用户覆盖时,该函数或变量名可以声明为一个弱符号。可以通过__attribute__((weak))来定义弱符号。

4. 弱符号的使用

在开发中,假如我们不确定外部模块是否提供一个函数func,但是我们不得不用这个函数,即自己模块的代码必须用到func函数:

extern int func(void);...int a = func;...

我们不知道func函数是否被定义了,这会导致2个结果:

外部存在这个函数func,那么在自己的模块使用这个函数func,正确。

外部如果不存在这个函数,那么我们使用func,程序直接崩溃。

所以这个时候,__attribute__((weak))派上了用场,在自己的模块中定义:

int __attribute__((weak))func(......){ return0;}

将本模块的func转成弱符号类型,如果遇到强符号类型(即外部模块定义了func),那么我们在本模块执行的func将会是外部模块定义的func。如果外部模块没有定义,那么,将会调用这个弱符号。

我们发现Bugly对某些没有解析正确的崩溃,堆栈都会定位到项目中的弱符号上,同时我们还发现在58同城App中,Bugly不单单定位到RCTFBQuickPerformanceLoggerConfigureHooks这一个弱符号上,还有大量的崩溃定位到了其他的弱符号上。

上面我们通过atos还原了正确的日志,并定位到了是弱符号的问题,下面我们结合符号表来看一下日志符号化的原理。

如何处理Bugly解析异常的数据

Crash日志在被符号化之前是不可读的,所谓符号化就是把堆栈信息解释成源码里可读的函数名或方法名,也就是所谓的符号。只有符号化成功后,Crash日志才能更好的帮助开发者定位问题。日志的解析需要用到dSYM文件,dSYM指的是Debug Symbols,也就是调试符号。

DWARF是一种被众多编译器和调试器使用的用于支持源码级别调试的调试文件格式,该格式是一个固定的数据格式,dSYM就是按照DWARF格式保存调试信息的文件,我们常常称为符号表文件。

日志的符号化有很多种方式,例如xcode分析、symbolicatecrash、atos、dwarfdump等,本质其实就是查找崩溃指令在符号表哪个函数的指令区间。今天我们主要讲一下Bugly解析不准的日志怎么在符号表里查找出正确的堆栈。

1. Bugly还原正确堆栈的原理

以弱符号RCTFBQuickPerformanceLoggerConfigureHooks函数为例,还原一下日志的解析原理。

0 CoreFoundation 0x00000001804f504c 0x000000018045c000+6267641 Foundation 0x0000000181dae6cc 0x0000000181c7e000+12469242 UIKit 0x0000000198e5cf30 0x0000000198e57000+243683 AppName 0xe622388106d79fcc RCTFBQuickPerformanceLoggerConfigureHooks+162444 CoreTelephony 0x0000000198e5e628 0x0000000198e57000+302485 CoreTelephony 0x0000000108f68fe4 0x0000000198e57000+784552606 CoreTelephony 0x00000001061ed870 0x0000000198e57000+307636247 CoreTelephony 0x0000000108f657ec 0x0000000198e57000+784409328 AppName 0x0000000108f67024 _ZN6tflite19AcquireFlexDelegateEv+784471329 Foundation 0x0000000108f67024 _NSGetUsingKeyValueGetter+88

上图中我们看到RCTFBQuickPerformanceLoggerConfigureHooks这行调用栈的虚拟内存地址存在异常,一般地址地址小于0xFFFFFFFFFF,示例中明显大很多。我们将高位地址清洗后来保证堆栈正常。调整后,地址为0x106d79fcc,但当然不是每个Bugly解析错误的日志虚拟内存地址都异常,如果是正常的,则不用改变。

查看其他信息,找到基地址App base addr,此处为0x102604000。如果崩溃发生在其他动态库,那么查找下方对应动态库的地址。

经过第一步和第二步,我们获取到了0x106d79fcc和0x102604000。

指令偏移地址为:0x4775FCC = (步骤1)0x106d79fcc - (步骤2)0x102604000。

找到此次打包对应的Bugly符号表,并以文本的方式打开。

查找0x4775FCC在哪一行符号区间内。

最终查找到其在0x4775fb4 ≤ 0x4775FCC < 0x4775fd0,即3997407行的符号,符号区间遵循前闭后开原则。

通过以上步骤我们找到了RCTFBQuickPerformanceLoggerConfigureHooks函数的实际崩溃位置,并且与我们用atos工具验证后的结果一致,说明这个结果是正确的。

上面我们在符号表里查找到Bugly解析错误的日志的正确堆栈,那如果没有符号表怎么呢,这就涉及到了提取符号表。

2. 如何提取符号表

如果符号表丢失了,但是代码没有改动,那么可以尝试在相同的环境下重新编译和提取符号表,这个步骤有两个前提:

代码要与之前保持一致;

编译和链接环境都相同,防止由于Debug/Release对最终包有影响。如果是Debug包,可以用过dsymutil xxx.app/xxx -o xxx.dSYM来提取符号表。

有了以上两个前提就可以通过dSYM文件来提取符号表了,目前我们实现了Bugly轻量符号表的提取,并且文件体积相对于Bugly符号表体积减少到60%。推动ICI(58项目管理平台)按照一定规则输出符号表,目前可以做到根据崩溃日志的UUID直接下载对应的符号表,日志解析和问题排查效率极大提高。

3. 无符号表符号化日志

如果既找不到符号表(dSYM文件或symbol文件),也无法恢复到原先的代码重新生成符号表,那么可以考虑借助无符号表符号化工具WBBlades来还原日志:https://github.com/wuba/WBBlades。

WBBlades是基于Mach-O文件解析的工具集,包括未使用代码检测(支持ObjC和Swift)、应用程序大小分析、不需要dSYM文件的日志恢复。

由于方案自身的限制,目前还不能解析除了OC方法以外的崩溃日志,如:block的崩溃、自定义C函数的崩溃。后续需要考虑如何将block的崩溃日志进行符号化。

优化成果与授收益

现在我们知道了当Bugly解析不准的时候,我们可以利用Bugly给我们提供的其他信息在符号表里找到正确的答案。通过以上研究,我们通过自研解析工具重新对Bugly的日志进行符号化,通过工具我们在集团内部解决了除RN的HOOK函数问题以外还解决了多个遗留已久的历史版本崩溃问题,这里简单的介绍几个比较有代表性的。

1. 拿不到基地址的问题

通过RCTFBQuickPerformanceLoggerConfigureHooks函数的崩溃的介绍,我们可以在Bugly的其他信息里获取到日志的基地址,通过这个地址我们不论是用atos验证还是手动在符号表里查找都可以还原正确的堆栈,但是如果Bugly的其他信息里没有基地址怎么办,我们来看一下下面的这种崩溃日志。

0 CoreFoundation 0x00000001835891b80x0000000183459000+12456245 UIKit 0x000000018963a6600x000000018942f000+21438406 AppName 0x00000001075c9904str_to_integral_8ExpectedIT_NS_14Conversion +19500527 AppName 0x000000010627f94cRCTFBQuickPerformanceLoggerConfigureHooks +30983448 AppName 0x00000001062015a0RCTFBQuickPerformanceLoggerConfigureHooks +25813089 AppName 0x00000001061fe498 RCTFBQuickPerformanceLoggerConfigureHooks+256875610 AppName 0x00000001061fed38RCTFBQuickPerformanceLoggerConfigureHooks +257096411 AppName 0x00000001061ed900RCTFBQuickPerformanceLoggerConfigureHooks +250025212 AppName 0x0000000105231bd8_ZZGetAppIdTableEvE12arAppIdTable +5732512813 libdispatch.dylib0x00000001824121fc0x0000000182411000+460421 UIKit 0x00000001894a4534UIApplicationMain +20822 AppName 0x00000001085b73e8_ZN15CTXAppidConvert13GetAppIdTableEv +852123623 libdyld.dylib0x00000001824455b80x0000000182441000+17848

通过上面的堆栈信息我们看到崩溃的调用栈也停留在了弱符号RCTFBQuickPerformanceLoggerConfigureHooks上,但与我们上面举的例子的不同点是这个崩溃在Bugly上的其他信息一栏里是空的,也就是拿不到基地址,因此我们使用atos命令是不可行的,所以只能在符号表里查找,但是我们要首先要拿到基地址。下面我们来看一下遇到这种情况该怎样拿到基地址。

首先我们看到22 AppName0x00000001085b73e8 str_to_integral_8ExpectedIT_NS_14Conversion + 8521236这一行信息,熟悉Bugly与crash日志的同学一定知道,这一行大概率是main函数,那么我们就在这里找到突破口。

我们看到main函数的调用栈符号是_ZN15CTXAppidConvert13GetAppIdTableEv,这个函数运行地址是0x00000001085b73e8。

那这个函数的运行起始地址为0x00000001085b73e8 - 0x8521236 = 0x107D96DD4。

打开符号表,找到_ZN15CTXAppidConvert13GetAppIdTableEv这个符号的偏移地址为0x7d86dd4。

则App base addr(基地址): 0x107D96DD4 - 0x7d86dd4 = 0x100010000。

这样我们就拿到了这个日志的基地址,然后利用上面的方式在符号表里找到正确的堆栈,因此也就能将这个日志正确的解析了。

2. 百度地图SDK的崩溃问题

除了RN的HOOK函数问题,我们还发现有大量的崩溃日志调用栈都指向了百度地图SDK。

首先我们通过Bugly显示的堆栈信息以为是百度地图SDK的崩溃,这个崩溃在某几个版本中占58同城App总崩溃率的40%左右,是58 App内崩溃率最高的一个模块,在更换了新的SDK后崩溃率也并没有下降,而这么高的崩溃率,我们在开发与测试中却从未遇到过。通过Bugly上的跟踪数据我们看到最后的页面记录停留在了金融业务内,而金融业务与百度地图没有任何关系。因此这个崩溃应该与上面描述的一样,解析错误。拿到基地址与运行地址,通过我们自研的工具拿到了正确的堆栈。结果为金融业务使用的一个人脸识别SDK的崩溃,文件名称与Bugly上的跟踪日志也相同。

3. 安居客IM登录问题

我们编写了脚本文件,利用脚本文件定位了一个安居客存在很久的问题。在Bugly上排名比较靠前,崩溃占比很高,Bugly上的堆栈显示异常,因此这个崩溃之前并没有定位到具体原因。脚本协助安居客定位是IMSDK的原因。

以上是几个比较具有代表性的Bugly解析错误的日志,我们通过研究分析将这些错误的堆栈还原正确并解决了问题。

目前我们支持按版本自动排查出Bugly上前200名崩溃中解析异常的日志,并且可以将异常日志自动符号化成正确的日志。整个过程在符号表已经提前下载并解析好的前提下,只有10秒左右,大大提升了我们日常研发以及解决问题的效率。通过对Bugly上的疑难崩溃的治理,目前为止我们修复了Bugly上70%左右的疑难崩溃,大大降低了58 App的崩溃率。

除了上述我们研究的Bugly解析异常的日志可以正确解析外,58同城还支持其他异常日志的解析。例如,App内存在段迁移发生崩溃后的日志,段迁移崩溃日志中的库名变成了异常字符、丢失了进程的起始地址,获取到错误的偏移地址,这种情况下我们可以进行自动修正并解析出正确的堆栈信息。

总结与展望

本文分享了58 同城App使用Bugly遇到的RN的HOOK函数问题,通过这个问题,我们提出Bugly可能解析存在错误的疑问,后续用atos命令以及符号表排查找到了正确的答案,过程中又发现了弱符号的问题。按照这个研究方向我们在集团内做了一系列工具并解决了多个版本的历史遗留问题,大大的降低了58同城iOS App的崩溃率,也提高了日常工作研发效率。

App的性能优化对用户的体验十分重要,而崩溃作为其中最重要的一个环节需要我们持续的钻研与探索。也希望本文能为正在与疑难崩溃问题做“斗争”的同行带来一定的启发。

作者介绍:

周影杰,58 同城 – 平台技术部 – iOS 技术部 高级研发工程师

邓竹立,58 同城 – 平台技术部 - iOS 技术部 资深研发工程师

朴惠姝,58 同城 – 平台技术部 – iOS 技术部 高级研发工程师

相关问答

为什么我的英雄联盟会总是断线 说 英雄联盟与服务器已经断开 ...

应该是游戏崩溃服务器不行自己的网不行不会提示英雄联盟与服务器已经断开英...雷军第五次年度演讲发布会提前看:八大新品,还有onemorething评论30屏幕好...

苹果 崩溃 记录说明什么?

可能是手机系统中毒;2、手机里的垃圾软件太多;应该要及时清理;3、可能卸载软件的时候误删了系统的软件;4、手机硬件问题,不过这个一般是不会出现的。5、...

win10 1803 资源管理器 无限 崩溃 - Microsoft Community

[回答]错误应用程序名称:explorer.exe,版本:10.0.17134.1,时间戳:0x425b30b2错误模块名称:ntdll.dll,版本:10.0.17134.137,时间戳:0xf...

itools系统 崩溃日志 文件可以删除吗?

这个日志里有报错是不影响使用的,详情看下面。1、这个日志主要是用来提供给开发者的。2、可以忽略不急,也可以删除。3、奔溃日志一般是系统出错或者某个...

苹果app闪退是什么原因怎么处理-ZOL问答

另外,当苹果app闪退时,也可以尝试发送崩溃日志,便于App开发者及时发现现场问题...4、依次点击进入“设置”—“iTunesStore和AppStore”—点击AppleID(你目前...

问题事件名称appcrash - Microsoft Community

[回答]了解到您所说的有关使用Office组件遇到的问题,这个问题的话首先很抱歉给您带来的不便,这边的话建议您收集下日志文件:==============1、点击开始,输...

iPhone微信 崩溃 ,聊天记录全消失,该怎么办?

苹果手机微信崩溃我们经常遇到,有的是手机系统BUG,有的是手机内存满了,有的是微信软件本身问题,这样会导致我们的记录丢失,这里分享一下方法:一、打开微信...苹...

lol提示无法重连游戏重开?

如果我们的英雄联盟在进入的过程中,他提示无法重新连接游戏,这个时候是需要重开的,因为有可能是这场游戏的运行程序崩溃,导致后台没有记录,所以说我们是没有...

为什么pps会总是停止工作啊?

可能是软件冲突造成的。要检查原因的话,你输入eventvwr命令,打开事件察看器,展开windows日志-系统,然后在右边的操作里可以筛选日志,比如你可以筛选“记录时...

工学云因自身原因 崩溃 了怎么搞?

如果工学云因为自身原因崩溃了,可以尝试以下步骤来解决问题:1.重新启动工学云:尝试重新启动工学云应用程序或者重新启动服务器,看看能否解决问题。2.检查...

 暗影世界  忏悔面具 
王经理: 180-0000-0000(微信同号)
10086@qq.com
北京海淀区西三旗街道国际大厦08A座
©2024  上海羊羽卓进出口贸易有限公司  版权所有.All Rights Reserved.  |  程序由Z-BlogPHP强力驱动
网站首页
电话咨询
微信号

QQ

在线咨询真诚为您提供专业解答服务

热线

188-0000-0000
专属服务热线

微信

二维码扫一扫微信交流
顶部