腾讯 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


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


相关文章
|
9月前
|
人工智能
Let’s Make-It-3D!上交&微软最新开源2D转3D生成研究,Star超过1k星
Let’s Make-It-3D!上交&微软最新开源2D转3D生成研究,Star超过1k星
286 0
|
8月前
|
Java
腾讯 Matrix 增量编译 bug 解决,PR 已被官方采访(二)
腾讯 Matrix 增量编译 bug 解决,PR 已被官方采访
|
存储 自动驾驶 API
十年积累,5.4万GitHub Star一朝清零:开源史上最大意外损失
十年积累,5.4万GitHub Star一朝清零:开源史上最大意外损失
203 0
|
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 发布 | 思否周刊
136 0
|
安全 Java 程序员
Gitee 关闭部分开源仓库;大厂一半以上程序员愿意降薪跳槽;Deno 1.22 发布 | 思否周刊
Gitee 关闭部分开源仓库;大厂一半以上程序员愿意降薪跳槽;Deno 1.22 发布 | 思否周刊
198 0
|
安全 开发者 流计算
印度萌新令人绝望的操作:提交 PR“轰炸”近 40 万开发者,GitHub 负责?
又一场波及数十万人的电子邮件风暴(Email storm)意外发生,这次的地点是在 GitHub 平台,事件主角是一位仅 18 岁的来自印度的年轻开发者 Rohith Sreedharan,他近日不小心给 GitHub 上约 40 万名用户发送了电子邮件。
192 0
印度萌新令人绝望的操作:提交 PR“轰炸”近 40 万开发者,GitHub 负责?
GitHub Discussions 功能进入 Beta 阶段!这是准备夺走 StackOverflow 一块蛋糕?
GitHub 团队正在开发一项名为 Discussions 的新功能。有了 GitHub Discussions,我们可以像在 StackOverflow 上一样,进行讨论或者提出问题。
406 0
GitHub Discussions 功能进入 Beta 阶段!这是准备夺走 StackOverflow 一块蛋糕?
DHL
|
算法 前端开发 安全
[译][2.4K Star] 放弃 Dagger 拥抱 Koin
Koin 是为 Kotlin 开发者提供的一个实用型轻量级依赖注入框架,采用纯 Kotlin 语言编写而成,仅使用功能解析,无代理、无代码生成、无反射。
DHL
349 0
[译][2.4K Star] 放弃 Dagger 拥抱 Koin
|
机器学习/深度学习 人工智能 大数据
全球最火DL课程Fast.ai 2020版今日上线!我们拿到了独家授权,中文版同步免费放出
全球最火DL课程Fast.ai 2020版今日上线!我们拿到了独家授权,中文版同步免费放出
371 0