更多课程 选择中心

C/C++培训
达内IT学院

400-996-5531

贾金莉 | C/C++代码全量覆盖率统计

  • 发布:贾金莉
  • 来源:百度QA
  • 时间:2017-09-13 16:09

代码覆盖率

代码的覆盖程度,一种度量方式。

语句覆盖

也叫行覆盖、段覆盖、基本块覆盖,度量被测代码中每个可执行语句被执行的占比。

判定覆盖

也叫分支覆盖、所有边界覆盖、基本路径覆盖、判定路径覆盖,度量被测代码中每个判定分支被执行的占比。

函数覆盖

度量是否调用了每个函数或过程

覆盖率工具简介

BullseyeCoverage

Bullseye Coverage 是Bullseye 公司提供的一款C/C++代码覆盖率测试工具,提供的代码覆盖率是分支覆盖率,不能统计语句覆盖率,是付费使用的。

Gcov/Lcov(免费)

Gcov 是gcc自带的,免费使用且可以同时统计函数覆盖率、分支覆盖率和行覆盖率。

Bullseyecoverage的统计步骤

Bullseyecoverage的原理是在C 或C++代码被编译之前,在源文件中插入一些额外的“打桩”代码。这些桩信息随源代码一起由编译器进行编译,编译形成的二进制文件便具备了被Bullseyecoverage进行代码分析的条件。当程序被执行时,代码覆盖的情况就被写到指定的.cov记录文件,Bullseyecoverage便可以从这个文件得知哪些代码被执行过。

创建编译目录并下载代码,确认安装bullseyscoverage

注意:c++版本库和bullseyscoverage兼容问题(-std=c++11 和-std=c++1y)

比如coverag版本 8.9.39,如果是和使用了c++1y的版本一起使用是会编译报错的,然后在bullseyscoverage8.9.75版本解决了兼容c++1y问题的版本,所以低版本的coverage编译的话只能用-std=c++11

指定test.cov文件目录并编译

如果程序复杂有很多编译依赖且不需要统计依赖的覆盖率信息,就在拉取和编译依赖的时候关掉coverage,在编译运行程序之前指定test.cov的目录,该文件会记录以下信息:

Ø 源代码文件(相对路径)

Ø 函数名及其起始行号,其中的n代表line,后面的数字即是其所在行。

Ø d代表decision,后面的数字表示其所在行相对于函数起始行的偏移。

Ø c代表condition

运行程序和回归case

编译产出可执行程序之后,运行可执行程序且执行回归代码,发现test.cov文件会被修改,表示在程序执行的时候,记录了程序被执行时候覆盖的的分支和函数的信息。

注意:test.cov文件必须和可执行程序是绑定的,如果有任何修改编译产出,就不能使用以前的test.cov文件,且test.cov文件是追加写。

统计覆盖率信息

分别通过covclass,covdir,covfn可以分别查看在类(命名空间)、源代码目录、函数层的覆盖统计信息。

gcov+lcov覆盖率统计步骤

下面介绍针对后台进程常驻的工程,分别统计可执行程序以及动图链接库的覆盖率(函数覆盖率,行覆盖率和分支覆盖率)。

统计可执行程序的覆盖率

本文都针对一个复杂工程做统计,不针对一个cpp文件做覆盖率统计,实践使用百度自己的编译工具comake2 会自动产出makefile文件,除工具差异,其他使用方法和步骤类似。

Ø 新建编译目录并拉取待编译工程,确认gcc版本,确认lcov工具安装

Ø 由于很多工程都是后台常驻,且都有守护进程,所以基本不像一些程序执行完成后退出,针对这些程序一般gcov无法直接生成gcda的统计覆盖率信息,就需要在处理信号的代码中加入如下几行,或者在main函数中添加信号处理代码:

Ø 在所以需要编译依赖中添加 -coverage -lgcov -fprofiles-arcs,编译链接中添加 -lgcov,编译后产出gcno文件,.gcno是由-ftest-coverage产生的,它包含了重建基本块图和相应的块的源码的行号的信息。

