diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index 6a12c025f..bfa21d40f 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -11,6 +11,8 @@ env: UMF_VERSION: 0.10.0 BUILD_DIR : "${{github.workspace}}/build" INSTL_DIR : "${{github.workspace}}/../install-dir" + COVERAGE_DIR : "${{github.workspace}}/coverage" + COVERAGE_NAME : "exports-coverage-basic" jobs: ubuntu-build: @@ -122,8 +124,8 @@ jobs: - name: Install apt packages run: | sudo apt-get update - sudo apt-get install -y clang cmake libnuma-dev libjemalloc-dev - + sudo apt-get install -y clang cmake libnuma-dev libjemalloc-dev lcov + - name: Install TBB apt package if: matrix.install_tbb == 'ON' run: | @@ -167,6 +169,7 @@ jobs: -DUMF_TESTS_FAIL_ON_SKIP=ON -DUMF_DISABLE_HWLOC=${{matrix.disable_hwloc}} -DUMF_LINK_HWLOC_STATICALLY=${{matrix.link_hwloc_statically}} + ${{ matrix.build_type == 'Debug' && matrix.compiler.c == 'gcc' && '-DUMF_USE_COVERAGE=ON' || '' }} - name: Build UMF run: | @@ -177,7 +180,23 @@ jobs: working-directory: ${{env.BUILD_DIR}} run: | ${{ matrix.compiler.cxx == 'icpx' && '. /opt/intel/oneapi/setvars.sh' || true }} - ctest --output-on-failure --test-dir test + ctest --output-on-failure # run all tests for better coverage + + - name: Check coverage + if: ${{ matrix.build_type == 'Debug' && matrix.compiler.c == 'gcc' }} + working-directory: ${{env.BUILD_DIR}} + run: | + export COVERAGE_FILE_NAME=${{env.COVERAGE_NAME}}-${{matrix.os}}-shared-${{matrix.shared_library}}-no_hwloc-${{matrix.disable_hwloc}} + echo "COVERAGE_FILE_NAME: $COVERAGE_FILE_NAME" + ../scripts/coverage/coverage_capture.sh $COVERAGE_FILE_NAME + mkdir -p ${{env.COVERAGE_DIR}} + mv ./$COVERAGE_FILE_NAME ${{env.COVERAGE_DIR}} + + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + if: ${{ matrix.build_type == 'Debug' && matrix.compiler.c == 'gcc' }} + with: + name: ${{env.COVERAGE_NAME}}-${{matrix.os}}-shared-${{matrix.shared_library}}-no_hwloc-${{matrix.disable_hwloc}} + path: ${{env.COVERAGE_DIR}} - name: Remove the installation directory run: rm -rf ${{env.INSTL_DIR}} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 000000000..08816c37a --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,49 @@ +# Coverage build +name: Coverage + +on: workflow_call + +permissions: + contents: read + +env: + COVERAGE_DIR : "${{github.workspace}}/coverage" + +jobs: + Coverage: + name: Coverage build + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + + - name: Install dependencies (ubuntu-latest) + run: | + sudo apt-get update + sudo apt-get install -y lcov + + - name: Download all coverage artifacts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + pattern: exports-coverage-* + path: coverage + merge-multiple: true + + - name: Compute coverage + working-directory: ${{env.COVERAGE_DIR}} + run: | + echo "DIR: $(pwd)" && ls -al + ../scripts/coverage/merge_coverage_files.sh exports-coverage total_coverage + genhtml --no-function-coverage -o html_report total_coverage 2>&1 | tee output.txt + mkdir coverage_report + mv html_report ./coverage_report/ + tail -n2 output.txt >> $GITHUB_STEP_SUMMARY + + - name: Upload coverage report + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + with: + name: coverage_html_report + path: coverage/coverage_report diff --git a/.github/workflows/dax.yml b/.github/workflows/dax.yml index 04c36cf80..b776d3eff 100644 --- a/.github/workflows/dax.yml +++ b/.github/workflows/dax.yml @@ -28,6 +28,8 @@ env: UMF_TESTS_FSDAX_PATH: "/mnt/pmem1/file" BUILD_DIR : "${{github.workspace}}/build" INSTL_DIR : "${{github.workspace}}/../install-dir" + COVERAGE_DIR : "${{github.workspace}}/coverage" + COVERAGE_NAME : "exports-coverage-dax" jobs: dax: @@ -83,6 +85,7 @@ jobs: -DUMF_BUILD_LIBUMF_POOL_JEMALLOC=ON -DUMF_BUILD_LEVEL_ZERO_PROVIDER=OFF -DUMF_TESTS_FAIL_ON_SKIP=ON + ${{ matrix.build_type == 'Debug' && '-DUMF_USE_COVERAGE=ON' || '' }} - name: Build UMF run: cmake --build ${{env.BUILD_DIR}} --config ${{matrix.build_type}} -j $(nproc) @@ -100,3 +103,19 @@ jobs: UMF_TESTS_FSDAX_PATH=${{env.UMF_TESTS_FSDAX_PATH}} ctest -C ${{matrix.build_type}} -R umf-provider_file_memory -V UMF_TESTS_FSDAX_PATH=${{env.UMF_TESTS_FSDAX_PATH}} ctest -C ${{matrix.build_type}} -R umf_example_dram_and_fsdax -V UMF_TESTS_FSDAX_PATH=${{env.UMF_TESTS_FSDAX_PATH}} ctest -C ${{matrix.build_type}} -R umf-ipc_file_prov_fsdax -V + + - name: Check coverage + if: ${{ matrix.build_type == 'Debug' }} + working-directory: ${{env.BUILD_DIR}} + run: | + export COVERAGE_FILE_NAME=${{env.COVERAGE_NAME}}-shared-${{matrix.shared_library}} + echo "COVERAGE_FILE_NAME: $COVERAGE_FILE_NAME" + ../scripts/coverage/coverage_capture.sh $COVERAGE_FILE_NAME + mkdir -p ${{env.COVERAGE_DIR}} + mv ./$COVERAGE_FILE_NAME ${{env.COVERAGE_DIR}} + + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + if: ${{ matrix.build_type == 'Debug' }} + with: + name: ${{env.COVERAGE_NAME}}-shared-${{matrix.shared_library}} + path: ${{env.COVERAGE_DIR}} diff --git a/.github/workflows/gpu.yml b/.github/workflows/gpu.yml index 279f977b1..91e13b674 100644 --- a/.github/workflows/gpu.yml +++ b/.github/workflows/gpu.yml @@ -12,19 +12,21 @@ permissions: env: BUILD_DIR : "${{github.workspace}}/build" INSTL_DIR : "${{github.workspace}}/../install-dir" + COVERAGE_DIR : "${{github.workspace}}/coverage" jobs: gpu: name: Build env: - BUILD_TYPE: Release VCPKG_PATH: "${{github.workspace}}/../../../../vcpkg/packages/hwloc_x64-windows;${{github.workspace}}/../../../../vcpkg/packages/tbb_x64-windows;${{github.workspace}}/../../../../vcpkg/packages/jemalloc_x64-windows" + COVERAGE_NAME : "exports-coverage-gpu" # run only on upstream; forks will not have the HW if: github.repository == 'oneapi-src/unified-memory-framework' strategy: matrix: shared_library: ['ON', 'OFF'] os: ['Ubuntu', 'Windows'] + build_type: ['Debug', 'Release'] include: - os: 'Ubuntu' compiler: {c: gcc, cxx: g++} @@ -32,6 +34,9 @@ jobs: - os: 'Windows' compiler: {c: cl, cxx: cl} number_of_processors: '$Env:NUMBER_OF_PROCESSORS' + exclude: + - os: 'Windows' + build_type: 'Debug' runs-on: ["DSS-LEVEL_ZERO", "DSS-${{matrix.os}}"] steps: @@ -51,7 +56,7 @@ jobs: -DCMAKE_PREFIX_PATH="${{env.VCPKG_PATH}}" -B ${{env.BUILD_DIR}} -DCMAKE_INSTALL_PREFIX="${{env.INSTL_DIR}}" - -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_C_COMPILER=${{matrix.compiler.c}} -DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}} -DUMF_BUILD_SHARED_LIBRARY=${{matrix.shared_library}} @@ -73,7 +78,7 @@ jobs: cmake -B ${{env.BUILD_DIR}} -DCMAKE_INSTALL_PREFIX="${{env.INSTL_DIR}}" - -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_C_COMPILER=${{matrix.compiler.c}} -DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}} -DUMF_BUILD_SHARED_LIBRARY=${{matrix.shared_library}} @@ -88,31 +93,49 @@ jobs: -DUMF_BUILD_LEVEL_ZERO_PROVIDER=ON -DUMF_BUILD_CUDA_PROVIDER=OFF -DUMF_TESTS_FAIL_ON_SKIP=ON + ${{ matrix.build_type == 'Debug' && '-DUMF_USE_COVERAGE=ON' || '' }} - name: Build UMF - run: cmake --build ${{env.BUILD_DIR}} --config ${{env.BUILD_TYPE}} -j ${{matrix.number_of_processors}} + run: cmake --build ${{env.BUILD_DIR}} --config ${{matrix.build_type}} -j ${{matrix.number_of_processors}} - name: Run tests working-directory: ${{env.BUILD_DIR}} - run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure --test-dir test + run: ctest -C ${{matrix.build_type}} --output-on-failure --test-dir test - name: Run examples working-directory: ${{env.BUILD_DIR}} - run: ctest --output-on-failure --test-dir examples -C ${{env.BUILD_TYPE}} + run: ctest --output-on-failure --test-dir examples -C ${{matrix.build_type}} - name: Run benchmarks working-directory: ${{env.BUILD_DIR}} - run: ctest --output-on-failure --test-dir benchmark -C ${{env.BUILD_TYPE}} --exclude-regex umf-bench-multithreaded + run: ctest --output-on-failure --test-dir benchmark -C ${{matrix.build_type}} --exclude-regex umf-bench-multithreaded + + - name: Check coverage + if: ${{ matrix.build_type == 'Debug' && matrix.os == 'Ubuntu' }} + working-directory: ${{env.BUILD_DIR}} + run: | + export COVERAGE_FILE_NAME=${{env.COVERAGE_NAME}}-shared-${{matrix.shared_library}} + echo "COVERAGE_FILE_NAME: $COVERAGE_FILE_NAME" + ../scripts/coverage/coverage_capture.sh $COVERAGE_FILE_NAME + mkdir -p ${{env.COVERAGE_DIR}} + mv ./$COVERAGE_FILE_NAME ${{env.COVERAGE_DIR}} + + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + if: ${{ matrix.build_type == 'Debug' && matrix.os == 'Ubuntu' }} + with: + name: ${{env.COVERAGE_NAME}}-shared-${{matrix.shared_library}} + path: ${{env.COVERAGE_DIR}} gpu-CUDA: name: Build env: - BUILD_TYPE: Release + COVERAGE_NAME : "exports-coverage-gpu-CUDA" # run only on upstream; forks will not have the HW if: github.repository == 'oneapi-src/unified-memory-framework' strategy: matrix: shared_library: ['ON', 'OFF'] + build_type: ['Debug', 'Release'] # TODO add windows os: ['Ubuntu'] include: @@ -136,7 +159,7 @@ jobs: run: > cmake -B ${{env.BUILD_DIR}} -DCMAKE_INSTALL_PREFIX="${{env.INSTL_DIR}}" - -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_C_COMPILER=${{matrix.compiler.c}} -DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}} -DUMF_BUILD_SHARED_LIBRARY=${{matrix.shared_library}} @@ -151,18 +174,35 @@ jobs: -DUMF_BUILD_LEVEL_ZERO_PROVIDER=OFF -DUMF_BUILD_CUDA_PROVIDER=ON -DUMF_TESTS_FAIL_ON_SKIP=ON + ${{ matrix.build_type == 'Debug' && '-DUMF_USE_COVERAGE=ON' || '' }} - name: Build UMF - run: cmake --build ${{env.BUILD_DIR}} --config ${{env.BUILD_TYPE}} -j ${{matrix.number_of_processors}} + run: cmake --build ${{env.BUILD_DIR}} --config ${{matrix.build_type}} -j ${{matrix.number_of_processors}} - name: Run tests working-directory: ${{env.BUILD_DIR}} - run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure --test-dir test + run: ctest -C ${{matrix.build_type}} --output-on-failure --test-dir test - name: Run examples working-directory: ${{env.BUILD_DIR}} - run: ctest --output-on-failure --test-dir examples -C ${{env.BUILD_TYPE}} + run: ctest --output-on-failure --test-dir examples -C ${{matrix.build_type}} - name: Run benchmarks working-directory: ${{env.BUILD_DIR}} - run: ctest --output-on-failure --test-dir benchmark -C ${{env.BUILD_TYPE}} --exclude-regex umf-bench-multithreaded + run: ctest --output-on-failure --test-dir benchmark -C ${{matrix.build_type}} --exclude-regex umf-bench-multithreaded + + - name: Check coverage + if: ${{ matrix.build_type == 'Debug' && matrix.os == 'Ubuntu' }} + working-directory: ${{env.BUILD_DIR}} + run: | + export COVERAGE_FILE_NAME=${{env.COVERAGE_NAME}}-shared-${{matrix.shared_library}} + echo "COVERAGE_FILE_NAME: $COVERAGE_FILE_NAME" + ../scripts/coverage/coverage_capture.sh $COVERAGE_FILE_NAME + mkdir -p ${{env.COVERAGE_DIR}} + mv ./$COVERAGE_FILE_NAME ${{env.COVERAGE_DIR}} + + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + if: ${{ matrix.build_type == 'Debug' && matrix.os == 'Ubuntu' }} + with: + name: ${{env.COVERAGE_NAME}}-shared-${{matrix.shared_library}} + path: ${{env.COVERAGE_DIR}} diff --git a/.github/workflows/multi_numa.yml b/.github/workflows/multi_numa.yml index a9433018e..df00af181 100644 --- a/.github/workflows/multi_numa.yml +++ b/.github/workflows/multi_numa.yml @@ -6,6 +6,11 @@ on: [workflow_call] permissions: contents: read +env: + BUILD_DIR : "${{github.workspace}}/build" + COVERAGE_DIR : "${{github.workspace}}/coverage" + COVERAGE_NAME : "exports-coverage-multinuma" + jobs: multi_numa: name: ${{matrix.os}} @@ -40,6 +45,7 @@ jobs: -DUMF_BUILD_LIBUMF_POOL_DISJOINT=ON -DUMF_BUILD_LIBUMF_POOL_JEMALLOC=ON -DUMF_TESTS_FAIL_ON_SKIP=ON + ${{ matrix.os == 'ubuntu-22.04' && '-DUMF_USE_COVERAGE=ON' || '' }} - name: Build UMF run: cmake --build ${{github.workspace}}/build -j $(nproc) @@ -59,3 +65,19 @@ jobs: ctest --output-on-failure --test-dir test -E "umf-provider_os_memory_multiple_numa_nodes" ./test/umf_test-provider_os_memory_multiple_numa_nodes \ --gtest_filter="-*checkModeLocal/*:*checkModePreferredEmptyNodeset/*:testNuma.checkModeInterleave" + + - name: Check coverage + if: matrix.os == 'ubuntu-22.04' + working-directory: ${{env.BUILD_DIR}} + run: | + export COVERAGE_FILE_NAME=${{env.COVERAGE_NAME}}-os-${{matrix.os}} + echo "COVERAGE_FILE_NAME: $COVERAGE_FILE_NAME" + ../scripts/coverage/coverage_capture.sh $COVERAGE_FILE_NAME + mkdir -p ${{env.COVERAGE_DIR}} + mv ./$COVERAGE_FILE_NAME ${{env.COVERAGE_DIR}} + + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + if: matrix.os == 'ubuntu-22.04' + with: + name: ${{env.COVERAGE_NAME}}-os-${{matrix.os}} + path: ${{env.COVERAGE_DIR}} diff --git a/.github/workflows/pr_push.yml b/.github/workflows/pr_push.yml index c921eced6..50f8f1aa9 100644 --- a/.github/workflows/pr_push.yml +++ b/.github/workflows/pr_push.yml @@ -110,3 +110,6 @@ jobs: MultiNuma: needs: [Build] uses: ./.github/workflows/multi_numa.yml + Coverage: + needs: [Build, DevDax, GPU, MultiNuma, Qemu, ProxyLib] + uses: ./.github/workflows/coverage.yml diff --git a/.github/workflows/proxy_lib.yml b/.github/workflows/proxy_lib.yml index 581819c49..3babd205e 100644 --- a/.github/workflows/proxy_lib.yml +++ b/.github/workflows/proxy_lib.yml @@ -9,6 +9,8 @@ permissions: env: BUILD_DIR : "${{github.workspace}}/build" INSTL_DIR : "${{github.workspace}}/../install-dir" + COVERAGE_DIR : "${{github.workspace}}/coverage" + COVERAGE_NAME : "exports-coverage-proxy" jobs: proxy-ubuntu: @@ -30,7 +32,7 @@ jobs: - name: Install apt packages run: | sudo apt-get update - sudo apt-get install -y cmake libhwloc-dev libjemalloc-dev libtbb-dev + sudo apt-get install -y cmake libhwloc-dev libjemalloc-dev libtbb-dev lcov - name: Set ptrace value for IPC test run: sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope" @@ -52,6 +54,7 @@ jobs: -DUMF_BUILD_LIBUMF_POOL_DISJOINT=ON -DUMF_TESTS_FAIL_ON_SKIP=ON -DUMF_PROXY_LIB_BASED_ON_POOL=${{matrix.proxy_lib_pool}} + ${{ matrix.build_type == 'Debug' && '-DUMF_USE_COVERAGE=ON' || '' }} - name: Build UMF run: cmake --build ${{env.BUILD_DIR}} -j $(nproc) @@ -71,3 +74,19 @@ jobs: - name: Run "/usr/bin/date" with proxy library working-directory: ${{env.BUILD_DIR}} run: UMF_PROXY="page.disposition=shared-shm" LD_PRELOAD=./lib/libumf_proxy.so /usr/bin/date + + - name: Check coverage + if: ${{ matrix.build_type == 'Debug' }} + working-directory: ${{env.BUILD_DIR}} + run: | + export COVERAGE_FILE_NAME=${{env.COVERAGE_NAME}}-proxy_lib_pool-${{matrix.proxy_lib_pool}} + echo "COVERAGE_FILE_NAME: $COVERAGE_FILE_NAME" + ../scripts/coverage/coverage_capture.sh $COVERAGE_FILE_NAME + mkdir -p ${{env.COVERAGE_DIR}} + mv ./$COVERAGE_FILE_NAME ${{env.COVERAGE_DIR}} + + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + if: ${{ matrix.build_type == 'Debug' }} + with: + name: ${{env.COVERAGE_NAME}}-proxy_lib_pool-${{matrix.proxy_lib_pool}} + path: ${{env.COVERAGE_DIR}} diff --git a/.github/workflows/qemu.yml b/.github/workflows/qemu.yml index fa3089b67..7834a8b31 100644 --- a/.github/workflows/qemu.yml +++ b/.github/workflows/qemu.yml @@ -86,7 +86,7 @@ jobs: rsync -az -e "ssh -p 2222" ${{github.workspace}}/umf/ testuser@127.0.0.1:/home/testuser/ ssh testuser@127.0.0.1 -p 2222 -t "sudo chown -R testuser:users /home/testuser" - ssh testuser@127.0.0.1 -p 2222 -t "bash /home/testuser/scripts/qemu/run-build.sh" + ssh testuser@127.0.0.1 -p 2222 -t "bash /home/testuser/scripts/qemu/run-build.sh COVERAGE" ssh testuser@127.0.0.1 -p 2222 -t "sudo shutdown -h now" - name: Run tests in QEMU @@ -102,6 +102,13 @@ jobs: echo "\n ### Testing ${config_name} ###" umf/scripts/qemu/start_qemu.sh ${config_name} - ssh testuser@127.0.0.1 -p 2222 -t "bash /home/testuser/scripts/qemu/run-tests.sh" + ssh testuser@127.0.0.1 -p 2222 -t "bash /home/testuser/scripts/qemu/run-tests.sh COVERAGE ${config_name}" + scp -r -P 2222 testuser@127.0.0.1:/home/testuser/coverage ./ ssh testuser@127.0.0.1 -p 2222 -t "sudo shutdown -h now" done + ls -al ./coverage + + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + with: + name: exports-coverage-qemu-all + path: coverage diff --git a/CMakeLists.txt b/CMakeLists.txt index 280bfd218..42eeb50eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ option(UMF_USE_UBSAN "Enable UndefinedBehaviorSanitizer checks" OFF) option(UMF_USE_TSAN "Enable ThreadSanitizer checks" OFF) option(UMF_USE_MSAN "Enable MemorySanitizer checks" OFF) option(UMF_USE_VALGRIND "Enable Valgrind instrumentation" OFF) -option(UMF_USE_GCOV "Enable gcov support" OFF) +option(UMF_USE_COVERAGE "Build with coverage enabled (Linux only)" OFF) # set UMF_PROXY_LIB_BASED_ON_POOL to one of: SCALABLE or JEMALLOC set(KNOWN_PROXY_LIB_POOLS SCALABLE JEMALLOC) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48eb62579..cd4a2a790 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -209,12 +209,12 @@ origin: https://dependency_origin.com ## Code coverage After adding a new functionality add tests and check coverage before and after the change. -To do this, enable coverage instrumentation by turning on the UMF_USE_GCOV flag in CMake. +To do this, enable coverage instrumentation by turning on the UMF_USE_COVERAGE flag in CMake. Coverage instrumentation feature is supported only by GCC and Clang. An example flow might look like the following: ```bash -$ cmake -B build -DUMF_USE_GCOV=1 -DCMAKE_BUILD_TYPE=Debug +$ cmake -B build -DUMF_USE_COVERAGE=1 -DCMAKE_BUILD_TYPE=Debug $ cmake --build build -j $ cd build $ ctest diff --git a/README.md b/README.md index 0d2587f4e..2c4146dc6 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ List of options provided by CMake: | UMF_USE_TSAN | Enable ThreadSanitizer checks | ON/OFF | OFF | | UMF_USE_MSAN | Enable MemorySanitizer checks | ON/OFF | OFF | | UMF_USE_VALGRIND | Enable Valgrind instrumentation | ON/OFF | OFF | -| UMF_USE_GCOV | Enable gcov support (Linux only) | ON/OFF | OFF | +| UMF_USE_COVERAGE | Build with coverage enabled (Linux only) | ON/OFF | OFF | | UMF_LINK_HWLOC_STATICALLY | Link UMF with HWLOC library statically (Windows+Release only) | ON/OFF | OFF | | UMF_DISABLE_HWLOC | Disable features that requires hwloc (OS provider, memory targets, topology discovery) | ON/OFF | OFF | diff --git a/cmake/helpers.cmake b/cmake/helpers.cmake index a101c8615..1372531a0 100644 --- a/cmake/helpers.cmake +++ b/cmake/helpers.cmake @@ -247,11 +247,21 @@ function(add_umf_target_compile_options name) target_compile_options(${name} PRIVATE -fno-omit-frame-pointer -fstack-protector-strong) endif() - if(UMF_USE_GCOV) + if(UMF_USE_COVERAGE) if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - message(FATAL_ERROR "To use gcov, the build type must be Debug") + message( + FATAL_ERROR + "To use the --coverage flag, the build type must be Debug" + ) endif() target_compile_options(${name} PRIVATE --coverage) + if(${CMAKE_C_COMPILER} MATCHES "gcc") + # Fix for the following error: geninfo: ERROR: Unexpected + # negative count '-1' for provider_os_memory.c:1037. Perhaps you + # need to compile with '-fprofile-update=atomic + target_compile_options(${name} PRIVATE -fprofile-update=atomic + -g -O0) + endif() endif() elseif(MSVC) target_compile_options( @@ -283,10 +293,12 @@ function(add_umf_target_link_options name) if(NOT MSVC) if(NOT APPLE) target_link_options(${name} PRIVATE "LINKER:-z,relro,-z,now") - if(UMF_USE_GCOV) + if(UMF_USE_COVERAGE) if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") message( - FATAL_ERROR "To use gcov, the build type must be Debug") + FATAL_ERROR + "To use the --coverage flag, the build type must be Debug" + ) endif() target_link_options(${name} PRIVATE --coverage) endif() diff --git a/scripts/coverage/coverage_capture.sh b/scripts/coverage/coverage_capture.sh new file mode 100755 index 000000000..c77f1b141 --- /dev/null +++ b/scripts/coverage/coverage_capture.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# Copyright (C) 2024 Intel Corporation +# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# This script calculates coverage for a single build + +set -e + +[ "$1" != "" ] && OUTPUT_NAME="$1" || OUTPUT_NAME="output_coverage" + +set -x + +lcov --capture --directory . \ +--exclude "/usr/*" \ +--exclude "*/build/*" \ +--exclude "*/benchmark/*" \ +--exclude "*/examples/*" \ +--exclude "*/test/*" \ +--exclude "*/src/critnib/*" \ +--exclude "*/src/ravl/*" \ +--exclude "*proxy_lib_new_delete.h" \ +--output-file $OUTPUT_NAME || \ + ( echo "RETRY after ERROR !!!:" && \ + lcov --capture --directory . \ + --exclude "/usr/*" \ + --exclude "*/build/*" \ + --exclude "*/benchmark/*" \ + --exclude "*/examples/*" \ + --exclude "*/test/*" \ + --exclude "*/src/critnib/*" \ + --exclude "*/src/ravl/*" \ + --exclude "*proxy_lib_new_delete.h" \ + --ignore-errors mismatch,unused,negative,corrupt \ + --output-file $OUTPUT_NAME ) + +# Most common UMF source code directory on most GH CI runners +COMMON_UMF_DIR=/home/runner/work/unified-memory-framework/unified-memory-framework + +# Get the current UMF source code directory +# This is ${CURRENT_UMF_DIR}/scripts/coverage/coverage_capture.sh file, so +CURRENT_UMF_DIR=$(realpath $(dirname $0)/../..) + +# Coverage (lcov) has to be run in the same directory on all runners: +# /home/runner/work/unified-memory-framework/unified-memory-framework/build +# to be able to merge all results, so we have to replace the paths if they are different: +if [ "$CURRENT_UMF_DIR" != "$COMMON_UMF_DIR" ]; then + sed -i "s|$CURRENT_UMF_DIR|$COMMON_UMF_DIR|g" $OUTPUT_NAME +fi diff --git a/scripts/coverage/merge_coverage_files.sh b/scripts/coverage/merge_coverage_files.sh new file mode 100755 index 000000000..193600556 --- /dev/null +++ b/scripts/coverage/merge_coverage_files.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Copyright (C) 2024 Intel Corporation +# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# +# Arguments: +# +# This script looks for "${PREFIX}-*" lcov output files in the current directory, +# merges them and saves the merged output in the $OUTPUT_NAME file. +# + +[ "$1" != "" ] && PREFIX="$1" || PREFIX="exports-coverage" +[ "$2" != "" ] && OUTPUT_NAME="$2" || OUTPUT_NAME="total_coverage" + +OPTS="" +for file in $(ls -1 ${PREFIX}-*); do + OPTS="$OPTS -a $file" +done + +set -x + +lcov $OPTS -o $OUTPUT_NAME || \ + ( echo "RETRY after ERROR !!!:" && \ + lcov $OPTS \ + --ignore-errors mismatch,unused,negative,corrupt \ + --output-file $OUTPUT_NAME ) diff --git a/scripts/qemu/run-build.sh b/scripts/qemu/run-build.sh index 666bd2200..06d6043f6 100755 --- a/scripts/qemu/run-build.sh +++ b/scripts/qemu/run-build.sh @@ -5,9 +5,16 @@ set -e +[ "$1" = "COVERAGE" ] && COVERAGE=ON || COVERAGE=OFF + +# This is ${UMF_DIR}/scripts/qemu/run-build.sh file, so +UMF_DIR=$(dirname $0)/../.. +cd $UMF_DIR +pwd + echo password | sudo -Sk apt-get update echo password | sudo -Sk apt-get install -y git cmake gcc g++ pkg-config \ - numactl libnuma-dev hwloc libhwloc-dev libjemalloc-dev libtbb-dev valgrind + numactl libnuma-dev hwloc libhwloc-dev libjemalloc-dev libtbb-dev valgrind lcov mkdir build cd build @@ -21,6 +28,7 @@ cmake .. \ -DUMF_BUILD_LIBUMF_POOL_DISJOINT=ON \ -DUMF_BUILD_LIBUMF_POOL_JEMALLOC=ON \ -DUMF_BUILD_EXAMPLES=ON \ + -DUMF_USE_COVERAGE=${COVERAGE} \ -DUMF_TESTS_FAIL_ON_SKIP=ON make -j $(nproc) diff --git a/scripts/qemu/run-tests.sh b/scripts/qemu/run-tests.sh index 2faa68831..69f187990 100755 --- a/scripts/qemu/run-tests.sh +++ b/scripts/qemu/run-tests.sh @@ -5,6 +5,18 @@ set -e +COVERAGE=$1 +XML_CONFIG_FILE=$2 + +CONFIG_NAME=$(echo $XML_CONFIG_FILE | cut -d. -f1) # remove the '.xml' extension +COVERAGE_DIR=${HOME}/coverage +mkdir -p $COVERAGE_DIR + +# This is ${UMF_DIR}/scripts/qemu/run-build.sh file, so +UMF_DIR=$(dirname $0)/../.. +cd $UMF_DIR +UMF_DIR=$(pwd) + # Drop caches, restores free memory on NUMA nodes echo password | sudo sync; echo password | sudo sh -c "/usr/bin/echo 3 > /proc/sys/vm/drop_caches" @@ -20,6 +32,13 @@ ctest --verbose numactl -N 0 ctest --output-on-failure numactl -N 1 ctest --output-on-failure +if [ "$COVERAGE" = "COVERAGE" ]; then + COVERAGE_FILE_NAME=exports-coverage-qemu-$CONFIG_NAME + echo "COVERAGE_FILE_NAME: $COVERAGE_FILE_NAME" + ../scripts/coverage/coverage_capture.sh $COVERAGE_FILE_NAME + mv ./$COVERAGE_FILE_NAME $COVERAGE_DIR +fi + # run tests under valgrind echo "Running tests under valgrind memcheck ..." ../test/test_valgrind.sh .. . memcheck diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3020094b9..8a03fd195 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -498,7 +498,8 @@ if(LINUX (UMF_USE_ASAN OR UMF_USE_UBSAN OR UMF_USE_TSAN - OR UMF_USE_MSAN)) + OR UMF_USE_MSAN + OR UMF_USE_COVERAGE)) set(EXAMPLES "")