一、gcov简单介绍
Gcov是一个测试C/C++代码覆盖率的工具,伴随GCC发布,配合GCC共同实现对C/C++文件的语句覆盖、功能函数覆盖和分支覆盖测试。
二、gcov统计生成覆盖率流程
图1 gcov覆盖率生成过程
Gcc在编译阶段指定 –ftest-coverage 等覆盖率测试选项后,GCC会:
1、 在输出目标文件中留出一段存储区保存统计数据;
2、 在源代码中每行可执行语句生成的代码之后附加一段更新覆盖率统计结果的代码,也就是插桩(后面详细介绍);
3、 Gcc编译,会生成*.gcno文件,它包含重建基本块图和相应块的源码的行号信息;
4、 在最终可执行文件中,进入main函数之前调用gcov_init内部函数初始化统计数据区,并将gcov_init内部函数注册为exit_handers,用户代码调用exit正常结束时,gcov_exit函数得到调用,并继续调用__gcov_flush输出统计数据到*.gcda文件。
三、原理(插桩)
gcov是使用 基本块BB 和 跳转ARC 计数,结合程序流图来实现代码覆盖率统计的:
图2 程序流图
基本块BB:如果一段程序的第一条语句被执行过一次,这段程序中的每一个都要执行一次,称为基本块。一个BB中的所有语句的执行次数一定是相同的。一般由多个顺序执行语句后边跟一个跳转语句组成。所以一般情况下BB的最后一条语句一定是一个跳转语句,跳转的目的地是另外一个BB的第一条语句,如果跳转时有条件的,就产生了分支,该BB就有两个BB作为目的地。
跳转ARC:从一个BB到另外一个BB的跳转叫做一个arc,要想知道程序中的每个语句和分支的执行次数,就必须知道每个BB和ARC的执行次数。
如果把BB作为一个节点,这样一个函数中的所有BB就构成了一个有向图。要想知道程序中的每个语句和分支的执行次数,就必须知道每个BB和ARC的执行次数。根据图论可以知道有向图中BB的入度和出度是相同的,所以只要知道了部分的BB或者ARC大小,就可以推断所有的大小。
由ARC的执行次数来推断BB的执行次数。所以对部分 ARC插桩,只要满足可以统计出来所有的BB和ARC的执行次数即可。
记录BB块和ARB的数据结构为:
struct bb
{
long zero_word; //是否被插入到链表中
const char *file_name; //当前被测试文件名
long *count;//指向bx2的指针
long ncounts;//桩点个数
struct bb *next;//下一个文件的BX2信息
};
1、GCC在插桩的过程中会向源文件的末尾插入一个静态数组,BX2.,数组的大小就是这个源文件中桩点的个数。BX2+0代表第0个桩点的位置,BX2+n代表第n个桩点的位置,数组的值就是桩点的执行次数。
2、每个桩点插入汇编语句:
*按照我的理解,汇编语句是inc$(BX2+n).
3、 BX2数组链表:
为了便于统计,gcc还将各个源文件中的BX2数组链接成一个链表,这个链表结构是在测试main函数之前就产生了,在调用main之前会有一个类似构造函数的函数,进行构建链表。这个函数会在退出时调用exit函数计算执行次数生成.gcda文件。
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!