Fix some errors in processing of code coverage data

We were using mismatched compiler and gcov, which led to either
segfaults or errors like "GCOV_TAG_COUNTER_ARCS mismatch". Add some
cmake code that tries to find the gcov that matches the compiler.

This should hopefully fix some of the mysterious missing coverage
problems that we've been experiencing for a while.
This commit is contained in:
Alexander Kuzmenkov 2023-02-13 11:29:58 +04:00
parent 9ec11d8af6
commit d00c1f3721
3 changed files with 56 additions and 21 deletions

View File

@ -65,8 +65,6 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install flex bison lcov systemd-coredump gdb libipc-run-perl \ sudo apt-get install flex bison lcov systemd-coredump gdb libipc-run-perl \
libtest-most-perl ${{ matrix.extra_packages }} libtest-most-perl ${{ matrix.extra_packages }}
# The GCC 11 gcov segfaults with the coverage info generated by clang.
sudo ln -sf $(which llvm-cov-14) $(which gcov)
- name: Install macOS Dependencies - name: Install macOS Dependencies
if: runner.os == 'macOS' if: runner.os == 'macOS'
@ -173,12 +171,19 @@ jobs:
if: matrix.coverage if: matrix.coverage
run: make -j $MAKE_JOBS -k -C build coverage run: make -j $MAKE_JOBS -k -C build coverage
- name: Upload coverage report - name: Send coverage report to Codecov.io app
if: matrix.coverage if: matrix.coverage
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v3
with: with:
file: ./build/codecov/timescaledb-codecov.info file: ./build/codecov/timescaledb-codecov.info
- name: Save LCOV coverage report
if: matrix.coverage
uses: actions/upload-artifact@v3
with:
name: LCOV coverage report ${{ matrix.os }} ${{ matrix.name }} ${{ matrix.pg }}
path: ./build/codecov/codecov-report
- name: Show regression diffs - name: Show regression diffs
if: always() if: always()
id: collectlogs id: collectlogs

View File

@ -644,6 +644,7 @@ add_custom_target(licensecheck
if(CODECOVERAGE) if(CODECOVERAGE)
add_subdirectory(codecov) add_subdirectory(codecov)
endif() endif()
if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/.git) if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/.git)
configure_file(${PROJECT_SOURCE_DIR}/scripts/githooks/commit_msg.py configure_file(${PROJECT_SOURCE_DIR}/scripts/githooks/commit_msg.py
${PROJECT_SOURCE_DIR}/.git/hooks/commit-msg COPYONLY) ${PROJECT_SOURCE_DIR}/.git/hooks/commit-msg COPYONLY)

View File

