文章目錄
- GCOV 工具簡介
- gcov 使用
- lcov
- 相關編譯選項
GCOV 工具簡介
gcov是一個測試代碼覆蓋率的工具,它是 gcc 自帶的查看代碼覆蓋率的工具。
與GCC結合使用,可以分析您的程序以幫助創建更高效、運行更快的代碼,并發現程序中未經測試的部分。您可以將gcov作為性能分析工具使用,以幫助發現優化工作對代碼產生最佳效果的位置。您還可以將gcov與另一種性能分析工具gprof一起使用,以評估代碼的哪些部分使用了最多的計算時間。
性能分析工具可以幫助您分析代碼的性能。使用像gcov或gprof這樣的分析器,您可以找出一些基本的性能統計數據,例如:
- 每行代碼執行了多少次
- 實際執行了哪些代碼行
- 每個代碼段使用了多少計算時間
一旦您了解了編譯后的代碼如何工作,就可以查看每個模塊,看看哪些模塊應該進行優化。gcov可以幫助您確定在哪里進行優化工作。
軟件開發人員還結合測試套件使用覆蓋率測試,以確保軟件實際上足夠好,可以發布。測試套件可以驗證程序按預期工作;覆蓋率程序測試測試套件執行了程序的多少部分。然后,開發人員可以確定需要向測試套件中添加哪些類型的測試用例,以創建更好的測試和更好的最終產品。
如果您計劃使用gcov,應該在不優化的情況下編譯代碼,因為通過將一些代碼行組合成一個函數的優化,可能無法為您提供尋找“熱點”所需的信息,即代碼使用大量計算時間的地方。同樣,因為gcov按行(最低分辨率)累積統計數據,它最適合一種編程風格,即每行只放置一個語句。如果您使用復雜的宏,這些宏展開為循環或其他控制結構,統計數據就不那么有用——它們只報告宏調用出現的行。如果您的復雜宏表現得像函數,您可以用內聯函數替換它們來解決這個問題。
gcov創建一個名為sourcefile.gcov的日志文件,指示源文件sourcefile.c的每一行執行了多少次。您可以將這些日志文件與gprof一起使用,以幫助微調程序的性能。gprof提供您可以與從gcov獲得的信息一起使用的計時信息。
gcov僅適用于用GCC編譯的代碼。它與任何其他性能分析或測試覆蓋機制不兼容。
使用效果如下圖所示:
程序運行完成后,可以查看每個文件的代碼覆蓋率情況,上面報告中展示了每個文件的行覆蓋率,函數覆蓋率和分支覆蓋率。
打開一個文件的覆蓋率報告,頁面對開始有文件的基本信息描述,以 FreeRTOS 的 task.c 為例,它的有效代碼行數為 921 行,共 24 個函數(幾千行的文件其實也沒多少嘛~)
在覆蓋率的正文,有該文件的完整代碼,并用不同顏色進行高亮標注了:
- 藍色表示運行被覆蓋的代碼,前面的數字表示代碼執行次數。
- 紅色表示未執行代碼。
- 白色表示無效代碼,包括注釋,空行和未編譯代碼。
gcov 使用
使用 gcov 的流程非常簡單,只需要三步即可。 下面以 hello world 為例,展示生成覆蓋率報告。
代碼如下:
//main.c#include <stdio.h>int main(int argc, char **argv) {printf("hello world\n");return 0;
}
第一步: 添加編譯參數 -fprofile-arcs -ftest-coverage
在所需要的生產覆蓋率的文件中,添加編譯參數,編譯代碼生成目標文件,同時會生成 *.gcno 文件,其中包含文件的行號等信息。
GCC C/C++代碼覆蓋率統計生成
gcc main.c -c -fprofile-arcs -ftest-coverage -o main.o
ls # 輸出文件列表:
# main.c main.gcno main.o
gcc main.o -lgcov -o main
第二步: 添加鏈接參數 -lgcov
,運行程序
運行程序后,會生成一個 *.gcda 文件,里面包含代碼執行次數等數據。
gcc main.o -lgcov -o main
# 運行程序
./main
# hello world
ls # 輸出文件列表:
#main main.c main.gcda main.gcno main.o
第三步: 輸出覆蓋率報告 使用下面命令輸出覆蓋率報告
# 第一次使用前安裝工具
sudo apt install lcov# 生成覆蓋率文本報告
lcov -c -d . -o test.info --rc lcov_branch_coverage=1
# 生成覆蓋率網頁報告
genhtml --branch-coverage -o result test.info
輸入上面兩/三條命令后在,執行命令的文件路徑可以看到一個 result 文件夾,在里面就是對應的網頁覆蓋率報告。
用瀏覽器打開 index.html 就可以看到最開始展示的覆蓋率信息了。
lcov
lcov 是 GCC 覆蓋率測試工具 gcov 的圖形化前端,用于收集多個源文件的行、函數和分支覆蓋率數據,并創建包含覆蓋信息的 HTML 頁面。它還添加了概覽頁面,以便在文件結構中進行方便的導航。
使用 lcov 收集覆蓋率數據,并使用 genhtml 創建 HTML 頁面。覆蓋率數據可以從當前正在運行的 Linux 內核或用戶空間應用程序中收集。要做到這一點,您需要完成以下準備步驟:
- 收集覆蓋率數據:首先,使用 lcov 工具來收集代碼的覆蓋率數據。您可以通過在編譯時添加
-ftest-coverage
和-fprofile-arcs
選項來生成覆蓋率數據的中間文件。然后,通過運行應用程序或測試套件來執行代碼路徑,以便生成覆蓋率數據。 - 生成 HTML 頁面:接下來,使用 genhtml 工具將收集到的覆蓋率數據轉換為 HTML 頁面。genhtml 將分析覆蓋率數據,并生成帶有覆蓋信息注釋的源代碼的 HTML 頁面,以及用于導航文件結構的概覽頁面。
這些工具的結合使用使得開發人員能夠更直觀地查看代碼的覆蓋情況,從而更好地理解測試覆蓋范圍和質量。通過分析生成的 HTML 頁面,開發人員可以識別哪些部分的代碼需要更多的測試覆蓋,以及哪些部分已經得到了良好的覆蓋。
總的來說,lcov 和 genhtml 工具的組合為開發人員提供了一種強大的方式來收集和可視化代碼覆蓋率數據,從而幫助他們更好地進行測試和代碼質量管理。如果您需要進一步解釋或有其他問題,請隨時告訴我,我很樂意為您提供幫助。
關鍵選項:
-r tracefile pattern--remove tracefile patternRemove data from tracefile.Use this switch if you want to remove coverage data for a particular set of files from a tracefile. Additional command line parameters will be interpreted as shell wildcard patterns (notethat they may need to be escaped accordingly to prevent the shell from expanding them first). Every file entry in tracefile which matches at least one of those patterns will be removed.Note: The pattern must be specified to match the absolute path of each source file.The result of the remove operation will be written to stdout or the tracefile specified with -o.Only one of -z, -c, -a, -e, -r, -l, --diff or --summary may be specified at a time.--exclude patternExclude source files matching pattern.Use this switch if you want to exclude coverage data for a particular set of source files matching any of the given patterns. Multiple patterns can be specified by using multiple --excludecommand line switches. The patterns will be interpreted as shell wildcard patterns (note that they may need to be escaped accordingly to prevent the shell from expanding them first).Note: The pattern must be specified to match the absolute path of each source file.Can be combined with the --include command line switch. If a given file matches both the include pattern and the exclude pattern, the exclude pattern will take precedence.
--include patternInclude source files matching pattern.Use this switch if you want to include coverage data for only a particular set of source files matching any of the given patterns. Multiple patterns can be specified by using multiple --in‐clude command line switches. The patterns will be interpreted as shell wildcard patterns (note that they may need to be escaped accordingly to prevent the shell from expanding them first).Note: The pattern must be specified to match the absolute path of each source file.
--rc keyword=valueOverride a configuration directive.Use this option to specify a keyword=value statement which overrides the corresponding configuration statement in the lcovrc configuration file. You can specify this option more than once tooverride multiple configuration statements. See lcovrc(5) for a list of available keywords and their meaning.
相關編譯選項
- -fprofile-arcs
該選項用于在程序執行時對程序流程弧進行插裝,記錄每個分支和調用被執行的次數以及它們被取或返回的次數。對于支持具有優先級支持的構造函數的目標,剖析能夠正確處理構造函數、析構函數以及用作全局變量類型的類的C++構造函數(和析構函數)。
當編譯后的程序退出時,會將這些數據保存到名為 auxname.gcda
的文件中,每個源文件都會生成一個相應的 .gcda
文件。這些數據可以用于基于profile-directed 的優化(例如 -fbranch-probabilities
),或者用于測試覆蓋率分析(例如 -ftest-coverage
)。每個目標文件的 auxname
是從輸出文件的名稱生成的,如果顯式指定了輸出文件并且它不是最終可執行文件,則 auxname
會采用源文件的基本名稱。在這兩種情況下,任何后綴都會被移除(例如對于輸入文件 dir/foo.c
,auxname
為 foo.gcda
;對于指定為 -o dir/foo.o
的輸出文件,auxname
為 dir/foo.gcda
)。
簡而言之,通過使用 -fprofile-arcs
編譯選項,您可以收集程序在執行過程中的代碼覆蓋率和執行路徑信息,從而可以進行優化或測試覆蓋率分析。
- –coverage
這個選項是 -fprofile-arcs -ftest-coverage
(編譯時)和 -lgcov
(鏈接時)的同義詞。主要做了以下工作:
- 使用
-fprofile-arcs
和優化、代碼生成選項來編譯源文件。對于測試覆蓋率分析,還需額外加上-ftest-coverage
選項。并不需要對程序中的每個源文件都進行剖析。 - 另外使用
-fprofile-abs-path
選項來編譯源文件,以在.gcno
文件中創建絕對路徑名。這樣可以讓 gcov 在項目中的編譯發生在不同工作目錄的情況下正確找到源文件。 - 使用
-lgcov
或-fprofile-arcs
(后者暗含了前者)來鏈接您的目標文件。 - 運行程序以生成弧剖析信息。可以多次重復這個過程。您可以同時運行多個程序實例,只要文件系統支持鎖定,數據文件就會被正確地更新。除非啟用了嚴格的 ISO C 方言選項,否則 “fork” 調用會被檢測并正確處理,而不會重復計數。
- 對于基于profile-directed的優化,需要使用相同的優化和代碼生成選項再次編譯源文件,并加上
-fbranch-probabilities
。 - 對于測試覆蓋率分析,使用 gcov 從
.gcno
和.gcda
文件中生成人類可讀的信息。請參考 gcov 文檔以獲取更多信息。
特殊環境使用注意點:
在正常使用 gcov 是非常簡單的,但是在特殊項目中使用 gcov 需要注意一些坑,否則就會跟我一樣掉進去出不來。。。
- 鏈接時,會在 .init_array 段插入 __gcov_init() 函數,該函數在 main 運行之前初始化 gcov 運行環境。
- 如果修改過鏈接腳本,注意 .init_array 的全局構造函數是否執行成功。
- 和上面一樣,鏈接時,會在全局析構函數中插入 __gcov_exit() 函數,在 main 執行結束后,輸出 *.gcda 文件。
- 如果程序為異常退出,則不會生成 *.gcda 文件,此時需要在代碼適當位置插入 __gcov_flush() 函數,將文件進行保存。
- 默認狀態 *.gcda 文件和 *.gcno 文件所在文件夾相同,如果需要修改輸出文件夾,可通過添加環境變量:GCOV_PREFIX 和 GCOV_PREFIX_STRIP
- GCOV_PREFIX_STRIP=16 , 為將原有路徑裁剪16個文件夾。 GCOV_PREFIX=/home/tester/build , 為將裁剪后的路徑添加前綴 例如:上文中默認 main.gcda 所在的文件 /home/tester/main.gcda,裁剪后添加前綴后變為/home/tester/build/main.gcda