Skip to content

Commit f89ade3

Browse files
committed
cmake: Add Coverage and CoverageFuzz scripts
1 parent 6230d47 commit f89ade3

6 files changed

+215
-6
lines changed

CMakeLists.txt

+16
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,22 @@ else()
484484
unset(c_flags_debug_overridden)
485485
endif()
486486

487+
set(CMAKE_CXX_FLAGS_COVERAGE "-Og --coverage -fprofile-update=prefer-atomic")
488+
set(CMAKE_OBJCXX_FLAGS_COVERAGE "-Og --coverage -fprofile-update=prefer-atomic")
489+
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "--coverage")
490+
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "--coverage")
491+
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
492+
if(is_multi_config)
493+
if(NOT "Coverage" IN_LIST CMAKE_CONFIGURATION_TYPES)
494+
list(APPEND CMAKE_CONFIGURATION_TYPES Coverage)
495+
endif()
496+
endif()
497+
498+
configure_file(cmake/script/Coverage.cmake Coverage.cmake COPYONLY)
499+
configure_file(cmake/script/CoverageFuzz.cmake CoverageFuzz.cmake COPYONLY)
500+
configure_file(cmake/script/CoverageInclude.cmake.in CoverageInclude.cmake @ONLY)
501+
configure_file(contrib/filter-lcov.py filter-lcov.py COPYONLY)
502+
487503
include(cmake/optional.cmake)
488504

489505
# Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review.

cmake/cov_tool_wrapper.sh.in

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) 2024-present The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or https://opensource.org/license/mit/.
4+
5+
exec @COV_TOOL@ "$@"

cmake/script/Coverage.cmake

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Copyright (c) 2024-present The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or https://opensource.org/license/mit/.
4+
5+
include(${CMAKE_CURRENT_LIST_DIR}/CoverageInclude.cmake)
6+
7+
set(functional_test_runner test/functional/test_runner.py)
8+
if(EXTENDED_FUNCTIONAL_TESTS)
9+
list(APPEND functional_test_runner --extended)
10+
endif()
11+
if(DEFINED JOBS)
12+
list(APPEND CMAKE_CTEST_COMMAND -j ${JOBS})
13+
list(APPEND functional_test_runner -j ${JOBS})
14+
endif()
15+
16+
execute_process(
17+
COMMAND ${CMAKE_CTEST_COMMAND} --build-config Coverage
18+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
19+
COMMAND_ERROR_IS_FATAL ANY
20+
)
21+
execute_process(
22+
COMMAND ${LCOV_COMMAND} --capture --directory src --test-name test_bitcoin --output-file test_bitcoin.info
23+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
24+
)
25+
execute_process(
26+
COMMAND ${LCOV_COMMAND} --zerocounters --directory src
27+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
28+
)
29+
execute_process(
30+
COMMAND ${LCOV_FILTER_COMMAND} test_bitcoin.info test_bitcoin_filtered.info
31+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
32+
)
33+
execute_process(
34+
COMMAND ${LCOV_COMMAND} --add-tracefile test_bitcoin_filtered.info --output-file test_bitcoin_filtered.info
35+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
36+
)
37+
execute_process(
38+
COMMAND ${LCOV_COMMAND} --add-tracefile baseline_filtered.info --add-tracefile test_bitcoin_filtered.info --output-file test_bitcoin_coverage.info
39+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
40+
)
41+
execute_process(
42+
COMMAND ${GENHTML_COMMAND} test_bitcoin_coverage.info --output-directory test_bitcoin.coverage
43+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
44+
)
45+
46+
execute_process(
47+
COMMAND ${functional_test_runner}
48+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
49+
COMMAND_ERROR_IS_FATAL ANY
50+
)
51+
execute_process(
52+
COMMAND ${LCOV_COMMAND} --capture --directory src --test-name functional-tests --output-file functional_test.info
53+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
54+
)
55+
execute_process(
56+
COMMAND ${LCOV_COMMAND} --zerocounters --directory src
57+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
58+
)
59+
execute_process(
60+
COMMAND ${LCOV_FILTER_COMMAND} functional_test.info functional_test_filtered.info
61+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
62+
)
63+
execute_process(
64+
COMMAND ${LCOV_COMMAND} --add-tracefile functional_test_filtered.info --output-file functional_test_filtered.info
65+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
66+
)
67+
execute_process(
68+
COMMAND ${LCOV_COMMAND} --add-tracefile baseline_filtered.info --add-tracefile test_bitcoin_filtered.info --add-tracefile functional_test_filtered.info --output-file total_coverage.info
69+
COMMAND ${GREP_EXECUTABLE} "%"
70+
COMMAND ${AWK_EXECUTABLE} "{ print substr($3,2,50) \"/\" $5 }"
71+
OUTPUT_FILE coverage_percent.txt
72+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
73+
)
74+
execute_process(
75+
COMMAND ${GENHTML_COMMAND} total_coverage.info --output-directory total.coverage
76+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
77+
)

