Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmake: Add Coverage script #191

Merged
merged 1 commit into from
Jun 8, 2024
Merged

cmake: Add Coverage script #191

merged 1 commit into from
Jun 8, 2024

Conversation

hebasto
Copy link
Owner

@hebasto hebasto commented May 6, 2024

Using separated scripts provides additional flexibility as there is no need to specify coverage report specific options during the build configuration stage.

Examples of usage on Ubuntu 24.04 with LCOV v2.0:

  • total coverage:
$ cmake -B build -DCMAKE_BUILD_TYPE=Coverage -DCMAKE_CXX_FLAGS="-fprofile-update=atomic"
$ cmake --build build
$ cmake -DLCOV_OPTS="--ignore-errors mismatch" -P build/Coverage.cmake
# OR
$ cmake \
    -DJOBS=$(nproc) \
    -DEXTENDED_FUNCTIONAL_TESTS=ON \
    -DLCOV_OPTS="--rc branch_coverage=1 --ignore-errors mismatch" \
    -P build/Coverage.cmake
  • fuzz coverage:
$ cmake -B build -DCMAKE_BUILD_TYPE=Coverage -DCMAKE_CXX_FLAGS="-fprofile-update=atomic" -DBUILD_FUZZ_BINARY=ON
$ cmake --build build
$ cmake \
    -DFUZZ_SEED_CORPUS_DIR=<path/to/seed/corpus> \
    -DLCOV_OPTS="--rc branch_coverage=1 --ignore-errors mismatch" \
    -P build/CoverageFuzz.cmake

This PR supports both LCOV v1 and LCOV v2. Hence, it is possible to test it on Ubuntu 24.04.

@hebasto hebasto added the enhancement New feature or request label May 7, 2024
@hebasto hebasto force-pushed the 240506-cmake-EW branch from 62a97b7 to 2da8214 Compare May 7, 2024 23:21
@hebasto hebasto added this to the UI, 23th May milestone May 8, 2024
@hebasto hebasto force-pushed the 240506-cmake-EW branch 2 times, most recently from 5989b1f to fb5f30a Compare May 8, 2024 13:39
@hebasto
Copy link
Owner Author

hebasto commented May 8, 2024

Included changes from bitcoin#30063.

@hebasto hebasto force-pushed the 240506-cmake-EW branch from fb5f30a to 739adea Compare May 8, 2024 15:31
@hebasto
Copy link
Owner Author

hebasto commented May 9, 2024

@maflcko Want to tested this PR in your https://github.com/maflcko/b-c-cov?

