Enable coverage information generation and tracking

This MR enables the generation of code coverage reports for the source code. More specifically, it introduces the following changes:

  • Adds the gkfs-code-coverage.cmake CMake script under {PROJECT_ROOT}/CMake/ and includes it in the project's main CMakeLists.txt. This script defines the GKFS_ENABLE_CODE_COVERAGE option to control whether code coverage should be enabled when building, as well as the target_code_coverage() function to ensure that the appropriate flags are added to a CMake target.
  • Adds gcovr to GekkoFS docker build image.
  • Adds a coverage.sh script under {PROJECT_ROOT}/scripts/ci that provides both a capture and a merge work modes. This script abstracts away the direct usage of gcovr and allows the appropriate generation of coverage reports when running tests in parallel in the CI. The script relies on a .coverage-exclusions file (also under {PROJECT_ROOT}/scripts/ci) to control which paths should be excluded from coverage reporting (e.g. /usr/include/.*).
  • Adds a new report stage in the CI pipeline that upon completion generates artifacts both for a Cobertura XML and a HTML code coverage reports.
Edited by Alberto Miranda

Merge request reports

Loading
+82 −0
Changes for CMake/gkfs-code-coverage.cmake: 82 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Variables
option(GKFS_ENABLE_CODE_COVERAGE
  "Builds GekkoFS targets with code coverage instrumentation."
  OFF
  )

# Common initialization/checks
if(GKFS_ENABLE_CODE_COVERAGE AND NOT GKFS_CODE_COVERAGE_ADDED)

  set(GKFS_CODE_COVERAGE_ADDED ON)

  if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
     OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")

    message(STATUS "[gekkofs] Building with LLVM Code Coverage Tools")

  elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
                                              "GNU")

    message(STATUS "[gekkofs] Building with GCC Code Coverage Tools")

    if(CMAKE_BUILD_TYPE)
      string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type)
      if(NOT ${upper_build_type} STREQUAL "DEBUG")
        message(
          WARNING
            "Code coverage results with an optimized (non-Debug) build may be misleading"
        )
      endif()
    else()
      message(
        WARNING
          "Code coverage results with an optimized (non-Debug) build may be misleading"
      )
    endif()
  else()
    message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.")
  endif()
endif()

# Adds code coverage instrumentation to libraries and executable targets.
# ~~~
# Required:
# TARGET_NAME - Name of the target to generate code coverage for.
# Optional:
# PUBLIC   - Sets the visibility for added compile options to targets to PUBLIC
#            instead of the default of PRIVATE.
# PRIVATE - Sets the visibility for added compile options to targets to
#           INTERFACE instead of the default of PRIVATE.
# ~~~
function(target_code_coverage TARGET_NAME)
  # Argument parsing
  set(options PUBLIC INTERFACE)
  cmake_parse_arguments(target_code_coverage "${options}" "" "" ${ARGN})

  # Set the visibility of target functions to PUBLIC, INTERFACE or default to
  # PRIVATE.
  if(target_code_coverage_PUBLIC)
    set(TARGET_VISIBILITY PUBLIC)
  elseif(target_code_coverage_INTERFACE)
    set(TARGET_VISIBILITY INTERFACE)
  else()
    set(TARGET_VISIBILITY PRIVATE)
  endif()

  if(GKFS_ENABLE_CODE_COVERAGE)

    # Add code coverage instrumentation to the target's linker command
    if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
       OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
      target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY}
                             -fprofile-instr-generate -fcoverage-mapping)
      target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY}
                          -fprofile-instr-generate -fcoverage-mapping)
    elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
                                                "GNU")
      target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs
        -ftest-coverage)
      target_link_libraries(${TARGET_NAME} ${TARGET_VISIBILITY} gcov)
    endif()
  endif()
endfunction()
+12 −5
Changes for docker/debian_build_env.docker: 12 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -8,7 +8,8 @@ ENV SCRIPTS_PATH ${GKFS_PATH}/scripts
ENV DEPS_SRC_PATH	${GKFS_PATH}/deps_src
ENV INSTALL_PATH	${GKFS_PATH}/build_deps

RUN apt-get update && apt-get install -y --no-install-recommends \
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
		git \
		curl \
		ca-certificates \
@@ -38,8 +39,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
		valgrind \
		uuid-dev \
		python3 \
		python3-pip \
		python3-dev \
		python3-venv \
		python3-setuptools \
		expect \
		# clang 10 deps
		lsb-release \
@@ -47,13 +50,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
		software-properties-common \
		gnupg2 \
    # add clang-10 repos
&& wget https://apt.llvm.org/llvm.sh -P /tmp && chmod +x /tmp/llvm.sh && /tmp/llvm.sh 10 \
    wget https://apt.llvm.org/llvm.sh -P /tmp && chmod +x /tmp/llvm.sh && /tmp/llvm.sh 10 && \
    # install clang-format
&& apt-get update && apt-get install -y --no-install-recommends clang-format-10 \
    apt-get update && apt-get install -y --no-install-recommends clang-format-10 && \
    # install gcovr
    # (required for partial coverage reports in parallel runs)
    python3 -m pip install --upgrade pip && \
    pip3 install gcovr && \
    # Clean apt cache to reduce image layer size
