JaCoCo探针策略原理及案例总结

1 探针策略

  • 在一串字节码指令中插入这些探针,只要该探针被执行了,说明其之前的指令都被执行了

  • 注意方法结束了是在 return 指令前放置探针哦
  • 跳转语句的记录
  • 条件语句

2 探针特点

探测的唯一目的是记录它至少执行过一次。探测器不记录它被调用的次数或收集任何时间信息。后者超出了代码覆盖率分析的范围,更多的是在性能分析工具的目标中

  • 最小的运行时间开销
  • 对应用程序代码无副作用
  • 线程安全
  • 记录字节码的执行
  • 标识不同类型探针
    使用的 boolean 数组记录对应的指令是否被执行

3 为什么最小的性能开销?


javap -c Fun

...
       0: getstatic     #2                  // Field $assertionsDisabled:Z
       3: ifne          18
       6: iload_1
       7: ifne          18
      10: new           #3                  // class java/lang/AssertionError
      13: dup
      14: invokespecial #4                  // Method java/lang/AssertionError."<init>":()V
      17: athrow
      18: return
...
  • 其实翻译过来代码就是这样子
  • 通过将这种原理用于 jacoco,降低了性能开销

4 如何实现代码注入

JaCoCo通过ASM在字节码中插入Probe指针(探测指针),每个探测指针都是一个BOOL变量(true表示执行、false表示没有执行),程序运行时通过改变指针的结果来检测代码的执行情况(不会改变原代码的行为).

增量注入

JaCoCo默认全量注入.

源码中注入的逻辑主要在ClassProbesAdapter

ASM在遍历字节码时,每次访问一个方法定义,都会回调这个类的visitMethod方法 ,在visitMethod方法中再调用ClassProbeVisitorvisitMethod方法,并最终调用MethodInstrumenter完成注入。部分代码片段如下:

@Override
public final MethodVisitor visitMethod(final int access, final String name,
		final String desc, final String signature,
		final String[] exceptions) {
	final MethodProbesVisitor methodProbes;
	final MethodProbesVisitor mv = cv.visitMethod(access, name, desc,
			signature, exceptions);
	if (mv == null) {
		// 无论如何,我们都需要访问该方法,否则探针的ID无法重现
		methodProbes = EMPTY_METHOD_PROBES_VISITOR;
	} else {
		methodProbes = mv;
	}
	return new MethodSanitizer(null, access, name, desc, signature,
			exceptions) {

		@Override
		public void visitEnd() {
			super.visitEnd();
			LabelFlowAnalyzer.markLabels(this);
			final MethodProbesAdapter probesAdapter = new MethodProbesAdapter(
					methodProbes, ClassProbesAdapter.this);
			if (trackFrames) {
				final AnalyzerAdapter analyzer = new AnalyzerAdapter(
						ClassProbesAdapter.this.name, access, name, desc,
						probesAdapter);
				probesAdapter.setAnalyzer(analyzer);
				methodProbes.accept(this, analyzer);
			} else {
				methodProbes.accept(this, probesAdapter);
			}
		}
	};
}

自动获取运行时数据

代码中通过反射执行下面的函数来获取运行时数据,并保存到当前执行代码的设备中:

org.jacoco.agent.rt.RT.getAgent().getExecutionData(false)

生成报告时需要用到运行时数据,为了生成的覆盖率报告更准确、开发同学用起来更方便,分别在如下时机把运行时数据保存到当前设备中:

  • 每个页面执行onDestory时
  • 程序发生崩溃时
  • 收到特定广播(一个自定义的广播,在执行生成覆盖率报告的task前发送)时

并在生成覆盖率报告之前把设备中的运行时数据同步到本地开发环境中。

上面可以看到,因为获取时机比较多,可能会得到多份运行时数据,对于这些数据,可以通过JaCoCo的mergeTask把ClassId相同的运行时数据进行merge。如下图所示,JaCoCo会对ClassId相同的运行时数据进行merge,并对相同位置的probe指针取或:

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
您好!要将 Jacoco 探针嵌入到您的应用程序中进行代码覆盖率检测,可以按照以下步骤进行操作: 1. 首先,在您的构建工具中添加 Jacoco 插件的配置。具体配置方式可能因使用的构建工具而异,例如对于 Maven,您可以在项目的 `pom.xml` 文件中添加以下插件配置: ```xml <build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> <executions> <execution> <id>jacoco-initialize</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>jacoco-report</id> <phase>prepare-package</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build> ``` 2. 然后,重新构建您的应用程序以应用 Jacoco 插件的配置。这将确保在编译和测试期间自动插入 Jacoco 探针。 3. 运行您的应用程序并执行一些测试用例。Jacoco 将在运行时收集代码覆盖率数据。 4. 完成测试后,Jacoco 将生成覆盖率报告。您可以在构建输出目录中找到报告文件(默认为 `target/site/jacoco/index.html`)。打开该文件以查看详细的覆盖率信息。 请注意,以上步骤是一个基本示例,实际操作可能会因您的项目和构建工具而有所不同。此外,确保您已经正确配置了构建工具和 Jacoco 插件的版本以及其他依赖项。 希望对您有所帮助!如果您还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值