From c687cfb25a8d0ba44af15e8f3ce1af4d76a89e4b Mon Sep 17 00:00:00 2001 From: Gideon Geier <43006546+ggeier@users.noreply.github.com> Date: Thu, 5 Mar 2026 11:15:56 +0100 Subject: [PATCH 1/2] Add clang coverage build target driven by tests --- CMakeLists.txt | 50 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 7 +++++++ test/CMakeLists.txt | 4 +++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d34e46bf..627a9183 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,8 @@ project( VERSION 0.1 LANGUAGES C CXX) +option(COLDTRACE_ENABLE_COVERAGE "Enable Clang coverage instrumentation" OFF) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_compile_definitions(LOG_PREFIX="coldtrace: ") @@ -21,8 +23,19 @@ endif() add_compile_options(-fPIC -g3 -D_GNU_SOURCE -DDICE_MAIN_START_DISABLE) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +if(COLDTRACE_ENABLE_COVERAGE) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) +endif() set(DICE_MEMSET ON) +if(COLDTRACE_ENABLE_COVERAGE) + if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") + message(FATAL_ERROR "COLDTRACE_ENABLE_COVERAGE requires Clang") + endif() + add_compile_options(-fprofile-instr-generate -fcoverage-mapping) + add_link_options(-fprofile-instr-generate) +endif() + add_subdirectory(deps/dice) find_package(libvsync 4.2 CONFIG) if(NOT libvsync_DIR) @@ -48,6 +61,43 @@ add_subdirectory(src) enable_testing() add_subdirectory(test) +if(COLDTRACE_ENABLE_COVERAGE) + find_program(LLVM_PROFDATA_TOOL llvm-profdata REQUIRED) + find_program(LLVM_COV_TOOL llvm-cov REQUIRED) + + get_property(COLDTRACE_TEST_TARGETS GLOBAL PROPERTY COLDTRACE_TEST_TARGETS) + set(COVERAGE_DIR ${CMAKE_CURRENT_BINARY_DIR}/coverage) + set(COVERAGE_PROFILE ${COVERAGE_DIR}/coldtrace.profraw) + set(COVERAGE_DATA ${COVERAGE_DIR}/coldtrace.profdata) + + if(NOT COLDTRACE_TEST_TARGETS) + message(FATAL_ERROR "No test targets were found for coverage generation") + endif() + list(GET COLDTRACE_TEST_TARGETS 0 COVERAGE_MAIN_TARGET) + + set(COVERAGE_OBJECTS) + foreach(TARGET ${COLDTRACE_TEST_TARGETS}) + list(APPEND COVERAGE_OBJECTS -object=$) + endforeach() + string(JOIN " " COVERAGE_OBJECTS_STR ${COVERAGE_OBJECTS}) + + add_custom_target( + coverage + COMMAND ${CMAKE_COMMAND} -E make_directory ${COVERAGE_DIR} + COMMAND ${CMAKE_COMMAND} -E env LLVM_PROFILE_FILE=${COVERAGE_PROFILE} + ${CMAKE_CTEST_COMMAND} --output-on-failure + COMMAND ${LLVM_PROFDATA_TOOL} merge -sparse ${COVERAGE_PROFILE} -o + ${COVERAGE_DATA} + COMMAND ${LLVM_COV_TOOL} report + $ + ${COVERAGE_OBJECTS} + -instr-profile=${COVERAGE_DATA} + COMMAND bash -lc "${LLVM_COV_TOOL} export -format=lcov $ ${COVERAGE_OBJECTS_STR} -instr-profile=${COVERAGE_DATA} > ${COVERAGE_DIR}/coverage.lcov" + DEPENDS ${COLDTRACE_TEST_TARGETS} + USES_TERMINAL + COMMENT "Running tests and producing a Clang coverage report") +endif() + file(GLOB_RECURSE ALL_SOURCE_FILES ${CMAKE_SOURCE_DIR}/src/*.cpp ${CMAKE_SOURCE_DIR}/src/*.c diff --git a/README.md b/README.md index 3b223b51..236fd67c 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,13 @@ See the log output (optional): scripts/trace_dump.py traces/freezer_log_1_0.bin +Run tests and generate a Clang coverage report (optional): + + cmake -S . -Bbuild-coverage -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCOLDTRACE_ENABLE_COVERAGE=ON + cmake --build build-coverage --target coverage + +Coverage output files are written to `build-coverage/coverage/`. + Run freezer analysis (optional): freezer -f traces diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 756d305a..c70a7f75 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,10 +27,12 @@ foreach(SRC ${SRCS}) target_link_options(${TARGET} PRIVATE -fsanitize=thread) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") target_compile_options(${TARGET} PRIVATE -Wno-use-after-free) - elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang") + elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT COLDTRACE_ENABLE_COVERAGE) target_link_options(${TARGET} PRIVATE -shared-libsan) endif() + set_property(GLOBAL APPEND PROPERTY COLDTRACE_TEST_TARGETS ${TARGET}) + # if this is a trace test, we also link with mock_checker and set the test # runner accordingly if(${TARGET} MATCHES "^trace_.*") From f39a72339b148eb355abede32c40ed0eaba65374 Mon Sep 17 00:00:00 2001 From: Gideon Geier <43006546+ggeier@users.noreply.github.com> Date: Thu, 5 Mar 2026 11:28:41 +0100 Subject: [PATCH 2/2] Fix clang test linking in coverage builds --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c70a7f75..63160894 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,7 +27,7 @@ foreach(SRC ${SRCS}) target_link_options(${TARGET} PRIVATE -fsanitize=thread) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") target_compile_options(${TARGET} PRIVATE -Wno-use-after-free) - elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT COLDTRACE_ENABLE_COVERAGE) + elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang") target_link_options(${TARGET} PRIVATE -shared-libsan) endif()