cmake/script/CoverageFuzz.cmake

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright (c) 2024-present The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or https://opensource.org/license/mit/.
4+
5+
include(${CMAKE_CURRENT_LIST_DIR}/CoverageInclude.cmake)
6+
7+
if(NOT DEFINED FUZZ_SEED_CORPUS_DIR)
8+
set(FUZZ_SEED_CORPUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/qa-assets/fuzz_seed_corpus)
9+
endif()
10+
11+
execute_process(
12+
COMMAND test/fuzz/test_runner.py ${FUZZ_SEED_CORPUS_DIR} --loglevel DEBUG
13+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
14+
COMMAND_ERROR_IS_FATAL ANY
15+
)
16+
execute_process(
17+
COMMAND ${LCOV_COMMAND} --capture --directory src --test-name fuzz-tests --output-file fuzz.info
18+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
19+
)
20+
execute_process(
21+
COMMAND ${LCOV_COMMAND} --zerocounters --directory src
22+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
23+
)
24+
execute_process(
25+
COMMAND ${LCOV_FILTER_COMMAND} fuzz.info fuzz_filtered.info
26+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
27+
)
28+
execute_process(
29+
COMMAND ${LCOV_COMMAND} --add-tracefile fuzz_filtered.info --output-file fuzz_filtered.info
30+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
31+
)
32+
execute_process(
33+
COMMAND ${LCOV_COMMAND} --add-tracefile baseline_filtered.info --add-tracefile fuzz_filtered.info --output-file fuzz_coverage.info
34+
COMMAND ${GREP_EXECUTABLE} "%"
35+
COMMAND ${AWK_EXECUTABLE} "{ print substr($3,2,50) \"/\" $5 }"
36+
OUTPUT_FILE coverage_percent.txt
37+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
38+
)
39+
execute_process(
40+
COMMAND ${GENHTML_COMMAND} fuzz_coverage.info --output-directory fuzz.coverage
41+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
42+
)

