簡體   English   中英

gcov 在 Clang 和 GCC 上產生不同的結果

[英]gcov produces different results on Clang and GCC

我試圖了解如何通過使用 CMake、googletest 和 gcov 進行測試覆蓋來正確構建 C++ 項目。 我想構建一個適用於任何平台/編譯器的通用 CMakeLists.txt。

是我的第一次嘗試。 但是,如果我嘗試構建項目然后運行 lcov(以生成報告),我發現使用 CLang(正確結果)或 GCC(錯誤結果)會得到不同的結果。 請注意,我在 MacOs 上,我通過 brew 安裝了 gcc ( brew install gcc )。

此外,我在主CMakeLists.txt中使用了以下標志:

if(CODE_COVERAGE)
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage" )
endif()

注意:如果您在我的CMakeLists.txt文件或lcov用法中發現錯誤/奇怪的地方,我願意接受任何類型的反饋!

我的圖書館

#include "library.h"

#include <iostream>

void foo(){
    std::cout << "Foo!" << std::endl;
}

void bar(int n){
    if (n > 0){
        std::cout << "n is grater than 0!" << std::endl;
    }
    else if (n < 0){
        std::cout << "n is less than 0!" << std::endl;
    }
    else{
        std::cout << "n is exactly 0!" << std::endl;
    }
}

void baz(){  // LCOV_EXCL_START
    std::cout << "Baz!" << std::endl;
}
// LCOV_EXCL_STOP

我的測試


#ifndef GCOV_TUTORIAL_TEST_LIBRARY_H
#define GCOV_TUTORIAL_TEST_LIBRARY_H

#include "../src/library.h"

#include <gtest/gtest.h>


namespace gcov_tutorial::tests {
    TEST(TestFooSuite,TestFoo){
        foo();
    }
    TEST(TestBarSuite,TestBarGreaterThanZero){
        bar(100);
    }
    TEST(TestBarSuite,TestBarEqualToZero){
        //bar(0);
    }
    TEST(TestBarSuite,TestBarLessThanZero){
        bar(-100);
    }
}

#endif //GCOV_TUTORIAL_TEST_LIBRARY_H

CLang 匯編

#!/bin/bash

# Rationale: https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail

# BASE_DIR is the project's directory, containing the src/ and tests/ folders.
BASE_DIR=$PWD
COVERAGE_FILE=coverage.info

GCOV_PATH=/usr/bin/gcov
CLANG_PATH=/usr/bin/clang
CLANGPP_PATH=/usr/bin/clang++

rm -rf build
mkdir build && cd build

# Configure
cmake -DCMAKE_C_COMPILER=$CLANG_PATH -DCMAKE_CXX_COMPILER=$CLANGPP_PATH -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Release ..

# Build (for Make on Unix equivalent to `make -j $(nproc)`)
cmake --build . --config Release

# Clean-up for any previous run.
rm -f $COVERAGE_FILE
lcov --zerocounters --directory .
# Run tests
./tests/RunTests
# Create coverage report by taking into account only the files contained in src/
lcov --capture --directory tests/ -o $COVERAGE_FILE --include "$BASE_DIR/src/*" --gcov-tool $GCOV_PATH
# Create HTML report in the out/ directory
genhtml $COVERAGE_FILE --output-directory out
# Show coverage report to the terminal
lcov --list $COVERAGE_FILE
# Open HTML
open out/index.html

使用 CLang 編譯時的覆蓋率報告

GCC 匯編

#!/bin/bash

# Rationale: https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail

# BASE_DIR is the project's directory, containing the src/ and tests/ folders.
BASE_DIR=$PWD
COVERAGE_FILE=coverage.info

GCOV_PATH=/usr/local/bin/gcov-11
GCC_PATH=/usr/local/bin/gcc-11
GPP_PATH=/usr/local/bin/g++-11

rm -rf build
mkdir build && cd build

# Configure
cmake -DCMAKE_C_COMPILER=$GCC_PATH -DCMAKE_CXX_COMPILER=$GPP_PATH -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Release ..

# Build (for Make on Unix equivalent to `make -j $(nproc)`)
cmake --build . --config Release

