gcov是一个分析代码覆盖率的工具,包括语句覆盖、分支覆盖。它可以报告每一行语句的执行次数、每个分支的执行概率。
gcov程序调用的格式为:
gcov [options] sourcefilelist
待分析的程序源码:bubblesort.cpp
#include <stdio.h>
void bubbleSort(int list[], int size)
{
int i, j, temp, swap = 1;
while (swap)
{
swap = 0;
for (i = (size - 1); i >= 0; i--)
{
for (j = 1; j <= i; j++)
{
if (list[j - 1] > list[j])
{
temp = list[j - 1];
list[j - 1] = list[j];
list[j] = temp;
swap = 1;
}
}
}
}
}
void func()
{
int i, total;
total = 0;
for (i = 0; i < 10; i++)
total += i;
if (total != 45)
printf ("Failure/n");
else
printf ("Success/n");
}
int main()
{
int theList[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int i;
/* Invoke the bubble sort algorithm */
bubbleSort(theList, 10);
/* Print out the final list */
for (i = 0; i < 10; i++)
{
printf("%d\n", theList[i]);
}
func();
return 0;
}
编译:g++ -g -Wall -ftest-coverage -fprofile-arcs -o bubblesort bubblesort.cpp。生成.gcno文件(gcov note文件)
运行:./bubblesort。生成.gcda文件(gcov data文件)
分析:gcov bubblesort.cpp。生成.cpp.gcov文件
查看报告:more bubblesort.cpp.gcov。每行语句前面的数字就是该语句的执行次数,如下:
-: 0:Source:bubblesort.cpp
-: 0:Graph:bubblesort.gcno
-: 0:Data:bubblesort.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
1: 2:void bubbleSort(int list[], int size)
-: 3:{
1: 4: int i, j, temp, swap = 1;
-: 5:
4: 6: while (swap)
-: 7: {
2: 8: swap = 0;
-: 9:
22: 10: for (i = (size - 1); i >= 0; i--)
-: 11: {
110: 12: for (j = 1; j <= i; j++)
-: 13: {
90: 14: if (list[j - 1] > list[j])
-: 15: {
45: 16: temp = list[j - 1];
45: 17: list[j - 1] = list[j];
45: 18: list[j] = temp;
45: 19: swap = 1;
-: 20: }
-: 21: }
-: 22: }
-: 23: }
1: 24:}
1: 25:void func()
-: 26:{
-: 27: int i, total;
-: 28:
1: 29: total = 0;
-: 30:
11: 31: for (i = 0; i < 10; i++)
10: 32: total += i;
-: 33:
1: 34: if (total != 45)
#####: 35: printf ("Failure/n");
-: 36: else
1: 37: printf ("Success/n");
1: 38:}
1: 39:int main()
-: 40:{
1: 41: int theList[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
-: 42: int i;
-: 43:
-: 44: /* Invoke the bubble sort algorithm */
1: 45: bubbleSort(theList, 10);
-: 46:
-: 47: /* Print out the final list */
11: 48: for (i = 0; i < 10; i++)
-: 49: {
10: 50: printf("%d\n", theList[i]);
-: 51: }
-: 52:
1: 53: func();
-: 54:
1: 55: return 0;
-: 56:}
可以看到每行代码的执行次数,如第12行代码执行了110次。没有执行的行被标记为#####,如第35行代码。
gcov -b bubblesort.cpp的输出如下:
-: 0:Source:bubblesort.cpp
-: 0:Graph:bubblesort.gcno
-: 0:Data:bubblesort.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
function _Z10bubbleSortPii called 1 returned 100% blocks executed 100%
1: 2:void bubbleSort(int list[], int size)
-: 3:{
1: 4: int i, j, temp, swap = 1;
-: 5:
4: 6: while (swap)
branch 0 taken 67%
branch 1 taken 33% (fallthrough)
-: 7: {
2: 8: swap = 0;
-: 9:
22: 10: for (i = (size - 1); i >= 0; i--)
branch 0 taken 91%
branch 1 taken 9% (fallthrough)
-: 11: {
110: 12: for (j = 1; j <= i; j++)
branch 0 taken 82%
branch 1 taken 18% (fallthrough)
-: 13: {
90: 14: if (list[j - 1] > list[j])
branch 0 taken 50% (fallthrough)
branch 1 taken 50%
-: 15: {
45: 16: temp = list[j - 1];
45: 17: list[j - 1] = list[j];
45: 18: list[j] = temp;
45: 19: swap = 1;
-: 20: }
-: 21: }
-: 22: }
-: 23: }
1: 24:}
function _Z4funcv called 1 returned 100% blocks executed 86%
1: 25:void func()
-: 26:{
-: 27: int i, total;
-: 28:
1: 29: total = 0;
-: 30:
11: 31: for (i = 0; i < 10; i++)
branch 0 taken 91%
branch 1 taken 9% (fallthrough)
10: 32: total += i;
-: 33:
1: 34: if (total != 45)
branch 0 taken 0% (fallthrough)
branch 1 taken 100%
#####: 35: printf ("Failure/n");
call 0 never executed
-: 36: else
1: 37: printf ("Success/n");
call 0 returned 100%
1: 38:}
function main called 1 returned 100% blocks executed 100%
1: 39:int main()
-: 40:{
1: 41: int theList[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
-: 42: int i;
-: 43:
-: 44: /* Invoke the bubble sort algorithm */
1: 45: bubbleSort(theList, 10);
call 0 returned 100%
-: 46:
-: 47: /* Print out the final list */
11: 48: for (i = 0; i < 10; i++)
branch 0 taken 91%
branch 1 taken 9% (fallthrough)
-: 49: {
10: 50: printf("%d\n", theList[i]);
call 0 returned 100%
-: 51: }
-: 52:
1: 53: func();
call 0 returned 100%
-: 54:
1: 55: return 0;
-: 56:}
可以看到每个while、for、if语句都会引入分支,有时候需要参考汇编才能理解分支点在哪里。
注意:
1,gcov是基于gcc编译器的覆盖率测试的程序,它只支持gcc。
2,对于单元测试来说,Test Harness程序(包括stubs、driver)需要自己手动编写,当然测试用例(Test Suites)更需要你自己来写,gcov能够做得,只是帮助你统计覆盖信息。
3,如果你想使用gcov,在代码编写时就要有所注意,其中之一就是“每一行只有一条语句”,具体讲,什么算一条语句呢?
——注释不算语句
——声明算语句,如UNIT x; UNIT y; is not allowed
——混合的语句不允许 如{x =1; y=2; } is not allowd
while(x<MAX) { y = 2} is not allowed
... ...
为什么呢?因为gcov的结果是以“行”来统计的。如果一行执行语句太多,不便于结果的分析。
4,如果你代码中使用复杂的宏,比如说这个宏展开后是循环或者其他控制结构,这样的话gcov的结果对你的帮助一般不会太大,因为gcov只在宏调用出现的那一行报告。
如果复杂的宏看起来像函数,你应该用内联函数解决这个问题。
5,要想更好的度量进而调优你的程序的性能,gcov应该和gprof配合使用。二者协作,可以给出
每一行代码执行的次数 how often each line of code executes
那些代码行被执行过 what lines of code are actually executed
每块代码计算时间是多少 how much computing time each section of code uses
6,图形化工具:lcov genhtml
7,.gcno和.gcda文件是特定格式的二进制文件,可以用gcov-dump工具来查看
gcov的选项:
-a, --all-blocks
在.gcov文件中输出每个基本快(basic block)的执行次数。如果没有-a选项,则输出'main'函数这个block的执行次数
-b, --branch-probabilities
在.gcov文件中输出每个分支的执行概率,并有分支统计信息。
-c, --branch-counts
-c是默认选项,打印各个语句的调用次数
-f 查看函数级覆盖度,-f选项可以和-b选项一起使用得到以函数为单位的分支覆盖率。
gcov没有提供多个文件报告汇总的功能。