背景
某次灰度发布之后没多久就收到线上ANR告警,经排查定位到是某个页面onCreate方法执行太久导致,而火焰图中的耗时堆栈指向了我们用于监控页面启动速度的一段插桩代码,反编译Apk之后发现本该是if语句的代码竟变成了一个do-while语句,形成了死循环最终导致主线程卡死。
此后每构建二、三十次都会复现一次该问题,且每次的异常页面,异常方法完全随机。

问题分析
if和do-while两个完全不相干的语句为什么出现互相转化的情况?在jadx反编译而来的smali代码中不难看出,if语句对应的标签正常情况下应该指向的是return语句,和Java源码中if语句块后面紧跟着return语句对应。而异常情况下标签跑到了整个函数的开头,故被jadx翻译成了do-while,因此问题的关键就在这个label上面。

初步分析
出现此问题的这段插桩代码出自我们的APM页面启动监控,原本是插桩在Activity和Fragment的onCreate等关键生命周期中用于耗时统计。其所在的类是由我们自定义的插桩plugin weaver所生成(基于byteX开发的一个plugin,支持插入,代理和替换等自定义的插桩行为)。
因此我们要对从该plugin所在的byteX transform开始,直到最终产出dex文件的R8 transform结束这期间的所有transform挨个分析。
由于问题偶现,且每次异常的类和方法完全随机,说明大概率是一个多线程并发读写的问题,因此我们在分析过程中会需要重点关注涉及并发读写的逻辑。
分析R8
我们在输入给R8的jar包中找到了这个异常类的class文件,这里可以看到jadx反编译这个方法会失败,看class字节码中if语句跳转指向的标签L29,但是函数中并没有定义L29指向的是哪里,并且smali视图下查看可以看到if语句指向的标签在整个函数体中也没有声明,但是前面反编译DEX文件得到的结论是标签有声明但是在函数体的第一行,两者不一致,说明R8可能在执行过程中编辑了字节码导致异常。
(这里我们早期误以为标签丢失并不会导致语句变化这种程度的错误,因此直接将范围锁定在了R8,虽然后续证明了此问题与R8无关,但这段分析也为最终解开谜底提供了关键线索)


最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



