From add85cb18dab84f4cb199c3c8e4f5ee442c1fc62 Mon Sep 17 00:00:00 2001 From: Dennis Klein Date: Wed, 10 Jun 2026 16:15:31 +0200 Subject: [PATCH] ci: add tsan spack environment with instrumented libzmq - mirror spack-latest.yaml, with -fsanitize=thread on the libzmq and libsodium nodes so tsan can observe the happens-before edges established inside libzmq's lock-free queues, plus the libstdcxx-tsan root spec - flags are applied per node instead of via the propagating '==' operator, which could reach the gcc node and trigger a compiler rebuild - unchanged roots (fairlogger, boost, ninja, cmake) keep their spec hashes, so they are shared with the regular buildcache entries; the instrumented nodes hash differently and coexist in the content-addressed cache - exclude libstdcxx-tsan from concretizer reuse so recipe changes always take effect; unchanged recipes still hit the buildcache because the spec hash is identical - add the tsan env to the buildcache matrix (rebuilding also on spack_repo changes) so the instrumented binaries are cached instead of rebuilt on every CI run --- .github/actions/setup-deps/action.yml | 2 +- .github/workflows/buildcache.yml | 3 ++ test/ci/spack-tsan.yaml | 47 +++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/ci/spack-tsan.yaml diff --git a/.github/actions/setup-deps/action.yml b/.github/actions/setup-deps/action.yml index 9c7c0eeb..298c879b 100644 --- a/.github/actions/setup-deps/action.yml +++ b/.github/actions/setup-deps/action.yml @@ -6,7 +6,7 @@ inputs: description: 'GCC version to use' required: true env: - description: 'Spack environment name (latest, boost187)' + description: 'Spack environment name (latest, boost187, tsan)' default: 'latest' fresh: description: 'Use fresh concretization' diff --git a/.github/workflows/buildcache.yml b/.github/workflows/buildcache.yml index ab7179e6..f63d38bf 100644 --- a/.github/workflows/buildcache.yml +++ b/.github/workflows/buildcache.yml @@ -8,6 +8,7 @@ on: branches: [dev, master] paths: - 'test/ci/spack-*.yaml' + - 'test/ci/spack_repo/**' - '.github/workflows/buildcache.yml' - '.github/actions/setup-deps/**' @@ -30,6 +31,8 @@ jobs: include: - gcc: '15' env: 'boost187' + - gcc: '15' + env: 'tsan' steps: - uses: actions/checkout@v6 diff --git a/test/ci/spack-tsan.yaml b/test/ci/spack-tsan.yaml new file mode 100644 index 00000000..f0f019a5 --- /dev/null +++ b/test/ci/spack-tsan.yaml @@ -0,0 +1,47 @@ +spack: + specs: + # fairlogger and boost are left uninstrumented on purpose: no tsan report + # has ever implicated them. Should that change, instrument them with + # per-node flags like libzmq below. + - "fairlogger@2.2.0 ^fmt@:11" + - "boost@1.66: +container +program_options +filesystem +date_time +regex" + # libzmq (and the libsodium it links) are built with ThreadSanitizer + # instrumentation so tsan can observe the happens-before edges that + # libzmq's lock-free queues establish between user and I/O threads. + # Flags are set per node on purpose: the propagating '==' operator could + # reach the gcc node (the compiler is a dependency since spack 1.0) and + # trigger a full compiler rebuild. + - "libzmq@4.1.4: cflags='-g -fno-omit-frame-pointer -fsanitize=thread' + cxxflags='-g -fno-omit-frame-pointer -fsanitize=thread' + ldflags='-fsanitize=thread' + ^libsodium cflags='-g -fno-omit-frame-pointer -fsanitize=thread' + ldflags='-fsanitize=thread'" + # ThreadSanitizer-instrumented libstdc++ (from test/ci/spack_repo), a + # drop-in runtime replacement injected per test via LD_LIBRARY_PATH (see + # FAIRMQ_TEST_LD_LIBRARY_PATH in test/CMakeLists.txt). + # Keep the major version in lockstep with the gcc used by the tsan job. + - "libstdcxx-tsan@15" + - ninja + # the per-test env injection uses ctest's ENVIRONMENT_MODIFICATION, + # which needs cmake >= 3.22 + - "cmake@3.22:" + + concretizer: + targets: + granularity: generic + reuse: + # always re-concretize libstdcxx-tsan from the recipe in + # test/ci/spack_repo: spec reuse would keep resurrecting previously + # built (and cached) variants after the recipe changed; unchanged + # recipes still hit the buildcache because the hash is identical + exclude: ["libstdcxx-tsan"] + + packages: + all: + require: + - target=x86_64_v3 + + mirrors: + ghcr-buildcache: + url: oci://ghcr.io/fairrootgroup/fairmq-spack-buildcache + signed: false