原文: http://www.cnblogs.com/turtle-fly/archive/2013/01/09/2851474.html

----------------------------------------------------------------------------------------

写在前面

这个过程几乎从0开始,在此之前,我几乎没有在 linux 下编译链接过项目、没有接触过 makefile、没有读过 man-db、只 gcov 过一个仅有几个C文件的项目

现在,我用 gcov 完成了对 VIM 源码的覆盖,并通过 lcov 生成了非常易读的覆盖率报告

中间碰到了许多疑难杂症,但是更多的是若干教程中叮嘱的“不要放弃”,所以我大概按照下面的节点完成了这个工具的入门:

虚拟机安装Ubuntu,配置gcov和lcov环境

  --> 编译链接单个C文件

    --> 写一个多个C文件的项目,用 makefile 进行编译连接,完成覆盖

      --> 覆盖优秀的开源软件,例如 VIM

这里会按照上述节点逐渐展开,希望帮助和我一样从0开始的朋友们更容易的完成这个过程

如果您对一些问题已经有了研究,那么这里的内容可能太过浅显,敬请继续往下翻阅

如果您没有碰到这些问题,那么恭喜一切都很顺利

如果您要深究一些技术的原理,那么这里可能无法提供您所需要的信息:一是我期望在这里精炼出成功配置环境的方法,更倾向于去解决问题而非深入研究;二是掌握一项技术归根结底还要自己一步一步走下去,一点一点踏实学,绝不是一篇博文就能简单解决的

好了,下面开始 :-)

虚拟机配置安装 Ubuntu

1. 选择虚拟机 VirtualBox,至于为什么不用 VMWare,请参考:

http://www.crifan.com/virtual_machine_soft_choice_vmware_or_virtualbox_definitely_recommend_virtualbox/

2. Ubuntu镜像:

http://www.ubuntu.com/download

3. 安装过程大体略去,只是您在用虚拟机加载镜像的时候可能碰到 VT-x feature locked or unavailable 的问题,那么请参考:

http://www.crifan.com/virtualboxvt_x_features_locked_or_unavailable_in_msr_verr_vmx_msr_locked_or_disabled/

如果还不能成功,请从本地硬盘以管理员身份启动 VirtualBox

或者,给这个虚拟机镜像分配少一点的内存

(很奇怪的是,我在安装 x86 Ubuntu 时,分配内存超过 3G 就会出现此问题,理论不是 4G 吗?求解)

4. 安装完之后您可能会向我一样需要两个功能:调整分辨率和共享文件夹,这都需要安装虚拟机的增强功能

所谓的安装增强功能,是在登录到 Ubuntu 之后,挂载一个光盘镜像,并安装一些内容,但是您在安装增强功能时可能会碰到以下问题:

“分配介质虚拟光盘 xxx\VBoxsGuestAdditions.iso 到虚拟电脑 xxx 失败。您是否要强制卸载分配该介质?”

请参考:

http://www.crifan.com/virtualbox_ubuntu_install_guest_addtions_fail_could_not_mount_the_media/

原因简而言之是,因为你需要加载一个镜像到虚拟机的光驱,但是虚拟机光驱内已经有内容了,卸载掉,或者直接从已加载的内容中安装

分辨率在重启虚拟机之后就可以生效。

5. 共享文件夹,因为会需要从宿主 Windows 中共享一些资源到虚拟机的 Ubuntu 中,在安装完增强功能之后

选择好宿主机上的共享目录,之后在 Ubuntu 终端上运行命令:

$ sudo mount -t vboxsf <shared_folder_on_windows> <mount_point_on_ubuntu>

这样在 Ubuntu 的挂载点上就能访问 Windows 上共享的资源了。

配置 Ubuntu,安装 gcov & lcov

1. 网络源和安装软件

为什么要配置源?您当然可以手动安装各种软件包,但是繁琐的依赖关系会让你痛不欲生。而从网络源安装会检查依赖并自动部署(安装),仅需一条命令:

$ sudo apt-get install <software_name>

