前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JaCoCo探针策略原理及案例总结

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

作者头像
JavaEdge
发布2020-05-27 10:00:28
1.3K0
发布2020-05-27 10:00:28
举报
文章被收录于专栏:JavaEdgeJavaEdge

1 探针策略

  • 在一串字节码指令中插入这些探针,只要该探针被执行了,说明其之前的指令都被执行了
  • 注意方法结束了是在 return 指令前放置探针哦
  • 跳转语句的记录
  • 条件语句

2 探针特点

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

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

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

javap -c Fun

代码语言:javascript
复制
...
       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完成注入。部分代码片段如下:

代码语言:javascript
复制
@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);
			}
		}
	};
}

自动获取运行时数据

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

代码语言:javascript
复制
org.jacoco.agent.rt.RT.getAgent().getExecutionData(false)

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

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

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 探针策略
  • 2 探针特点
  • 3 为什么最小的性能开销?
  • 4 如何实现代码注入
  • 增量注入
  • 自动获取运行时数据
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档