cmake/script/CoverageInclude.cmake.in

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright (c) 2024-present The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or https://opensource.org/license/mit/.
4+
5+
if("@CMAKE_CXX_COMPILER_ID@" STREQUAL "Clang")
6+
find_program(LLVM_COV_EXECUTABLE llvm-cov REQUIRED)
7+
set(COV_TOOL "${LLVM_COV_EXECUTABLE} gcov")
8+
else()
9+
find_program(GCOV_EXECUTABLE gcov REQUIRED)
10+
set(COV_TOOL "${GCOV_EXECUTABLE}")
11+
endif()
12+
13+
# COV_TOOL is used to replace a placeholder.
14+
configure_file(
15+
cmake/cov_tool_wrapper.sh.in ${CMAKE_CURRENT_LIST_DIR}/cov_tool_wrapper.sh
16+
FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE
17+
GROUP_READ GROUP_EXECUTE
18+
WORLD_READ
19+
@ONLY
20+
)
21+
22+
find_program(LCOV_EXECUTABLE lcov REQUIRED)
23+
separate_arguments(LCOV_OPTS)
24+
set(LCOV_COMMAND ${LCOV_EXECUTABLE} --gcov-tool ${CMAKE_CURRENT_LIST_DIR}/cov_tool_wrapper.sh ${LCOV_OPTS})
25+
26+
find_program(GENHTML_EXECUTABLE genhtml REQUIRED)
27+
set(GENHTML_COMMAND ${GENHTML_EXECUTABLE} --show-details ${LCOV_OPTS})
28+
29+
find_program(GREP_EXECUTABLE grep REQUIRED)
30+
find_program(AWK_EXECUTABLE awk REQUIRED)
31+
32+
set(LCOV_FILTER_COMMAND ./filter-lcov.py)
33+
list(APPEND LCOV_FILTER_COMMAND -p "/usr/local/")
34+
list(APPEND LCOV_FILTER_COMMAND -p "/usr/include/")
35+
list(APPEND LCOV_FILTER_COMMAND -p "/usr/lib/")
36+
list(APPEND LCOV_FILTER_COMMAND -p "/usr/lib64/")
37+
list(APPEND LCOV_FILTER_COMMAND -p "src/leveldb/")
38+
list(APPEND LCOV_FILTER_COMMAND -p "src/crc32c/")
39+
list(APPEND LCOV_FILTER_COMMAND -p "src/bench/")
40+
list(APPEND LCOV_FILTER_COMMAND -p "src/crypto/ctaes")
41+
list(APPEND LCOV_FILTER_COMMAND -p "src/minisketch")
42+
list(APPEND LCOV_FILTER_COMMAND -p "src/secp256k1")
43+
list(APPEND LCOV_FILTER_COMMAND -p "depends")
44+
45+
execute_process(
46+
COMMAND ${LCOV_COMMAND} --capture --initial --directory src --output-file baseline.info
47+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
48+
)
49+
execute_process(
50+
COMMAND ${LCOV_FILTER_COMMAND} baseline.info baseline_filtered.info
51+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
52+
)
53+
execute_process(
54+
COMMAND ${LCOV_COMMAND} --add-tracefile baseline_filtered.info --output-file baseline_filtered.info
55+
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
56+
)

doc/developer-notes.md

+19-6
Original file line numberDiff line numberDiff line change
@@ -481,22 +481,35 @@ $ ./test/functional/test_runner.py --valgrind
481481

482482
### Compiling for test coverage
483483

484-
LCOV can be used to generate a test coverage report based upon `make check`
484+
LCOV can be used to generate a test coverage report based upon `ctest`
485485
execution. LCOV must be installed on your system (e.g. the `lcov` package
486486
on Debian/Ubuntu).
487487

488488
To enable LCOV report generation during test runs:
489489

490490
```shell
491-
./configure --enable-lcov
492-
make
493-
make cov
491+
cmake -B build -DCMAKE_BUILD_TYPE=Coverage
492+
cmake --build build
493+
cmake -P build/Coverage.cmake
494494

495-
# A coverage report will now be accessible at `./test_bitcoin.coverage/index.html`,
496-
# which covers unit tests, and `./total.coverage/index.html`, which covers
495+
# A coverage report will now be accessible at `./build/test_bitcoin.coverage/index.html`,
496+
# which covers unit tests, and `./build/total.coverage/index.html`, which covers
497497
# unit and functional tests.
498498
```
499499

500+
To enable test parallelism:
501+
```
502+
cmake -DJOBS=$(nproc) -P build/Coverage.cmake
503+
```
504+
505+
Additional LCOV options can be specified using `LCOV_OPTS`, but may be dependant
506+
on the version of LCOV. For example, when using LCOV `2.x`, branch coverage can be
507+
enabled by setting `LCOV_OPTS="--rc branch_coverage=1"`:
508+
509+
```
510+
cmake -DLCOV_OPTS="--rc branch_coverage=1" -P build/Coverage.cmake
511+
```
512+
500513
### Performance profiling with perf
501514

502515
Profiling is a good way to get a precise idea of where time is being spent in

0 commit comments

Comments
 (0)