自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux下c/c++項(xiàng)目代碼覆蓋率的產(chǎn)生方法

系統(tǒng) Linux
最近做了一系列的單元測(cè)試相關(guān)的工作,除了各種規(guī)范及測(cè)試框架以外,討論比較多的就是關(guān)于代碼覆蓋率的產(chǎn)生,c/c++與其他的一些高級(jí)語(yǔ)言或者腳本語(yǔ)言相比較而言,例如 Java、.Net和php/python/perl/shell等,由于沒(méi)有這些高級(jí)語(yǔ)言和腳本語(yǔ)言的反射的特性,其代碼覆蓋率的產(chǎn)生過(guò)程會(huì)稍微復(fù)雜一些。發(fā)現(xiàn)許多同學(xué)對(duì)C++的覆蓋率如何產(chǎn)生在都不太清楚,這里做一個(gè)簡(jiǎn)單的介紹。

最近做了一系列的單元測(cè)試相關(guān)的工作,除了各種規(guī)范及測(cè)試框架以外,討論比較多的就是關(guān)于代碼覆蓋率的產(chǎn)生,c/c++與其他的一些高級(jí)語(yǔ)言或者腳本語(yǔ)言相比較而言,例如 Java、.Net和php/python/perl/shell等,由于沒(méi)有這些高級(jí)語(yǔ)言和腳本語(yǔ)言的反射的特性,其代碼覆蓋率的產(chǎn)生過(guò)程會(huì)稍微復(fù)雜一些。發(fā)現(xiàn)許多同學(xué)對(duì)C++的覆蓋率如何產(chǎn)生在都不太清楚,這里做一個(gè)簡(jiǎn)單的介紹。

一、基本使用方法

在Linux上的c/c++開(kāi)發(fā)一般都使用gcc/g++作為主要的編譯器,如果需要產(chǎn)生覆蓋率數(shù)據(jù)需要在Makefile或者Scons文件中做下面的編譯鏈接設(shè)置,

  • 編譯的時(shí)候,增加 -fprofile-arcs -ftest-coverage 或者 –coverage;
  • 鏈接的時(shí)候,增加 -fprofile-arcs 或者 –lgcov;
  • 打開(kāi)–g3 選項(xiàng),去掉-O2以上級(jí)別的代碼優(yōu)化選項(xiàng);否則編譯器會(huì)對(duì)代碼做一些優(yōu)化,例如行合并,從而影響行覆蓋率結(jié)果;

基本要求就上面三點(diǎn),但有一個(gè)建議,為了上述幾個(gè)編譯選項(xiàng)的使用不影響到正常的編譯過(guò)程(否則會(huì)極大地影響程序的運(yùn)行效率)。在使用makefile中通過(guò)參數(shù)傳遞來(lái)支持覆蓋率產(chǎn)生,可以在makefile使用下面的方式,

ifeq ($(coverage), yes)

CXXFLAGS       +=  -fprofile-arcs -ftest-coverage

LINKERCXX      +=  -fprofile-arcs -ftest-coverage

OPT_FLAGS     =  -g3

endif

這樣,可以使用 make coverage=yes 來(lái)引入這些編譯選項(xiàng)而不會(huì)影響到正常的編譯(scons同理)。

二、簡(jiǎn)單示例

這里寫(xiě)了一個(gè)簡(jiǎn)單的程序做測(cè)試,主要包含三個(gè)文件:Rectangle.cpp, RectangleTest.cpp, Makefile。

1)Rectangle.cpp 是被測(cè)代碼,里面定義了一個(gè)簡(jiǎn)單的類Rectangle(長(zhǎng)方形),里面有三個(gè)方法:

  • set_values(),設(shè)置長(zhǎng)方形對(duì)象的長(zhǎng)和寬;
  • area(),求長(zhǎng)方形的面積;
  • lenth(),求長(zhǎng)放形的周長(zhǎng);

2)RectangleTest.cpp 是一個(gè)簡(jiǎn)單的測(cè)試程序,為了demo使用,并沒(méi)有使用cppunit/gtest這樣的單元測(cè)試框架,直接使用了main()函數(shù)來(lái)調(diào)用Rectangle里面的方法;

Rectangle.cpp和RectangleTest.cpp的代碼如下圖,

3)Makefile比較簡(jiǎn)單,主要支持在coverage=yes的參數(shù)支持。 可以使用-fprofile-arcs -ftest-coverage 選項(xiàng),這里為了簡(jiǎn)化使用了 –coverage。

覆蓋率產(chǎn)生的過(guò)程如下面四個(gè)步驟所示,其中步驟3和4,根據(jù)需要使用其中一種即可。

1. 編譯鏈接帶覆蓋率參數(shù)的源代碼;

2. 運(yùn)行測(cè)試程序;

3. 使用gcov獲取文本形式的覆蓋率數(shù)據(jù);

4. 使用lcov獲取html形式的覆蓋率數(shù)據(jù);


下面針對(duì)本例,做這一過(guò)程的逐步演示。

1. 編譯鏈接帶覆蓋率參數(shù)的源代碼;

由于Makeifle中已經(jīng)支持了coverage=yes選項(xiàng),直接運(yùn)行 “make coverage=yes”,這個(gè)時(shí)候會(huì)產(chǎn)生測(cè)試程序,并同時(shí)生成gcno文件(關(guān)于gcno文件的詳細(xì)解釋,參見(jiàn)第三部分背后原理),如下圖,

2. 運(yùn)行測(cè)試程序;

運(yùn)行./RectangleTest 測(cè)試程序,運(yùn)行結(jié)束后,會(huì)針對(duì)所有的cpp源代碼文件產(chǎn)生相應(yīng)的*.gcda文件(關(guān)于gcda文件的詳細(xì)解釋,參見(jiàn)第三部分背后原理),如下圖

3. 使用gcov獲取文本形式的覆蓋率數(shù)據(jù);

需要注意的是,這個(gè)步驟不是必須的,如果需要文本格式(*.gcov)的覆蓋率結(jié)果,可是走這個(gè)步驟。如果想看html格式的結(jié)果,直接跳過(guò)這一步驟。gcov是gcc自帶的覆蓋率結(jié)果產(chǎn)生工具,無(wú)需單獨(dú)安裝。

針對(duì)某個(gè)源代碼文件,例如 Rectangle.cpp,執(zhí)行”gcov Rectangle.cpp” 會(huì)產(chǎn)生Rectangle.cpp.gcov文件。

 

這是一個(gè)存文本文件,可以通過(guò)vim打開(kāi),看到詳細(xì)的行覆蓋率數(shù)據(jù),如下

4. 使用lcov獲取html形式的覆蓋率數(shù)據(jù);

有些時(shí)候需要使用html結(jié)果的數(shù)據(jù)展示,這樣看起來(lái)更加直觀一些。IBM開(kāi)源了lcov這個(gè)工具,更多參見(jiàn) http://ltp.sourceforge.net/coverage/lcov.php

工具使用,如下圖,

 

手動(dòng)把cc_result目錄拷貝到http/apache等服務(wù)器的htdocs目錄下,可以通過(guò)瀏覽器來(lái)查看覆蓋率結(jié)果,如下,

 

整個(gè)覆蓋率生成的流程按照上面四個(gè)步驟就可以搞定。下面一節(jié)對(duì)其原理做簡(jiǎn)單的闡述。

 

三、基本原理

1. 術(shù)語(yǔ)解釋

在了解背后原理之前,需要對(duì)覆蓋率技術(shù)的一些概念有簡(jiǎn)單的了解。主要是基本塊(Basic Block),基本塊圖(Basic Block Graph),行覆蓋率(line coverage), 分支覆蓋率(branch coverage)等。

  • 基本塊(Basic Block),”A basic block is a sequence of instructions with only entry and only one exit. If any one of the instructions are executed, they will all be executed, and in sequence from first to last.”  這里可以把基本塊看成一行整體的代碼,基本塊內(nèi)的代碼是線性的,要不全部運(yùn)行,要不都不運(yùn)行;
  • 基本塊圖(Basic Block Graph),基本塊的最后一條語(yǔ)句一般都要跳轉(zhuǎn),否則后面一條語(yǔ)句也會(huì)被計(jì)算為基本塊的一部分。 如果跳轉(zhuǎn)語(yǔ)句是有條件的,就產(chǎn)生了一個(gè)分支(arc),該基本塊就有兩個(gè)基本塊作為目的地。如果把每個(gè)基本塊當(dāng)作一個(gè)節(jié)點(diǎn),那么一個(gè)函數(shù)中的所有基本塊就構(gòu)成了一個(gè)有向圖,稱之為基本塊圖(Basic Block Graph)。且只要知道圖中部分BB或arc的執(zhí)行次數(shù)就可以推算出所有的BB和所有的arc的執(zhí)行次數(shù);
  • 打樁,意思是在有效的基本塊之間增加計(jì)數(shù)器,計(jì)算該基本塊被運(yùn)行的次數(shù);打樁的位置都是在基本塊圖的有效邊上;
  • 行覆蓋率(line coverage),源代碼有效行數(shù)與被執(zhí)行的代碼行的比率;
  • 分支覆蓋率(branch coverage),有判定語(yǔ)句的地方都會(huì)出現(xiàn)2個(gè)分支,整個(gè)程序經(jīng)過(guò)的分支與所有分支的比率是分支覆蓋率。注意,與條件覆蓋率(condition coverage)有細(xì)微差別,條件覆蓋率在判定語(yǔ)句的組合上有更細(xì)的劃分。
