Jacoco的覆盖率原理

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
可观测可视化 Grafana 版,10个用户账号 1个月
可观测监控 Prometheus 版,每月50GB免费额度
简介: JaCoCo(Java Code Coverage)是一种广泛使用的代码覆盖率工具,通过在字节码中插入探针(Probe)来收集覆盖率信息。

Jacoco的覆盖率原理

收集覆盖率信息的方法

Runtime Profiling

Runtime Profiling是一种在程序运行时进行的性能分析技术,它可以帮助开发者了解程序的运行情况,识别性能瓶颈和优化程序性能。由于是在程序运行时进行,runtime profiling 能够提供实时的数据,便于理解程序在实际运行条件下的行为。JVMTI(Java Virtual Machine Tool Interface)是一个由Java虚拟机(JVM)提供的原生编程接口,它用于开发可以监控和控制JVM内部状态的工具。JVMTI是JVMPI(Java Virtual Machine Profiler Interface)和JVMDI(Java Virtual Machine Debug Interface)的后续版本,提供了更丰富的功能和更高效的性能。JVMTI提供了广泛的功能,包括但不限于调试、性能分析、线程分析、内存分析等。JVMTI是一个原生接口,这意味着它需要用C或C++等语言来编写代理程序(Agent),这些代理程序可以与JVM交互。JVMTI允许代理程序注册对特定事件的兴趣,如类加载、方法调用、异常抛出等,JVM会在这些事件发生时通知代理程序。代理程序可以通过JVMTI获取JVM的运行时数据,包括堆内存使用情况、线程状态、类信息等。除了监控,JVMTI还允许代理程序在一定程度上控制JVM的行为,例如暂停和恢复线程的执行。JVMTI是跨平台的,可以在不同的操作系统和硬件架构上运行。虽然JVMTI提供了强大的功能,但是需要注意的是,并非所有的JVM实现都支持JVMTI的所有特性。JVMTI与JDWP(Java Debug Wire Protocol)和JDI(Java Debug Interface)一起工作,形成了JDPA(Java Debugging and Profiling Architecture),这是一个完整的调试和性能分析体系。使用JVMTI可能会对JVM的性能产生一定影响,尤其是在启用了大量事件通知的情况下。JVMTI提供了对JVM内部状态的深入访问,因此在设计和实现基于JVMTI的工具时需要考虑安全性和稳定性。

Instrumentation

通过Instrumentation可以动态对运行中的Java程序进行操作,Intrumentation的api都在java.lang.instrument包里,可以实现性能监控、代码覆盖分析、动态追踪等功能。代码覆盖可以通过ASM技术在bytecode(字节码)中注入,从而实现覆盖率的统计,Java中比较有名的Jacoco就是通过这种方式实现的。

Jacoco

Jacoco的Probe探针的原理

Jacoco目前支持on-the-fly模型和offline模式两种注入形式。on-the-fly模型通过指定-javaagent的参数指定特殊的Jar包启动instrumentation代理程序,这个代理程序通过Class Loader 装载一个class前判断是否转换修改class文件,将统计代码注入到class中,测试覆盖率分析在JVM执行测试代码的过程中就完成了。offline实现对被测试代码生成已经完成插桩的变异后文件(class或者jar),在测试环境部署插桩后的文件,这样就可以完成覆盖率的收集工作了。Jacoco的字节码注入方式通过Probe探针的方式注入。
Jacoco是根据控制流Type采取不同的探针插入策略的,一个用java字节码定义的java方法的控制流图可能有以下的Type,每一个Type连接一个源指令与目标指令,Type不同探针的注入策略也会不同,如下是Type定义:

type source target remarks
ENTRY First instraction in method
SEQUENCE Instruction, except GOTO, xRETURN, THROW, TABLESWITCH and LOOKUPSWITCH subsequent instruction
JUMP GOTO, IFx, TABLESWITCH or LOOKUPSWITCH instruction Taget instruction TABLESWITCH and LOOKUPSWITCH will define multiple edges
EXHANDLER Any instruction in handler scope Target instruction
EXIT xRETURN or THROW instruction
EXEXIT Any instruction unhandled exception

如果是每一个字节码都插入一个probe探针,那么最终的class会比原来大好几倍,所以这个probe探针是按照如下几种情况进行插入的(红色边是需要插入探针的流程)。

  • SEQUENCE:在一个简单的序列探针插入两个字节码指令之间。
  • JUMP(unconditional):无条件跳转的指令,就放在GOTO指令之前
  • JUMP(conditional):反转操作码的语义,然后在条件跳转指令后加探针,然后在探针后添加GOTO指令跳转到原本的位置(这是因为字节码是顺序执行的,所以需要添加一个goto,完成无条件跳转的。)下图是Jacoco给的解释示意图。

    如果对应的JUMP是包含IF和ELSE的逻辑,那么如下的图更容易理解。

  • EXIT:RETURN or THROW在这些语句之前添加探针。

Jacoco动态注入

public class JacocoTest {
    private static transient /* synthetic */ boolean[] $jacocoData;

