石佳琦,陳 鵬
1(武漢郵電科學研究院,武漢 430010)
2(烽火通信科技股份有限公司,武漢 430010)
覆蓋率對于軟件測試有著非常重要的作用.通過代碼覆蓋率,可以發現程序的未測試部分,估計程序中哪段代碼最耗時,幫助開發人員創建更高效、更快的運行代碼,提高代碼質量.
GCOV是一個GNU的本地覆蓋測試工具[1],配合GCC共同實現對C或者C++文件的語句覆蓋和分支覆蓋測試.而在Linux環境下,shell腳本的魅力大放異彩,通過帶參數的指令集,可以實現許多的功能.Expect腳本工具可以實現交互式任務,而無需人的干預.Cygwin 工具,更是為我們提供了一種在Windows環境下類Linux的環境.本文基于GCOV工具覆蓋率生成流程,配合上述軟件工具完成Linux平臺下代碼覆蓋率報告自動化輸出設計[2,3].
基本塊BB(Basic Block)是程序中一個順序執行的語句序列,BB中的所有語句的執行次數一定相同,一般由多個順序執行語句后跟一個跳轉語句組成.ARC分支,從一個BB到另一個BB的跳轉記為一個ARC.
如圖1所示.如果跳轉語句是有條件的,就產生了一個分支(ARC),該基本塊就有兩個基本塊作為目的地.如果把每個基本塊當作一個節點,那么一個函數中的所有基本塊就構成了一個有向圖,稱之為基本塊圖.只要知道BB或ARC的執行次數就可以推算出所有的BB和所有的ARC的執行次數.GCOV根據BB和ARC的統計情況來統計各BB內各行代碼執行情況,從而計算整個程序的覆蓋率情況[4].

圖1 基本塊圖
主要工作流程見圖2.
1)編譯前,在編譯器中加入編譯器參數-fprofilearcs-ftest-coverage.
2)源碼經過編譯預處理,在生成匯編文件的階段完成插樁.生成可執行文件,并且生成關聯BB和跳轉次數ARC的*.gcno文件.
3)執行可執行文件,在運行過程中“樁點”負責收集程序的執行信息[5].
4)生成具有BB和ARC的執行統計次數等數據信的*.gcda文件.
5)通過lcov和genhtml可將*.gcno、*.gcda中的統計信息圖形化,生成具體的報告文檔.
由于實際運行環境為嵌入式分布式設備,且編譯環境和測試環境分別在不同的服務器上,我們采用模塊化設計,將各功能的實現模塊化,便于后續的管理優化,降低各階段執行的耦合率,提高覆蓋率自動化測試的時效性.

圖2 GCOV收集覆蓋率信息流程圖
整體環境分為四部分: 編譯環境、運行環境、服務器端、用戶端.
編譯環境: 主要實現不同模塊軟件源代碼編譯打包,完成覆蓋率報告的生成.
運行環境: 軟件運行的實際環境.通過更新軟件實際運行,實現代碼在具體目標終端設備機上 運行的代碼覆蓋率實際情況.
服務器端: 工程測試人員操作平臺,主要完成源代碼編譯的操作的控制、更新代碼在目標終端設備機上運行情況的控制以及代碼自動化覆蓋率測試及輸出的.
用戶端: 工程測試人員個人PC覆蓋率報告文檔的目標用戶,可以設置多個.
圖3為系統結構拓撲圖.
Cygwin是一個在Windows平臺上運行的類Linux模擬環境,它提供一個UNIX模擬 DLL 以及在其上上層構建的多種可以在 Linux 系統中找到的軟件包.
Expect是一種免費的編程工具語言,用來實現自動交互式任務進行通信,而無需人工值守.它是一個用來實現自動交互功能的軟件套件.
在當前Windows工作服務器安裝Cygwin 軟件,在安裝時勾選除Linux環境一些常見的軟件包外,還需勾選基于TCL 的Expect 軟件包,配置支持遠程遠程登錄的ssh等服務[2].

