Skip to content

Commit fc49df9

Browse files
committed
ci: add TSan job with instrumented libc++
Add a ThreadSanitizer configuration to the Bitcoin Core CI workflow, using instrumented libc++ (matching Bitcoin Core's own TSan CI setup). Uses Bitcoin Core's depends build system to compile all dependencies (capnproto, boost, libevent, sqlite) with the instrumented libc++ for correct ABI compatibility. Both the instrumented libc++ build and the depends output are cached via GitHub Actions cache for faster subsequent runs.
1 parent 66aebeb commit fc49df9

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed

.github/workflows/bitcoin-core-ci.yml

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ concurrency:
1919
env:
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

2730
jobs:
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

Comments
 (0)