&& rm -rf /var/lib/apt/lists/* && rm /tmp/llvm.sh \
    rm -rf /var/lib/apt/lists/* && rm /tmp/llvm.sh && \
    # Clean apt caches of packages
&& apt-get clean && apt-get autoclean
    apt-get clean && apt-get autoclean

## COPY scripts/dl_dep.sh		$SCRIPTS_PATH/
## COPY scripts/compile_dep.sh $SCRIPTS_PATH/
+54 −9
Changes for .gitlab-ci.yml: 54 added lines, 9 removed lines.
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ stages:
  - build deps
  - build
  - test
  - report

variables:
  DEPS_SRC_PATH:                "${CI_PROJECT_DIR}/deps/src"
@@ -60,6 +61,7 @@ compile GekkoFS:
      -Wdev
      -Wdeprecate
      -DCMAKE_BUILD_TYPE=Debug
      -DGKFS_ENABLE_CODE_COVERAGE:BOOL=ON
      -DGKFS_BUILD_TESTS:BOOL=ON
      -DGKFS_INSTALL_TESTS:BOOL=ON
      -DGKFS_ENABLE_FORWARDING:BOOL=ON
@@ -68,13 +70,18 @@ compile GekkoFS:
      -DCMAKE_INSTALL_PREFIX=${INSTALL_PATH}
      ${CI_PROJECT_DIR}
    - make -j$(nproc) install
    # the following commands are intended to reduce artifact size
    - rm -rf ${BUILD_PATH}/_deps
    - find ${BUILD_PATH} -name "*.o" -delete
    # cleanup nlohmann_json: it includes around 500MiB of test data
    - rm -rf ${BUILD_PATH}/_deps/nlohmann_json-src/test/data
    - rm -rf ${BUILD_PATH}/_deps/nlohmann_json-src/benchmarks/data
    - rm -rf ${BUILD_PATH}/_deps/nlohmann_json-src/.git
  artifacts:
    paths:
      - ${BUILD_PATH}
      - ${INSTALL_PATH}
    exclude:
      # remove object files
      - ${BUILD_PATH}/**/*.o

compile tests:
  stage: build
@@ -91,23 +98,39 @@ compile tests:
integration tests:
  stage: test
  script:
    - mkdir -p ${INTEGRATION_TESTS_RUN_PATH}
    - cd ${INTEGRATION_TESTS_BIN_PATH} 
    - TMPDIR=${INTEGRATION_TESTS_RUN_PATH} unbuffer ${PYTEST} -v | tee ${INTEGRATION_TESTS_RUN_PATH}/session.log
    ## run tests
    - cd ${BUILD_PATH}/tests/integration
    - unbuffer ${PYTEST} -v | tee session.log
    ## capture coverage information
    - cd ${BUILD_PATH}
    - ${CI_PROJECT_DIR}/scripts/ci/coverage.sh
          --capture integration
          --root-dir ${CI_PROJECT_DIR}
          --build-dir ${BUILD_PATH}
          --exclusions "${CI_PROJECT_DIR}/scripts/ci/.coverage-exclusions"
  artifacts:
    when: on_failure
    #    when: on_failure
    paths:
      - "${INTEGRATION_TESTS_RUN_PATH}"
      #      - "${INTEGRATION_TESTS_RUN_PATH}"
      - ${BUILD_PATH}

unit:
  stage: test
  script:
    ## run actual tests
    - cd ${BUILD_PATH}
    - ctest -j $(nproc) -L unit::all
    ## capture coverage information
    - ${CI_PROJECT_DIR}/scripts/ci/coverage.sh
          --capture unit
          --root-dir ${CI_PROJECT_DIR}
          --build-dir ${BUILD_PATH}
          --exclusions "${CI_PROJECT_DIR}/scripts/ci/.coverage-exclusions"
  artifacts:
    when: on_failure
    #    when: on_failure
    paths:
      - Testing
      - ${BUILD_PATH}
        #      - Testing

test wr:
  stage: test
@@ -156,3 +179,25 @@ test lseek:
    when: on_failure
    paths:
     - "${LOG_PATH}"


################################################################################
## Generation of code coverage reports
################################################################################

coverage:
  stage: report
  image: gekkofs/coverage
  script:
    - cd ${BUILD_PATH}
    ## merge the partial coverage files from each test in the pipeline
    - ${CI_PROJECT_DIR}/scripts/ci/coverage.sh
          --merge
          --root-dir ${CI_PROJECT_DIR}
          --build-dir ${BUILD_PATH}
  artifacts:
    reports:
      cobertura: ${BUILD_PATH}/.coverage/coverage-cobertura.xml
    paths:
      - ${BUILD_PATH}/.coverage
    expire_in: 2 weeks
+10 −0
Changes for CMakeLists.txt: 10 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -74,6 +74,16 @@ configure_file(include/version.hpp.in include/version.hpp)

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})


################################################################################
## Coverage generation support:
## ============================
## 
## The `gkfs-code-coverage' module enables the GKFS_ENABLE_CODE_COVERAGE option
## as well as the target_code_coverage() function.
################################################################################
include(gkfs-code-coverage)

set(CMAKE_EXPORT_COMPILE_COMMANDS 0)

# Rocksdb dependencies
+1 −0
Changes for README.md: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![pipeline status](https://storage.bsc.es/gitlab/hpc/gekkofs/badges/master/pipeline.svg)](https://storage.bsc.es/gitlab/hpc/gekkofs/commits/master)
[![coverage report](https://storage.bsc.es/gitlab/hpc/gekkofs/badges/master/coverage.svg)](https://storage.bsc.es/gitlab/hpc/gekkofs/-/commits/master)

GekkoFS is a file system capable of aggregating the local I/O capacity and performance of each compute node
in a HPC cluster to produce a high-performance storage space that can be accessed in a distributed manner.
Loading
Loading