4、GCC与Gcov入门:安装GCC、理解Gcov工作原理、编译带覆盖率信息的程序
好,咱们正式开始动手了。这一章,我带你搞定最基础的工具链——GCC和Gcov。说白了,GCC负责编译,Gcov负责告诉你哪些代码跑过了、哪些没跑过。这两个东西是覆盖率分析的基石,绕不开的。
4.1 安装GCC:别小看这一步
我个人习惯用Linux环境做覆盖率分析,Windows上虽然也能折腾,但坑多。如果你用的是Ubuntu或Debian系,安装GCC就一行命令:
sudo apt-get update
sudo apt-get install gcc g++ gcov
嗯,这里要注意:Gcov是GCC自带的,不需要单独装。你装好GCC,Gcov就有了。验证一下:
gcc --version
gcov --version
看到版本号输出,就说明环境搭好了。我在项目中遇到过有人装了半天发现gcov命令找不到,结果是因为只装了gcc没装g++——其实gcov是跟着gcc一起的,但有些发行版会拆成多个包。建议你直接装build-essential这个元包,省心:
sudo apt-get install build-essential
yum install gcc gcc-c++。macOS用户可以用Homebrew装 brew install gcc,但注意macOS自带的clang和GCC不是一回事,别搞混了。
4.2 Gcov工作原理:它到底在干嘛?
Gcov的工作原理,说白了就三步:
- 编译时插桩——GCC在编译时往你的代码里插入一些“计数器”。每执行一行代码,对应的计数器就加1。
- 运行时收集——程序跑起来,这些计数器默默记录执行次数。
- 事后分析——程序退出后,Gcov读取计数器数据,生成报告。
为什么会这样设计?你想想看,如果等程序跑完了再去分析,根本不知道哪些分支走了、哪些没走。插桩就像在代码里装了个行车记录仪,每经过一个路口就拍一张照。
我刚开始接触Gcov时,以为它是在运行时动态分析的,后来才发现它是在编译阶段就埋好了“探针”。这个理解很重要——它决定了你必须在编译时就开启覆盖率选项,否则后面啥也拿不到。
-fprofile-arcs -ftest-coverage 这两个选项。
4.3 编译带覆盖率信息的程序
好,理论说完了,咱们直接上手。写一个简单的C程序,就叫 test.c:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
int x = 10;
int y = 20;
int z = add(x, y);
printf("z = %d\n", z);
return 0;
}
编译时加上覆盖率选项:
gcc -fprofile-arcs -ftest-coverage test.c -o test
这条命令会生成两个额外文件:
test.gcno——这是编译时生成的,记录了代码的块结构和行号信息。说白了就是“地图”。test.gcda——这是运行时生成的,记录了实际的执行次数。程序跑完才会出现。
然后运行程序:
./test
这时候你会看到 test.gcda 文件出现了。最后用Gcov分析:
gcov test.c
它会生成一个 test.c.gcov 文件,打开看看:
-: 0:Source:test.c
-: 0:Graph:test.gcno
-: 0:Data:test.gcda
-: 0:Runs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:int add(int a, int b) {
1: 4: return a + b;
-: 5:}
-: 6:
1: 7:int main() {
-: 8: int x = 10;
-: 9: int y = 20;
1: 10: int z = add(x, y);
1: 11: printf("z = %d\n", z);
1: 12: return 0;
-: 13:}
看到左边的数字了吗?那就是每行代码的执行次数。第4行显示 1,说明 add 函数被调用了1次。如果某行显示 #####,表示这行代码从未被执行过——这就是覆盖率分析的起点。
-o 参数指定路径。我曾经因为没注意这个,折腾了半天才发现报告是空的。
4.4 一张图看懂GCC+Gcov工作流
下面这张SVG图,把整个流程串起来了。你一看就明白:
4.5 避坑指南:我踩过的几个坑
讲几个我实际遇到过的坑,你遇到了能少走弯路:
- 坑一:忘记加编译选项——编译时没加
-fprofile-arcs -ftest-coverage,结果gcov报错说找不到.gcno文件。这个我犯过不止一次,后来养成了习惯:编译命令写进Makefile,用变量控制。 - 坑二:多文件项目只编译了一个文件——如果你有多个.c文件,每个都要加编译选项。只加一个的话,其他文件的覆盖率数据是空的。
- 坑三:程序没正常退出——Gcov的数据是在程序退出时刷盘的。如果程序被kill -9强杀,.gcda文件可能不完整。我曾经跑一个长时间的服务,忘了处理退出信号,结果覆盖率数据全丢了。
- 坑四:优化选项干扰——GCC的优化(比如-O2)会改变代码结构,导致行号对应不上。建议分析覆盖率时用
-O0关闭优化。
coverage 目标,专门用来编译带覆盖率信息的版本。这样既不影响正常编译,又能随时切到覆盖率模式。具体写法后面章节会讲。
好了,这一章的内容就这些。你装好GCC,跑通上面的例子,就算入门了。下一章咱们聊聊怎么用Gcov分析分支覆盖率和函数覆盖率——那才是真正有用的东西。