@@ -19,10 +19,13 @@ concurrency:
1919env :
2020 BITCOIN_REPO : bitcoin/bitcoin
2121 LLVM_VERSION : 22
22+ LIBCXX_DIR : /tmp/libcxx-build/
2223 ASAN_UBSAN_UNIT_TEST_RUNS : 1
2324 ASAN_UBSAN_FUNCTIONAL_TEST_RUNS : 1
2425 MACOS_UNIT_TEST_RUNS : 1
2526 MACOS_FUNCTIONAL_TEST_RUNS : 1
27+ TSAN_UNIT_TEST_RUNS : 1
28+ TSAN_FUNCTIONAL_TEST_RUNS : 1
2629
2730jobs :
2831 bitcoin-core :
@@ -190,3 +193,209 @@ jobs:
190193 with :
191194 path : ${{ env.CCACHE_DIR }}
192195 key : ccache-${{ matrix.name }}-${{ github.ref }}-${{ github.sha }}
196+
197+ bitcoin-core-tsan :
198+ name : TSan
199+ runs-on : ubuntu-24.04
200+ timeout-minutes : 180
201+
202+ env :
203+ CCACHE_MAXSIZE : 400M
204+ CCACHE_DIR : ${{ github.workspace }}/.ccache
205+ LIBCXX_FLAGS : >-
206+ -fsanitize=thread
207+ -nostdinc++
208+ -nostdlib++
209+ -isystem /tmp/libcxx-build/include/c++/v1
210+ -L/tmp/libcxx-build/lib
211+ -Wl,-rpath,/tmp/libcxx-build/lib
212+ -lc++
213+ -lc++abi
214+ -lpthread
215+ -Wno-unused-command-line-argument
216+ TSAN_OPTIONS : suppressions=${{ github.workspace }}/test/sanitizer_suppressions/tsan:halt_on_error=1:second_deadlock_stack=1
217+
218+ steps :
219+ - name : Checkout Bitcoin Core
220+ uses : actions/checkout@v4
221+ with :
222+ repository : ${{ env.BITCOIN_REPO }}
223+ fetch-depth : 1
224+
225+ - name : Add LLVM apt repository
226+ run : |
227+ curl -s "https://apt.llvm.org/llvm-snapshot.gpg.key" | sudo tee "/etc/apt/trusted.gpg.d/apt.llvm.org.asc" > /dev/null
228+ source /etc/os-release
229+ echo "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-${LLVM_VERSION} main" | sudo tee "/etc/apt/sources.list.d/llvm.list"
230+ sudo apt-get update
231+
232+ - name : Install packages
233+ run : |
234+ sudo apt-get install --no-install-recommends -y \
235+ ccache \
236+ "clang-${LLVM_VERSION}" \
237+ "llvm-${LLVM_VERSION}" \
238+ "llvm-${LLVM_VERSION}-dev" \
239+ "libclang-${LLVM_VERSION}-dev" \
240+ "libclang-rt-${LLVM_VERSION}-dev" \
241+ ninja-build \
242+ pkgconf \
243+ python3-pip \
244+ bison
245+ sudo update-alternatives --install /usr/bin/clang++ clang++ "/usr/bin/clang++-${LLVM_VERSION}" 100
246+ sudo update-alternatives --install /usr/bin/clang clang "/usr/bin/clang-${LLVM_VERSION}" 100
247+ sudo update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer "/usr/bin/llvm-symbolizer-${LLVM_VERSION}" 100
248+ sudo update-alternatives --set clang "/usr/bin/clang-${LLVM_VERSION}"
249+ sudo update-alternatives --set clang++ "/usr/bin/clang++-${LLVM_VERSION}"
250+ sudo update-alternatives --set llvm-symbolizer "/usr/bin/llvm-symbolizer-${LLVM_VERSION}"
251+ pip3 install --break-system-packages pycapnp
252+
253+ - name : Restore instrumented libc++ cache
254+ id : libcxx-cache
255+ uses : actions/cache@v4
256+ with :
257+ path : ${{ env.LIBCXX_DIR }}
258+ key : libcxx-Thread-llvmorg-${{ env.LLVM_VERSION }}.1.0
259+
260+ - name : Build instrumented libc++
261+ if : steps.libcxx-cache.outputs.cache-hit != 'true'
262+ run : |
263+ export PATH="/usr/lib/llvm-${LLVM_VERSION}/bin:$PATH"
264+ ls -l /usr/bin/clang /usr/bin/clang++ /usr/bin/llvm-symbolizer
265+ which clang clang++ llvm-symbolizer
266+ clang --version
267+ clang++ --version
268+ "/usr/bin/clang-${LLVM_VERSION}" --version
269+ "/usr/bin/clang++-${LLVM_VERSION}" --version
270+ git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-${LLVM_VERSION}.1.0" /tmp/llvm-project
271+ cmake -G Ninja -B "$LIBCXX_DIR" \
272+ -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
273+ -DCMAKE_BUILD_TYPE=Release \
274+ -DLLVM_USE_SANITIZER=Thread \
275+ -DCMAKE_C_COMPILER="/usr/bin/clang-${LLVM_VERSION}" \
276+ -DCMAKE_CXX_COMPILER="/usr/bin/clang++-${LLVM_VERSION}" \
277+ -DLLVM_TARGETS_TO_BUILD=Native \
278+ -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF \
279+ -DLIBCXX_INCLUDE_TESTS=OFF \
280+ -DLIBCXXABI_INCLUDE_TESTS=OFF \
281+ -DLIBUNWIND_INCLUDE_TESTS=OFF \
282+ -DLIBCXXABI_USE_LLVM_UNWINDER=OFF \
283+ -S /tmp/llvm-project/runtimes
284+ grep -E 'CMAKE_(C|CXX)_COMPILER' "$LIBCXX_DIR/CMakeCache.txt"
285+ ninja -C "$LIBCXX_DIR" "-j$(nproc)" -v
286+ rm -rf /tmp/llvm-project
287+
288+ - name : Determine host
289+ id : host
290+ run : echo "host=$(./depends/config.guess)" >> "$GITHUB_OUTPUT"
291+
292+ - name : Restore depends cache
293+ id : depends-cache
294+ uses : actions/cache/restore@v4
295+ with :
296+ path : |
297+ depends/built
298+ depends/${{ steps.host.outputs.host }}
299+ key : depends-tsan-${{ hashFiles('depends/packages/*.mk') }}-${{ env.LLVM_VERSION }}
300+
301+ - name : Build depends (stage 1, without IPC)
302+ if : steps.depends-cache.outputs.cache-hit != 'true'
303+ run : |
304+ make -C depends "-j$(nproc)" \
305+ CC=clang \
306+ CXX=clang++ \
307+ CXXFLAGS="${LIBCXX_FLAGS}" \
308+ NO_QT=1 \
309+ NO_ZMQ=1 \
310+ NO_USDT=1 \
311+ NO_QR=1 \
312+ NO_IPC=1
313+
314+ - name : Save depends cache
315+ uses : actions/cache/save@v4
316+ if : steps.depends-cache.outputs.cache-hit != 'true'
317+ with :
318+ path : |
319+ depends/built
320+ depends/${{ steps.host.outputs.host }}
321+ key : depends-tsan-${{ hashFiles('depends/packages/*.mk') }}-${{ env.LLVM_VERSION }}
322+
323+ - name : Checkout libmultiprocess
324+ uses : actions/checkout@v4
325+ with :
326+ path : _libmultiprocess
327+
328+ - name : Replace libmultiprocess subtree
329+ run : |
330+ rm -rf src/ipc/libmultiprocess
331+ mv _libmultiprocess src/ipc/libmultiprocess
332+
333+ - name : Build depends (stage 2, IPC packages including libmultiprocess)
334+ run : |
335+ make -C depends "-j$(nproc)" \
336+ CC=clang \
337+ CXX=clang++ \
338+ CXXFLAGS="${LIBCXX_FLAGS}" \
339+ NO_QT=1 \
340+ NO_ZMQ=1 \
341+ NO_USDT=1 \
342+ NO_QR=1
343+
344+ - name : Restore ccache
345+ id : ccache-restore
346+ uses : actions/cache/restore@v4
347+ with :
348+ path : ${{ env.CCACHE_DIR }}
349+ key : ccache-TSan-${{ github.ref }}-${{ github.sha }}
350+ restore-keys : |
351+ ccache-TSan-${{ github.ref }}-
352+ ccache-TSan-
353+
354+ - name : Reset ccache stats
355+ run : |
356+ which ccache
357+ ccache --version
358+ ccache --zero-stats
359+
360+ - name : CMake configure
361+ run : |
362+ cmake -S . -B build \
363+ --preset=dev-mode \
364+ -DCMAKE_BUILD_TYPE=Debug \
365+ -DBUILD_GUI=OFF \
366+ -DBUILD_GUI_TESTS=OFF \
367+ -DWITH_ZMQ=OFF \
368+ -DWITH_USDT=OFF \
369+ -DBUILD_BENCH=OFF \
370+ -DBUILD_FUZZ_BINARY=OFF \
371+ -DWITH_QRENCODE=OFF \
372+ -DSANITIZERS=thread \
373+ -DAPPEND_CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKCONTENTION -D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES' \
374+ -DCMAKE_TOOLCHAIN_FILE=depends/${{ steps.host.outputs.host }}/toolchain.cmake \
375+ -G Ninja
376+
377+ - name : Build
378+ run : cmake --build build "-j$(nproc)"
379+
380+ - name : Show ccache stats
381+ run : ccache --show-stats
382+
383+ - name : Run IPC and miner unit tests
384+ run : |
385+ ctest --test-dir build --tests-regex "ipc|miner_tests" --output-on-failure --timeout 480 --repeat "until-fail:${TSAN_UNIT_TEST_RUNS}"
386+
387+ - name : Run IPC functional tests
388+ run : |
389+ tests=()
390+ for _ in $(seq 1 "${TSAN_FUNCTIONAL_TEST_RUNS}"); do
391+ tests+=(interface_ipc)
392+ done
393+ LD_LIBRARY_PATH="depends/${{ steps.host.outputs.host }}/lib" \
394+ build/test/functional/test_runner.py -j "$(nproc)" "${tests[@]}" --timeout-factor=10
395+
396+ - name : Save ccache
397+ uses : actions/cache/save@v4
398+ if : github.ref == 'refs/heads/master' || steps.ccache-restore.outputs.cache-hit != 'true'
399+ with :
400+ path : ${{ env.CCACHE_DIR }}
401+ key : ccache-TSan-${{ github.ref }}-${{ github.sha }}
0 commit comments