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

举报
JavaEdge 发表于 2021/06/04 00:51:21 2021/06/04
【摘要】 1 探针策略 在一串字节码指令中插入这些探针,只要该探针被执行了,说明其之前的指令都被执行了 注意方法结束了是在 return 指令前放置探针哦 跳转语句的记录 条件语句 2 探针特点 探测的唯一目的是记录它至少执行过一次。探测器不记录它被调用的次数或收集任何时间信息。后者超出了代码覆盖率分析的范围,更多的是在性能分析工具的目标中 最小的运行时间开销对应用...

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
...

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 其实翻译过来代码就是这样子
  • 通过将这种原理用于 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); }
		}
	};
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

自动获取运行时数据

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

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

  
 
  • 1

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

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

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

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

文章来源: javaedge.blog.csdn.net,作者:JavaEdge.,版权归原作者所有,如需转载,请联系作者。

原文链接:javaedge.blog.csdn.net/article/details/105044476

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。