Enabling code coverage in an external ITK module with gcov

2 minute read

This post explains how to run code coverage using gcov in an external or remote module of ITK. It only has some specific of ITK, so it can be useful for using gcov for other projects.

First, change the compile flags to report code coverage. I am only interested in running it for my module, if you want to run it for all your project, just append COVERAGE_FLAGS to CMAKE_CXX_FLAGS, and all your targets will compile with those flags.

In my case, I will modify the compile flags of the test driver of the module.

# in test/CMakeFiles.txt. External Module:(${ITK_SRC}/Modules/Remote/IsotropicWavelets)
CreateTestDriver(IsotropicWavelets "${Libraries}" "${IsotropicWaveletsTests}")

#Add coverage compile options
set(COVERAGE_FLAGS "-g -O0 -coverage -fprofile-arcs -ftest-coverage")
set_target_properties(IsotropicWaveletsTestDriver
  PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_FLAGS}"
               LINK_FLAGS "-fprofile-arcs"
                 )

Compile:

cd ${ITK_BUILD}/Modules/Remote/IsotropicWavelets; make -j5

Coverage files .gcno will be generated in the same folder than the object files cxx.o. Check it: ls test/CMakeFiles/IsotropicWaveletsTestDriver.dir/ | grep gcno

Now run your test:

I have a little bash script to run itk tests, if I want to change the test to run I just have to change the CTEST_ARGS variable, for example: export CTEST_ARGS='-R FrequencyBandPass -V'.

export CTEST_ARGS='-L IsotropicWavelet'
build_dir=~/Software/ITK/build/
function test(){make test -C $build_dir ARGS=$CTEST_ARGS}

To run all the test of my module, I export CTEST_ARGS='-L IsotropicWavelet' and call test anywhere in the terminal. This will generate .gcda files ONLY if all the test exit succesfully (no errors, no uncaught exceptions).

To visualize the data stored in those .gcda we can use lcov and genhtml to generate beatiful html reports, I am following these two posts: C++ code coverage profiling with GCC/GCOV and the post C++ Code Coverage Analysis with CMake and Jenkins.

In arch-linux, lcov is in the AUR.

export COVERAGE_PREFIX="$HOME/Software/ITK/build/Modules/Remote/IsotropicWavelets/test/CMakeFiles/IsotropicWaveletsTestDriver.dir"

You can use: lcov --directory $COVERAGE_PREFIX --zerocounters to clean the coverage data before a new run of your tests.

When the data is up-to-date, use:

lcov --directory $COVERAGE_PREFIX --capture --output-file $COVERAGE_PREFIX/app.info

This will parse all .gcda files in the directory.

Now use genhtml:

genhtml --output-directory $COVERAGE_PREFIX/coverage \
        --demangle-cpp --num-spaces 2 --sort \
        --title "Coverage" \
        --function-coverage --branch-coverage --legend \
        $COVERAGE_PREFIX/app.info

Then use your favourite browser to open the html report:

firefox $COVERAGE_PREFIX/coverage/index.html

lcov-screenshot

Well, it seems I have some work to do!

I have put these functions in a bash script coverage.sh:

#!/bin/bash
# GCOV_PREFIX and GCOV_PREFIX_STRIP are env variables for lcov to change where .gcda files are generated http://bobah.net/book/export/html/2
export COVERAGE_PREFIX="$HOME/Software/ITK/build/Modules/Remote/IsotropicWavelets/test/CMakeFiles/IsotropicWaveletsTestDriver.dir"

function lcovclean(){
    lcov --directory $COVERAGE_PREFIX --zerocounters
}

function lcovrun(){
    lcov --directory $COVERAGE_PREFIX --capture --output-file $COVERAGE_PREFIX/app.info
}

function lcovgen(){
    genhtml --output-directory $COVERAGE_PREFIX/coverage \
            --demangle-cpp --num-spaces 2 --sort \
            --title "Coverage" \
            --function-coverage --branch-coverage --legend \
            $COVERAGE_PREFIX/app.info
}

function lcovopen(){
    firefox $COVERAGE_PREFIX/coverage/index.html &
}

function cleantest(){
    lcovclean && test
}

function dolcov(){
    lcovrun && lcovgen && lcovopen
}

function lcovall(){
    cleantest && dolcov
}