2.  gcc/g++ 編譯選項(xiàng)

gcc需要靜態(tài)注入目標(biāo)程序編譯選項(xiàng),在編譯鏈接的時(shí)候加入2個(gè)選項(xiàng)(-ftest-coverage -fprofile-arcs ),編譯結(jié)束之后會(huì)生成 *.gcno 文件,而經(jīng)過(guò)靜態(tài)注入的目標(biāo)程序在“正常結(jié)束”后,會(huì)在運(yùn)行目錄下產(chǎn)生*.gcda數(shù)據(jù)文件,通過(guò)gcov工具就可產(chǎn)生覆蓋率數(shù)據(jù)結(jié)果。

-ftest-coverage

Produce a notes file that the gcov code-coverage utility (see gcov—a Test Coverage Program) can use to show program coverage. Each source file’s note file is called auxname.gcno. Refer to the -fprofile-arcs option above for a description of auxname and instructions on how to generate test coverage data. Coverage data matches the source files more closely if you do not optimize.
讓編譯器生成與源代碼同名的.gcno文件(note file),這種文件含有重建基本塊依賴圖和將源代碼關(guān)聯(lián)至基本塊的必要信息;


-fprofile-arcs

Add code so that program flow arcs are instrumented. During execution the program records how many times each branch and call is executed and how many times it is taken or returns. When the compiled program exits it saves this data to a file called auxname.gcda for each source file. The data may be used for profile-directed optimizations (-fbranch-probabilities), or for test coverage analysis (-ftest-coverage). Each object file’s auxname is generated from the name of the output file, if explicitly specified and it is not the final executable, otherwise it is the basename of the source file. In both cases any suffix is removed (e.g. foo.gcda for input file dir/foo.c, ordir/foo.gcda for output file specified as -o dir/foo.o). See Cross-profiling.

讓編譯器靜態(tài)注入對(duì)每個(gè)源代碼行關(guān)聯(lián)的計(jì)數(shù)器進(jìn)行操作的代碼,并在鏈接階段鏈入經(jīng)態(tài)度libgcov.a,其中包含在程序正常結(jié)束時(shí)生成*.gcda文件的邏輯;

下面通過(guò)源碼解析來(lái)說(shuō)明到底這2個(gè)選項(xiàng)做了什么。通過(guò)g++ -S選項(xiàng),產(chǎn)生匯編語(yǔ)言Rectangle.s 和 Rectangle_cc.s (增加–coverage選項(xiàng)),命令如下,

g++ -c -o Rectangle.s Rectangle.cpp -g -Wall -S

g++ -c -o Rectangle_cc.s Rectangle.cpp -g -Wall –coverage -S

vimdiff Rectangle.s 和 Rectangle_cc.s,如下圖


通過(guò)這樣匯編語(yǔ)言的對(duì)比,可以看出gcc通過(guò)這2個(gè)參數(shù),把打樁的過(guò)程完成了。

更深入的內(nèi)容,例如,如果想知道gcno/gcda文件的格式,可以參考 @livelylittlefish 的一篇文章,GCC Coverage代碼分析-.gcda/.gcno文件及其格式分析http://blog.csdn.net/livelylittlefish/article/details/6448885)。

 

四、擴(kuò)展話題

通過(guò)上面三部分的介紹,相信絕大多數(shù)覆蓋率問(wèn)題都可以解決,下面2個(gè)問(wèn)題是我們?cè)趯?shí)際運(yùn)行過(guò)程中遇到的,也分享一下。

  1. 覆蓋率的結(jié)果只有被測(cè)試到的文件會(huì)被顯示,并非所有被編譯的代碼都被作為覆蓋率的分母