# Clean-up for any previous run.
rm -f $COVERAGE_FILE
lcov --zerocounters --directory .
# Run tests
./tests/RunTests
# Create coverage report by taking into account only the files contained in src/
lcov --capture --directory tests/ -o $COVERAGE_FILE --include "$BASE_DIR/src/*" --gcov-tool $GCOV_PATH
# Create HTML report in the out/ directory
genhtml $COVERAGE_FILE --output-directory out
# Show coverage report to the terminal
lcov --list $COVERAGE_FILE
# Open HTML
open out/index.html

GCC 的覆蓋率報告

您實際上在這里問了兩個問題。

  1. 為什么這兩個編譯器的覆蓋率結果不同?
  2. 如何為代碼覆蓋構建一個 CMake 項目?

答案 1:覆蓋范圍差異

此處的簡單答案是您正在以Release模式而不是RelWithDebInfo模式構建。 默認情況下,GCC 不像 Clang 那樣放入那么多調試信息。 在我的系統上,將-DCMAKE_CXX_FLAGS="-g"添加到build-and-run-cov-gcc.sh腳本會產生與 Clang 相同的結果,就像在RelWithDebInfo中構建一樣。

無論出於何種原因,Clang 似乎在默認情況下或在啟用覆蓋時跟蹤更多調試信息。 GCC 沒有這些相同的護欄。 要吸取的教訓是:收集覆蓋率信息是一種調試形式; 如果你想要准確的結果,你必須為你的編譯器使用調試感知配置。

答案2:構建系統結構

在您的構建中設置CMAKE_CXX_FLAGS通常是一個糟糕的主意。 該變量旨在成為您的構建用戶注入他們自己的標志的掛鈎。 正如我在本網站的另一個答案中所詳述的那樣,存儲此類設置的現代方法是在預設

我會去掉頂級 CMakeLists.txt 的if (CODE_COVERAGE)部分,然后創建以下CMakePresets.json文件:

{
  "version": 4,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 23,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "gcc-coverage",
      "displayName": "Code coverage (GCC)",
      "description": "Enable code coverage on GCC-compatible compilers",
      "binaryDir": "${sourceDir}/build",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "RelWithDebInfo",
        "CMAKE_CXX_FLAGS": "-fprofile-arcs -ftest-coverage"
      }
    }
  ],
  "buildPresets": [
    {
      "name": "gcc-coverage",
      "configurePreset": "gcc-coverage",
      "configuration": "RelWithDebInfo"
    }
  ]
}

然后你的構建腳本可以大大簡化。

#!/bin/bash

# Rationale: https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail

# Set up defaults for CC, CXX, GCOV_PATH
export CC="${CC:-gcc-11}"
export CXX="${CXX:-g++-11}"
: "${GCOV_PATH:=gcov-11}"

# Record the base directory
BASE_DIR=$PWD

# Clean up old build
rm -rf build

# Configure
cmake --preset gcc-coverage

# Build
cmake --build --preset gcc-coverage

# Enter build directory
cd build

# Clean-up counters for any previous run.
lcov --zerocounters --directory .

# Run tests
./tests/RunTests

# Create coverage report by taking into account only the files contained in src/
lcov --capture --directory tests/ -o coverage.info --include "$BASE_DIR/src/*" --gcov-tool $GCOV_PATH

# Create HTML report in the out/ directory
genhtml coverage.info --output-directory out

# Show coverage report to the terminal
lcov --list coverage.info

# Open HTML
open out/index.html

這里的關鍵是以下幾行:

# Configure
cmake --preset gcc-coverage
# Build
cmake --build --preset gcc-coverage

該腳本現在允許您通過環境變量改變編譯器和覆蓋工具,並且CMakeLists.txt不必對正在使用的編譯器做出任何假設。

在我的 (Linux) 系統上,我可以成功運行以下命令:

$ CC=gcc-12 CXX=g++-12 GCOV=gcov-12 ./build-and-run-cov.sh

結果-gcc

$ CC=clang-13 CXX=clang++-13 GCOV=$PWD/llvm-cov-13.sh ./build-and-run-cov.sh

其中llvm-cov-13.shllvm-cov-13的包裝器,用於與--gcov-tool標志兼容。 有關詳細信息,請參閱此答案

#!/bin/bash
exec llvm-cov-13 gcov "$@"

結果鏗鏘

如您所見,由於使用了正確的標志,結果無法區分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM