Gcov 详解 + 内核函数覆盖率测试方法详述及产生错误解决办法
2015-01-13 10:56
393 查看
1. gcov是什么?
Gcov is GCC Coverage
是一个测试代码覆盖率的工具
是一个命令行方式的控制台程序
伴随GCC发布,配合GCC共同实现对C/C++文件的语句覆盖和分支覆盖测试;
与程序概要分析工具(profiling tool,例如gprof)一起工作,可以估计程序中哪一段代码最耗时;
注:程序概要分析工具是分析代码性能的工具。
2. gcov能做什么?
gcov可以统计
每一行代码的执行频率
实际上哪些代码确实被执行了
每一段代码(section code)的耗时(执行时间)
因此,gcov可以帮你优化代码,当然这个优化动作还是应该有开发者完成。
3. Gcov的实现原理简介
Gcc中指定-ftest-coverage 等覆盖率测试选项后,gcc会:在输出目标文件中留出一段存储区保存统计数据
在源代码中每行可执行语句生成的代码之后附加一段更新覆盖率统计结果的代码
在最终可执行文件中进入用户代码 main函数之前调用 gcov_init内部函数初始化统计数据区,并将gcov_exit内部函数注册为 exit handlers
用户代码调用exit 正常结束时,gcov_exit函数得到调用,其继续调用 __gcov_flush函数输出统计数据到 *.gcda 文件中
4. 如何使用gcov?
4.1 - 准备工作
==============
配置内核(Configure the kernel with):
CONFIG_DEBUG_FS=y
CONFIG_GCOV_KERNEL=y
选择gcov的格式(select the gcc's gcov format, default is autodetect based on gcc version):
CONFIG_GCOV_FORMAT_AUTODETECT=y
获取内核数据覆盖率(and to get coverage data for the entire kernel):
CONFIG_GCOV_PROFILE_ALL=y
Note that kernels compiled with profiling flags will be significantly
larger and run slower. Also CONFIG_GCOV_PROFILE_ALL may not be supported
on all architectures.
挂载debugfs(Profiling data will only become accessible once debugfs has been mounted):
mount -t debugfs none /sys/kernel/debug
到此Gcov的准备工作已经完成,下面要做的就是编译内核
(参考:http://blog.csdn.net/wangyezi19930928/article/details/41985795)
4.2 - 相关文件介绍:
========
The gcov kernel support creates the following files in debugfs:
/sys/kernel/debug/gcov
Parent directory for all gcov-related files.
/sys/kernel/debug/gcov/reset
Global reset file: resets all coverage data to zero when written to.
Invoking gcov /sys/kernel/debug/gcov/path/to/compile/dir/file.gcda
The actual gcov data file as understood by the gcov tool. Resets file coverage data to zero when written to.
/sys/kernel/debug/gcov/path/to/compile/dir/file.gcno
Symbolic link to a static data file required by the gcov tool. This file is generated by gcc when compiling with
option -ftest-coverage.
4.3 - Invoking gcov
下面我们就来具体的介绍一下如何使用gcov!
上述例子是Gcov的官方暗网站给出来的,这里我们就直接使用!
此时该目录中只有一个源文件:
1)编译:
-fprofile-arcs -ftest-coverage告诉编译器生成gcov需要的额外信息,并在目标文件中插入gcov需要的extra
profiling information。因此,该命令在生成可执行文件的同时生成gcov note文件(tmp.gcno)。
2)收集信息:
执行该程序,生成gcov data文件(tmp.gcda)。
3)生成数据报告
总结:
1)
如何使用gcov
用GCC编译的时候加上-fprofile-arcs -ftest-coverage选项,链接的时候也加上。
fprofile-arcs参数使gcc创建一个程序的流图,之后找到适合图的生成树。
只有不在生成树中的弧被操纵(instrumented):gcc添加了代码来清点这些弧执行的次数。当这段弧是一个块的唯一出口或入口时,操纵工具代码(instrumentation code)将会添加到块中,否则创建一个基础块来包含操纵工具代码。
gcov主要使用.gcno和.gcda两个文件。
.gcno是由-ftest-coverage产生的,它包含了重建基本块图和相应的块的源码的行号的信息。
.gcda是由加了-fprofile-arcs编译参数的编译后的文件运行所产生的,它包含了弧跳变的次数和其他的概要信息(而gcda只能在程序运行完毕后才能产生的)。
Gcov执行函数覆盖、语句覆盖和分支覆盖。
2. 使用主要有三个步骤:
1)编译阶段:加入编译选项gcc –fprofile-arcs –ftest-coverage tmp.c,生成记录程序流程图等信息
2)数据收集与提取阶段:./a.out,生成具体的运行信息
这一阶段生成的数据信息自动保存到.o所在的目录,也就是说如果存在/usr/build/hello.o,则生成/usr/build/hello.gcda,但是如果前边目录不存在就会出错。
3)生成数据报告: gcov tmp.c
此时该目录中有两个源文件:
程序代码由main.c和tmp.c两个文件组成,编译、链接、运行程序
1) 编译:gcc -fprofile-arcs -ftest-coverage -o myapp main.c tmp.c
2) 运行:./myapp
3)然后 输入命令: gcov main.c,gcov tmp.c
这个时候当前目录下有了新的文档main.c.gcov,和tmp.c.gcov
若想保存覆盖率文件,上述命令修改为:
命令:gcov main.c >>yourfilename,gcov tmp.c >>yourfilename
5. 对内核函数使用Gcov:
1)、进入到内核源码树目录(包含源文件、目标文件、.gcno文件):
2)、为分析数据文件(.gcda)创建软连接
因为*.gcda文件是内核运行时动态生成的,所以不包含在内核源码数目录下。但是要执行gcov必须要有此文件,因此需要为内核源码树目录
下的源文件对应的.gcda文件创建软连接,目的是在执行 “gcov sourcefile ” 时能找到源文件对应的分析数据。
gcov信息目录(包含要分析的数据)下的
/sys/kernel/debug/gcov/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel
因为kernel中包含很多文件所以需要为很多文件创建软连接,方便期间使用如下命令可以一次性为所有文件创建软连接,需要注意的是/sys/kernel/debug 文件夹是一个临时文件夹,不存在于磁盘当中,是在内存当中存在的,其中的文件也是系统运行是动态产生的,所以当你重新启动机器时会发现之前创建的软连接会全部断开,所以在每次开机重启之后所有的软连接需要重新创建:
软连接创建成功:
3)执行gcov 命令:
File 'kernel/signal.c'
Lines executed:45.95% of 1073
Creating 'signal.c.gcov'
File 'include/linux/signal.h'
Lines executed:86.67% of 15
Creating 'signal.h.gcov'
File 'include/linux/tracehook.h'
Lines executed:75.00% of 4
Creating 'tracehook.h.gcov'
File 'include/linux/signalfd.h'
Lines executed:100.00% of 4
Creating 'signalfd.h.gcov'
File 'include/linux/wait.h'
Lines executed:100.00% of 1
Creating 'wait.h.gcov'
......
这时会在当前目录下产生相应的.gcov文件,该文件包含我们需要的信息:
4. Error Record:
1)
解决办法:
加上相应的源文件目录即可!
2)
由于.gcda文件是内核运行时动态产生的,所以在内核源码树下是不存在的,所以需要对其创建软连接到相应源文件目录下,相见上述!
3)
Reference:
https://gcc.gnu.org/onlinedocs/gcc/Gcov.html#Gcov
/article/9733323.html
/kernel_sources/Documentation/gcov.txt
Gcov is GCC Coverage
是一个测试代码覆盖率的工具
是一个命令行方式的控制台程序
伴随GCC发布,配合GCC共同实现对C/C++文件的语句覆盖和分支覆盖测试;
与程序概要分析工具(profiling tool,例如gprof)一起工作,可以估计程序中哪一段代码最耗时;
注:程序概要分析工具是分析代码性能的工具。
2. gcov能做什么?
gcov可以统计
每一行代码的执行频率
实际上哪些代码确实被执行了
每一段代码(section code)的耗时(执行时间)
因此,gcov可以帮你优化代码,当然这个优化动作还是应该有开发者完成。
3. Gcov的实现原理简介
Gcc中指定-ftest-coverage 等覆盖率测试选项后,gcc会:在输出目标文件中留出一段存储区保存统计数据
在源代码中每行可执行语句生成的代码之后附加一段更新覆盖率统计结果的代码
在最终可执行文件中进入用户代码 main函数之前调用 gcov_init内部函数初始化统计数据区,并将gcov_exit内部函数注册为 exit handlers
用户代码调用exit 正常结束时,gcov_exit函数得到调用,其继续调用 __gcov_flush函数输出统计数据到 *.gcda 文件中
4. 如何使用gcov?
4.1 - 准备工作
==============
配置内核(Configure the kernel with):
CONFIG_DEBUG_FS=y
CONFIG_GCOV_KERNEL=y
选择gcov的格式(select the gcc's gcov format, default is autodetect based on gcc version):
CONFIG_GCOV_FORMAT_AUTODETECT=y
获取内核数据覆盖率(and to get coverage data for the entire kernel):
CONFIG_GCOV_PROFILE_ALL=y
Note that kernels compiled with profiling flags will be significantly
larger and run slower. Also CONFIG_GCOV_PROFILE_ALL may not be supported
on all architectures.
挂载debugfs(Profiling data will only become accessible once debugfs has been mounted):
mount -t debugfs none /sys/kernel/debug
到此Gcov的准备工作已经完成,下面要做的就是编译内核
(参考:http://blog.csdn.net/wangyezi19930928/article/details/41985795)
4.2 - 相关文件介绍:
========
The gcov kernel support creates the following files in debugfs:
/sys/kernel/debug/gcov
Parent directory for all gcov-related files.
/sys/kernel/debug/gcov/reset
Global reset file: resets all coverage data to zero when written to.
Invoking gcov /sys/kernel/debug/gcov/path/to/compile/dir/file.gcda
The actual gcov data file as understood by the gcov tool. Resets file coverage data to zero when written to.
/sys/kernel/debug/gcov/path/to/compile/dir/file.gcno
Symbolic link to a static data file required by the gcov tool. This file is generated by gcc when compiling with
option -ftest-coverage.
4.3 - Invoking gcov
gcov [options] files
wangye@selfimpro:~$ gcov Usage: gcov [OPTION]... SOURCE|OBJ... Print code coverage information. -h, --help Print this help, then exit -v, --version Print version number, then exit -a, --all-blocks Show information for every basic block -b, --branch-probabilities Include branch probabilities in output -c, --branch-counts Given counts of branches taken rather than percentages -n, --no-output Do not create an output file -l, --long-file-names Use long output file names for included source files -f, --function-summaries Output summaries for each function -o, --object-directory DIR|FILE Search for object files in DIR or called FILE -s, --source-prefix DIR Source prefix to elide -r, --relative-only Only show data for relative sources -p, --preserve-paths Preserve all pathname components -u, --unconditional-branches Show unconditional branch counts too -d, --display-progress Display progress information
下面我们就来具体的介绍一下如何使用gcov!
上述例子是Gcov的官方暗网站给出来的,这里我们就直接使用!
#include <stdio.h> int main (void) { int i, total; total = 0; for (i = 0; i < 10; i++) total += i; if (total != 45) printf ("Failure\n"); else printf ("Success\n"); return 0; }
此时该目录中只有一个源文件:
wangye@selfimpro:~/Gcov_study$ ls tmp.c
1)编译:
wangye@selfimpro:~/Gcov_study$ gcc -fprofile-arcs -ftest-coverage tmp.c wangye@selfimpro:~/Gcov_study$ ls a.out tmp.c tmp.gcno
-fprofile-arcs -ftest-coverage告诉编译器生成gcov需要的额外信息,并在目标文件中插入gcov需要的extra
profiling information。因此,该命令在生成可执行文件的同时生成gcov note文件(tmp.gcno)。
2)收集信息:
wangye@selfimpro:~/Gcov_study$ ./a.out Success wangye@selfimpro:~/Gcov_study$ ls a.out tmp.c tmp.gcda tmp.gcno
执行该程序,生成gcov data文件(tmp.gcda)。
3)生成数据报告
wangye@selfimpro:~/Gcov_study$ gcov tmp.c File 'tmp.c' Lines executed:87.50% of 8 Creating 'tmp.c.gcov' wangye@selfimpro:~/Gcov_study$ ls a.out tmp.c tmp.c.gcov tmp.gcda tmp.gcno
wangye@selfimpro:~/Gcov_study$ cat tmp.c.gcov -: 0:Source:tmp.c -: 0:Graph:tmp.gcno -: 0:Data:tmp.gcda -: 0:Runs:1 -: 0:Programs:1 -: 1:#include <stdio.h> 1: 2:int main (void) -: 3:{ -: 4: int i, total; 1: 5: total = 0; 11: 6: for (i = 0; i < 10; i++) 10: 7: total += i; 1: 8: if (total != 45) #####: 9: printf ("Failure\n"); -: 10: else 1: 11: printf ("Success\n"); 1: 12: return 0; -: 13:}
总结:
1)
如何使用gcov
用GCC编译的时候加上-fprofile-arcs -ftest-coverage选项,链接的时候也加上。
fprofile-arcs参数使gcc创建一个程序的流图,之后找到适合图的生成树。
只有不在生成树中的弧被操纵(instrumented):gcc添加了代码来清点这些弧执行的次数。当这段弧是一个块的唯一出口或入口时,操纵工具代码(instrumentation code)将会添加到块中,否则创建一个基础块来包含操纵工具代码。
gcov主要使用.gcno和.gcda两个文件。
.gcno是由-ftest-coverage产生的,它包含了重建基本块图和相应的块的源码的行号的信息。
.gcda是由加了-fprofile-arcs编译参数的编译后的文件运行所产生的,它包含了弧跳变的次数和其他的概要信息(而gcda只能在程序运行完毕后才能产生的)。
Gcov执行函数覆盖、语句覆盖和分支覆盖。
2. 使用主要有三个步骤:
1)编译阶段:加入编译选项gcc –fprofile-arcs –ftest-coverage tmp.c,生成记录程序流程图等信息
2)数据收集与提取阶段:./a.out,生成具体的运行信息
这一阶段生成的数据信息自动保存到.o所在的目录,也就是说如果存在/usr/build/hello.o,则生成/usr/build/hello.gcda,但是如果前边目录不存在就会出错。
3)生成数据报告: gcov tmp.c
此时该目录中有两个源文件:
程序代码由main.c和tmp.c两个文件组成,编译、链接、运行程序
1) 编译:gcc -fprofile-arcs -ftest-coverage -o myapp main.c tmp.c
2) 运行:./myapp
3)然后 输入命令: gcov main.c,gcov tmp.c
这个时候当前目录下有了新的文档main.c.gcov,和tmp.c.gcov
若想保存覆盖率文件,上述命令修改为:
命令:gcov main.c >>yourfilename,gcov tmp.c >>yourfilename
5. 对内核函数使用Gcov:
1)、进入到内核源码树目录(包含源文件、目标文件、.gcno文件):
/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel //内核源码树目录
wangye@selfimpro:~$ cd kernel_sources/kernel_test/linux-3.14.8/kernel/ root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel# ls acct.c groups.c relay.gcno relay.o acct.gcno groups.gcno ......
2)、为分析数据文件(.gcda)创建软连接
因为*.gcda文件是内核运行时动态生成的,所以不包含在内核源码数目录下。但是要执行gcov必须要有此文件,因此需要为内核源码树目录
下的源文件对应的.gcda文件创建软连接,目的是在执行 “gcov sourcefile ” 时能找到源文件对应的分析数据。
gcov信息目录(包含要分析的数据)下的
/sys/kernel/debug/gcov/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel
因为kernel中包含很多文件所以需要为很多文件创建软连接,方便期间使用如下命令可以一次性为所有文件创建软连接,需要注意的是/sys/kernel/debug 文件夹是一个临时文件夹,不存在于磁盘当中,是在内存当中存在的,其中的文件也是系统运行是动态产生的,所以当你重新启动机器时会发现之前创建的软连接会全部断开,所以在每次开机重启之后所有的软连接需要重新创建:
root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel# for i in `ls /sys/kernel/debug/gcov/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel/*gcda`; do ln -s $i ${i##*\/};done
软连接创建成功:
root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel# ls acct.c groups.c relay.gcno acct.gcda groups.gcda relay.o acct.gcno groups.gcno res_counter.c acct.o groups.o res_counter.gcda arch_hweight.h.gcov hrtimer.c res_counter.gcno ......
3)执行gcov 命令:
<pre name="code" class="plain">root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel# cd..root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8# gcov kernel/signal.c
File 'kernel/signal.c'
Lines executed:45.95% of 1073
Creating 'signal.c.gcov'
File 'include/linux/signal.h'
Lines executed:86.67% of 15
Creating 'signal.h.gcov'
File 'include/linux/tracehook.h'
Lines executed:75.00% of 4
Creating 'tracehook.h.gcov'
File 'include/linux/signalfd.h'
Lines executed:100.00% of 4
Creating 'signalfd.h.gcov'
File 'include/linux/wait.h'
Lines executed:100.00% of 1
Creating 'wait.h.gcov'
......
这时会在当前目录下产生相应的.gcov文件,该文件包含我们需要的信息:
<span style="font-size:14px;color:#000000;">root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8# cat signal.c.gcov ...... -: 3296:SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset, #####: 3297: old_sigset_t __user *, oset) -: 3298:{ -: 3299: old_sigset_t old_set, new_set; -: 3300: sigset_t new_blocked; -: 3301: #####: 3302: old_set = current->blocked.sig[0]; ......</span>
4. Error Record:
1)
root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel# gcov range.c File 'kernel/range.c' Lines executed:51.35% of 74 Creating 'range.c.gcov' Cannot open source file kernel/range.c
解决办法:
root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8/kernel# cd .. root@selfimpro:/home/wangye/kernel_sources/kernel_test/linux-3.14.8# gcov /kernel/range.c产生该错误的主要原因是路径不对,range.c中使用了内核中其他目录下的文件,因为路径问题找不到调用的文件,所以回退到内核根目录下
加上相应的源文件目录即可!
2)
/sys/kernel/debug/gcov/home/wangye/kernel_sources/linux-3.14.8/kernel/cgroup.gcda:cannot open data file, assuming not executed解决办法:
由于.gcda文件是内核运行时动态产生的,所以在内核源码树下是不存在的,所以需要对其创建软连接到相应源文件目录下,相见上述!
3)
gcov /kernel/key.c gcno:cannot open graph file这时到相应目录下才查看文件发现内核运行过程中没有执行key.c这个文件,所以不会有相应的.gcno 和 .o 文件,因此执行gcov操作会产生该错误。
Reference:
https://gcc.gnu.org/onlinedocs/gcc/Gcov.html#Gcov
/article/9733323.html
/kernel_sources/Documentation/gcov.txt
相关文章推荐
- 调用 inet_ntoa 函数产生的段错误 “Segmentation fault (core dumped)” 的原因及解决办法
- WINCE 6.0 VS2008 中使用WINSOCK函数产生:无法解析的外部符号 等错误的解决办法
- 调用 inet_ntoa 函数产生的段错误 “Segmentation fault (core dumped)” 的原因及解决办法
- uClinux 内核编译常见错误及解决方法(zt)
- vc++ "Error spawning 'vcspawn.exe'. The build could not be performed" 错误解决办法 (测试通过)
- 测试工具LoadRunner里错误 -27728的解决方法
- Linux 2.6.x 内核模块加载错误 “Invalid module format” 解决办法
- 创建实体化视图产生ORA-600 [ksmovrflow], [kkznxddl.begin]错误的解决方法
- vc编译错误:不能解析某些函数的解决方法
- iis服务没有及时响应启动或控制请求错误产生原因及解决方法
- 创建实体化视图产生ORA-600 [ksmovrflow], [kkznxddl.begin]错误的解决方法
- 解决方法:windows 2000: 打开我的电脑,就提示explorer.exe产生了错误
- 下载的时候提示:“写入到文件时产生错误(请用磁盘检查工具检查修复)”的解决办法
- java中使用ObjectOutputStream和ObjectInputStream时产生的“invalid stream header”错误解决方法
- 手工修改spfile文件后,产生ora-00600错误解决方法
- 编译升级linux由2.4.20-8到2.6.10内核时,遇到的错误,以及解决方法
- ExpressQuantumGrid在BDS中编译时产生错误的解决办法
- DataGuard - Logical Standby测试过程中的错误和解决方法
- 当前命令发生了严重错误,应放弃任何可能产生的结果的解决办法
- Oracle10g安装过程中"无法确定主机的IP地址时产生该异常错误" :解决办法