實(shí)際上,可以看到整個(gè)覆蓋率的產(chǎn)生的過(guò)程是4個(gè)步驟的流程,一般都通過(guò)外圍腳本,或者makefile/shell/python來(lái)把整個(gè)過(guò)程自動(dòng)化。2個(gè)思路去解決這個(gè)問(wèn)題,都是通過(guò)外圍的偽裝。第一個(gè),就是修改lcov的 app.info ,中間文件,找到其他的文件與覆蓋率信息的地方,結(jié)合makefile,把所有被編譯過(guò)的源程序檢查是否存于 app.info 中,如果沒(méi)有,增加進(jìn)去。第二個(gè)偽裝,是偽裝 *.gcda,沒(méi)有一些源碼覆蓋率信息的原因就是該文件沒(méi)有被調(diào)用到,沒(méi)有響應(yīng)的gcda文件產(chǎn)生。toast(http://toast.taobao.org/)是通過(guò)第一種偽裝來(lái)實(shí)現(xiàn)的,更多了解需要去看下開(kāi)源代碼。

2. 后臺(tái)進(jìn)程的覆蓋率數(shù)據(jù)收集;
 

其實(shí)上述覆蓋率信息的產(chǎn)生,不僅可以針對(duì)單元測(cè)試,對(duì)于功能測(cè)試同樣適用。但功能測(cè)試,一般linux下c/c++都是實(shí)現(xiàn)了某個(gè)Daemon進(jìn)程,而覆蓋率產(chǎn)生的條件是程序需要正常退出,即用戶代碼調(diào)用 exit 正常結(jié)束時(shí),gcov_exit 函數(shù)才得到調(diào)用,其繼續(xù)調(diào)用 __gcov_flush 函數(shù)輸出統(tǒng)計(jì)數(shù)據(jù)到 *.gcda 文件中。同樣2個(gè)思路可以解決這個(gè)問(wèn)題,

第一,給被測(cè)程序增加一個(gè) signal handler,攔截 SIGHUP、SIGINT、SIGQUIT、SIGTERM 等常見(jiàn)強(qiáng)制退出信號(hào),并在 signal handler 中主動(dòng)調(diào)用 exit 或 __gcov_flush 函數(shù)輸出統(tǒng)計(jì)結(jié)果。但這個(gè)需要修改被測(cè)程序。這個(gè)也是我們之前的通用做法。但參加過(guò)清無(wú)同學(xué)的一個(gè)講座后,發(fā)現(xiàn)了下面第二種更好的方法。

第二,借用動(dòng)態(tài)庫(kù)預(yù)加載技術(shù)和 gcc 擴(kuò)展的 constructor 屬性,我們可以將 signalhandler 和其注冊(cè)過(guò)程都封裝到一個(gè)獨(dú)立的動(dòng)態(tài)庫(kù)中,并在預(yù)加載動(dòng)態(tài)庫(kù)時(shí)實(shí)現(xiàn)信號(hào)攔截注冊(cè)。這樣,就可以簡(jiǎn)單地通過(guò)如下命令行來(lái)實(shí)現(xiàn)異常退出時(shí)的統(tǒng)計(jì)結(jié)果輸出了。

 

五、其他編程語(yǔ)言

在我們的工程實(shí)踐中,還有其他的編程語(yǔ)言,都涉及到覆蓋率的產(chǎn)生,我們的工程實(shí)踐推薦下面的方法,
 
  • c/c++,  本文介紹的方法;
  • Java,  Maven  cobertura 插件;
  • Python, PyUnit +  coverage.py;
  • Php, phpunit +  –coverage-html ;
  • Perl,  Test::Class 和 Devel::Cover;
  • Shell,  shUnit2 + shcov;
責(zé)任編輯:張浩 來(lái)源: 淘寶直公的博客
相關(guān)推薦

2011-11-01 10:10:48

ScriptCover

2022-05-31 09:01:18

SwiftApp 項(xiàng)目

2023-10-27 08:49:00

JCovOpenJDK

2021-12-25 22:30:27

Chrome DevTJavaScript調(diào)試工具

2012-04-11 11:21:57

ibmdw

2019-09-25 09:20:41

谷歌代碼開(kāi)發(fā)者

2020-06-17 12:22:44

C覆蓋重載

2024-05-31 12:50:49

C++編程NaN

2011-04-25 09:49:20

代碼測(cè)試

2024-06-14 12:04:33

2025-02-25 00:18:45

AIC#單元測(cè)試

2021-10-15 13:47:19

覆蓋率檢測(cè) istanbul 總代碼的比例

2010-02-01 17:02:53

C++產(chǎn)生隨機(jī)數(shù)

2015-04-17 10:35:51

c++c++程序內(nèi)存泄漏檢測(cè)代碼

2010-02-03 14:30:04

C++棧對(duì)象

2015-11-09 17:56:57

WebPHP函數(shù)覆蓋

2016-01-13 10:14:15

WebPHP函數(shù)覆蓋

2022-05-13 09:40:51

代碼可行應(yīng)用性能

2022-10-21 15:29:32

5G網(wǎng)絡(luò)

2010-01-21 10:23:53

C++代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)