比如,分别执行如下两条命令,就自动完成 gcov 和 lcov 的安装

$ sudo apt-get install gcov
$ sudo apt-get install lcov

等一下,网络源还没配……

其实就是把“要从哪里获取资源”这个信息告诉 Ubuntu,可以参考:

http://wiki.ubuntu.org.cn/index.php?title=Qref/Source&variant=zh-cn

上述资料简述成为以下三步:

  a) 备份系统自带的源,原始的源列表就在 /etc/apt/sources.list

$ sudo cp /etc/apt/sources.list /etc/apt/sources.list_backup

  以上命令的意思是,把原始内容原封不动的 backup 一下,以后你就可以从这个 sources.list_backup 中恢复了

  b) 全世界有很多的源可供您维护系统更新软件,依据您的网络环境添加合适的源(例如,如果人在欧洲,那么添加网易的开源镜像服务器就不太合适了)

  很久以前在大三的水过的 Linux 课上老师提过 CN99 放在常州,不过现在我使用的网易的源,中间的变迁似乎还有段野史,感兴趣的请自行搜索

  c) 获取源的目录等信息,改成你期望的源之后,请一定要执行以下命令:

$sudo apt-get update

2. 就在上面这条指令更新源的时候碰到的问题:Hash 和 public key ,详细错误信息类似下面这两条:

W: There is no public key available for the following key IDs:

xxxxxxxxxxxxxxxx
W: You may want to run apt-get update to correct these problems
W: Failed to fetch 

bzip2:/var/lib/apt/lists/partial/ppa.launchpad.net_webupd8team_java_ubuntu_dists_precise_main_binary-i386_Packages 

Hash Sum mismatch

Public key not available

在选用更新源的时候,切记不能混用不同发行版的源,甚至同一发行版的不同版本也不能混用

在上面更新源的简介资料中,给出的范例是 Quantal(12.10) 版本,而我虚拟机安装的其实是 Precise(12.04) 版本,因此参考网易的帮助文档:

http://mirrors.163.com/.help/ubuntu.html

选用正确的 Precise 源更新到 sources.list 文件中,公钥问题解决。

Hash Sum mismatch

参考以下材料,似乎国内总会碰到这样的问题,似乎和敏**词过滤有关,内容被篡改了?

http://forum.ubuntu.org.cn/viewtopic.php?t=393662

这个问题的原因尚不确定,有经验的朋友也请给些解答。在家就总会 Hash Sum mismatch ,而在公司就解决了。

解决了以上问题之后,就可以用 sudo apt-get install 把 gcov lcov 装起来了。

GCOV 用于简单项目的覆盖

gcov 适用的场合:GNU C/C++,因此适用的编译器:cc, gcc, g++

这里举斐波那契数列的一个程序为例

 1 #include <stdio.h>
2
3 int fibonacci(int n);
4
5 int main ()
6 {
7 int fib;
8 int n;
9
10 for (n = 0; n <= 41; n++) {
11 fib = fibonacci(n);
12 printf("fibonnaci(%d) = %d\n", n, fib);
13 }
14
15 return 0;
16 }
17
18 int fibonacci(int n)
19 {
20 int fib;
21 if (n <= 0) {
22 fib = 0;
23 }
24 else if (n == 1) {
25 fib = 1;
26 }
27 else {
28 fib = fibonacci(n -1) + fibonacci(n - 2);
29 }
30
31 return fib;
32 }

1. 编译

$ gcc -c fib.c -ftest-coverage -fprofile-arcs

除了 fib.o 之外,还生成了 fib.gcno 的话,成功了

.gcno是由-ftest-coverage产生的,它包含了重建基本块图和相应的块的源码的行号的信息。

2. 链接

$ gcc fib.o -o fib

诶……怎么回事?

在看到了一封乘坐了时光机的来自2003年的邮件之后,查阅了一下 gcc 的 man-db

我当时邮件给时光机的两个主角问了一下 gcov 的近况,并没有期望得到回复