Comment on lines +29 to +32
execute_process(
COMMAND ${LCOV_FILTER_COMMAND} test_bitcoin.info test_bitcoin_filtered.info
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file consists mostly of such execute_process() calls. The most natural way to execute a bunch of commands is from a shell script. I would suggest to use a shell script for this. This 77 lines cmake file would be equivalent roughly to the following:

functional_test_runner="test/functional/test_runner.py"
if [ -n "$EXTENDED_FUNCTIONAL_TESTS" ] ; then
    functional_test_runner="$functional_test_runner --extended"
fi
if [ -n "$JOBS" ] ; then
    CMAKE_CTEST_COMMAND="$CMAKE_CTEST_COMMAND -j $JOBS"
    functional_test_runner="$functional_test_runner -j $JOBS"
fi

cd $CMAKE_CURRENT_LIST_DIR
$CMAKE_CTEST_COMMAND --build-config Coverage
$LCOV_COMMAND --capture --directory src --test-name test_bitcoin --output-file test_bitcoin.info
$LCOV_COMMAND --zerocounters --directory src
$LCOV_FILTER_COMMAND test_bitcoin.info test_bitcoin_filtered.info
$LCOV_COMMAND --add-tracefile test_bitcoin_filtered.info --output-file test_bitcoin_filtered.info
$LCOV_COMMAND --add-tracefile baseline_filtered.info --add-tracefile test_bitcoin_filtered.info --output-file test_bitcoin_coverage.info
$GENHTML_COMMAND test_bitcoin_coverage.info --output-directory test_bitcoin.coverage
$functional_test_runner
$LCOV_COMMAND --capture --directory src --test-name functional-tests --output-file functional_test.info
$LCOV_COMMAND --zerocounters --directory src
$LCOV_FILTER_COMMAND functional_test.info functional_test_filtered.info
$LCOV_COMMAND --add-tracefile functional_test_filtered.info --output-file functional_test_filtered.info
$LCOV_COMMAND --add-tracefile baseline_filtered.info --add-tracefile test_bitcoin_filtered.info --add-tracefile functional_test_filtered.info --output-file total_coverage.info | $GREP_EXECUTABLE "%" | $AWK_EXECUTABLE "{ print substr($3,2,50) \"/\" $5 }" > coverage_percent.txt
$GENHTML_COMMAND total_coverage.info --output-directory total.coverage

Copy link
Owner Author

@hebasto hebasto May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most natural way to execute a bunch of commands is from a shell script. I would suggest to use a shell script for this.

From CMake's point of view, using a platform independent approach is more natural :)

However, I have to admit that it is not expected that the user will run coverage scripts on Windows.

Comment on lines +5 to +7
if("@CMAKE_CXX_COMPILER_ID@" STREQUAL "Clang")
find_program(LLVM_COV_EXECUTABLE llvm-cov REQUIRED)
set(COV_TOOL "${LLVM_COV_EXECUTABLE} gcov")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clang has a different native workflow: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html which I guess produces better results compared to its gcc compatibility layer.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. But this PR aims to mirror the master branch behaviour. Further improvements can be done later, no?

./configure --enable-lcov
make
make cov
cmake -B build -DCMAKE_BUILD_TYPE=Coverage
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think with/without coverage should be independent option from debug/release. For example one may want to gather coverage on debug builds or release builds? I do not think we need a dedicated build type "Coverage" in order to pass a few extra flags which is already supported by the build system with APPEND_..._FLAGS.

For clang, that would be extra -fprofile-instr-generate -fcoverage-mapping and compiling and running the tests as usual. I guess for gcc that would be --coverage instead.

I know this would be different from how autotools works, but I would propose to have a shell script in contrib/ that compiles with coverage enabled (e.g. by running cmake -DAPPEND_..._FLAGS="-fprofile-instr-generate -fcoverage-mapping"), then runs the tests and gathers the coverage (actually 2 shell scripts - one for clang and one for gcc).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example one may want to gather coverage on debug builds or release builds?

Is this something that is commonly done? What would the purpose of this be?

I know this would be different from how autotools works, but I would propose to have a shell script

Not sure about this. I prefer to work with the build system directly without having to understand a wrapper script first if I want to tweak something.

@hebasto hebasto force-pushed the 240506-cmake-EW branch 2 times, most recently from b675988 to 8321299 Compare May 25, 2024 14:42
@hebasto
Copy link
Owner Author

hebasto commented May 25, 2024

  1. Switched from the "Coverage" build type to the "coverage" preset. It improves flexibility and hopefully addresses @vasild's cmake: Add Coverage script #191 (comment).

  2. Now supports both LCOV v1 and LCOV v2. Hence, it is possible to test on Ubuntu 24.04.

@hebasto
Copy link
Owner Author

hebasto commented May 25, 2024

Friendly ping code coverage connoisseurs @maflcko @dergoegge :)

Your opinion will be much appreciated by all people in the CMake working group.

@ONLY
)

set(LCOV_OPTS --ignore mismatch)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be hardcoded here (it's not needed with lcov 1.x, and isn't the only ignore that might be required with 2.x)?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Deleted.

@hebasto hebasto force-pushed the 240506-cmake-EW branch from 8321299 to 003faac Compare May 30, 2024 09:25
@hebasto
Copy link
Owner Author

hebasto commented May 30, 2024

Addressed the recent @fanquake's comments.

The doc change has been aligned with bitcoin#30192.

./configure --enable-lcov
make
make cov
cmake -B build --preset coverage

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different from the examples in the PR description and also skips building the fuzz binary, is that expected?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example in the PR description also does not build the fuzz binary:

