使用Gcov统计C、C++代码的覆盖率
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一、Gcov的介绍
GCOV是一个GNU的本地覆盖测试工具, 伴随GCC发布,配合GCC共同实
现对C或者C++文件的语句覆盖和分支覆盖测试。
是一个命令行方式的控制台
程序。
需要工具链的支持。
当构建一个程序时,gcov会监视一个程序的执行,并且会标识出执行了哪一行
源码,哪一行没有执行。
更进一步,gcov可以标识出某一行源执行的次数,这
对于执行配置很有用(程序在哪里花费了大多数的时间)。
LCOV是GCOV图形化的前端工具,是Linux Test Project维护的开放源代码工具,最初被设计用来支持Linux内核覆盖率的度量基于Html输出,并生成一棵
完整的HTML树
输出包括概述、覆盖率百分比、图表,能快速浏览覆盖率数据
支持大项目,提供三个级别的视图:目录视图、文件视图、源码视图。
二、Gcov原理和使用
基本概念
1、基本快
基本快可以认为是一组顺序执行的指令组成的程序块,基本的特点就是当这个基本快的第一条命令执行了,那么该快中其他的语句也要执行,始终会保持一个基本快中所有的指令执行的次数是相同的。
一般情况下,一个基本快是由一个跳转指令结束。
如果一段指令中出现了多个跳转指令,也就是分支语句,那么就会被认为是不同的基本快。
2.跳转
跳转就是从一个基本快运行到另外一个基本快的一行指令。
从一个基本快到另外一个基本快的动作就不叫做一个跳转,实际上跳转和基本快构成了统计代码覆盖的基本结构。
要想知道程序中的每个语句和分支的执行次数,就必须知道每个基本快和跳转的执行次数。
3. 程序流图
如果把基本块作为一个节点,这样一个函数中的所有基本块就构成了一个有向图。
,要想知道程序中的每个语句和分支的执行次数,就必须知道每个基本块和
跳转的执行次数。
根据图论可以知道有向图中基本块的入度和出度是相同的,所以只要知道了部分的基本块或者跳转大小,就可以推断所有的大小。
这里选择由跳转的执行次数来推断基本快的执行次数。
以下是针对某个函数的程序流图:
2.2.1原理简介
GCOV是一个纯软件的覆盖测试工具,被测程序的预处理,插桩和编译成目标文件三个步骤由GCC一次完成。
GCOV本身只负责数据处理和结果显示,下图是GCOV的工作原理。
gcov工作原理
从左图可以看出,GCOV统计覆盖率主要包括三个阶段:
编译阶段:
如下一段C语言代码(test.c):
#include
int main(void)
{
int i,total;
total = 0;
for(i=0;i<5;i++){
if(i%2==0){
printf("The number is %d is not odd" , i); }
else{
printf("The number is %d is odd" , i);
}
total += i;
}
if(total != 45)
printf("Failure\n");
else
printf("Success\n");
}
加入编译选项执行:gcc –o test –fprofile-arcs –ftest-coverage test.c
编译后首先会生成.gcno文件。
该文件是由-ftest-coverage产生的,它包含了重建基本块图和相应的块的源码的行号的信息。
编译完成后,运行该程序:./test
执行后生成.gcda文件。
.gcda是由加了-fprofile-arcs编译参数的编译后的文件
运行所产生的,它包含了弧跳变的次数和其他的概要信息。
Gcov就是利用上面生成的两个文件.gcda和.gcno进行代码覆盖率的统计。
这时候可以通过执行gcov hello.c 统计总体的代码覆盖率
生成gcda文件之后执行命令gcov test.c就会在屏幕上打印出测试的总体覆盖率。
$ gcov test.c
File…test.c‟
已执行的行数:90.91% (共11 行)
test.c:正在创建…test.c.gcov‟
并同时生成文件“test.c.gcov”,然后用vi打开就可以看见哪行被覆盖掉了。
$ vi test.c.gcov
-: 0:Source:test.c
-: 0:Graph:test.gcno
-: 0:Data:test.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include
-: 2:
1: 3:int main(void)
-: 4:{
-: 5: int i,total;
1: 6: total = 0;
6: 7: for(i=0;i<5;i++){
5: 8: if(i%2==0){
3: 9: printf("The number is %d is not odd" , i);
-: 10: }
-: 11: else{
2: 12: printf("The number is %d is odd" , i);
-: 13: }
5: 14: total += i;
-: 15: }
-: 16:
1: 17: if(total != 45)
1: 18: printf("Failure\n");
#####: 20: printf("Success\n");
1: 21: return 0;
-: 22:}
~
看一下内容,第一列出显示出每一行代码的覆盖执行次数。
比如第7行代码执行了6次,for语句执行了6次,前5次进入循环内部。
第6次因为判断为假,退出了循环。
因此也退出了第8行的if语句只运行了5次。
其中有写语句标识了”-“,表示不进入统计,这些只是并不会影响代码的简单C源码元素。
我们也可以使用-b选项来查看程序的分支数据。
这个选项会输出程序中每一个分支的频度与相应的摘要。
例如,我们使用-b选项来执行gcov命令:
执行gcov –b test.c 会重新生成test.c.gcov。
打开生成的文件可以清晰的看出哪些分支被执行了,执行的次数,已经执行覆盖的百分比统计。
$ vi test.c.gcov
-: 0:Source:test.c
-: 0:Graph:test.gcno
-: 0:Data:test.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include
-: 2:
function main called 1 returned 100% blocks executed 90%
1: 3:int main(void)
-: 4:{
-: 5: int i,total;
1: 6: total = 0;
6: 7: for(i=0;i<5;i++){
branch 0 被执行83%
branch 1 被执行17% (fallthrough)
5: 8: if(i%2==0){
branch 0 被执行60% (fallthrough)
branch 1 被执行40%
3: 9: printf("The number is %d is not odd" , i);
call 0 返回了100%
-: 10: }
-: 11: else{
2: 12: printf("The number is %d is odd" , i);
call 0 返回了100%
-: 13: }
5: 14: total += i;
-: 15: }
-: 16:
1: 17: if(total != 45)
branch 0 被执行100% (fallthrough)
branch 1 被执行0%
1: 18: printf("Failure\n");
call 0 返回了100%
-: 19: else
#####: 20: printf("Success\n");
call 0 从未被执行
1: 21: return 0;
-: 22:}
三、使用lcov生成统计报告
Lcov则是上的gcov 结果展现的一个前端,可以将覆盖率信息转换成html展现Makefile 在编译和link环节都加入-fprofile-arcs -ftest-coverage 选项,收集覆盖率数据生成文件。
执行lcov --directory . --capture --output-file ./ 生成文件
使用gcovhtml将生成的代码统计信息转换成html格式
genhtml -o ./result ./
生成的result目录就包含了html格式的报告。
将该报告在浏览器中打开就形成了可视化的代码覆盖率的信息。
可以点开目录,展示具体的代码覆盖情况。