ci: switch thread-sanitizer job to gcc with instrumented deps

- build with the spack gcc toolchain like every other job: no clang++, no
  -fuse-ld=lld and no lld install step (the GNU BFD failure on tsan objects
  was specific to clang's tsan runtime)
- use gcc 15 for the freshest libtsan runtime; the asan entry stays on
  gcc 14, so the matrix now carries per-entry gcc and spack env names
- consume the tsan spack env and load the instrumented libstdc++ into each
  test's environment via FAIRMQ_TEST_LD_LIBRARY_PATH (it shares the soname
  of the compiler's own, so it substitutes process-wide at load time)
- use -fno-omit-frame-pointer for readable reports; optimization comes
  from the project's Debug -Og
- verify the wiring: assert the test environment resolves libstdc++ to the
  instrumented copy and that libzmq is tsan-instrumented, since both
  failure modes are silent (the suite still passes, with reduced race
  coverage)
This commit is contained in:
Dennis Klein
2026-06-10 16:16:30 +02:00
committed by Dennis Klein
parent add85cb18d
commit 1fb5ad8c33

View File

@@ -75,15 +75,18 @@ jobs:
matrix: matrix:
sanitizer: sanitizer:
- name: asan+lsan+ubsan - name: asan+lsan+ubsan
gcc: '14'
env: latest
options: | options: |
ENABLE_SANITIZER_ADDRESS=ON ENABLE_SANITIZER_ADDRESS=ON
ENABLE_SANITIZER_LEAK=ON ENABLE_SANITIZER_LEAK=ON
ENABLE_SANITIZER_UNDEFINED_BEHAVIOUR=ON ENABLE_SANITIZER_UNDEFINED_BEHAVIOUR=ON
cxx-flags: -O1 -fno-omit-frame-pointer cxx-flags: -O1 -fno-omit-frame-pointer
- name: tsan - name: tsan
gcc: '15'
env: tsan
options: ENABLE_SANITIZER_THREAD=ON options: ENABLE_SANITIZER_THREAD=ON
cxx-compiler: clang++ cxx-flags: -fno-omit-frame-pointer
cxx-flags: -fuse-ld=lld
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
@@ -95,7 +98,8 @@ jobs:
- name: Setup spack environment - name: Setup spack environment
uses: ./.github/actions/setup-deps uses: ./.github/actions/setup-deps
with: with:
gcc: '14' gcc: ${{ matrix.sanitizer.gcc }}
env: ${{ matrix.sanitizer.env }}
- name: ccache - name: ccache
uses: hendrikmuhs/ccache-action@v1 uses: hendrikmuhs/ccache-action@v1
@@ -103,23 +107,55 @@ jobs:
key: ${{ github.job }}-${{ matrix.sanitizer.name }} key: ${{ github.job }}-${{ matrix.sanitizer.name }}
max-size: 500M max-size: 500M
- name: Install lld - name: Locate instrumented libstdc++
if: matrix.sanitizer.name == 'tsan' if: matrix.sanitizer.name == 'tsan'
run: sudo apt-get update && sudo apt-get install -y lld shell: spack-bash {0}
# The test processes must load the tsan-instrumented libstdc++
# instead of the compiler's own (same soname, LD_LIBRARY_PATH beats
# the RUNPATH). Set per test via ctest, not at the job level: like
# any shared library built with gcc -fsanitize=thread it has
# unresolved __tsan_* symbols, so loading it into uninstrumented
# tools (cmake, ctest, ninja) would break them.
run: |
prefix=$(spack -e fairmq location -i libstdcxx-tsan)
echo "test_library_path=$prefix/lib" >> $GITHUB_ENV
- name: Configure and Build - name: Configure and Build
uses: threeal/cmake-action@v2 uses: threeal/cmake-action@v2
with: with:
generator: Ninja generator: Ninja
cxx-compiler: ${{ matrix.sanitizer.cxx-compiler }}
cxx-flags: ${{ matrix.sanitizer.cxx-flags }} cxx-flags: ${{ matrix.sanitizer.cxx-flags }}
options: | options: |
CMAKE_BUILD_TYPE=Debug CMAKE_BUILD_TYPE=Debug
BUILD_TESTING=ON BUILD_TESTING=ON
CMAKE_C_COMPILER_LAUNCHER=ccache CMAKE_C_COMPILER_LAUNCHER=ccache
CMAKE_CXX_COMPILER_LAUNCHER=ccache CMAKE_CXX_COMPILER_LAUNCHER=ccache
FAIRMQ_TEST_LD_LIBRARY_PATH=${{ env.test_library_path }}
${{ matrix.sanitizer.options }} ${{ matrix.sanitizer.options }}
- name: Verify tsan instrumentation wiring
if: matrix.sanitizer.name == 'tsan'
shell: spack-bash {0}
run: |
set -x
# the test environment must resolve libstdc++ to the instrumented copy
LD_LIBRARY_PATH=$test_library_path ldd build/test/testsuite_Channel \
| grep 'libstdc++' | tee /dev/stderr | grep -q libstdcxx-tsan
# libzmq must be instrumented
nm -D --undefined-only "$(spack -e fairmq location -i libzmq)/lib/libzmq.so" \
| grep -q __tsan_
# the instrumented libstdc++ must match the compiler release exactly,
# or binaries may reference GLIBCXX versions the runtime lacks
# (--color=never: the CI config forces SPACK_COLOR=always, which
# would wrap the version in ANSI escapes)
test "$(spack --color=never -e fairmq find --format '{version}' libstdcxx-tsan)" \
= "$(g++ -dumpfullversion)"
# the LD_LIBRARY_PATH prepend must reach the registered tests
# (via a file: grep -q quits on first match, and SIGPIPE on the
# large json output would fail the step under pipefail)
ctest --test-dir build --show-only=json-v1 > ctest-show-only.json
grep -q libstdcxx-tsan ctest-show-only.json
- name: Test - name: Test
run: | run: |
# Region/segment tests mlock() shared memory; raise the locked-memory # Region/segment tests mlock() shared memory; raise the locked-memory