从Instrument的方式比较Emma与Clover

 从Instrument的方式比较Emma与Clover

from:http://qa.taobao.com/?p=6425

1 INTRODUCTION

最近在做与code coverage相关的工作,接触了一些code coverage 的知识。在这里与大家做个分享。下文主要是在Instrumentation的方式上,对Emma与Clover进行了比较,并且分析了由于Instrumentation方式上的不同而导致的coverage的结果不同的原因。
关于clover和emma,是两个大家都厂家的工具,这里就不多说了。仅给出链接,供详细了解。
Clover: http://www.atlassian.com/software/clover/
Emma: emma.sourceforge.net
注:本文所说的代码仅局限在Java代码上。
2 INSTRUMENTATION 方式介绍
在这一章中,我们大致的介绍一下Instrumentation的不同的方式。
为了获得code coverage measurement, 目前大致有三种Instrumentation的方式:
 源代码的Instrumentation(source code instrumentation): 这种方式在源代码上,直接进行Instrumentation. 然后将Instrument后的代码编译成.class 文件。
 中间代码的Instrumentation(Intermediate code instrumentatio): 这种方式是直接在编译后的.class文件是直接加入新的字节码。
 实时信息的获取( Runtime Information Collection):这种方式是在代码运行过程中收集动态的运行消息而决定coverage的信息。
目前Clover采用的是第一种方式(源代码的Instrumentation),而Emma采用的是第二种方式(中间代码的Instrumentation)。
3 CLOVER的INSTRUMENTATION 方式
在这一章中,我们将详细描述一下源代码的Instrumentation的原理。就拿图一的代码做个例子. (图一的代码功能及其简单,这里就不多说了)。

图一:before Instrumentation

我们再看看Instrument以后的代码(见图二)。



图二: After instrumentation


由图一和图二的对比,我们可以发现,在每一个method, statement和branch之前,都添加了一行代码。这行代码代表着是一个整形数的counter.
特别的,如果针对branch,我们要区分是“true”还是”false”的branch被执行了,所以做了特别的处理。如图二中所示,if 后的Boolean expression变成了
if ( ( data==0 && ++CT[338]!=0 | true ) || ( ++CF[338] == 0 & false ) )

 如果data==0是true, 那么 && 之后的部分也会执行。 这样CT[338]就会被增加一。而最后的“|true”是为了保证整个的boolean expression的值还是保证为”true”.
 如歌data==0是false,那么“||”之后的部分会被执行。这样CF[338]就会被增加一。而最后的“&false”是为了保证整个的boolean expression的值还是保证为”false”.
从以上的Instrument的方式,我们可以看出,每次在源代码中加入一个Counter, 我们都需要一个整形数(4 bytes)。 这样对于一个大型的项目,开销还是非常大的!
4 EMMA 的INSTRUMENTATION方式
至于Emma 的Instrumentation 的方式,可以参见图三。

图三:Instrumentation in Emma.

如图三所示,Emma通过对.class文件中的bytecode进行分析, 从而找出在.class文件中每一个basic block. 并且在每个basic block之后插入一个Probe(探针)。 这样每个probe被执行后,程序运行时coverage的信息也就被收集到了。
在这里每一个basic block是指一段没有保护任何跳转指令的bytecode 指令。 而每个probe是指一段用于记录某个basic block是否被运行的bytecode. 具体的每个probe包含以下四条指令(或者是类似这四条的指令):
1. ALOAD probearray #在emma 会在每个class中新建一个boolean的数组,用于记录每个basic block是否被执行。这行指令,就是把这个数组的引用加载进操作数栈
2. ICONST probeid #将某个probe的id, 加载进操作数栈
3. ICONST_1 #将常量”1”加载进操作数栈
4. BASTORE #将probearry[probeid] 的值改为1.
至于 emma的内存开销,主要也就是花费在每个class所开的boolean数组(1 byte per element)。 而这个数组的大小是这个class内的basic block的数量决定的。
5 不同INSTRUMENTATION 方式所得到的结果
那么由这两种不同的Instrumenetation方式得到的coverage的结果又会有什么不同呢。当然正常情况下,clover 和emma基本类似。但是遇到特别的情况就不一样了。
就拿下面的程序做个例子, 我们给coverageTest()的输入是0, 因此期望只执行” (a==0)? (data+1): (data+2);”中的一个分支
public static void main(String[] args) {
int a = 0;
coverageTest( a );
}
public static void coverageTest( int a ){
int data = 0;
a = (a==0)? (data+1): (data+2);
}
在clover 下运行的结果如下图所示,全部被覆盖:Line coverage: 100%

在emma下的结果如下图所示:

上图显示“(a==0)? (data+1): (data+2);”为黄色。这个在emma中表示的意思是这行代码没有全部被覆盖。特别的, line coverage为92% (2.8/3)。这个背后的原因是“(a==0)? (data+1): (data+2);”在byte code level被分成不同的basic block来处理。

6 结论
上文简单的就clover和emma的不同的instrumentation的方式进行了讨论,并且举例说明了不同的instrumentation方式对所得到coverage的准确度的影响。显而易见,emma是能更加准确的反应代码的覆盖情况的。
当然这两种Instrumentataion 方式还有很多不同,还没深入研究,这里就不敢多说了。
最后从Clover的官方网站上截取了一段话给大家看看:
Clover uses source code instrumentation, because although it requires developers to perform an instrumented build, source code instrumentation produces the most accurate coverage measurement for the least runtime performance overhead.

你可能感兴趣的:(职场,emma,休闲,clover)