但是就在昨天 Nathan 他老人家竟然回邮件了!带上以上所有已经提供的信息,他还感慨了一下 gcov has changed a lot since then...

回归正题,链接的时候下面三条任选一个执行即可

$ gcc fib.o -o fib --coverage
$ gcc fib.o -o fib -lgcov
$ gcc fib.o -o fib -fprofile-arcs

应该会正常生成 fib

3. 运行程序 fib

$ ./fib

会生成 .gcda 文件,.gcda是由加了-fprofile-arcs编译参数的编译后的文件运行所产生的,它包含了弧跳变的次数和其他的概要信息。

4. 生成 gcov 报告

$ gcov fib.c

生成的 fib.c.gcov 文件中就包含了代码覆盖的统计数据,数字代表了每行代码被执行的次数及行号,相信这个不难分析

         -:    0:Source:fib.c
-: 0:Graph:fib.gcno
-: 0:Data:fib.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:int fibonacci(int n);
-: 4:
1: 5:int main ()
-: 6:{
-: 7: int fib;
-: 8: int n;
-: 9:
43: 10: for (n = 0; n <= 41; n++) {
42: 11: fib = fibonacci(n);
42: 12: printf("fibonnaci(%d) = %d\n", n, fib);
-: 13: }
-: 14:
1: 15: return 0;
-: 16:}
-: 17:
1402817422: 18:int fibonacci(int n)
-: 19:{
-: 20: int fib;
1402817422: 21: if (n <= 0) {
267914296: 22: fib = 0;
-: 23: }
1134903126: 24: else if (n == 1) {
433494436: 25: fib = 1;
-: 26: }
-: 27: else {
701408690: 28: fib = fibonacci(n -1) + fibonacci(n - 2);
-: 29: }
-: 30:
1402817422: 31: return fib;
-: 32:}
-: 33:

至于 gcov 的更多选项,例如 -b 分支覆盖 -f 函数覆盖, 就 man 吧。

5. 存在的问题

gcov 对每个源码的分析分散在对应的 .cov 文件中,不容易整理分析;文本,无图表……

这就是要使用 lcov 的原因

另外,如果您对gcc也十分不熟悉,正在寻求入门的话,可以参考这里:

http://wiki.ubuntu.org.cn/Gcchowto

LCOV 整理覆盖率数据

1. 汇总覆盖率数据,使用已经生成的 .gcno .gcda 文件生成覆盖率数据

$ lcov -c -o fib.info -d .

简单解释一下三个选项

-c: lcov 的一个操作,表示要去捕获覆盖率数据

-o: 输出文件

-d: .gcno .gcda 所在的文件夹,注意这里有个“.”,是从当前文件夹中获取数据的

问题又来了,开始在 lcov 的过程中,碰到 Negative length 的问题,顺着提示找到 lcov 源码中的一处 $(length) ,之后并没有头绪为什么会是负值传入的,于是根据 sourceforge 上面的地址,发了一封邮件询问了一下,回信意思是我使用的 gcc 版本为 4.7.2,需要 lcov 1.10+ 版本支持,使用 1.09 或更低版本的 lcov 会出现这样的问题。于是到以下地址去下载了最新的 lcov

http://ltp.sourceforge.net/coverage/lcov.php

在 lcov 1.10 的 release notes 中写明了对 gcc 4.7+ 提供了支持。

2. 生成 html 格式的报告

$ genhtml fib.info -o fib_result

genhtml 是安装 lcov 时附带的,使用上面产生的 .info 文件生成报告,存放于 fib_result 文件夹中

没错,这里的报告并不只是一个文件,有好多存放在你 -o 指定的目录下,生成之后进入 fib_result 就可以看见念想很久的 index.html 了

这里再分享一下怎么从 terminal 用浏览器打开网页:

$ firefox index.html

3. gcov lcov 资料汇总

在学习过程中检索到的一些文章有对这两个工具的解读,我将有所收获、编排整齐的几篇列举如下,由浅入深,您也可以直接参考他们的文章:

i) gcov 和 lcov 的简明使用教程:http://magustest.com/blog/whiteboxtesting/using-gcov-lcov/

ii) gcov 和 lcov 的简单介绍,包括一些选项的含义,

  gcov: http://blog.csdn.net/livelylittlefish/article/details/6321861

  lcov: http://blog.csdn.net/livelylittlefish/article/details/6321887

iii) gcov 产生的覆盖率结果会存放在 .cov 文件中,这里有对 .cov 文件的解读:http://blog.csdn.net/ashhyc/article/details/1558598

iv) lcov 中间产物 .info 文件的解读:http://blog.csdn.net/vivasoft/article/details/8330186

v) gcov lcov 产生各类文件的简介:http://wx782870649.blog.163.com/blog/static/12989164120127224317532/

vi) gcov official online manual: http://gcc.gnu.org/onlinedocs/gcc/Gcov.html

vii) gcov FAQ: https://oss.oracle.com/~smushran/.debug/gcov/FAQ

viii) 这里提到了怎么用 gcov 对 linux kernel 进行覆盖:http://blog.csdn.net/yukin_xue/article/details/7653482

ix) 这里分析了 gcov 的工作原理,并直接操纵其获取数据的出入口,实现了对后台进程的覆盖统计:

http://blog.linezing.com/2011/03/使用gcov完成代码覆盖率的测试

覆盖大项目-学习Makefile

为什么要有 makefile ?

  因为编译、链接项目如果需要一条一条手动敲命令的话,那对那种动辄几十几百个文件的项目实在太恐怖了,需要这样一个建设性的懒惰,于是有了 makefile

makefile 是什么?

  原本归根结底,makefile 是原来的编译、链接命令的集合,把源文件逐个编译、最后链接,产生可执行文件

  至于为了灵活性而衍生出来的各类语法、变量、函数、隐晦规则……刚入门时可以先不必纠结

makefile 我还总结不出什么心得,这一阵儿学习是参考的陈皓老师的博客:http://blog.csdn.net/haoel/article/details/2886

或者这里有 pdf 文档,http://ishare.iask.sina.com.cn/f/8359780.html

我觉得,为了后面的工作,至少读通这份 pdf 的前8页,知道 makefile 怎么使用变量

1. 环境变量

相信您或多或少都听说过环境变量这个词,也知道他大概是什么意思,很多我们看不到的系统调用会用到这些变量,举个栗子:

打出命令 gcc 干嘛干嘛的时候,系统怎么执行你这个命令?系统不会听人说话,其实您已经调用了一个可执行文件 gcc

那这个 gcc 又是从哪调用的?其实系统会从一些目录下去找这个执行文件 gcc ,而这些目录就写在环境变量 $(PATH) 中,可以打印这个变量出来看看

$ echo $(PATH)

而 gcc 可执行程序在 /usr/bin 这个文件夹中,他的路径已经写在 $(PATH) 里了,应该可以看到

Ubuntu 系统的环境变量存储在以下5个配置文件中:

/etc/environment

  系统登录时读取的第一个文件,用于为所有进程设置环境变量

/etc/profile

  系统登录时读取的第二个文件,会设定所有用户的环境变量

~/.profile

  对应当前登录用户的 profile 文件,用于定制当前用户的个人工作环境

/etc/bash.bashrc

  对应所有用户的 bash 初始化文件,这里设定的环境变量将应用于所有用户的 shell 中,此文件会在用户每次打开 shell 时执行一次

~/.bashrc

  对应当前登录用户 bash 的初始化文件,当用户每次打开shell时,系统都会执行此文件一次

这几个文件的读取书序依此是:

/etc/environment -> /etc/profile -> ~/.profile -> /etc/bash.bashrc -> ~/.bashrc

还可以进行一些实验验证,请参考:http://blog.sina.com.cn/s/blog_6405313801012pxw.html

2. makefile 中的变量

为什么要用变量?

  再举个栗子,gcc 有选项 -O0 -O2,前者表示编译时不优化,后者表示最大程度优化,现在有个 makefile 如下:

executable: main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
gcc -o executable \
main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o main.o: main.c defs.h
gcc -O2 -c main.c
kbd.o: kbd.c defs.h command.h
gcc -O2 -c kbd.c
command.o: command.c defs.h command.h
gcc -O2 -c command.c
display.o: display.c defs.h buffer.h
gcc -O2 -c display.c
insert.o: insert.c defs.h buffer.h
gcc -O2 -c insert.c
search.o: search.c defs.h buffer.h
gcc -O2 -c search.c
files.o: files.c defs.h buffer.h command.h
gcc -O2 -c files.c
utils.o: utils.c defs.h
gcc -O2 -c utils.c

当你要做覆盖率分析的时候,你期望编译过程不要优化,于是又要把所有的 -O2 改为 -O0 ……

当未来有一个比 gcc 更好的编译器 xcc ,又要把所有的 gcc 改为 xcc ......

当然,现在可以用 replace ,但是不管是期望更灵活的在以后来修改,还是强迫症……不如这样改写上述 makefile :

CC="gcc"
CFLAGS="-O2 -c"
object=main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o executable: $(object)
$(CC) -o executable $(object) main.o: main.c defs.h
$(CC) $(CFLAGS) main.c
kbd.o: kbd.c defs.h command.h
$(CC) $(CFLAGS) kbd.c
command.o: command.c defs.h command.h
$(CC) $(CFLAGS) command.c
display.o: display.c defs.h buffer.h
$(CC) $(CFLAGS) display.c
insert.o: insert.c defs.h buffer.h
$(CC) $(CFLAGS) insert.c
search.o: search.c defs.h buffer.h
$(CC) $(CFLAGS) search.c
files.o: files.c defs.h buffer.h command.h
$(CC) $(CFLAGS) files.c
utils.o: utils.c defs.h
$(CC) $(CFLAGS) utils.c

在 VIM src 的 INSTALL 文档中有这么几行

至于 CFLAGS, CXXFLAGS, LIBS 这些变量的含义,请参考:http://www.cnblogs.com/taskiller/archive/2012/12/14/2817650.html

这里我用了另一种方法来确定我需要关注那些变量,在项目路径下,执行:

$ ./configure -h

会显示 configure 的帮助文档,其中有这么几行:

把 gcvo lcov 中提到的知识应用到这儿,我们只需要设定好编译和链接相关的两个环境变量 CFLAGS 和 LIBS

如下设定:

$ export CFLAGS="-c -ftest-coverage -fprofile-arcs"
$ export LIBS="-fprofile-arcs"

随后在 VIM 项目目录中

$ make
$ make install

在 <VIM>/src/objects 中应该生成了许多 .o 和 .gcno 文件吧,随后运行 VIM 生成 .gcda ,汇总覆盖率数据生成 .info ,将信息整理成 html 格式的命令都可以参考上面有关 gcov lcov 的使用

最后打开 index.html,就可以看到本文最开始出现的覆盖率数据了

交叉编译项目及 Linux 内核的覆盖

【转】gcov lcov 覆盖c/c++项目入门的更多相关文章

  1. GCOV&LCOV&GCOVR入门

    索引 一.概述 二.关于gcov的安装 三.代码覆盖率测试(以GCOV为例) 1.编译源代码 2.运行可执行程序 3.通过gcov指令生成代码覆盖率报告 四.生成更全面.直观的代码覆盖率报告 1.LC ...

  2. 转 Katana 项目入门

    Katana 项目入门 Howard Dierking 当 ASP.NET 首次在 2002 年发布时,时代有所不同. 那时,Internet 仍处于起步阶段,大约有 5.69 亿用户,每个用户平均每 ...

  3. 在MyEclipse下创建Java Web项目 入门(图文并茂)经典教程

    http://jijiaa12345.iteye.com/blog/1739754 在MyEclipse下创建Java Web项目 入门(图文并茂)经典教程 本文是一篇在Myeclipse下构建Jav ...

  4. Intellij Idea 创建Web项目入门(一)转

    Intellij Idea 创建Web项目入门(一) 相关软件: Intellij Idea14:http://pan.baidu.com/s/1nu16VyD JDK7:http://pan.bai ...

  5. unity 代码有调整,重新导出 iOS 最烦的就是 覆盖导出后项目不能打开

    unity  代码有调整,重新导出 iOS 最烦的就是 覆盖导出后项目不能打开,原因是 editor 里面的脚本,破坏了 Unity-iPhone.xcodeproj 里面的结构,具体是什么原因,也不 ...

  6. JavaWeb_(SSH论坛)_一、项目入门

    基于SSH框架的小型论坛项目 一.项目入门 传送门 二.框架整合 传送门 三.用户模块 传送门 四.页面显示 传送门 五.帖子模块 传送门 六.点赞模块 传送门 七.辅助模块 传送门 项目已上传至gi ...

  7. Vue项目入门实例

    前言 本文记录Vue2.x + Element-UI + TypeScript语法入门实例 为什么要用TypeScript? 1.TypeScript是JavaScript的超集,利用es6语法,实现 ...

  8. scrapy爬虫简单项目入门练习

    [写在开头] scrapy环境配置配置好了之后,开始着手简单项目入门练习.关于环境配置见上一篇博客https://www.cnblogs.com/ljxh/p/11235079.html. [正文部分 ...

  9. Unity3d项目入门之Rolling Ball

    下面通过分析制作一个简单的收集特定物体的滚球游戏来入门unity,包括操作面板和C#脚本的编写导入,创建Game Object和给Object添加组件等等. 一 初始设置 在Assert下创建主场景M ...

随机推荐

  1. A8ERP权限管理系统

  2. shim和polyfill有什么区别

    在JavaScript的世界里,有两个词经常被提到,shim和polyfill.它们指的都是什么,又有什么区别? 一个shim是一个库,它将一个新的API引入到一个旧的环境中,而且仅靠旧环境中已有的手 ...

  3. oracle数据库过期

    本文转载自http://soft.chinabyte.com/database/6/12320006.shtml[来源:比特网 作者:悠虎] 由于Oracle11G的新特性所致,经常会遇到使用sqlp ...

  4. Tomcat环境的搭建

    一.Tomcat的简单介绍 大家应该知道平时所说的C/S和B/S系统架构:C/S架构是基于客户端C和服务端S的,B/S架构是基于浏览器B和S服务端的,B/S架构中的server就是web服务器. To ...

  5. day18-常用模块III (numpy、pandas、matplotlib)

    目录 numpy模块 创建矩阵 获取矩阵的行列数 切割矩阵 矩阵元素替换 矩阵的合并 通过函数创建矩阵 矩阵的运算 矩阵的点乘与转置 矩阵的逆 矩阵的其他操作 numpy.random生成随机数 pa ...

  6. WINVER WIN32 WINNT

    WINVER 和 _WIN32_WINNT 请在WINDOWS.H前定义 从 Visual C++ 2008 开始,Visual C++ 不支持面向 Windows 95.Windows 98.Win ...

  7. Autolayout性能优化

    客户的需求就是我们进步的动力.最近有客户提出大数据量Topo图的自动布局问题,在Topo中除了Node.Link,还包括Group.Subnetwork等容器组件.在这样的情况下,我们抛开布局算法不谈 ...

  8. Number 数据类型

    //Number 数据类型//包含 整数 小数 NaN(not a number)var a = 1233;var b = 12.34;//1/'a'//把其他数据类型转化成数字,他在转化时,只要字符 ...

  9. FileReader实现读取文件内容并输出到屏幕上

    FileReader与FileInputStream都是从文件读数据,而前者一次读一个字符,后者一次读一个字节(在Unicode编码环境下1个字符=2个字节) package com.janson.d ...

  10. python3虚拟环境应用

    python3自带虚拟环境venv,大致操作只有三步 1. 创建虚拟环境 python3 -m venv venv(名称随意) 2. 激活虚拟环境 source venv/bin/activate 3 ...