    public JacocoTest() {
        boolean[] arrbl = JacocoTest.$jacocoInit();
        arrbl[0] = true;
    }

    public static void main(String[] arrstring) {
        boolean[] arrbl = JacocoTest.$jacocoInit();
        int a = 10;
        ++a;
        arrbl[1] = true;
        System.out.println();
        if (++a > 10) {
            arrbl[2] = true;
            JacocoTest.test1();
            arrbl[3] = true;
        } else {
            JacocoTest.test2();
            arrbl[4] = true;
        }
        System.out.println();
        arrbl[5] = true;
    }

    public static void test1() {
        boolean[] arrbl = JacocoTest.$jacocoInit();
        System.out.println("");
        arrbl[6] = true;
    }

    public static void test2() {
        boolean[] arrbl = JacocoTest.$jacocoInit();
        System.out.println("");
        arrbl[7] = true;
        arrbl[8] = true;
        throw new RuntimeException("");
    }

    private static /* synthetic */ boolean[] $jacocoInit() {
        boolean[] arrbl = $jacocoData;
        boolean[] arrbl2 = arrbl;
        if (arrbl != null) return arrbl2;
        Object[] arrobject = new Object[]{4473305039327547984L, "com/xin/test/JacocoTest", 9};
        UnknownError.$jacocoAccess.equals(arrobject);
        arrbl2 = $jacocoData = (boolean[])arrobject[0];
        return arrbl2;
    }
}

从上面代码可以看出,Jacoco是用来一个boolean数组作为标记(boolean[] arrbl = JacocoTest.$jacocoInit();),只要执行过对应代码就对这个数组赋值True,这里也能看出并不是每一行都给出了标记。Probe探针在不影响原来的指令执行流程的前提下插入到两个指令之间,每一个探针都插入在程序的control flow的边中,如果探针被执行了,我们可以确定这个边上的代码就会被执行。Jacoco官方介绍文档

Jacoco的常用命令

instrument

Instrument命令是实现代码覆盖率测量的关键步骤之一,主要实现了Java字节码中插入代码覆盖数据收集的探针(Probe)。

java -jar jacococli.jar instrument [<sourcefiles> ...] --dest <dir> [--help] [--quiet]
  • <sourcefiles>:原文件
  • --dest <dir> :将加入探针后注入的classes存入<dir>
  • --quiet :执行过程中不会让控制台(标准输出stdout)打印信息
    举例:
    java -jar jacococli.jar instrument C:\test.jar --dest classes
    

dump

dump命令使用Jacoco CLI连接到远程服务端,请求覆盖率数据,并将其保存到本地文件。运行dump的服务器是客户端,运行agent的是服务端。

java -jar jacococli.jar dump [--address <address>] --destfile <path> [--help] [--port <port>] [--quiet] [--reset] [--retry <count>]
  • --address <address>:server的IP地址
  • --destfile <path> :exec文件存储位置
  • --port <port> :server的port,默认是 (default 6300)
  • --quiet :执行过程中不会让控制台(标准输出stdout)打印信息
  • --reset:重置覆盖率报告(exec文件),在转存完当前的覆盖率数据之后,会清空之前收集的数据,以便从下一次执行开始重新收集。
  • --retry <count> :用于指定在无法连接到Jacoco Agent时,CLI应该尝试重连的次数。 (默认是 10)
    举例:
    java -jar jacococli.jar dump --address 192.168.0.4 --destfile test.exec
    

merge

合并多个exec报告文件,生成一个新的文件。官方明确说过,merge只能是相同代码生成的exec,主要是为了解决多副本的部署服务,由于请求经过负载均衡会在不同机器上产生exec文件,因此通过merge可以完成该类exec的文件。

java -jar jacococli.jar merge [<execfiles> ...] --destfile <path> [--help] [--quiet]
  • <execfiles> :需要合并的exec文件
  • --destfile <path>: 合并后的报告输出目录
  • --quiet :执行过程中不会让控制台(标准输出stdout)打印信息

举例:

java -jar jacococli.jar merge *.exec --destfile merge/all.exec

report

依据exec和classes文件生成覆盖率报告

java -jar jacococli.jar report [<execfiles> ...] --classfiles <path> [--csv <file>] [--encoding <charset>] [--help] [--html <dir>] [--name <name>] [--quiet] [--sourcefiles <path>] [--tabwith <n>] [--xml <file>]
  • <execfiles>:需要合并的exec文件
  • --classfiles <path>:class文件
  • --csv <file> :CSV报告
  • --html <dir> :html报告
  • --name <name> :报告名字
  • --quiet :执行过程中不会让控制台(标准输出stdout)打印信息
  • --sourcefiles <path>:这个参数告诉 Jacoco 在生成报告时包括源代码文件。这样,报告中会显示源代码,并在被执行的代码行上打上标记,通常以不同的颜色或样式来区分。
  • --tabwith <n> :源页面的制表符宽度(默认为 4)
  • --xml <file> :xml格式报告
    举例:
    java -jar jacococli.jar report test.exec --classfiles .\test.jar --html report.html --sourcefiles .\test\src\main\java
    

