jacoco原理分析及案例分析《三》控制流分析

 

  • Java方法的控制流分析

实现支持语句(C0)以及分支覆盖范围(C1)的覆盖工具需要详细分析Java方法的内部控制流。由于JaCoCo的体系结构,这种分析发生在已编译类文件的字节码上。本文档描述了JaCoCo在运行时将探针插入控制流并分析实际代码覆盖率的策略。Marc R. Hoffmann201111

  • Java字节码的控制流程图

作为起点,我们采用以下包含单个分支点的示例方法:

  1. public static void example (){  
  2.     a ();
  3.     if cond ()){ 
  4.         b ();
  5.     } else { 
  6.         c ();
  7.     }
  8.     d ();
  9. }

Java编译器将从此示例方法创建以下字节码。Java字节码是线性指令序列。控制流程使用条件或无条件操作码等跳转指令 实现。跳转目标在技术上是与目标指令的相对偏移。为了更好的可读性,我们使用符号标签(,)代替(ASM API也使用这样的符号标签): IFEQGOTOL1L2

  1. public static example ()V
  2.       INVOKESTATIC a ()V
  3.       INVOKESTATIC cond ()Z
  4.       IFEQ L1
  5.       INVOKESTATIC b ()V
  6.       GOTO L2
  7.   L1 INVOKESTATIC c ()V
  8.   L2 INVOKESTATIC d ()V
  9.       返回

上面的字节码中可能的控制流程可以用图表表示。节点是字节代码指令,图形的边缘表示指令之间可能的控制流程。该示例的控制流程显示在此图的左侧框中:

  • 流动边缘

Java字节代码定义的Java方法的控制流程图可以具有以下边缘。每条边连接源指令和目标指令。在某些情况下,源指令或目标指令不存在(方法入口和出口的虚拟边)或无法精确指定(异常处理程序)。

类型

资源

目标

备注

条目

-

方法中的第一条指令

 

序列

指令,除了GOTOxRETURN THROWTABLESWITCHLOOKUPSWITCH

后续指示

 

GOTOIFxTABLESWITCH LOOKUPSWITCH 指令

目标指令

TABLESWITCHLOOKUPSWITCH将定义多个边缘。

EXHANDLER

处理程序范围内的任何指令

目标指令

 

出口

xRETURNTHROW指示

-

 

EXEXIT

任何指示

-

未处理的异常。

当前的JaCoCo实现忽略了由隐式异常和方法条目引起的边缘。这意味着我们考虑SEQUENCEJUMPEXIT

 

  • 探针插入策略

探针是可以在现有指令之间插入的附加指令。它们不会改变方法的行为,但会记录它们已被执行的事实。可以认为探针​​放置在控制流图的边缘上。从理论上讲,我们可以在控制流图的每个边缘插入一个探针。由于探测器实现本身需要多个字节码指令,这会多次增加类文件的大小,并显着降低已检测类的执行速度。幸运的是,这不是必需的,实际上我们每个方法只需要一些探针,具体取决于方法的控制流程。例如,没有任何分支的方法仅需要单个探测。

如果已经执行了探测,我们知道已经访问了相应的边缘。从这个边缘我们可以得出其他前面的节点和边:

  • 如果访问了边,我们知道该边的源节点已被执行。
  • 如果节点已被执行且节点仅是一个边缘的目标,则我们知道已经访问了该边缘。

递归地应用这些规则允许确定方法的所有指令的执行状态 - 假设我们在正确的位置具有探针。因此,JaCoCo插入探针

  • 在每个方法退出(返回或抛出)和
  • 在目标指令是多个边缘的目标的每个边缘。

我们记得,探针只是一小部分需要在控制流边缘插入的附加指令。下表说明了在不同边缘类型的情况下如何添加此额外指令。

类型

之前

备注

序列

在简单序列的情况下,将探针简单地插入两个指令之间。

JUMP(无条件)

由于在任何情况下都执行无条件跳转,我们也可以在GOTO指令之前插入探针。

JUMP(有条件的)

向条件跳转添加探测器有点棘手。我们反转操作码的语义,并在条件跳转后立即添加探测。随后的GOTO指令我们跳转到原始目标。请注意,此方法不会引入向后跳转,如果堆栈上有未初始化的对象,则会导致Java验证程序出现问题。

出口

实际上离开方法的RETURNTHROW语句的本质是我们在这些语句之前添加探测。

现在让我们看看这个规则如何应用于上面的示例代码段。我们看到该 INVOKE d()指令是唯一具有多个传入边缘的节点。因此,我们需要在这些边上放置探针,并在唯一的出口节点上放置另一个探针。结果显示在上图的右侧框中。

 

  • 线之间的额外探测

到目前为止描述的探针插入策略不考虑例如从调用的方法抛出的隐式异常。如果两个探测器之间的控制流被未使用throw语句显式创建的异常中断,则其间的所有指令都被视为未被覆盖。这会导致意外的结果,尤其是当指令块跨越多行源代码时。