Ø 编译产出结果之后运行工程,执行工程的回归代码,找到pid,执行kill -10 pid,发现本地会产出gcda文件,.gcda是由加了-fprofile-arcs编译参数的编译后的文件运行所产生的,它包含了弧跳变的次数和其他的概要信息

Ø 下面的统计步骤待介绍动态链接库的覆盖率之后一起介绍

统计动态链接库的覆盖率

对于直接的编译产出的覆盖率用以上方法已经统计出来,但是如果工程复杂,有动态链接文件,且很多代码策略均在动态链接,那就需要统计动态链接的覆盖率。如下图所示是gcov+lcov统计动态链接库产出覆盖率的对比图,下面介绍具体的统计步骤。

Ø 建立编译目录,拉取或者编写待编译代码

Ø 我们把直接编译产出的可执行文件叫做框架代码,把动态链接库文件叫策略代码。且框架不依赖策略可以直接运行,但是策略编译会依赖框架。编译条件仍然是在常驻进程且现在统计策略覆盖。

Ø 通常,gcov是应用程序终止时会写入覆盖率数据。但是这只是从应用程序本身而不是共享库中刷新数据,且如果是常驻进程需要参考上面的做法,针对动态链接库的统计需要从每个共享库内调用flush功能,以下方法包括一个导出函数,后者是在接收到自定义信号处理期间被主程序动态调用的。所以我们就需要在每一个动态链接库的调用时都执行flush函数,然后在框架中新增运行时接收USR1信号,接收后执行函数会检测执行flush函数的动态链接库,然后执行gcovflush,可执行程序写出动态链接库的覆盖率信息。

在框架编译目录下新增export.c文件,内容如下:

Ø 编译export.c生成export.o,查找框架的Makefile文件,在框架编译产出的依赖中新加export.o 并make –j4编译框架

Ø 进入动态链接库编译的目录中,新增export.c文件如下


Ø 编译产出export.o,同样在编译依赖中添加 -coverage -lgcov -fprofiles-arcs,编译链接中添加 -lgcov,编译后产出gcno文件。

在Makefile文件中找到动态链接库产出的结果依赖前新增export.o,执行make -j4执行策略代码,产出动态链接库

Ø 准备工程执行所需要的其他依赖文件,启动程序,执行自动化case,然后执行kill -10 pid,然后可以看到在调用动态链接库libas.so时,执行了flush函数,写出了覆盖率信息,检查目录已经产出gcda文件为统计成功。

Ø 然后然后进入策略的编译目录执行抽取覆盖率文件的命令

lcov -c -o test_cov_# -d . --rc lcov_branch_coverage=1

-c覆盖率 -o抽取结果信息 -d当前目录 --rc lcov_branch_coverage=11加上分支覆盖率信息

Ø 由于很多策略代码会使用第三方库的依赖,比如会加入gtest等库,我们不需要计算第三方函数的覆盖率,这样就从上面的覆盖信息中抽取我们想要的目录的文件或者是不要的目录文件,下面使用抽取需要的目录:

lcov -e test_cov_# $pwd -o test_cov_# --rc lcov_branch_coverage=1

-e抽取 $pwd要计算的源代码目录 -o输出名字 --rc lcov_branch_coverage=1加上分支覆盖率信息

Ø 产出覆盖率报告:

genhtml test_cov_# -o test_cov_strategy --rc lcov_branch_coverage=1

最终统计出的报告在根目录下有index.html

作者介绍

贾金莉

华中科技大学计算机科学与技术硕士,现任百度内容生态质量部测试工程师 ,主要负责百度手机助手后端检索测试工作。

预约申请免费试听课

填写下面表单即可预约申请免费试听!怕钱不够?可就业挣钱后再付学费! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!

上一篇:C/C++编程开发环境搭建教程
下一篇:深受C/C++程序员欢迎的11款IDE_开发工具

超全的C语言标识符知识

C指针——指针类型转换

C指针——指针和结构类型的关系

C指针——数组和指针的关系

Copyright © 2023 Tedu.cn All Rights Reserved 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有

选择城市和中心
黑龙江省

吉林省

河北省

湖南省

贵州省

云南省

广西省

海南省