腾讯 Matrix 增量编译 bug 解决,PR 已被官方采访(一)

简介: 腾讯 Matrix 增量编译 bug 解决,PR 已被官方采访

前言


最近,我们项目在接入微信 Matrix,刚开始接入的时候,还蛮顺利的。到了下午,运行项目,偶现 crash。看了一下报错信息,某些 class 文件在 dex 文件中没有找到,即 ClassNotFoundException 。


clean 了一下,发现好了,就继续开发,跑了几次,发现突然又 crash 了,这时候我第一感觉怀疑是 matrix 导致的。


于是,我把 matrix trace 插件关了之后,本地全量编译,还有增量编译,发现都没有这个问题了,于是我可以确定,这肯定是引入 Matrix 带来的问题。


这时候,我就去 github 上面搜 issue,关键字是 ClassNotFoundException ,发现很多人都遇到这个问题,但是一直没有修复。


f5b3822e8d60f3c9e88808a84e55f1ec_c009742a7e195010fefee135d1a3386f.png


这时候怎么办呢?是偶现的,不是必现的。那当然要找出复现路径呢?于是,又折腾了半天多,终于发现了复现路径。在增量编译的情况下,修改某个 library moudle 一行代码,可以稳定复现。

于是,又上去上面搜了一波,关键字是增量编译


3cf9a6b99410ede3c4a0c63a4787adf8_2e8f5a2d217b44a3685a77da5f34e7b9.png


果不其然,也有挺多人遇到,而且官方也明确标记为 bug,这时候我是怎么解决的呢?


欲知下事如何,请看下文,哈哈,卖一下关子。


b29123de6b943b952613edf09ea77c18_170c4986da48d86a45bd997f2e4c6c53.png


现象


我们回到问题的本身,先描述一下现象,问题描述清楚真的很重要,尤其是在网上想别人请教的时候,你懂的。


异常类型:编译异常& app crash

matrix版本:2.0.1

gradle版本:4.1.0

问题描述:第一次编译正常运行,第二次编译运行,会出现某些 class 找不到,报 ClassNotFoundException,出现问题之后需要 clean 项目,运行项目才正常


堆栈信息:


java.lang.NullPointerException
  at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
  at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
  at com.tencent.matrix.trace.MethodCollector$TraceClassAdapter.visit(MethodCollector.java:284)
  at org.objectweb.asm.ClassReader.accept(ClassReader.java:524)
  at org.objectweb.asm.ClassReader.accept(ClassReader.java:391)
  at com.tencent.matrix.trace.MethodCollector$CollectJarTask.run(MethodCollector.java:171)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
java.lang.NullPointerException
  at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
  at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
  at com.tencent.matrix.trace.MethodCollector$TraceClassAdapter.visit(MethodCollector.java:284)
  at org.objectweb.asm.ClassReader.accept(ClassReader.java:524)
  at org.objectweb.asm.ClassReader.accept(ClassReader.java:391)
  at com.tencent.matrix.trace.MethodCollector$CollectJarTask.run(MethodCollector.java:171)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
java.lang.NullPointerException
  at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
  at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
  at com.tencent.matrix.trace.MethodCollector$TraceClassAdapter.visit(MethodCollector.java:284)
  at org.objectweb.asm.ClassReader.accept(ClassReader.java:524)
  at org.objectweb.asm.ClassReader.accept(ClassReader.java:391)
  at com.tencent.matrix.trace.MethodCollector$CollectJarTask.run(MethodCollector.java:171)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
[I][MethodCollector] [saveIgnoreCollectedMethod] size:9626 path:D:\githubRep\gradleLearing\app\build\outputs\mapping\debug\ignoreMethodMapping.txt
[I][MethodCollector] [saveCollectedMethod] size:24989 incrementCount:24988 path:D:\githubRep\gradleLearing\app\build\outputs\mapping\debug\methodMapping.txt
[E][Matrix.MethodTracer] [innerTraceMethodFromJar] input:C:\Users\N21616\.gradle\caches\transforms-2\files-2.1\48590e038f1555cf787fe85359f8a35d\jetified-kotlin-stdlib-jdk7-1.5.20.jar output:D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\36.jar e:java.lang.UnsupportedOperationException: This feature requires ASM6
java.nio.file.FileSystemException: D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\36.jar: 另一个程序正在使用此文件,进程无法访问。
  at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)
  at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
  at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
  at sun.nio.fs.WindowsFileCopy.copy(WindowsFileCopy.java:165)
  at sun.nio.fs.WindowsFileSystemProvider.copy(WindowsFileSystemProvider.java:278)
  at java.nio.file.Files.copy(Files.java:1274)
  at com.tencent.matrix.trace.MethodTracer.innerTraceMethodFromJar(MethodTracer.java:204)
  at com.tencent.matrix.trace.MethodTracer.access$100(MethodTracer.java:60)
  at com.tencent.matrix.trace.MethodTracer$2.run(MethodTracer.java:108)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
> Task :app:transformClassesWithMatrixTraceTransformForDebug
[I][Matrix.Trace] [doTransform] Step(1)[Parse]... cost:48ms
[I][Matrix.Trace] [doTransform] Step(2)[Collection]... cost:1264ms
[E][Matrix.MethodTracer] [innerTraceMethodFromJar] input:C:\Users\N21616\.gradle\caches\transforms-2\files-2.1\bb37a7de696e1bea72b3b0dd87cdc726\jetified-kotlin-stdlib-jdk8-1.5.20.jar output:D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\35.jar e:java.lang.UnsupportedOperationException: This feature requires ASM6
java.nio.file.FileSystemException: D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\35.jar: 另一个程序正在使用此文件,进程无法访问。
  at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)
  at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
  at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
  at sun.nio.fs.WindowsFileCopy.copy(WindowsFileCopy.java:165)
  at sun.nio.fs.WindowsFileSystemProvider.copy(WindowsFileSystemProvider.java:278)
  at java.nio.file.Files.copy(Files.java:1274)
  at com.tencent.matrix.trace.MethodTracer.innerTraceMethodFromJar(MethodTracer.java:204)
  at com.tencent.matrix.trace.MethodTracer.access$100(MethodTracer.java:60)
  at com.tencent.matrix.trace.MethodTracer$2.run(MethodTracer.java:108)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
[E][Matrix.MethodTracer] [innerTraceMethodFromJar] input:C:\Users\N21616\.gradle\caches\transforms-2\files-2.1\32898900927cbb3ddb95f2fe14af33ec\jetified-kotlin-stdlib-1.5.20.jar output:D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\37.jar e:java.lang.UnsupportedOperationException: This feature requires ASM6
java.nio.file.FileSystemException: D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\37.jar: 另一个程序正在使用此文件,进程无法访问。
  at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)
  at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
  at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
  at sun.nio.fs.WindowsFileCopy.copy(WindowsFileCopy.java:165)
  at sun.nio.fs.WindowsFileSystemProvider.copy(WindowsFileSystemProvider.java:278)
  at java.nio.file.Files.copy(Files.java:1274)
  at com.tencent.matrix.trace.MethodTracer.innerTraceMethodFromJar(MethodTracer.java:204)
  at com.tencent.matrix.trace.MethodTracer.access$100(MethodTracer.java:60)
  at com.tencent.matrix.trace.MethodTracer$2.run(MethodTracer.java:108)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
[E][Matrix.MethodTracer] [innerTraceMethodFromJar] input:D:\githubRep\gradleLearing\mylibrary\build\intermediates\runtime_library_classes_jar\debug\classes.jar output:D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\60.jar e:java.util.zip.ZipException: zip file is empty
java.util.zip.ZipException: zip file is empty
  at java.util.zip.ZipFile.open(Native Method)
  at java.util.zip.ZipFile.<init>(ZipFile.java:225)
  at java.util.zip.ZipFile.<init>(ZipFile.java:155)
  at java.util.zip.ZipFile.<init>(ZipFile.java:169)
  at com.tencent.matrix.trace.MethodTracer.innerTraceMethodFromJar(MethodTracer.java:186)
  at com.tencent.matrix.trace.MethodTracer.access$100(MethodTracer.java:61)
  at com.tencent.matrix.trace.MethodTracer$2.run(MethodTracer.java:113)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at java.lang.Thread.run(Thread.java:748)
[E][Matrix.MethodTracer] [innerTraceMethodFromJar] input:D:\githubRep\gradleLearing\mylibrary\build\intermediates\runtime_library_classes_jar\debug\classes.jar is empty
[E][Matrix.MethodTracer] Close stream err!
[E][Matrix.MethodTracer] [innerTraceMethodFromJar] input:C:\Users\N21616\.gradle\caches\transforms-2\files-2.1\e378b9fe89a5fe15cf3fa9c9da712ef7\jetified-kotlin-stdlib-jdk8-1.5.20.jar output:D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\35.jar e:java.lang.UnsupportedOperationException: This feature requires ASM6
[E][Matrix.MethodTracer] Close stream err!
[E][Matrix.MethodTracer] [innerTraceMethodFromJar] input:C:\Users\N21616\.gradle\caches\transforms-2\files-2.1\ca30333b1699ed3075710b30785c2fac\jetified-kotlin-stdlib-1.5.20.jar output:D:\githubRep\gradleLearing\app\build\intermediates\transforms\MatrixTraceTransform\debug\37.jar e:java.lang.UnsupportedOperationException: This feature requires ASM6
[E][Matrix.MethodTracer] Close stream err!
> Task :app:transformClassesWithMatrixTraceTransformForDebug
[I][Matrix.Trace] [doTransform] Step(3)[Trace]... cost:2304ms
[I][Matrix.TraceTransform]  Insert matrix trace instrumentations cost time: 3671ms.


问题直接原因


就像文章开头说的,在本地搞了半天多, 才终于发现必现路径,增编编译,运行的时候,会直接 crash。


于是,我先去官方 issue 上面搜索,一搜,发现很多人都遇到,但是一直没有解决,官方标记为 bug,issue 链接  issue 592, 这里特别感谢他们提供的思路。


d81ac56213cd1c2c9690e8af867cae15_98c2a9dc68b8977c8891853561f18fdb.png


可以看到,很多人出现都是增编编译的时候出现问题,

于是,我在想,我先把增量编译关了,看行不行。


说干就干,于是我把 MatrixTraceTransform#isIncremental,MatrixTraceLegacyTransform##isIncremental 都返回 false,发现我们项目增量编译也 ok 了,不会 crash 了。


特意去看了一下编译耗时,在我们项目中,编译一次,transformClassesWithRealmTransformerForDebug,耗时大概是 20 - 30 ms 左右,增量编译在 10 - 15 ms,关闭 matrix transfrom 增量编译的话,大概慢 10 - 15 ms,貌似也可以接受。


菜逼的我留下了眼泪。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c4nPvoEz-1637060739950)(https://raw.githubusercontent.com/gdutxiaoxu/blog_image/master/21/08/20211104173950.png)]


问题探索


于是,我先去接入 matrix 相关功能了,但是这个增量编译的问题,一直在想着,到底是什么问题了?有时候吃饭都在想。


想着想着,我再次进入这个坑。gradlew installDebug --stacktrace ,查看编译 error 级别的信息,主要有四个地方,也是我重点怀疑的。


  • java.lang.NullPointerException 空指针问题
  • ASM 版本的问题,java.lang.UnsupportedOperationException: This feature requires ASM6
  • windows 文件 fd 占用问题,对应的提醒信息是 另一个程序正在使用此文件,进程无法访问。
  • zip file is empty 问题


第一次尝试,java.lang.NullPointerException 空指针问题?


11f5b4fec3962bfab3e5612404697677_08009920e07254d35a55f8993622ad4f.png

看堆栈信息,很快定位到 com.tencent.matrix.trace.MethodCollector.TraceClassAdapter#visit,里面有这样一个逻辑


public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
            super.visit(version, access, name, signature, superName, interfaces);
            this.className = name;
            if ((access & Opcodes.ACC_ABSTRACT) > 0 || (access & Opcodes.ACC_INTERFACE) > 0) {
                this.isABSClass = true;
            }
            collectedClassExtendMap.put(className, superName);
        }


debug 发现当 className 是 META-INF/versions/9/module-info.class,superName 为 null,导致报错。因为 ConcurrentHashMap 是不允许 key 或者 value 为 null 的。

于是我增加了判空逻辑,代码运行,App crash。初步排除这个原因。


module-info.class 这个 的 superName 为 null,这个很奇怪,按理来说,是不可能为 null 的,因为 java 默认都会继承 Object 。


那这个 module-info.class 到底是什么东东?搜了一下,发现 module-info.class 不是标准的 class。


module kotlin.stdlib.jdk8 {
    requires transitive kotlin.stdlib;
    requires kotlin.stdlib.jdk7;
    exports kotlin.collections.jdk8;
    exports kotlin.streams.jdk8;
    exports kotlin.text.jdk8;
    opens kotlin.internal.jdk8 to kotlin.stdlib;
}


简单来讲,就是JDK9支持模块化,类似Dart语言的包组织,JS的export,这样可以管理或者重新组织一个新的包,而不是像JDK8以下一样,只能通过Java修饰符来控制访问权限;而这个module-info.class就是来管理和描述这个包的;


在JDK8及以下,module-info.class并不会起作用,只有在JDK9以上才会起作用;

可以看到这个class并不是一个正常的class,并不包含类或者方法,所以asm和javassist处理这个class时,就会解析报错;


具体的可以看一下这篇文章


Android Gradle Plugin处理module-info.class报错


第二次尝试,ASM 版本问题?


2741052f75b2db77791cdc4cd69b3cfd_226a2fe42e17b5bc42c4fb2453363c60.png

一开始,编译日志提醒说 requires ASM6,以为是 asm 版本的问题,本地更新了 asm 版本,结果还是会出现 crash。排除,应该不是这个原因。


第三次尝试, windows 文件 fd 占用问题?


看堆栈信息,通过代码,可看到是在这里报错 com.tencent.matrix.trace.MethodTracer#innerTraceMethodFromJar


7096690e3c3c69c94e9a01a1bc3bc9f5_813953f8f4e20d2b848a2b01b9c04c97.png


具体报错的原因是插桩的过程中发生 exception,这时候调用 Files.copy(input.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING); 出错了,这个只会在 windows 上面出现,linux, mac 都不会。突然想说一句, mac 真香,没有 windows 这些乱七八糟的问题。


于是我在 catch exception 的时候,关闭一下 IO 流,代码如下


private void innerTraceMethodFromJar(File input, File output) {
        ZipOutputStream zipOutputStream = null;
        ZipFile zipFile = null;
        try {
           // 省略若干代码
        } catch (Exception e) {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.finish();
                    zipOutputStream.flush();
                    zipOutputStream.close();
                    zipOutputStream = null;
                }
                if (zipFile != null) {
                    zipFile.close();
                    zipFile = null;
                }
            } catch (Exception e2) {
                Log.e(TAG, "close stream err!, e2 is "+ e2);
            }
            Log.e(TAG, "[innerTraceMethodFromJar] input:%s output:%s e:%s", input, output, e);
            if (e instanceof ZipException) {
                e.printStackTrace();
            }
            try {
                if (input.length() > 0) {
                    Files.copy(input.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING);
                } else {
                    Log.e(TAG, "[innerTraceMethodFromJar] input:%s is empty", input);
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.finish();
                    zipOutputStream.flush();
                    zipOutputStream.close();
                }
                if (zipFile != null) {
                    zipFile.close();
                }
            } catch (Exception e) {
                Log.e(TAG, "close stream err!");
            }
        }
    }


