Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore exec file compatibility after upgrade of ASM to version 9.5 #1492

Merged

Conversation

Godin
Copy link
Member

@Godin Godin commented Jul 10, 2023

JaCoCo version 0.8.8 was removing from LineNumberTable entries with zero line numbers (see stacktrace of RuntimeException and classinfo output below) due to a bug in ASM (https://gitlab.ow2.org/asm/asm/-/issues/317989), this was fixed by an upgrade of ASM to 9.5 in JaCoCo version 0.8.9 (#1416), which unfortunately also lead to the insertion of more probes (see execinfo output below) and so broke exec-file compatibility (see ArrayIndexOutOfBoundsException and IllegalStateException below).


Using the following Generator.java which generates class file with zero line number for a method invocation instruction

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.FileOutputStream;
import java.io.IOException;

public class Generator {
        public static void main(String[] args) throws IOException {
                ClassWriter c = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                c.visit(Opcodes.V1_5, 0, "Example", null, "java/lang/Object", null);

                c.visitSource("Example.java", null);

                MethodVisitor m = c.visitMethod(
                                Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main",
                                "([Ljava/lang/String;)V", null, null);
                m.visitCode();
                m.visitInsn(Opcodes.NOP);
                Label label1 = new Label();
                m.visitLabel(label1);
                m.visitLineNumber(0, label1);
                m.visitMethodInsn(Opcodes.INVOKESTATIC, "Example", "throw", "()V", false);
                Label label2 = new Label();
                m.visitLabel(label2);
                m.visitLineNumber(1, label2);
                m.visitInsn(Opcodes.RETURN);
                m.visitMaxs(0, 0);
                m.visitEnd();

                m = c.visitMethod(Opcodes.ACC_STATIC, "throw", "()V", null, null);
                m.visitCode();
                m.visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException");
                m.visitInsn(Opcodes.DUP);
                m.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
                                "<init>", "()V", false);
                m.visitInsn(Opcodes.ATHROW);
                m.visitMaxs(0, 0);
                m.visitEnd();

                c.visitEnd();

                FileOutputStream fos = new FileOutputStream("Example.class");
                fos.write(cw.toByteArray());
                fos.close();
        }
}

execution of

java -cp ~/.m2/repository/org/ow2/asm/asm/9.5/asm-9.5.jar Generator.java

java -cp . Example

produces

Exception in thread "main" java.lang.RuntimeException
        at Example.main(Example.java:0)

Before this change

Usage of JaCoCo version 0.8.8 for instrumentation and versions 0.8.9 or 0.8.10 for analysis

java -javaagent:jacoco-0.8.8/lib/jacocoagent.jar=append=false -cp . Example

java -jar jacoco-0.8.8/lib/jacococli.jar classinfo Example.class --verbose

java -jar jacoco-0.8.9/lib/jacococli.jar classinfo Example.class --verbose

java -jar jacoco-0.8.9/lib/jacococli.jar report jacoco.exec --classfiles Example.class

leads to ArrayIndexOutOfBoundsException:

Exception in thread "main" java.lang.RuntimeException
        at Example.throw(Example.java)
        at Example.main(Example.java)

  INST   BRAN   LINE   METH   CXTY   ELEMENT
     7      0      1      2      2   class 0x37a683e967bd1949 Example
     3      0      1      1      1   +- method main([Ljava/lang/String;)V
     1      0                        |  +- line 1
     4      0      0      1      1   +- method throw()V

  INST   BRAN   LINE   METH   CXTY   ELEMENT
     7      0      2      2      2   class 0x37a683e967bd1949 Example
     3      0      2      1      1   +- method main([Ljava/lang/String;)V
     1      0                        |  +- line 0
     1      0                        |  +- line 1
     4      0      0      1      1   +- method throw()V

[INFO] Loading execution data file /private/tmp/jacoco.exec.
Exception in thread "main" java.io.IOException: Error while analyzing Example.class with JaCoCo 0.8.9.202303310957/c0ad781.
        at org.jacoco.cli.internal.core.analysis.Analyzer.analyzerError(Analyzer.java:163)
        at org.jacoco.cli.internal.core.analysis.Analyzer.analyzeClass(Analyzer.java:135)
        at org.jacoco.cli.internal.core.analysis.Analyzer.analyzeClass(Analyzer.java:158)
        at org.jacoco.cli.internal.core.analysis.Analyzer.analyzeAll(Analyzer.java:195)
        at org.jacoco.cli.internal.core.analysis.Analyzer.analyzeAll(Analyzer.java:228)
        at org.jacoco.cli.internal.commands.Report.analyze(Report.java:110)
        at org.jacoco.cli.internal.commands.Report.execute(Report.java:84)
        at org.jacoco.cli.internal.Main.execute(Main.java:90)
        at org.jacoco.cli.internal.Main.main(Main.java:105)
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2
        at org.jacoco.cli.internal.core.internal.analysis.InstructionsBuilder.addProbe(InstructionsBuilder.java:149)
        at org.jacoco.cli.internal.core.internal.analysis.MethodAnalyzer.visitInsnWithProbe(MethodAnalyzer.java:169)
        at org.jacoco.cli.internal.core.internal.flow.MethodProbesAdapter.visitInsn(MethodProbesAdapter.java:107)
        at org.jacoco.cli.internal.asm.tree.InsnNode.accept(InsnNode.java:65)
        at org.jacoco.cli.internal.core.internal.analysis.MethodAnalyzer.accept(MethodAnalyzer.java:52)
        at org.jacoco.cli.internal.core.internal.analysis.ClassAnalyzer$1.accept(ClassAnalyzer.java:107)
        at org.jacoco.cli.internal.core.internal.flow.ClassProbesAdapter$2.visitEnd(ClassProbesAdapter.java:91)
        at org.jacoco.cli.internal.asm.ClassReader.readMethod(ClassReader.java:1518)
        at org.jacoco.cli.internal.asm.ClassReader.accept(ClassReader.java:744)
        at org.jacoco.cli.internal.asm.ClassReader.accept(ClassReader.java:424)
        at org.jacoco.cli.internal.core.analysis.Analyzer.analyzeClass(Analyzer.java:117)
        at org.jacoco.cli.internal.core.analysis.Analyzer.analyzeClass(Analyzer.java:133)
        ... 7 more

Usage of JaCoCo version 0.8.8 together with versions 0.8.9 or 0.8.10 for instrumentation

java -javaagent:jacoco-0.8.8/lib/jacocoagent.jar=append=false -cp . Example

java -javaagent:jacoco-0.8.9/lib/jacocoagent.jar -cp . Example

java -jar jacoco-0.8.9/lib/jacococli.jar execinfo jacoco.exec

java -jar jacoco-0.8.8/lib/jacococli.jar report jacoco.exec --classfiles Example.class

java -jar jacoco-0.8.9/lib/jacococli.jar report jacoco.exec --classfiles Example.class

leads to IllegalStateException at analysis time

Exception in thread "main" java.lang.RuntimeException
        at Example.throw(Example.java)
        at Example.main(Example.java)

Exception in thread "main" java.lang.RuntimeException
        at Example.throw(Example.java)
        at Example.main(Example.java:0)

[INFO] Loading exec file jacoco.exec.
CLASS ID         HITS/PROBES   CLASS NAME
Session "godins-macbook-pro.home-cefeb549": Mon Jul 10 23:03:15 CEST 2023 - Mon Jul 10 23:03:15 CEST 2023
37a683e967bd1949    1 of   2   Example
Session "godins-macbook-pro.home-e0b3d098": Mon Jul 10 23:03:15 CEST 2023 - Mon Jul 10 23:03:15 CEST 2023
37a683e967bd1949    2 of   3   Example

[INFO] Loading execution data file /private/tmp/jacoco.exec.
Exception in thread "main" java.lang.IllegalStateException: Incompatible execution data for class Example with id 37a683e967bd1949.
        at org.jacoco.cli.internal.core.data.ExecutionData.assertCompatibility(ExecutionData.java:197)
        at org.jacoco.cli.internal.core.data.ExecutionData.merge(ExecutionData.java:160)
        at org.jacoco.cli.internal.core.data.ExecutionData.merge(ExecutionData.java:133)
        at org.jacoco.cli.internal.core.data.ExecutionDataStore.put(ExecutionDataStore.java:55)
        at org.jacoco.cli.internal.core.data.ExecutionDataStore.visitClassExecution(ExecutionDataStore.java:178)
        at org.jacoco.cli.internal.core.data.ExecutionDataReader.readExecutionData(ExecutionDataReader.java:151)
        at org.jacoco.cli.internal.core.data.ExecutionDataReader.readBlock(ExecutionDataReader.java:116)
        at org.jacoco.cli.internal.core.data.ExecutionDataReader.read(ExecutionDataReader.java:93)
        at org.jacoco.cli.internal.core.tools.ExecFileLoader.load(ExecFileLoader.java:60)
        at org.jacoco.cli.internal.core.tools.ExecFileLoader.load(ExecFileLoader.java:74)
        at org.jacoco.cli.internal.commands.Report.loadExecutionData(Report.java:99)
        at org.jacoco.cli.internal.commands.Report.execute(Report.java:83)
        at org.jacoco.cli.internal.Main.execute(Main.java:90)
        at org.jacoco.cli.internal.Main.main(Main.java:105)

[INFO] Loading execution data file /private/tmp/jacoco.exec.
Exception in thread "main" java.lang.IllegalStateException: Incompatible execution data for class Example with id 37a683e967bd1949.
        at org.jacoco.cli.internal.core.data.ExecutionData.assertCompatibility(ExecutionData.java:197)
        at org.jacoco.cli.internal.core.data.ExecutionData.merge(ExecutionData.java:160)
        at org.jacoco.cli.internal.core.data.ExecutionData.merge(ExecutionData.java:133)
        at org.jacoco.cli.internal.core.data.ExecutionDataStore.put(ExecutionDataStore.java:55)
        at org.jacoco.cli.internal.core.data.ExecutionDataStore.visitClassExecution(ExecutionDataStore.java:178)
        at org.jacoco.cli.internal.core.data.ExecutionDataReader.readExecutionData(ExecutionDataReader.java:151)
        at org.jacoco.cli.internal.core.data.ExecutionDataReader.readBlock(ExecutionDataReader.java:116)
        at org.jacoco.cli.internal.core.data.ExecutionDataReader.read(ExecutionDataReader.java:93)
        at org.jacoco.cli.internal.core.tools.ExecFileLoader.load(ExecFileLoader.java:60)
        at org.jacoco.cli.internal.core.tools.ExecFileLoader.load(ExecFileLoader.java:74)
        at org.jacoco.cli.internal.commands.Report.loadExecutionData(Report.java:99)
        at org.jacoco.cli.internal.commands.Report.execute(Report.java:83)
        at org.jacoco.cli.internal.Main.execute(Main.java:90)
        at org.jacoco.cli.internal.Main.main(Main.java:105)

Usage of JaCoCo versions 0.8.9 or 0.8.10 for instrumentation and analysis

java -javaagent:jacoco-0.8.9/lib/jacocoagent.jar=append=false -cp . Example

java -jar jacoco-0.8.9/lib/jacococli.jar report jacoco.exec --classfiles Example.class --html report

produces

Screenshot 2023-07-11 at 00 02 01

whereas usage of JaCoCo versions 0.8.9 or 0.8.10 for instrumentation and version 0.8.8 for analysis

java -javaagent:jacoco-0.8.9/lib/jacocoagent.jar=append=false -cp . Example

java -jar jacoco-0.8.8/lib/jacococli.jar report jacoco.exec --classfiles Example.class --html report

leads to a misleading report

Screenshot 2023-07-10 at 22 49 40

After this change

Usage of JaCoCo version 0.8.8 for instrumentation and version with this change for analysis

java -javaagent:jacoco-0.8.8/lib/jacocoagent.jar=append=false -cp . Example

java -javaagent:jacoco-snapshot/lib/jacocoagent.jar -cp . Example

java -jar jacoco-snapshot/lib/jacococli.jar classinfo Example.class --verbose

java -jar jacoco-snapshot/lib/jacococli.jar execinfo jacoco.exec

java -jar jacoco-snapshot/lib/jacococli.jar report jacoco.exec --classfiles Example.class

does not lead to ArrayIndexOutOfBoundsException:

Exception in thread "main" java.lang.RuntimeException
        at Example.throw(Example.java)
        at Example.main(Example.java)

Exception in thread "main" java.lang.RuntimeException
        at Example.throw(Example.java)
        at Example.main(Example.java:0)

  INST   BRAN   LINE   METH   CXTY   ELEMENT
     7      0      2      2      2   class 0x37a683e967bd1949 Example
     3      0      2      1      1   +- method main([Ljava/lang/String;)V
     1      0                        |  +- line 0
     1      0                        |  +- line 1
     4      0      0      1      1   +- method throw()V

[INFO] Loading exec file jacoco.exec.
CLASS ID         HITS/PROBES   CLASS NAME
Session "godins-macbook-pro.home-c5f6be3c": Mon Jul 10 23:08:04 CEST 2023 - Mon Jul 10 23:08:04 CEST 2023
37a683e967bd1949    1 of   2   Example
Session "godins-macbook-pro.home-f3138653": Mon Jul 10 23:08:04 CEST 2023 - Mon Jul 10 23:08:04 CEST 2023
37a683e967bd1949    1 of   2   Example

[INFO] Loading execution data file /private/tmp/jacoco.exec.
[INFO] Analyzing 1 classes.

however

usage of JaCoCo versions 0.8.9 or 0.8.10 for instrumentation and version with this change for analysis
will still lead to a misleading report

and usage of JaCoCo version with this change together with versions 0.8.9 or 0.8.10 for instrumentation
will still lead to IllegalStateException at analysis time.


Fixes #1471

@Godin Godin added this to the 0.8.11 milestone Jul 10, 2023
@Godin Godin added this to Implementation in Current work items via automation Jul 10, 2023
@Godin Godin added component: core type: bug 🐛 Something isn't working labels Jul 10, 2023
@Godin Godin self-assigned this Jul 10, 2023
@Godin Godin force-pushed the restore_exec_file_compatibility_after_ASM_upgrade branch 4 times, most recently from ae7ac80 to 0c7757a Compare July 10, 2023 15:34
@Godin Godin changed the title Restore exec file compatibility after ASM upgrade Restore exec file compatibility after upgrade of ASM to version 9.5 Jul 10, 2023
@Godin Godin force-pushed the restore_exec_file_compatibility_after_ASM_upgrade branch from 0c7757a to cb8a3b4 Compare July 10, 2023 22:28
@ykhandelwal913

This comment was marked as off-topic.

@Godin

This comment was marked as off-topic.

@ykhandelwal913

This comment was marked as off-topic.

@Godin

This comment was marked as off-topic.

@ykhandelwal913
Copy link

@Godin it worked with snapshot version. We are using jacoco jenkins plugin to publish the report so i had to build that plugin locally with the snapshot version provided by you and things worked after that.

@ykhandelwal913
Copy link

@Godin sorry to asking question again. As we verified and working fine, when can we expect the release for the same?

@Godin Godin force-pushed the restore_exec_file_compatibility_after_ASM_upgrade branch from cb8a3b4 to 42e6233 Compare October 9, 2023 09:49
assertEquals(1, nextProbeId);
// workaround for zero line number can be removed if needed
// during change of exec file version
assertEquals(0x1007, ExecutionDataWriter.FORMAT_VERSION);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this idea! 👍

Copy link
Member Author

@Godin Godin Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marchof I have a strong feeling of déjà vu... oh no, actually it indeed happened - #636 (comment) 😆

@Godin Godin force-pushed the restore_exec_file_compatibility_after_ASM_upgrade branch from 42e6233 to 9f007ba Compare October 10, 2023 08:36
@Godin Godin marked this pull request as ready for review October 10, 2023 09:10
@Godin Godin moved this from Implementation to Review in Current work items Oct 10, 2023
@marchof marchof merged commit 206e5be into jacoco:master Oct 10, 2023
23 checks passed
Current work items automation moved this from Review to Done Oct 10, 2023
@Godin Godin deleted the restore_exec_file_compatibility_after_ASM_upgrade branch October 10, 2023 14:18
@Godin Godin mentioned this pull request Oct 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: core type: bug 🐛 Something isn't working
Projects
Development

Successfully merging this pull request may close these issues.

Exception with Jacoco 0.8.9 while analyzing inline reified coroutine function (filterIsInstance)
3 participants