前段时间课程要求对自己写的计算器程序实现缺陷定位,也算认识了一种新的工具和方法。我主要学习了两种工具,一种是 C\C++的缺陷定位工具 gcov ,包含在 gcc 工具链中,另一个是 python 的 coverage.py 库,他提供了命令行工具和编程接口两种使用方法。

Gcov

Gocv 是 GNU 项目中的一个软件,它可以对代码进行覆盖测试,我仅仅只是测试了一下基本的使用方法,原理等其他方面并没有深入探究。

我们创建一个待测试的程序,命名为sample.c

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 10;
if (a >= 5)
{
printf("%d\n", a);
}
else
{
printf("%d\n", a + 1);
}
}

gcc 文档给出的插桩选项有很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-p  -pg  -fprofile-arcs  --coverage  -ftest-coverage
-fprofile-abs-path
-fprofile-dir=path -fprofile-generate -fprofile-generate=path
-fprofile-info-section -fprofile-info-section=name
-fprofile-note=path -fprofile-prefix-path=path
-fprofile-update=method -fprofile-filter-files=regex
-fprofile-exclude-files=regex
-fprofile-reproducible=[multithreaded|parallel-runs|serial]
-fsanitize=style -fsanitize-recover -fsanitize-recover=style
-fsanitize-trap -fsanitize-trap=style
-fasan-shadow-offset=number -fsanitize-sections=s1,s2,...
-fsanitize-undefined-trap-on-error -fbounds-check
-fcf-protection=[full|branch|return|none|check]
-fharden-compares -fharden-conditional-branches
-fstack-protector -fstack-protector-all -fstack-protector-strong
-fstack-protector-explicit -fstack-check
-fstack-limit-register=reg -fstack-limit-symbol=sym
-fno-stack-limit -fsplit-stack
-fvtable-verify=[std|preinit|none]
-fvtv-counts -fvtv-debug
-finstrument-functions -finstrument-functions-once
-finstrument-functions-exclude-function-list=sym,sym,…
-finstrument-functions-exclude-file-list=file,file,…
-fprofile-prefix-map=old=new

我也没有深究他们每一个的作用,只做了最基本的使用
进行编译,编译中要加上选项-fprofile-arcs -ftest-coverage
其中,-ftest-coverage是生成.gcno文件,其中包括了重建的源代码基本块依赖图和基本快的必要信息。而-fprofile-arcs是让编译器对代码进行插桩,并在链接的时候链接 libgcov.a,虽然我用 ldd 没有看到这个链接,另外,使用--coverage能代替上述两个参数直接完成。

完成之后,文件夹中应该会出现两个文件

1
2
ls
sample.c sample sample.gcno

运行sample,运行之后会生成一个 sample.gcda文件。之后执行

1
gcov sample.c

可以看到命令行输出

1
2
3
File 'sample.c'
Lines executed:80.00% of 5
Creating 'sample.c.gcov'

如果源代码名称和可执行程序名称不一样需要指定,具体如何指定文档里面写了。
运行之后会生成一个 sample.c.gcov文件,打开文件可以看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    -:    0:Source:sample.c
-: 0:Graph:sample.gcno
-: 0:Data:sample.gcda
-: 0:Runs:1
-: 1:#include <stdio.h>
1: 2:int main(int argc, char *argv[])
-: 3:{
1: 4: int a = 10;
1: 5: if (a >= 5)
-: 6: {
1: 7: printf("%d\n", a);
-: 8: }
-: 9: else
-: 10: {
#####: 11: printf("%d\n", a + 1);
-: 12: }
-: 13:}

每一行的运行次数和没有运行到的行数,如果不想整其他的可以直接从这个文件读取数据了

Coverage.py

coverage是python下的一个测试代码覆盖率的库,官方文档:文档,安装仅需要

1
pip install coverage

具体例子先鸽了,jetbrains的教育认证到期了刚刚申请

使用方法很简单,我们有一个sample.py文件,我们只需要使用

1
coverage run sample.py

即可(他还可以结合pytest等其他工具使用),他会生成一个sqlLite文件,这个数据库中存有覆盖测试的相关信息,可以通过

1
coverage html

即可生成网页报告,它还可以有其他的格式(text,HTML,XML,LCOV,JSON),
coverage.py还提供了api

1
2
3
4
5
6
7
8
9
10
11
import coverage

cov = coverage.Coverage()
cov.start()

# .. call your code ..

cov.stop()
cov.save()

cov.html_report()

同理还可以使用其他的报告格式