一、问题描述
某阿里云EMAS客户的APK基于最新的线上版本发布了第十个补丁,发布1小时后在崩溃检测平台收集到crash日志,并收到用户反馈:部分手机上的APP会闪退。
客户开发人员紧急上报EMAS技术支持,双方沟通后初步判断是补丁加载导致APP闪退。同时我们本地和客户本地经过多次、多轮测试,没有一台手机可以复现闪退,此时崩溃率已达到9% 左右,经讨论决定先回滚本次补丁。
二、调查难点
难点一:该问题当时无法在本地复现,技术支持同学经过充分、多次测试,包括用户出问题的同款手机,均无法复现闪退问题。
难点二:日志平台抓取的日志signal 6为系统层面日志,无法指导定位代码层面问题。 唯一一条有用日志:/data/app/com.xxx.xxx-l-Lc_A9dXeo8oWIupXFKxA==/oat/arm64/base.odex。
*备注:EMAS远程日志提供客户端全量日志拉取、诊断、管理能力,帮助开发者建立分钟级线上问题定位能力
三、调查方向
1.日志抓到的报错对代码没有用,但是可以看出来是odex问题。我们需要反编译APK文件,查看具体class内容。
2.在APP中关键节点加日志, 判断该类是否成功加载到补丁里以及加载补丁的时候有没有加载这个类,两个重要节点
四、调查经过
第一阶段:根据抓到的日志显示崩溃在odex中:
怀疑和加固有关,经确认,客户使用了加固,但是日志上看走的是非加固模式,我们提供热修复走加固模式的API(下图),适配后重新测试。
第二阶段:客户走加固模式后,提供了测试包(APK未加固),经测试有10%的手机会崩溃(如荣耀、华为品牌的手机,失败日志如下:)
日志上看是崩溃在外部类调用内部类的checkInputTxt方法上,通过方法签名看,外部类是旧包中的,内部类是补丁包的,但是外部类有变更,理论上应该加载补丁中的外部类。此时,我们怀疑外部类ChatRoomManager没打包进补丁包,反编译补丁包检查(如下图)
可以看出外部类打包进了补丁包,此步骤和流程没有问题,但是从补丁加载日志上看,加载的补丁包中也有外部类,此时我们怀疑外部类在加载补丁之前已经被类加载器加载。(由于部分机型必现崩溃,部分机型正常,开始怀疑不同机型系统的类加载机制问题,但是普遍认知中类加载机制一般不会出问题。)
第三阶段:此时重点关注类加载的时机和类加载器对象:加了类加载过程的日志和类加载器哈希打印。
日志中崩溃的设备在加载补丁包前后都没有看到外部类的加载日志。此时确定是类加载器的问题。考虑到加固本身也会使用自定义类加载器,让客户对APK进行加固,测试正常。
五、问题解决
基于结论,我们给客户适配了加固模式测试,加固模式会使用自己的类加载器,避免了双亲委派机制失效的影响。经过monkey测试30台机器均无问题,至此反馈客户问题解决。
六、总结
- 匿名内部类的热修的需要加一些java代码来确保所有的方法都被打进补丁包。
- 适配加固的方案需要严格适配加固模式。