因此,只要后续行包含至少一个方法调用,JaCoCo就会在两行的指令之间添加额外的探测。这限制了从方法调用到单行源的隐式异常的影响。该方法仅适用于使用调试信息(行号)编译的类文件,并且不考虑除方法调用(例如NullPointerExceptionArrayIndexOutOfBoundsException)之外的其他指令的隐式异常 

 

  • 探索实施

代码覆盖率分析是一个运行时指标,它提供被测软件的执行细节。这需要详细记录已执行的指令(指令覆盖)。对于分支覆盖,还必须记录决策的结果。在任何情况下,执行数据都由所谓的探测器收集:

探针是可以被插入到一个Java字节码方法的指令序列。执行探测时,会记录此事实,并可由coverage运行时报告。探针不得更改原始代码的行为。

探测的唯一目的是记录它至少执行过一次。探测器不记录它被调用的次数或收集任何时间信息。后者超出了代码覆盖率分析的范围,更多的是在性能分析工具的目标中。通常需要将多个探针插入每个方法中,因此需要识别探针。此外,探针实现和它依赖的存储机制需要是线程安全的,因为多线程执行是java应用程序的常见场景(尽管不适用于普通单元测试)。探针不得对方法的原始代码产生任何副作用。他们也应该增加最小的开销。

因此,总结执行探针的要求:

  • 记录执行
  • 鉴定不同的探针
  • 线程安全
  • 对应用程序代码没有副作用
  • 最小的运行时开销

JaCoCo使用boolean[]每个类的数组实例实现探测。每个探针对应于此数组中的条目。无论何时执行探测,都将条目设置为true以下四个字节码指令:

ALOAD探针

阵列xPUSH 

探针

ICONST_1 BASTORE

请注意,此探测代码是线程安全的,不会修改操作数堆栈或修改局部变量,也是线程安全的。它也不会通过外部调用离开该方法。唯一的先决条件是探测器阵列可用作局部变量。为此,在每个方法的开头,需要添加额外的检测代码以获取与所属类关联的数组实例。为了避免代码重复,初始化被委托给一个静态私有方法$jacocoinit(),该方法 被添加到每个非接口类中。

上面的探测代码的大小取决于探测器阵列变量的位置和探测器标识符的值,因为可以使用不同的操作码。如下表所示,每个探测的开销范围在47个字节的附加字节码之间:

可能的操作码

闵。大小[字节]

最大。大小[字节]

总:

4

7

ALOAD_xALOAD 1

1

2

ICONST_xBIPUSHSIPUSHLDCLDC_W 2

1

3

ICONST_1

1

1

BASTORE

1

1

1探测器数组是参数后的第一个变量。如果方法参数不消耗超过3个时隙,则可以使用1字节操作码。

  1. 2用于ID 052字节的操作码的ID多达127个,3个字节的操作码为IDS高达32768以上32767 ID中的值的1字节的操作码需要额外的常量存储库项。对于普通类文件,它不太可能需要超过32,000个探测器。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ANSYS是一款常用的仿真软件,其虚拟环境模拟功能广泛应用于工程设计和分析领域。在进行压电参数设置和案例分析时,可以通过添加附加命令流实现更精确的仿真结果。 首先,对于ANSYS的压电参数设置,可以通过如下步骤进行: 1. 创建压电材料模型:在ANSYS的材料库中选择合适的压电材料,如PZT-5H,然后设置其压电常数(d33、d31等)和介电常数等参数。 2. 定义边界条件:根据具体的模型和分析要求,设置合适的边界条件,如电压激励、机械载荷等。 3. 设置仿真类型:选择适当的分析类型,如静态分析、模态分析或瞬态分析。 4. 求解分析:运行求解器进行仿真分析。 其次,针对压电参数设置和案例分析,在ANSYS中可以通过添加附加命令流进行进一步优化和精确控制。例如,可以使用ANSYS中的APDL命令流语言写入一系列指令,进行以下操作: 1. 优化分析参数:根据具体的设计要求,通过设置APDL命令流来优化模型参数,如材料属性、几何形状、模型边界条件等。 2. 添加激励信号:根据需要,可以设置不同的激励信号,并通过APDL命令流将其应用到模型中,以模拟实际工况。 3. 控制求解器选项:通过APDL命令流可以修改求解器的收敛准则、迭代次数等参数,以优化求解的效率和准确性。 4. 后处理分析:通过APDL命令流可以进行更详细和高级的结果后处理,如应力分析、模态分析、频率响应分析等,以获取更全面的结果。 最后,在案例分析中,可以利用ANSYS中的附加命令流来优化分析过程,提高仿真结果的准确性和可靠性。通过合理设置压电参数和添加附加命令流,可以更好地模拟和分析压电器件的特性和性能。同时,可以借助ANSYS的其他功能模块,如结构力学、电磁场分析等,进行多物理场耦合分析,进一步提高仿真的精度和实用性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜕变之痛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值