@ -5,7 +5,7 @@
# the --coverage option for the compiler and this is done in the top-level # the --coverage option for the compiler and this is done in the top-level
# CMakeLists.txt so that the option covers all build targets in the project. # CMakeLists.txt so that the option covers all build targets in the project.
# #
# Given that all dependencies (lcov, genhtml) are installed, and CMake is # Given that all dependencies (lcov, gcov, genhtml) are installed, and CMake is
# initialized with -DCODECOVERAGE=ON, it should be possible to generate a code # initialized with -DCODECOVERAGE=ON, it should be possible to generate a code
# coverage report by running: # coverage report by running:
# #
@ -13,16 +13,43 @@
# #
# The report is generated in REPORT_DIR and can be viewed in a web browser. # The report is generated in REPORT_DIR and can be viewed in a web browser.
# If we use clang, prefer the LLVM gcov. The "normal" gcov segfaults with the
# coverage info generated by clang, and the LLVM gcov with GCC-generated
# coverage gives weird errors like GCOV_TAG_COUNTER_ARCS mismatch.
set(GCOV_NAMES "gcov")
if(CMAKE_C_COMPILER_ID MATCHES "Clang|AppleClang")
string(REGEX MATCH "^[0-9]+" CMAKE_C_COMPILER_VERSION_MAJOR
${CMAKE_C_COMPILER_VERSION})
list(PREPEND GCOV_NAMES "llvm-cov-${CMAKE_C_COMPILER_VERSION_MAJOR}")
endif()
find_program(GCOV NAMES ${GCOV_NAMES})
# Find lcov for html output # Find lcov for html output
find_program(LCOV lcov) find_program(LCOV lcov)
if(LCOV) if(NOT GCOV)
message(STATUS "Install gcov to generate code coverage reports")
elseif(NOT LCOV)
message(STATUS "Install lcov to generate code coverage reports")
else()
message(STATUS "Using lcov ${LCOV}:")
execute_process(COMMAND ${LCOV} --version)
message(STATUS "Using gcov ${GCOV}:")
execute_process(COMMAND ${GCOV} --version)
# Final tracefile for code coverage # Final tracefile for code coverage
set(OUTPUT_FILE "timescaledb-codecov.info") set(OUTPUT_FILE "timescaledb-codecov.info")
# Directory where to generate the HTML report # Directory where to generate the HTML report
set(REPORT_DIR "codecov-report") set(REPORT_DIR "codecov-report")
# We can't directly use llvm-cov as --gcov-tool, because it has to be called
# like "llvm-cov gcov <gcov args>" for that. Thankfully, if its $0 is gcov, it
# will understand that we want it to operate like gcov. Just create a symlink.
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gcov
COMMAND ln -s ${GCOV} ${CMAKE_CURRENT_BINARY_DIR}/gcov)
# The baseline run needs to run before tests to learn what zero coverage looks # The baseline run needs to run before tests to learn what zero coverage looks
# like # like
add_custom_command( add_custom_command(
@ -32,12 +59,12 @@ if(LCOV)
${LCOV} --capture --initial # Initial run ${LCOV} --capture --initial # Initial run
--no-external # Do not include external source files --no-external # Do not include external source files
--base-directory ${CMAKE_SOURCE_DIR} --directory ${CMAKE_BINARY_DIR} --base-directory ${CMAKE_SOURCE_DIR} --directory ${CMAKE_BINARY_DIR}
--output-file ${OUTPUT_FILE}.base --output-file ${OUTPUT_FILE}.base --gcov-tool
DEPENDS timescaledb-tsl timescaledb timescaledb-loader) ${CMAKE_CURRENT_BINARY_DIR}/gcov
DEPENDS timescaledb-tsl timescaledb timescaledb-loader
${CMAKE_CURRENT_BINARY_DIR}/gcov)
add_custom_target( add_custom_target(coverage_base DEPENDS ${OUTPUT_FILE}.base)
coverage_base DEPENDS timescaledb-tsl timescaledb timescaledb-loader
${OUTPUT_FILE}.base)
# Ensure baseline file is generated before tests # Ensure baseline file is generated before tests
add_dependencies(installcheck coverage_base) add_dependencies(installcheck coverage_base)
@ -46,8 +73,10 @@ if(LCOV)
add_custom_command( add_custom_command(
OUTPUT ${OUTPUT_FILE}.test OUTPUT ${OUTPUT_FILE}.test
COMMENT "Generating code coverage test file" COMMENT "Generating code coverage test file"
COMMAND ${LCOV} --capture --no-external --base-directory ${CMAKE_SOURCE_DIR} COMMAND
--directory ${CMAKE_BINARY_DIR} --output-file ${OUTPUT_FILE}.test ${LCOV} --capture --no-external --base-directory ${CMAKE_SOURCE_DIR}
--directory ${CMAKE_BINARY_DIR} --output-file ${OUTPUT_FILE}.test
--gcov-tool ${CMAKE_CURRENT_BINARY_DIR}/gcov
DEPENDS ${OUTPUT_FILE}.base coverage_base) DEPENDS ${OUTPUT_FILE}.base coverage_base)
# Make sure coverage_test runs after tests (installcheck) finish # Make sure coverage_test runs after tests (installcheck) finish
@ -59,8 +88,10 @@ if(LCOV)
add_custom_command( add_custom_command(
OUTPUT ${OUTPUT_FILE} OUTPUT ${OUTPUT_FILE}
COMMENT "Generating final code coverage file" COMMENT "Generating final code coverage file"
COMMAND ${LCOV} --add-tracefile ${OUTPUT_FILE}.base --add-tracefile COMMAND
${OUTPUT_FILE}.test --output-file ${OUTPUT_FILE} ${LCOV} --add-tracefile ${OUTPUT_FILE}.base --add-tracefile
${OUTPUT_FILE}.test --output-file ${OUTPUT_FILE} --gcov-tool
${CMAKE_CURRENT_BINARY_DIR}/gcov
DEPENDS ${OUTPUT_FILE}.test coverage_test) DEPENDS ${OUTPUT_FILE}.test coverage_test)
add_custom_target(coverage_final DEPENDS ${OUTPUT_FILE}) add_custom_target(coverage_final DEPENDS ${OUTPUT_FILE})
@ -90,14 +121,12 @@ if(LCOV)
add_dependencies(coverage coverage_final) add_dependencies(coverage coverage_final)
add_custom_command( add_custom_command(
TARGET coverage COMMAND
POST_BUILD echo
COMMENT
"Open file://${CMAKE_CURRENT_BINARY_DIR}/${REPORT_DIR}/index.html in a browser to view the report" "Open file://${CMAKE_CURRENT_BINARY_DIR}/${REPORT_DIR}/index.html in a browser to view the report"
) TARGET coverage POST_BUILD
COMMENT)
else() else()
message(STATUS "Install genhtml to generate code coverage reports") message(STATUS "Install genhtml to generate code coverage reports")
endif(GENHTML) endif(GENHTML)
else() endif()
message(STATUS "Install lcov to generate code coverage reports")
endif(LCOV)