Skip to content

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 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
@marchof marchof merged commit 206e5be into jacoco:master 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