Skip to content

Commit 50a7f05

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 3115b09 commit 50a7f05

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed

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

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ concurrency:
1919
env:
2020
BITCOIN_REPO: bitcoin/bitcoin
2121
LLVM_VERSION: 22
22+
LIBCXX_DIR: /tmp/libcxx-build/
2223

2324
jobs:
2425
bitcoin-core:
@@ -160,3 +161,184 @@ jobs:
160161
with:
161162
path: ${{ env.CCACHE_DIR }}
162163
key: ccache-${{ matrix.name }}-${{ github.ref }}-${{ github.sha }}
164+
165+
bitcoin-core-tsan:
166+
name: TSan
167+
runs-on: ubuntu-24.04
168+
timeout-minutes: 180
169+
170+
env:
171+
CCACHE_MAXSIZE: 400M
172+
CCACHE_DIR: ${{ github.workspace }}/.ccache
173+
LIBCXX_FLAGS: >-
174+
-fsanitize=thread
175+
-nostdinc++
176+
-nostdlib++
177+
-isystem /tmp/libcxx-build/include/c++/v1
178+
-L/tmp/libcxx-build/lib
179+
-Wl,-rpath,/tmp/libcxx-build/lib
180+
-lc++
181+
-lc++abi
182+
-lpthread
183+
-Wno-unused-command-line-argument
184+
TSAN_OPTIONS: suppressions=${{ github.workspace }}/test/sanitizer_suppressions/tsan:halt_on_error=1:second_deadlock_stack=1
185+
186+
steps:
187+
- name: Checkout Bitcoin Core
188+
uses: actions/checkout@v4
189+
with:
190+
repository: ${{ env.BITCOIN_REPO }}
191+
fetch-depth: 1
192+
193+
- name: Add LLVM apt repository
194+
run: |
195+
curl -s "https://apt.llvm.org/llvm-snapshot.gpg.key" | sudo tee "/etc/apt/trusted.gpg.d/apt.llvm.org.asc" > /dev/null
196+
source /etc/os-release
197+
echo "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-${LLVM_VERSION} main" | sudo tee "/etc/apt/sources.list.d/llvm.list"
198+
sudo apt-get update
199+
200+
- name: Install packages
201+
run: |
202+
sudo apt-get install --no-install-recommends -y \
203+
"clang-${LLVM_VERSION}" \
204+
"llvm-${LLVM_VERSION}" \
205+
"llvm-${LLVM_VERSION}-dev" \
206+
"libclang-${LLVM_VERSION}-dev" \
207+
"libclang-rt-${LLVM_VERSION}-dev" \
208+
ninja-build \
209+
pkgconf \
210+
python3-pip \
211+
bison
212+
sudo update-alternatives --install /usr/bin/clang++ clang++ "/usr/bin/clang++-${LLVM_VERSION}" 100
213+
sudo update-alternatives --install /usr/bin/clang clang "/usr/bin/clang-${LLVM_VERSION}" 100
214+
sudo update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer "/usr/bin/llvm-symbolizer-${LLVM_VERSION}" 100
215+
pip3 install --break-system-packages pycapnp
216+
217+
- name: Restore instrumented libc++ cache
218+
id: libcxx-cache
219+
uses: actions/cache@v4
220+
with:
221+
path: ${{ env.LIBCXX_DIR }}
222+
key: libcxx-Thread-llvmorg-${{ env.LLVM_VERSION }}.1.0
223+
224+
- name: Build instrumented libc++
225+
if: steps.libcxx-cache.outputs.cache-hit != 'true'
226+
run: |
227+
git clone --depth=1 https://github.com/llvm/llvm-project -b "llvmorg-${LLVM_VERSION}.1.0" /tmp/llvm-project
228+
cmake -G Ninja -B "$LIBCXX_DIR" \
229+
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
230+
-DCMAKE_BUILD_TYPE=Release \
231+
-DLLVM_USE_SANITIZER=Thread \
232+
-DCMAKE_C_COMPILER=clang \
233+
-DCMAKE_CXX_COMPILER=clang++ \
234+
-DLLVM_TARGETS_TO_BUILD=Native \
235+
-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF \
236+
-DLIBCXX_INCLUDE_TESTS=OFF \
237+
-DLIBCXXABI_INCLUDE_TESTS=OFF \
238+
-DLIBUNWIND_INCLUDE_TESTS=OFF \
239+
-DLIBCXXABI_USE_LLVM_UNWINDER=OFF \
240+
-S /tmp/llvm-project/runtimes
241+
ninja -C "$LIBCXX_DIR" "-j$(nproc)"
242+
rm -rf /tmp/llvm-project
243+
244+
- name: Determine host
245+
id: host
246+
run: echo "host=$(./depends/config.guess)" >> "$GITHUB_OUTPUT"
247+
248+
- name: Restore depends cache
249+
id: depends-cache
250+
uses: actions/cache/restore@v4
251+
with:
252+
path: |
253+
depends/built
254+
depends/${{ steps.host.outputs.host }}
255+
key: depends-tsan-${{ hashFiles('depends/packages/*.mk') }}-${{ env.LLVM_VERSION }}
256+
257+
- name: Build depends (stage 1, without IPC)
258+
if: steps.depends-cache.outputs.cache-hit != 'true'
259+
run: |
260+
make -C depends "-j$(nproc)" \
261+
CC=clang \
262+
CXX=clang++ \
263+
CXXFLAGS="${LIBCXX_FLAGS}" \
264+
NO_QT=1 \
265+
NO_ZMQ=1 \
266+
NO_USDT=1 \
267+
NO_QR=1 \
268+
NO_IPC=1
269+
270+
- name: Save depends cache
271+
uses: actions/cache/save@v4
272+
if: steps.depends-cache.outputs.cache-hit != 'true'
273+
with:
274+
path: |
275+
depends/built
276+
depends/${{ steps.host.outputs.host }}
277+
key: depends-tsan-${{ hashFiles('depends/packages/*.mk') }}-${{ env.LLVM_VERSION }}
278+
279+
- name: Checkout libmultiprocess
280+
uses: actions/checkout@v4
281+
with:
282+
path: _libmultiprocess
283+
284+
- name: Replace libmultiprocess subtree
285+
run: |
286+
rm -rf src/ipc/libmultiprocess
287+
mv _libmultiprocess src/ipc/libmultiprocess
288+
289+
- name: Build depends (stage 2, IPC packages including libmultiprocess)
290+
run: |
291+
make -C depends "-j$(nproc)" \
292+
CC=clang \
293+
CXX=clang++ \
294+
CXXFLAGS="${LIBCXX_FLAGS}" \
295+
NO_QT=1 \
296+
NO_ZMQ=1 \
297+
NO_USDT=1 \
298+
NO_QR=1
299+
300+
- name: Restore ccache
301+
id: ccache-restore
302+
uses: actions/cache/restore@v4
303+
with:
304+
path: ${{ env.CCACHE_DIR }}
305+
key: ccache-TSan-${{ github.ref }}-${{ github.sha }}
306+
restore-keys: |
307+
ccache-TSan-${{ github.ref }}-
308+
ccache-TSan-
309+
310+
- name: CMake configure
311+
run: |
312+
cmake -S . -B build \
313+
--preset=dev-mode \
314+
-DCMAKE_BUILD_TYPE=Debug \
315+
-DBUILD_GUI=OFF \
316+
-DBUILD_GUI_TESTS=OFF \
317+
-DWITH_ZMQ=OFF \
318+
-DWITH_USDT=OFF \
319+
-DBUILD_BENCH=OFF \
320+
-DBUILD_FUZZ_BINARY=OFF \
321+
-DWITH_QRENCODE=OFF \
322+
-DSANITIZERS=thread \
323+
-DAPPEND_CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKCONTENTION -D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES' \
324+
-DCMAKE_TOOLCHAIN_FILE=depends/${{ steps.host.outputs.host }}/toolchain.cmake \
325+
-G Ninja
326+
327+
- name: Build
328+
run: cmake --build build "-j$(nproc)"
329+
330+
- name: Run IPC and miner unit tests
331+
run: |
332+
ctest --test-dir build -R "ipc|miner_tests" --output-on-failure --timeout 480
333+
334+
- name: Run IPC functional tests
335+
run: |
336+
LD_LIBRARY_PATH="depends/${{ steps.host.outputs.host }}/lib" \
337+
build/test/functional/test_runner.py --filter interface_ipc --timeout-factor=8
338+
339+
- name: Save ccache
340+
uses: actions/cache/save@v4
341+
if: github.ref == 'refs/heads/master' || steps.ccache-restore.outputs.cache-hit != 'true'
342+
with:
343+
path: ${{ env.CCACHE_DIR }}
344+
key: ccache-TSan-${{ github.ref }}-${{ github.sha }}

0 commit comments

Comments
 (0)