圖3 系統結構拓撲圖
2.4.1 服務器端設計目的與實現的功能
由于程序需要在終端設備上運行,且測試環境和編譯環境在不同的服務器上,這就需要我們設置一個中心節點可以有效的將各模塊和環境聯系起來,以實現設計的完整性和操作連貫性.
不同用戶可以遠程到服務器端,依據GCOV覆蓋率報告生成的步驟,通過調用封裝好腳本工具實現如下功能:
自動登錄編譯環境完成編譯,拷貝可執行文至服務器端的用戶目錄;
拷貝可執行文件至測試環境,并將生成的*.gcda文件拷貝至用戶目錄;
自動檢查用戶目錄存在*.gcda文件時,拷貝*.gcda文件至編譯環境對應目錄,執行lcov 和 genhtml完成可視化覆蓋率報告的生成
自動檢查在編譯環境對應目錄下是否存在生成的覆蓋率報告,并拷貝至服務端用戶目錄.
2.4.2 編譯環境設計目的與實現的功能
大型的C工程的編譯都有各自的makefile和編譯腳本.編譯時工程開發人員通過鍵入不同的參數來編譯外鏈庫文件或者可執行文件.
該模塊設計主要目的是生成覆蓋率數據文件*.gcno.通過修改編譯腳本實現插樁編譯以及外鏈庫和可執行文件的單一編譯或混合編譯.通過判斷是否帶GCOV參數來控制是否進行插樁編譯,即使在不進行覆蓋率編譯的情況下,也可以實現普通編譯及文件拷貝,減少了開發人員與編譯服務器的交互.該模塊主要響應來自“服務器端”開發人員鍵入命令,對編譯環境下常用編譯文件、腳本的解析執行,完成軟件的編譯拷貝工作.將參數提取,根據工程開發人員在編譯模塊腳本鍵入不同的參數組合實現單一庫文件或可執行文件的編譯,以及庫文件可執行文件的混合編譯.服務器端不編譯服務器交互過程見圖4.
2.4.3 運行環境設計目的與實現的功能
運行環境設計的主要目的是生成覆蓋率數據文件*.gcda.運行可執行文件,在捕捉到外部信號時,對程序進行exit.檢查是否存在*.gcda文件并通過服務器端轉存到編譯環境.
2.4.4 用戶側設計目的與實現的功能
用戶側為遠程到服務器端的客戶端.是開發人員工作的物理環境.

圖4 服務器端不編譯服務器交互過程
3.1.1 流程提示工具
main.sh 參數:(0~9 的數字 或無)
功能說明:
主要通過echo 命令對整個流程進行索引,指導流程.參數: 一個數字.不帶參數時輸出整個流程提示,已數字為流程序號索引;帶參數時,輸出該數字對應流程調用的腳本及示例.
腳本示例:
main.sh 1
3.1.2 拷貝工具
copy.sh 參數: 1)拷貝環境;2)需要拷貝的文件;3)目標環境;4)目標路徑.
功能說明:
完成對本地文件拷貝到目標環境,或從目標環境拷貝文件到本地.其中目標環境可以是運行環境或用戶端.
功能實現:
遠程拷貝時需要輸入用戶名和密碼,以及路徑.由于我們拷貝文件的目錄比較固定,可以將這些靜態數據寫成配置文件,而動態的拷貝環境,文件名等作為腳本工具的參數.
local_to_dst 主要完成了本地到目標環境的遠程拷貝.將 scp 命令“嵌入”到Expect腳本中,對登錄用戶寫靜態配置,當目標環境提示輸入密碼時,將提示語“password”作為Expect響應關鍵字,此時將配置好的靜態密碼通過 Expect 中 send 命令發送,則實現了文件自動拷貝.
同理dst_to_local 實現了反向的功能.
腳本示例:
copy.sh local shell_shell_gcov dst board1
3.1.3 校驗工具
check.sh 參數: 1)校驗環境;2)工作目錄;3)文件類型;4)文件名稱;5)user_name;6)date.
功能說明:
完成對目標環境下目標文檔是否存在進行校驗,完成不同拷貝動作.
功能實現:
調用本地或拷貝到運行環境和編譯環境 shell_for_gcov 下的find_the_file.sh ,將目標文件校驗結果寫入校驗文件,并拷貝到服務器端建立的用戶目錄下.我們對不同的目標文件都設有一個以文件類型結尾的校驗結果文件,通過這個文件寫入的內容判斷是否存在目標文件,是否可以進行下一步的拷貝動作.
腳本示例:
check.sh root gcov gcda gcda-0506 sjq 20180506
3.1.4 編譯工具
complex_compile.sh
參數: 1)編譯類別;2)編譯目錄;3)工作路徑;4)版本信息;5)user_name;6)date.
功能說明:
通過編譯類別遠程不同的編譯服務器,通過編譯目錄執行不同的編譯腳本,加版本信息對每次編譯進行區分.
功能實現:
首先對編譯makefile修改增加和覆蓋率相關的編譯和鏈接選項.
其次在當前前Windows 服務器選定一個工作路徑,以以此路徑為基路徑,調用mkdir.sh 腳本,兩個參數 1)、人名,2)日期 ,以user_name為基路徑下一級目錄,日期作為一級目錄下二級目錄,用于存放相關文件.檢查基路徑下所有一級目錄名,若不存在則新建,若存在則進入該目錄,檢查一級目錄下二級目錄名,如不存在則新建,若存在則輸出提示,新建的目錄名后加bak后綴以作區分.
通過Expect交互腳本完成對可執行文件以及可執行文件依賴的外鏈庫進行自動化編譯.軟件包內的不同可執行文件是否需要編譯可以通過修改配置文件完成,編譯時將編譯配置文件內的參數傳入到編譯腳本.如果外鏈的庫需要編譯,則先編譯外鏈庫再將庫文件拷貝到編譯可執行文件的lib庫目錄下編譯[6,7].
編譯完成后拷貝到服務器端的建立用戶目錄下,完成軟件包的解壓,將可執行文件拷貝出.
腳本示例:
complex_compile.sh ccu " " 686214_V2R5_new 128 sjq 20171207
3.1.5 備份工具
backup.sh 參數: 1)備份環境;2)備份文件類型;3)是否恢復操作標志;4)user_name;5)date.
功能說明:
完成不同環境上容易變動的文件的備份,以便環境恢復出錯時可以降回到以前版本.
功能實現:
主要在備份環境上不同類型的文件備份目錄下以user_name為基路徑下一級目錄,日期作為一級目錄下二級目錄.將文件拷貝只該目錄下.需要恢復環境時,將該目錄下的文件拷回原目錄下.
腳本示例:
Backup.sh dst exe 0 sjq 20180506
功能說明:
完成對本地文件拷貝到目標環境,或從目標環境拷貝文件到本地.其中目標環境可以是運行環境或用戶端.
圖5為本系統工作的主要流程.
流程簡述:
1)調用mkdir.sh 輸入 user_name 和 date 信息,在服務器端建立用戶目錄用于存放文件;
2)調用compile.sh 完成外鏈庫或軟件的編譯,編譯成功通過控制臺會輸出關鍵字“success”;
3)通過expect交互腳本語言做出匹配該關鍵字的操作,即調用服務器上shell_for_gcov 下的find_the_file.sh查找是否存在編譯好的文件并寫校驗文件,然后將校驗文件拷貝到服務器端.此時,expect腳本會自動調用check.sh 讀取用戶目錄下的校驗文件,如果讀取可以拷貝,后將帶有覆蓋率編譯的軟件拷貝至用戶目錄下,解壓軟件將可執行文件取出;
4)將運行環境上的可執行文件備份,將用戶目錄的新文件拷貝運行環境運行;
5)登錄運行環境系統設置覆蓋率相關的環境變量,運行程序;
6)觸發指令操作,覆蓋代碼執行,待程序退出此時會在環境變量設置的目錄下生成存放同名C文件的GCDA 文件目錄;
7)校驗運行GCDA文件是否生成,并拷貝至編譯服務器上的編譯目錄;
8)在編譯環境執行覆蓋率命令,此時會將覆蓋率的信息以html格式的文件輸出;
9)校驗是否生成覆蓋率報告,并通過mail命令拷貝到用戶目錄.
本設計通過作者本人實際工作中進行覆蓋率測試的經驗和經歷提煉而來.在進行覆蓋率統計時,很多準備工作都是是單調重復的操作[8].由最開始shell腳本設計的批處理拷貝,在實踐中不斷完善,參考覆蓋率生成的步驟,將其設計成腳本工具,在實際中的確減少了人機的頻繁交互,節省了時間,提高了工作效率.
1)該設計并不是完全自動的,部分修改還需人工操作,如:
需要在測試main函數中增加信號捕捉函數,以便后臺程序可以exit;
需要在運行環境中手動設置環境變量,該步驟可以通過腳本實現;
程序的運行需要手動操作,該步驟也可以通過腳本實現;
2)本設計前提是在運行環境和編譯環境可以登錄的情況下進行的,沒有對環境的可用性進行檢查,可以通ping命令配合過腳本實現對環境的可用性檢查;
3)日志記錄并沒有保存文件,可以通過重定向的方式將控制臺打印按一定格式存文件;
4)本設計的分支測試需要外部命令觸發,可以通gtest工具對增加測試用例,配合GCOV實現對代碼的全覆蓋,給出更全面的報告;
5)目前的覆蓋率是全量的覆蓋率,可以優化實現增量代碼的覆蓋率.
還有很多不足之處和待優化的地方需要改進.

圖5 代碼覆蓋率報告自動輸出流程圖
1)如圖6,首先進入服務器端工作路徑,調用提示工具提示操作步驟;
2)調用目錄創建工具,完成用戶目錄的創建(見圖7);
3)調用編譯工具完成,軟件的編譯(見圖8);
4)在運行環境完成*.gcda文件生成,檢驗后拷貝到服務器端,此時調用覆蓋率自動輸出工具,會完成覆蓋率報告的自動輸出(見圖9),并拷貝的服務器端用戶目錄下,見圖10.

圖6 提示工具使用樣圖

圖7 目錄創建工具使用樣圖

圖8 編譯工具使用樣圖

圖9 覆蓋率報告自動輸出工具使用樣圖

圖10 拷貝到用戶目錄下的覆蓋率報告及各種校驗結果文件
如圖11所示,我們可以看到整個源碼的覆蓋率情況,和函數覆蓋百分比.點擊某個C文件我們可以看到每一行代碼執行的頻率,還有各函數的覆蓋情況以及內部分支執行的情況.

圖11 生成的覆蓋率報告
本文介紹了一種Linux平臺下代碼覆蓋率自動化輸出設計.首先對本設計依據原理進行了介紹,然后對系統整體的架構進行了介紹,以及各模塊的設計目的及實現功能進行了描述,最后依據該設計進行測試,并結合實際操作所遇到的問題,指出了本設計的不足之處和優化建議.
本設計是作者在進行Linux下覆蓋率測試時,發現基于GCOV的覆蓋率測試具有操作繁瑣單調、耗時的特點,為解決這一問題對覆蓋率輸出做出了改進優化.與原有GCOV覆蓋率輸出相比,本設計具有搭建方便、操作簡單,節省時間的的優點.同時通過修改編譯腳本和相關路徑及IP的配置文件,可實現移植.在現有的條件下極大的提供高了覆蓋率報告的輸出效率,為工程開發人員提供了有效依據,節約了大量時間.