JAVA覆盖率统计之01-Jacoco控制流案例

字节码注入与控制流

1 注入方式
JaCoCo是一个被广泛使用的JAVA覆盖率统计工具,它利用ASM库,通过注入字节码的方式来修改和生成java字节码,从而记录程序的执行数据,但它不会改变原有代码的行为。 最常用的方式是通过Java Agent以On-The-Fly的方式在runtime来注入和统计数据。这种方式就不会改变编译的class文件。


implementation.png

当然在本文中,笔者将介绍Jacoco的具体注入方式,因此,我们使用Jaccoco Offline 的模式,在编译时直接将覆盖率统计的探针(Probe)注入(Inject)到被打桩的class文件中。

2 Probe探针组成
Jacoco是通过一个Probe探针的方式来注入的,探针是字节指令集插入到java方法中,程序执行后可以被记录,它不会改变原有代码的行为。
根据Jacoco官方介绍,一个Probe可以由以下JVM指令组成:

JVM指令 Probe作用 说明
ALOAD probearray 将一个引用类型本地变量推送至栈顶
xPUSH probeid 将一个常量值推送至栈顶
ICONST_1 将int型推送至栈顶
BASTORE 从操作数栈存存储到数组

根据代码逻辑的不同,可以由如下的一些变化:

Possible Opcodes Min. Size [bytes] Max. Size [bytes]
ALOAD_x, ALOAD * 1 2
ICONST_x, BIPUSH, SIPUSH, LDC, LDC_W ** 1 3
ICONST_1 1 1
BASTORE 1 1
Total: 4 7
  • The probe array is the first variable after the arguments. If the method arguments do not consume more that 3 slots the 1-byte opcode can be used.
    ** 1-byte opcodes for ids 0 to 5, 2-byte opcode for ids up to 127, 3-byte opcode for ids up to 32767. Ids values of 32768 or more require an additional constant pool entry. For normal class files it is very unlikely to require more than 32,000 probes.

感兴趣的读者可以阅读以下链接了解更多

https://www.jacoco.org/jacoco/trunk/doc/flow.html

3 Probe探针插入策略
JaCoCo是根据控制流Type来采用不同的探针插入策略的。探针不改变该方法的行为,但记录他们已被执行的事实,从理论上讲,可以在控制流图的每一个边插入一个探针,作为探针实现本身需要多个字节码指令,这将增加几倍的类文件的大小和执行速度。事实上,只需要一个几个探头,根据每个方法的控制流的方法。具体策略如下:

sequence.JPG

jump.JPG

return.JPG

案例

以下案例来介绍IF/FOR/抛异常等场景下jacoco是如何进行注入的。

package com.demo.jacoco;
public class DemoClass {
    public static void callIf(boolean flag) {
        print("start");
        if(flag) {
            print("true");
        }else {
            print("flase");
        }
        print("done");
    }
    private static void print(String string) {
        System.out.println(string);
    }
    
    public static void callFor(int loop) {
        for(int i=0;i

我们通过javap 来解析DemoClass.class

javap -v -c DemoClass.class

1 IF

![](https://upload-images.jianshu.io/upload_images/2651525-0a155f5b1d9281a4.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

2 FOR

jacoco_for.JPG

3 Throw

jacoco_throw.JPG

4 默认构造方法

jacoco_1类构造方法.JPG

你可能感兴趣的:(JAVA覆盖率统计之01-Jacoco控制流案例)