execinfo

以可读的方式打印exec文件的内容。

java -jar jacococli.jar execinfo [<execfiles> ...] [--help] [--quiet]
  • <execfiles>:需要合并的exec文件
  • --quiet :执行过程中不会让控制台(标准输出stdout)打印信息
    举例:
    java -jar jacococli.jar execinfo *.exec
    

exec文件详解

使用execinfo解读出来的exec文件如下:

如上图所示:

  • 第一列是classid,例如8adcfe1de92e357b,这个classid是通过原始类文件的CRC64校验和来创建的,所以如果代码有修改,这个classid比如会变化。
  • 第二列是覆盖探针的统计信息,例如90 of 272,表示这个类插入Probe探针272个,覆盖了90个。
  • 第三列是类名,例如org/springframework/boot/logging/logback/ExtendedWhitespaceThrowableProxyConverter。
目录
相关文章
|
8天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
12天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
3天前
|
并行计算 前端开发 物联网
全网首发!真·从0到1!万字长文带你入门Qwen2.5-Coder——介绍、体验、本地部署及简单微调
2024年11月12日,阿里云通义大模型团队正式开源通义千问代码模型全系列,包括6款Qwen2.5-Coder模型,每个规模包含Base和Instruct两个版本。其中32B尺寸的旗舰代码模型在多项基准评测中取得开源最佳成绩,成为全球最强开源代码模型,多项关键能力超越GPT-4o。Qwen2.5-Coder具备强大、多样和实用等优点,通过持续训练,结合源代码、文本代码混合数据及合成数据,显著提升了代码生成、推理和修复等核心任务的性能。此外,该模型还支持多种编程语言,并在人类偏好对齐方面表现出色。本文为周周的奇妙编程原创,阿里云社区首发,未经同意不得转载。
|
8天前
|
人工智能 运维 双11
2024阿里云双十一云资源购买指南(纯客观,无广)
2024年双十一,阿里云推出多项重磅优惠,特别针对新迁入云的企业和初创公司提供丰厚补贴。其中,36元一年的轻量应用服务器、1.95元/小时的16核60GB A10卡以及1元购域名等产品尤为值得关注。这些产品不仅价格亲民,还提供了丰富的功能和服务,非常适合个人开发者、学生及中小企业快速上手和部署应用。
|
19天前
|
自然语言处理 数据可视化 前端开发
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
合合信息的智能文档处理“百宝箱”涵盖文档解析、向量化模型、测评工具等,解决了复杂文档解析、大模型问答幻觉、文档解析效果评估、知识库搭建、多语言文档翻译等问题。通过可视化解析工具 TextIn ParseX、向量化模型 acge-embedding 和文档解析测评工具 markdown_tester,百宝箱提升了文档处理的效率和精确度,适用于多种文档格式和语言环境,助力企业实现高效的信息管理和业务支持。
3940 3
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
|
8天前
|
算法 安全 网络安全
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
2024阿里云11.11金秋云创季活动火热进行中,活动月期间(2024年11月01日至11月30日)通过折扣、叠加优惠券等多种方式,阿里云WoSign SSL证书实现优惠价格新低,DV SSL证书220元/年起,助力中小企业轻松实现HTTPS加密,保障数据传输安全。
520 3
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
|
15天前
|
安全 数据建模 网络安全
2024阿里云双11,WoSign SSL证书优惠券使用攻略
2024阿里云“11.11金秋云创季”活动主会场,阿里云用户通过完成个人或企业实名认证,可以领取不同额度的满减优惠券,叠加折扣优惠。用户购买WoSign SSL证书,如何叠加才能更加优惠呢?
991 3
|
7天前
|
数据采集 人工智能 API
Qwen2.5-Coder深夜开源炸场,Prompt编程的时代来了!
通义千问团队开源「强大」、「多样」、「实用」的 Qwen2.5-Coder 全系列,致力于持续推动 Open Code LLMs 的发展。
|
12天前
|
机器学习/深度学习 存储 人工智能
白话文讲解大模型| Attention is all you need
本文档旨在详细阐述当前主流的大模型技术架构如Transformer架构。我们将从技术概述、架构介绍到具体模型实现等多个角度进行讲解。通过本文档,我们期望为读者提供一个全面的理解,帮助大家掌握大模型的工作原理,增强与客户沟通的技术基础。本文档适合对大模型感兴趣的人员阅读。
444 18
白话文讲解大模型| Attention is all you need
|
13天前
|
存储 分布式计算 流计算
实时计算 Flash – 兼容 Flink 的新一代向量化流计算引擎
本文介绍了阿里云开源大数据团队在实时计算领域的最新成果——向量化流计算引擎Flash。文章主要内容包括:Apache Flink 成为业界流计算标准、Flash 核心技术解读、性能测试数据以及在阿里巴巴集团的落地效果。Flash 是一款完全兼容 Apache Flink 的新一代流计算引擎,通过向量化技术和 C++ 实现,大幅提升了性能和成本效益。
660 10
实时计算 Flash – 兼容 Flink 的新一代向量化流计算引擎