1、Gcov是进行代码运行的覆盖率统计的工具,它随着gcc的发布一起发布的,它的使用也很简单,需要在编译和链接的时候加上-fprofile-arcs -ftest-coverage生成二进制文件,gcov主要使用.gcno和.gcda两个文件,.gcno是由-ftest-coverage产生的,它包含了重建基本块图和相应的块的源码的行号的信息。.gcda是由加了-fprofile-arcs编译参数的编译后的文件运行所产生的,它包含了弧跳变的次数和其他的概要信息。gcda文件的生成需要先执行可执行文件才能生成。生成gcda文件之后执行命令gcov *.cpp就会在屏幕上打印出测试的覆盖率,并同时生成文件“*cpp.gcov”,然后用vi打开就可以看见哪行被覆盖掉了。
2、lcov的安装很简单,下载源码执行make install就可以了,在生成的“*.cpp.gcov"文件中执行lcov --directory . --capture --output-file app.info生成info文件,再执行genhtml -o results app.info就会生成result目录,生成的html文件就在result目录下。
___________________________
http://www.linuxidc.com/Linux/2011-05/36544.htm
Content
1. Lcov是什么?
2. 如何在Linux平台安装Lcov?
3. 如何使用Lcov?
(1) 使用lcov收集覆盖率数据并写入文件
(2) 使用genhtml生成基于HTML的输出
(3) 该例子的图形显示
4. 编译lcov自带例子
5. 其他相关工具
(1) gcov-dump
(2) ggcov
1. Lcov是什么?
是GCOV图形化的前端工具
是Linux Test Project维护的开放源代码工具,最初被设计用来支持Linux内核覆盖率的度量
基于Html输出,并生成一棵完整的HTML树
输出包括概述、覆盖率百分比、图表,能快速浏览覆盖率数据
支持大项目,提供三个级别的视图:目录视图、文件视图、源码视图
Use lcov to collect coverage data and genhtml to create HTML pages. Coverage data can either be collected from the currently running Linux kernel or from a user space application. To do this, you have to complete the following preparation steps:
For Linux kernel coverage:
Follow the setup instructions for the gcov-kernel infrastructure:
http://ltp.sourceforge.net/coverage/gcov.php
For user space application coverage:
Compile the application with GCC using the options "-fprofile-arcs" and "-ftest-coverage".
2. 如何在Linux平台安装Lcov?
# wget http://downloads.sourceforge.net/ltp/lcov-1.9.tar.gz
# tar -zxvf lcov-1.9.tar.gz
# cd lcov-1.9
# ls
bin contrib descriptions.tests lcovrc man rpm
CHANGES COPYING example Makefile README
# make install
不需要编译,直接安装即可,lcov, gendesc, genhtml, geninfo, genpng将被安装到/usr/bin目录。
3. 如何使用Lcov?
以Linux平台代码覆盖率测试工具GCOV简介一文的例子为例。
(1) 使用lcov收集覆盖率数据并写入文件
# lcov --capture --directory . --output-file test.info --test-name test
Capturing coverage data from .
Found gcov version: 4.1.2
Scanning . for .gcda files ...
Found 1 data files in .
Processing test.gcda
Finished .info-file creation
.表示当前目录,收集coverage data,即.gcda文件中的信息,并写入test.info文件,且取名为test。其他选项请参考lcov的manual页。
test.info文件内容如下。
TN:test
SF:/home/zubo/gcc/2011-04-10.sample/test.c
FN:4,main
FNDA:1,main
FNF:1
FNH:1
BRDA:9,2,0,10
BRDA:9,2,1,1
BRDA:12,0,0,0
BRDA:12,0,1,1
BRF:4
BRH:3
DA:4,1
DA:7,1
DA:9,11
DA:10,10
DA:12,1
DA:13,0
DA:15,1
DA:16,1
LF:8
LH:7
end_of_record
(2) 使用genhtml生成基于HTML的输出
# genhtml test.info --output-directory output --title "a simple test" --show-details --legend
Reading data file test.info
Found 1 entries.
Found common filename prefix "/home/zubo"
Writing .css and .png files.
Generating output.
Processing file gcc/2011-04-10.sample/test.c
Writing directory view page.
Overall coverage rate:
lines......: 87.5% (7 of 8 lines)
functions..: 100.0% (1 of 1 function)
branches...: 75.0% (3 of 4 branches)
选项解释请参考genhtml的manual页。cd到output目录,可以看到,生成了很多相关文件,如下。
# cd output
# ls
amber.png gcov.css index-sort-b.html ruby.png
emerald.png glass.png index-sort-f.html snow.png
gcc index.html index-sort-l.html updown.png
(3) 该例子的图形显示
(3.1) top level的视图
(3.2) 文件或函数的视图
4. 编译lcov自带例子
# cd /usr/src/lcov-1.9/example
# make
编译、运行自带例子并查看结果是快速学习某个工具最好的方法。从example的makefile文件和编译输出,都可以学习相关概念和命令的使用方法。Html输出可以由/usr/src/lcov-1.9/example/output/index.html查看。读者可自行实验。
5. 其他相关工具
(1) gcov-dump
或许,我们还可以使用gcov-dump命令输出gcov的相关数据,但gcc默认不编译gcov-dump,因此,要使用它,可能需要重新编译gcc。
(2) ggcov
Ggcov is a Graphical tool for displaying gcov test coverage data. 详细信息可参考http://ggcov.sourceforge.net。
Reference
lcov的manual页
genhtml的manual页
geninfo的manual页
lcov的readme文件,本文/usr/src/lcov-1.9/README
lcov的makefile文件,本文为/usr/src/lcov-1.9/Makefile
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-05/36544.htm
*************************
代码覆盖率——gcov lcov的使用
一、关于gcov工具
gcov伴随gcc 发布。gcc编译加入-fprofile-arcs -ftest-coverage 参数生成二进制程序,执行测试用例生成代码覆盖率信息。
1、如何使用gcov
用GCC编译的时候加上-fprofile-arcs -ftest-coverage选项,链接的时候也加上。
fprofile-arcs参数使gcc创建一个程序的流图,之后找到适合图的生成树。只有不在生成树中的弧被操纵(instrumented):gcc添加了代码来清点这
些弧执行的次数。当这段弧是一个块的唯一出口或入口时,操纵工具代码(instrumentation code)将会添加到块中,否则创建一个基础块来包含操纵
工具代码。gcov主要使用.gcno和.gcda两个文件。
.gcno是由-ftest-coverage产生的,它包含了重建基本块图和相应的块的源码的行号的信息。
.gcda是由加了-fprofile-arcs编译参数的编译后的文件运行所产生的,它包含了弧跳变的次数和其他的概要信息。
Gcov执行函数覆盖、语句覆盖和分支覆盖。
举个例子,程序代码由main.c和tmp.c两个文件组成,编译、链接、运行程序
编译:gcc -fprofile-arcs -ftest-coverage -o myapp main.c tmp.c
运行:./myapp
然后 输入
命令: gcov main.c,gcov tmp.c
这个时候当前目录下有了新的文档main.c.gcov,和tmp.c.gcov
若想保存覆盖率文件,上述命令修改为:
命令:
gcov main.c >>yourfilename,
gcov tmp.c >>yourfilename
查看结果:
-: 65:/***************************************************************************************
-: 66: * name : main
-: 67: * return : 0 OK
-: 68: * other ERROR
-: 69: * history : 2006-06-13
-: 70:****************************************************************************************/
-: 71:int main( int argc, char *argv[] ) /* the entrance for program
*/
function main called 4 returned 100% blocks executed 81%
4: 72:{
4: 73: int loop = 0 ;
4: 74: int ret = OK ;
4: 75: int empty_line = 0 ;
4: 76: int code_line = 0 ;
4: 77: int annotation_line = 0 ;
4: 78: struct stat file_stat ; /* use for file state */
4: 79: char recu_name[256] ;
4: 80: char *pwd = NULL ;
4: 81: char *tmp = NULL ;
-: 82:
4: 83: if( argc = MAX_FILE ){ /* file size larger than max size */
#####: 98: printf( "file [%s] size is over 64K! \ncontinue....\n", argv[loop] ) ;
#####: 99: continue ;
-: 100: }
##### 这就是表示没跑到的
各个参数使用如下:
gcov [-b] [-c] [-v] [-n] [-l] [-f] [-o directory] sourcefile
-b
Write branch frequencies to the output file, and write branch summary info to the standard output. This option allows you to
see how often each branch in your program was taken.
//b(ranch),分支测试
-c
Write branch frequencies as the number of branches taken, rather than the percentage of branches taken.
-v
Display the gcov version number (on the standard error stream).
//太简单了吧,我上面用了
-n
Do not create the gcov output file.
-l
Create long file names for included source files. For example, if the header file `x.h' contains code, and was included in the
file `a.c', then running gcov on the file `a.c' will produce an output file called `a.c.x.h.gcov' instead of `x.h.gcov'. This can
be useful if `x.h' is included in multiple source files.
-f
Output summaries for each function in addition to the file level summary.
-o
The directory where the object files live. Gcov will search for `.bb', `.bbg', and `.da' files in this directory.
新版的是这么说的
-o directory│file
--object-directory directory
--object-file file
Specify either the directory containing the gcov data files, or the
object path name. The .gcno, and .gcda data files are searched for
using this option. If a directory is specified, the data files are
in that directory and named after the source file name, without its
extension. If a file is specified here, the data files are named
after that file, without its extension. If this option is not sup-
plied, it defaults to the current directory.
其他的更有新版的-u,
-u
--unconditional-branches
When branch counts are given, include those of unconditional
branches. Unconditional branches are normally not interesting.
-p
--preserve-paths
Preserve complete path information in the names of generated .gcov
files. Without this option, just the filename component is used.
With this option, all directories are used, with ’/’ characters
translated to ’#’ characters, ’.’ directory components removed and
’..’ components renamed to ’^’. This is useful if sourcefiles are
in several different directories. It also affects the -l option.
二、关于lcov
Lcov则是上的gcov 结果展现的一个前端,可以将覆盖率信息转换成html展现。
1、如何使用lcov
Makefile 在编译和link环节都加入 -fprofile-arcs -ftest-coverage 选项
收集覆盖率数据生成app.info文件
命令:cov --directory . --capture --output-file myapp.info
Capturing coverage data from .
Found gcov version: 3.4.6
Scanning . for .gcda files ...
Found 1 data files in .
Processing ./TestQuery.gcda
Finished .info-file creation
转换成html格式
命令:genhtml -o results app.info
Reading data file app.info
Found 18 entries.
Found common filename prefix "/home/search/isearch_yb/src"
Writing .css and .png files.
Generating output.
Processing file cpp/core/basis/GlobalDef.h
Processing file cpp/core/search/QueryCache.h
...
Writing directory view page.
Overall coverage rate: 117 of 514 lines (22.8%)
2、查看html文件
html包含代码覆盖的详细信息
更多命令选项
http://ltp.sourceforge.net/coverage/lcov/lcov.1.php?PHPSESSID=26d7173d1f492f5f691715ef8b7d0b40
参考资料
1、http://ltp.sourceforge.net/coverage/
*******************
Gcov作为gnu/gcc工作组件之一,是一款的免费的代码覆盖率测试工具,而且可以结合lcov生成美观的html的测试报表。本文介绍一些gcov的使用方法,基本原理,一些实际中可能会遇到的问题以及解决思路。
- Gcov的用法
1.1 编译
Gcov的使用方法很简单,首先需要给gcc编译的时候打开覆盖测试的开关
例如要对srcfile.c单个文件生成的程序进行代码覆盖测试,在gcc编译的时候:
1
2
|
gcc -fprofile-arcs -ftest-coverage srcfile.c -o srcfile
|
或者简化成:
1
2
|
gcc --coverage srcfile.c -o srcfile
|
如果源文件很多,需要编译,链接的时候,在gcc编译的时候:
编译:
1
2
|
gcc -fprofile-arcs -ftest-coverage -c srcfile.c
|
链接:
1
2
|
gcc srcfile.o -o srcfile -lgcov
|
或者
1
2
|
gcc srcfile.o –o srcfile -fprofile-arcs
|
看出来了没有,gcov可以只针对大项目中的某几个单独的文件进行代码覆盖测试,只要在这几个文件编译的时候,加上-ftest-coverage,其他的文件不变就行了,爽吧。
1.2 生成报表
编译完成后会同时生成 *.gcno 文件(gcov notes),gcov生成覆盖率报告时需要参考该文件。
运行生成的可执行文件,给予正常的工作负载,待其正常退出后会生成覆盖率统计数据 *.gcda 文件(gcov data)
通过如下命令行之一查看覆盖率报告:
gcov 生成文本统计结果和带 line-count 标注的源代码:gcov srcfile
lcov 生成较正式的 HTML 报告:
1
2
|
lcov -c -d srcfile_dir -o srcfile.info
genhtml -o report_dir srcfile.info
|
注意:另外,编译选项中最好加入 -g3 -O0,前者是为了增加调试信息,后者是为了禁用优化,免得覆盖率测试不准确。
1.3 一个单文件的例子
一个例子程序如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <stdio.h>
#include <stdlib.h>
int
main(
int
argc,
char
* argv[]) {
int
i = 0;
printf
(
"Begin Test...\n"
);
if
(1 == argc) {
printf
(
"argc == 1\n"
);
}
else
{
printf
(
"argc != 1\n"
);
for
(i = 0; i < argc; i++)
printf
(
"%d\tof\t%d\n"
, i+1, argc);
}
printf
(
"End Test!\n"
);
}
|
编译:
1
|
gcc
test
.c -fprofile-arcs -ftest-coverage -o
test
|
生成文件如下:
1
|
test
test
.c
test
.gcno
|
运行:
1
|
. /test
1 2 3 4
|
生成文件如下:
1
|
test
test
.c
test
.gcda
test
.gcno
|
生成覆盖测试报告:
1
|
gcov
test
|
生成的test.gcov如下:
第一列是覆盖情况,第二列是行号加源程序,其中第一列中数字开头的是执行的次数,####开头的是没有执行到的语句。
2. Gcov的实现原理简介
Gcc中指定-ftest-coverage 等覆盖率测试选项后,gcc 会:
- 在输出目标文件中留出一段存储区保存统计数据
- 在源代码中每行可执行语句生成的代码之后附加一段更新覆盖率统计结果的代码
- 在最终可执行文件中进入用户代码 main 函数之前调用 gcov_init 内部函数初始化统计数据区,并将gcov_exit 内部函数注册为 exit handlers
- 用户代码调用 exit 正常结束时,gcov_exit 函数得到调用,其继续调用 __gcov_flush 函数输出统计数据到 *.gcda 文件中
2 对后台服务程序进行覆盖率测
从 gcc coverage test 实现原理可知,若用户进程并非调用 exit 正常退出,覆盖率统计数据就无法输出,也就无从生成报告了。后台服务程序若非专门设计,一旦启动就很少主动退出,用 kill 杀死进程强制退出时就不会调用 exit,因此没有覆盖率统计结果产生。
为了解决这个问题,我们可以给待测程序增加一个 signal handler,拦截 SIGHUP、SIGINT、SIGQUIT、SIGTERM 等常见强制退出信号,并在 signal handler 中主动调用 exit 或 __gcov_flush 函数输出统计结果即可。如何使用gcov完成对后台驻守程序的测试
该方案仍然需要修改待测程序代码,不过借用动态库预加载技术和 gcc 扩展的 constructor 属性,我们可以将 signalhandler 和其注册过程都封装到一个独立的动态库中,并在预加载动态库时实现信号拦截注册。这样,就可以简单地通过如下命令行来实现异常退出时的统计结果输出了:
1
|
LD_PRELOAD=. /gcov_out .so . /daemon
|
测试完毕后可直接 kill 掉 daemon 进程,并获得正常的统计结果文件 *.gcda。
用来预加载的动态库gcov_out.so的代码如下,其中__attribute__ ((constructor))
是gcc的符号,它修饰的函数会在main函数执行之前调用,我们利用它把异常信号拦截到我们自己的函数中,然后调用__gcov_flush()输出错误信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#define SIMPLE_WAY
void
sighandler(
int
signo)
{
#ifdef SIMPLE_WAY
exit
(signo);
#else
extern
void
__gcov_flush();
// flush out gcov stats data
__gcov_flush();
// raise the signal again to crash process
raise
(signo);
#endif
}
__attribute__ ((constructor))
void
ctor()
{
int
sigs[] = {
SIGILL, SIGFPE, SIGABRT, SIGBUS,
SIGSEGV, SIGHUP, SIGINT, SIGQUIT,
SIGTERM
};
int
i;
struct
sigaction sa;
sa.sa_handler = sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;
for
(i = 0; i <
sizeof
(sigs)/
sizeof
(sigs[0]); ++i) {
if
(sigaction(sigs[i], &sa, NULL) == -1) {
perror
(
"Could not set signal handler"
);
}
}
}
|
编译:
1
|
gcc -shared -fPIC gcov_out.c -o gcov_out.so
|
4. 参考资料
man gcc
man gcov
lcov – http://ltp.sourceforge.net/coverage/lcov.php
注意,lcov 最好使用 1.9 及以上版本,否则可能遇到如下错误:
geninfo: ERROR: …: reached unexpected end of file