Tests:
  test_bitcoin ........................ ON
  test_bitcoin-qt ..................... OFF
  bench_bitcoin ....................... OFF
  fuzz binary ......................... OFF

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description has been updated.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UPD. Added --ignore-errors mismatch

@hebasto hebasto force-pushed the 240506-cmake-EW branch from 003faac to f89ade3 Compare May 31, 2024 17:04
@hebasto
Copy link
Owner Author

hebasto commented May 31, 2024

Re-tested, fixed bugs.

Reverted back to use of the "Coverage" build type because this guarantees that the Coverage.cmake script finds unit tests when using multi-config generators.

The PR description has been updated.

CMakeLists.txt Outdated
@@ -484,6 +484,22 @@ else()
unset(c_flags_debug_overridden)
endif()

set(CMAKE_CXX_FLAGS_COVERAGE "-Og --coverage -fprofile-update=prefer-atomic")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fprofile-update=prefer-atomic

Not sure this should be in here? Don't think it's needed with Clang, and only sometimes with GCC?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it hurt if applied in such cases? Or do you want me to remove it?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this should be in here?

Deleted.

The PR description has been updated.

@dergoegge
Copy link

Writing data to fuzz_filtered.info
Summary coverage rate:
  lines......: 20.2% (19593 of 97038 lines)
  functions..: 20.4% (18829 of 92323 functions)
  branches...: no data found
Reading data file fuzz_coverage.info
Found 1190 entries.
Found common filename prefix "/home/dergoegge/workspace/bitcoin"
Writing .css and .png files.
Generating output.
genhtml: ERROR: cannot read /home/dergoegge/workspace/bitcoin/build/src/include/boost/visit_each.hpp
Processing file build/src/include/boost/visit_each.hpp

My lcov version is quite old (1.0), maybe that's why this fails. I've had to fiddle with the versions in the past and I'm sure this would work with the right versions etc. (everything seemed to work here besides the actual html generation).

I've switched to using llvm's native coverage reporting (i.e. compiling with -fprofile-instr-generate -fcoverage-mapping) which has been a breeze so far. I switched because I kept running into weird lcov problems like this and the gcov reports are also not the best (e.g. inline branch coverage is reported in a really weird format and I can't find any docs).

@hebasto
Copy link
Owner Author

hebasto commented Jun 5, 2024

Rebased.

@hebasto hebasto force-pushed the 240506-cmake-EW branch from 129b0fe to 7669f29 Compare June 6, 2024 09:47
@maflcko
Copy link

maflcko commented Jun 6, 2024

In the first OR in the pull request description in the second branch, you forgot to specify DCMAKE_CXX_FLAGS and the build type?

Copy link

@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@hebasto
Copy link
Owner Author

hebasto commented Jun 6, 2024

In the first OR in the pull request description in the second branch, you forgot to specify DCMAKE_CXX_FLAGS and the build type?

This OR is related to the script invocation only:

$ cmake -DLCOV_OPTS="--ignore-errors mismatch" -P build/Coverage.cmake
# OR
$ cmake \
    -DJOBS=$(nproc) \
    -DEXTENDED_FUNCTIONAL_TESTS=ON \
    -DLCOV_OPTS="--rc branch_coverage=1 --ignore-errors mismatch" \
    -P build/Coverage.cmake

@hebasto hebasto merged commit 2677998 into cmake-staging Jun 8, 2024
36 checks passed
@vasild
Copy link

vasild commented Jun 11, 2024

Further improvements, that would deviate from autotools:

  • Reconsider -Og because it has different meanings for GCC and Clang. Optimizations could result in bogus line numbers in the output, maybe just use -O0 -g.
  • Rename cmake/script/Coverage.cmake to GccCoverage.cmake because Clang uses a different workflow: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html. Introduce ClangCoverage.cmake.
  • Drop the build target Coverage. Passing extra compiler flags is more suitable for a CMake option, rather than a build target. IMO that is very similar to -DWERROR=ON for example. Introduce -DCOVERAGE=OFF|GCC|CLANG.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants