C代碼覆蓋率測試工具Gcov


代碼覆蓋率測試反映了測試的廣度與深度,量化了測試和開發質量,是十分有必要的,業界目前有針對各種語言的覆蓋率測試工具,本文主要介紹C/C++相關的覆蓋率測試工具Gcov

介紹

簡介

Gcov是一個測試覆蓋程序,是集成在GCC中的,隨GCC一起發佈

基本概念

基本塊BB

基本塊指一段程序的第一條語句被執行過一次後,這段程序中的每一跳語句都需要執行一次,稱爲基本塊,因此基本塊中的所有語句的執行次數是相同的,一般由多個順序執行語句後邊跟一個跳轉語句組成

跳轉ARC

從一個BB到另外一個BB的跳轉叫做一個ARC,要想知道程序中的每個語句和分支的執行次數,就必須知道每個BBARC的執行次數

程序流圖

如果把BB作爲一個節點,這樣一個函數中的所有BB就構成了一個有向圖,要想知道程序中的每個語句和分支的執行次數,就必須知道每個BBARC的執行次數,根據圖論可以知道有向圖中BB的入度和出度是相同的,所以只要知道了部分的BB或者ARC大小,就可以推斷所有的大小,這裏選擇由ARC的執行次數來推斷BB的執行次數,所以對部分ARC插樁,只要滿足可以統計出來所有的BBARC的執行次數即可

原理

測試程序首先進行編譯預處理,生成彙編文件,並完成插樁,插樁的過程中會向源文件的末尾插入一個靜態數組,數組的大小就是這個源文件中樁點的個數,數組的值就是樁點的執行次數,每個樁點插入3~4條彙編語句,直接插入生成的*.s文件中,最後彙編文件經過彙編生成目標文件,在程序運行過程中樁點負責收集程序的執行信息

使用

編譯

測試代碼如下:
say.c:

#include <stdio.h>

int say(char *what) {
    printf("------ %s\n", what);
    return 0;
}

main.c

#include <stdio.h>

extern int say(const char *);

int main(int argc, const char *argv[]) {
    
    if (argv[1]) {
        say("hello");
    } else {
        say("bye");
    }
    return 0;
}

添加-fprofile-arcs -ftest-coverage -fPIC編譯參數編譯程序,生成可執行程序和*.gcno文件,裏面記錄了行信息和程序流圖信息:

$ gcc -fprofile-arcs -ftest-coverage -fPIC -O0 say.c main.c

$ ls
a.out  main.c  main.gcno  say.c  say.gcno  

數據收集

運行可執行文件,生成*.gcda在默認生成在相應*.o文件目錄,裏面記錄了*.c文件中程序的執行情況,包括跳變次數等:

$ ./a.out
------ bye

$ ls
a.out  main.c  main.gcda  main.gcno  say.c  say.gcda  say.gcno

可以通過設置環境變量GCOV_PREFIX=/xxx/xxxGCOV_PREFIX_STRIP=x來改變路徑,其中GCOV_PREFIX_STRIP表示去掉源代碼路徑中的前幾級,默認爲0,比如源代碼路徑爲/a/b/c/d.cGCOV_PREFIX_STRIP=2,則實際使用的路徑是c/d.c,如果GCOV_PREFIX=/e/f,則.gcda實際存放的路徑是/e/f/c/d.gcda

報告生成

針對某一個文件的執行情況,可以通過如下命令生成報告,並創建*.gcov文件:

$ gcov -a main.c
File 'main.c'
Lines executed:80.00% of 5
Creating 'main.c.gcov'

常用選項,更多可參考Invoking gcov

-b:分支覆蓋
-a:所有基本塊覆蓋
-f:函數覆蓋

注意事項

  1. 在編譯時不要加優化選項,否則代碼會發生變化,無法準確定位
  2. 代碼中複雜的宏,比如宏展開後是循環或者其他控制結構,可以用內聯函數來代替,因爲gcov只統計宏調用出現的那一行
  3. 代碼每一行最好只有一條語句
  4. *.gcno*.gcda需要匹配,兩個文件是有時間戳來記錄是不是匹配的
  5. 若是編譯動態庫,需要在鏈接時-lgcov

圖形化展示

gcov生成的報告分散在各個源碼文件所對應的*.gcov文件中,難以彙總分析,並且可視化效果較差,所以需要轉化成可視圖形化報告,有lcovgcovr兩個工具可以完成,兩者功能基本相同,本文主要介紹gcovr,是一個用Python編寫的開源軟件,大小隻有幾十KB,安裝參見官網

列表形式

  1. 代碼覆蓋率
$ gcovr -r .
------------------------------------------------------------------------------
                           GCC Code Coverage Report
Directory: .
------------------------------------------------------------------------------
File                                       Lines    Exec  Cover   Missing
------------------------------------------------------------------------------
main.c                                         5       4    80%   15
say.c                                          3       3   100%   
------------------------------------------------------------------------------
TOTAL                                          8       7    87%
------------------------------------------------------------------------------

報告展示程序運行後覆蓋了80%的代碼

  1. 分支覆蓋率
$ gcovr -b -r .
------------------------------------------------------------------------------
                           GCC Code Coverage Report
Directory: .
------------------------------------------------------------------------------
File                                    Branches   Taken  Cover   Missing
------------------------------------------------------------------------------
main.c                                         2       1    50%   14
say.c                                          0       0    --%   
------------------------------------------------------------------------------
TOTAL                                          2       1    50%
------------------------------------------------------------------------------

報告展示了在main.c中有一個分支沒有執行到

XML文件形式

$ gcovr --xml-pretty -r .
<?xml version="1.0" ?>
<!DOCTYPE coverage
  SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-04.dtd'>
<coverage branch-rate="0.5" branches-covered="1" branches-valid="2"
 complexity="0.0" line-rate="0.875" lines-covered="7" lines-valid="8"
 timestamp="1537930892" version="gcovr 3.4">
 <sources>
  <source>.</source>
 </sources>
 <packages>
  <package branch-rate="0.5" complexity="0.0" line-rate="0.875" name="">
   <classes>
    <class branch-rate="0.5" complexity="0.0" filename="main.c"
     line-rate="0.8" name="main_c">
     <methods/>
     <lines>
      <line branch="false" hits="1" number="12"/>
      <line branch="true" condition-coverage="50% (1/2)" hits="1" number="14">
       <conditions>
        <condition coverage="50%" number="0" type="jump"/>
       </conditions>
      </line>
      <line branch="false" hits="0" number="15"/>
      <line branch="false" hits="1" number="17"/>
      <line branch="false" hits="1" number="19"/>
     </lines>
    </class>
    <class branch-rate="0.0" complexity="0.0" filename="say.c" line-rate="1.0"
     name="say_c">
     <methods/>
     <lines>
      <line branch="false" hits="1" number="10"/>
      <line branch="false" hits="1" number="11"/>
      <line branch="false" hits="1" number="12"/>
     </lines>
    </class>
   </classes>
  </package>
 </packages>
</coverage>

HTML文件形式

$ gcovr -r . --html -o xxx.html
$ ls
a.out  main.c  main.gcda  main.gcno  say.c  say.gcda  say.gcno  xxx.html

可以發現添加--html參數後,可以生成html文件,用瀏覽器打開,如下圖:
在這裏插入圖片描述

還可以添加--html-details選項,爲每個代碼文件單獨生成html

$ gcovr -r . --html --html-details -o xxx.html
$ ls
a.out  main.c  main.gcda  main.gcno  say.c  say.gcda  say.gcno  xxx.html  xxx.main.c.html  xxx.say.c.html

可以發現多了xxx.main.c.htmlxxx.say.c.html,用瀏覽器打開xxx.html,如下圖:
在這裏插入圖片描述
文件名較之前帶上了下劃線,單擊文件名,可以看到具體的代碼覆蓋情況,如下圖:
在這裏插入圖片描述

其它

其它功能,如Filters等,可以參考官方文檔

Reference

About me

forthebadge

Creative Commons License This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
本作品採用知識共享署名-相同方式共享 4.0 國際許可協議進行許可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章