重新运行,项目跑起来,启动 App,还是一如既往得出人意料, App 直接 crash, 我的天。


cc8fcdaea2f358f15902046618f2ee10_2c4faa98cc01c60c1ffe57e804207b93.png


你以为我要放弃了嘛,不不,起来,我还能再战个十万回合。


相关文章
|
人工智能
Let’s Make-It-3D!上交&微软最新开源2D转3D生成研究,Star超过1k星
Let’s Make-It-3D!上交&微软最新开源2D转3D生成研究,Star超过1k星
358 0
|
27天前
|
C#
时隔半年 DotNetGuide 已突破了 6.6K + Star,持续更新,欢迎更多小伙伴PR投稿!
时隔半年 DotNetGuide 已突破了 6.6K + Star,持续更新,欢迎更多小伙伴PR投稿!
|
3月前
|
自然语言处理 搜索推荐 程序员
因为看不惯Notepad++,国内大佬开源了Notepad--:技术分享与工作学习中的新选择
【8月更文挑战第20天】在编程界,文本编辑器是每一位开发者日常工作中不可或缺的工具。Notepad++,这款曾经风靡一时的文本编辑器,以其强大的功能和简洁的界面赢得了众多程序员的喜爱。然而,近年来,由于其作者的一些不当言论和行为,引发了广泛争议,许多程序员开始寻找替代品。在这样的背景下,国内一位大佬挺身而出,开源了Notepad--,为开发者们带来了一个新的选择。
329 1
|
4月前
|
机器人 vr&ar 计算机视觉
腾讯 Matrix 增量编译 bug 解决,PR 已被官方采访(二)
腾讯 Matrix 增量编译 bug 解决,PR 已被官方采访
|
边缘计算 Kubernetes Cloud Native
恭喜我的同事黄玉奇入选开放原子开源基金会TOC
近日,开放原子开源基金会技术监督委员会(TOC)举行第 32 次例会。经过投票,阿里云云原生应用平台高级技术专家黄玉奇正式当选为开放原子开源基金会 TOC 成员。
恭喜我的同事黄玉奇入选开放原子开源基金会TOC
|
存储 自动驾驶 API
十年积累,5.4万GitHub Star一朝清零:开源史上最大意外损失
十年积累,5.4万GitHub Star一朝清零:开源史上最大意外损失
254 0
|
SQL Java 大数据
ChunJun 1.16 Release版本即将发布,bug 捉虫活动邀您参与!
【ChunJun 1.16 新版本 bug 捉虫活动开始啦!奖品丰厚,等你来拿!】 ChunJun 即将迎来 1.16 Release 版本的正式发布。由于升级初期测试验证的场景覆盖不全,需要更多小伙伴的力量一起来帮忙验证,发现和修复问题,因此 ChunJun 社区决定广发英雄帖,邀请各路捉虫达人,一起帮助 ChunJun 在稳定性上更上一层楼。我们也将送出智能音箱、星巴克礼品卡、蓝牙耳机等多项好礼作为报答。 详情请点进正文查看~
143 0
ChunJun 1.16 Release版本即将发布,bug 捉虫活动邀您参与!
|
Prometheus Cloud Native IDE
名垂千古的机会到了,一文说清【给开源大项目贡献代码】二三事(github,pr,fork,ci)
名垂千古的机会到了,一文说清【给开源大项目贡献代码】二三事(github,pr,fork,ci)
名垂千古的机会到了,一文说清【给开源大项目贡献代码】二三事(github,pr,fork,ci)
|
Web App开发 安全 Ubuntu
曝 iPhone 14 没有 mini 版本;百度员工跳槽字节被判赔 107 万元;Firefox 100 发布 | 思否周刊
曝 iPhone 14 没有 mini 版本;百度员工跳槽字节被判赔 107 万元;Firefox 100 发布 | 思否周刊
164 0
下一篇
无影云桌面