diff --git a/CMake/ClangCompilers.cmake b/CMake/ClangCompilers.cmake index 643c59820..44fb4792e 100644 --- a/CMake/ClangCompilers.cmake +++ b/CMake/ClangCompilers.cmake @@ -92,3 +92,12 @@ ENDIF(QMC_BUILD_STATIC) SET(CMAKE_CXX_FLAGS "$ENV{CXX_FLAGS} ${CMAKE_CXX_FLAGS}") SET(CMAKE_C_FLAGS "$ENV{CC_FLAGS} ${CMAKE_C_FLAGS}") +# Coverage +IF (ENABLE_GCOV) + SET(GCOV_COVERAGE TRUE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") +ENDIF(ENABLE_GCOV) + diff --git a/CMake/GNUCompilers.cmake b/CMake/GNUCompilers.cmake index 47399a963..509f65a4d 100755 --- a/CMake/GNUCompilers.cmake +++ b/CMake/GNUCompilers.cmake @@ -88,3 +88,13 @@ ENDIF(QMC_BUILD_STATIC) SET(CMAKE_CXX_FLAGS "$ENV{CXX_FLAGS} ${CMAKE_CXX_FLAGS}") SET(CMAKE_C_FLAGS "$ENV{CC_FLAGS} ${CMAKE_C_FLAGS}") +# Coverage +IF (ENABLE_GCOV) + SET(GCOV_SUPPORTED TRUE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") +ENDIF(ENABLE_GCOV) + + diff --git a/CMake/ctest_script.cmake b/CMake/ctest_script.cmake index 16b684e14..aa3cb1b35 100644 --- a/CMake/ctest_script.cmake +++ b/CMake/ctest_script.cmake @@ -118,20 +118,11 @@ ELSEIF( ${CTEST_SCRIPT_ARG} STREQUAL "coverage" ) SET( CMAKE_BUILD_TYPE "Debug" ) SET( CTEST_COVERAGE_COMMAND "gcov" ) SET( ENABLE_GCOV "true" ) - SET( COVERAGE_OPTIONS - "-DCMAKE_CXX_FLAGS=--coverage" - "-DCMAKE_C_FLAGS=--coverage" - "-DCMAKE_EXE_LINKER_FLAGS=--coverage" - "-DCMAKE_SHARED_LINKER_FLAGS=--coverage" - ) SET( CTEST_BUILD_NAME "${CTEST_BUILD_NAME}-coverage" ) ELSE() MESSAGE(FATAL_ERROR "Invalid build (${CTEST_SCRIPT_ARG}): ctest -S /path/to/script,build (debug/opt/valgrind") ENDIF() -IF ( NOT COVERAGE_COMMAND ) - SET( ENABLE_GCOV "false" ) -ENDIF() # Set the number of processors @@ -244,10 +235,6 @@ IF ( QMC_OPTIONS ) SET( CTEST_OPTIONS "${CTEST_OPTIONS};${QMC_OPTIONS}" ) ENDIF() -IF ( COVERAGE_OPTIONS ) - SET( CTEST_OPTIONS "${CTEST_OPTIONS};${COVERAGE_OPTIONS}" ) -ENDIF() - MESSAGE("Configure options:") MESSAGE(" ${CTEST_OPTIONS}") diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e5c13a2e..d62009e0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,10 @@ ELSE(QMC_CUDA) MESSAGE(" Full precision = ${OHMMS_PRECISION_FULL}") ENDIF(QMC_CUDA) +# Code coverage +SET(GCOV_SUPPORTED FALSE) +SET(ENABLE_GCOV FALSE CACHE BOOL "Enable code coverage") + ###################################################################### # enable MPI and OPNEMP if possible ###################################################################### @@ -495,6 +499,12 @@ ELSE(CMAKE_TOOLCHAIN_FILE) ENDIF(CMAKE_TOOLCHAIN_FILE) +IF (ENABLE_GCOV) + IF (NOT GCOV_SUPPORTED) + MESSAGE(FATAL_ERROR "ENABLE_GCOV was specified but compiler does not support GCC coverage flag") + ENDIF() +ENDIF(ENABLE_GCOV) + # check if C++11 is needed by QMCPACK features SET(CXX11_NEEDED FALSE) IF(BUILD_LMYENGINE_INTERFACE OR BUILD_AFQMC) diff --git a/tests/coverage/clean_gcda.sh b/tests/coverage/clean_gcda.sh new file mode 100755 index 000000000..c2365441b --- /dev/null +++ b/tests/coverage/clean_gcda.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +find . -name \*.gcda | xargs -i rm {} diff --git a/tests/coverage/run_coverage.sh b/tests/coverage/run_coverage.sh new file mode 100755 index 000000000..74d8971db --- /dev/null +++ b/tests/coverage/run_coverage.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# Assumes build has been build with ENABLE_GCOV and DEBUG +# Run from build directory + +# Output: +# cov_base - coverage for the base run(s) +# cov_unit - coverage for the unit tests (the usual code coverage metric) +# cov_diff - coverage for the unit tests relative to the base run + +# Each directory contains the *.gcov files, one for each source file. +# If the 'gcovr' tool is present ( http://gcovr.com/ ), an HTML report is also +# produced in cov_detail.html + +SRC_ROOT=`pwd`/.. + +# +# Run coverage on the base +# + +echo "Running base coverage" + +ctest -L coverage + +base_dir="cov_base" +if [ ! -d $base_dir ]; then + mkdir $base_dir +fi + +cd $base_dir +find `pwd`/.. -name \*.gcda | xargs -i gcov -b -p {} + +# Filter out unwanted files +python ${SRC_ROOT}/tests/coverage/compare_gcov.py -a process --base-dir . + + +# If gcovr is present, create an html report +if hash gcovr 2>/dev/null; then + echo "Generating HTML report for base coverage" + # Arguments: + # -k - keep the *.gcov files, don't delete them + # -g - operation on existing *.gcov files (by default it would run gcov again) + # --root - otherwise all the files are filtered out + # Run w/o the --html and --html-details options to get a text summary + gcovr -k -g --root=$SRC_ROOT -o cov_detail.html --html --html-details . +fi + +cd .. + +${SRC_ROOT}/tests/coverage/clean_gcda.sh + +# +# Unit test coverage +# + +echo "Running unit test coverage" +ctest -L unit + +unit_dir="cov_unit" + +if [ ! -d $unit_dir ]; then + mkdir $unit_dir +fi + +cd $unit_dir +find `pwd`/.. -name \*.gcda | xargs -i gcov -b -p {} + +# Filter out unwanted files +python ${SRC_ROOT}/tests/coverage/compare_gcov.py -a process --base-dir . + +# If gcovr is present, create an html report +if hash gcovr 2>/dev/null; then + echo "Creating HTML report for unit tests coverage" + gcovr -k -g --root=$SRC_ROOT -o cov_detail.html --html --html-details . +fi + +cd .. + + +# +# Compute diff +# + +echo "Computing coverage diff" + + +diff_dir="cov_diff" + +if [ ! -d $diff_dir ]; then + mkdir $diff_dir +fi + +# Compute diff +python ${SRC_ROOT}/tests/coverage/compare_gcov.py -a compare --base-dir $base_dir --unit-dir $unit_dir --output $diff_dir + +cd $diff_dir + +# If gcovr is present, create an html report +if hash gcovr 2>/dev/null; then + echo "Creating HTML report for diff coverage" + gcovr -k -g --root=$SRC_ROOT -o cov_detail.html --html --html-details . +fi + +cd ..