How to Use ThreadSanitizer to Find C++ Data Races
One-line answer: Compile and link with -fsanitize=thread, add -g and at least -O1, then run the binary—ThreadSanitizer (TSan) prints data races to stderr.
This howto follows the Clang ThreadSanitizer documentation and GCC’s Instrumentation Options for -fsanitize=thread. It does not retell an AddressSanitizer primer or merge in UBSan.
How Do You Enable TSan in the Build?
One-line answer: Pass -fsanitize=thread to both compile and link steps, use -g for file and line numbers, and use -O1 or higher for reasonable performance.
Clang’s usage note is simply: compile and link with -fsanitize=thread. Expect roughly 5×–15× slowdown and about 5×–10× memory overhead. Supported platforms are listed in the docs (for example Linux aarch64 and x86_64). 32-bit support is not planned.
clang++ -fsanitize=thread -g -O1 -o app main.cpp
# or
g++ -fsanitize=thread -g -O1 -o app main.cpp
The documentation’s tiny race example shares a global without synchronization:
#include <pthread.h>
int Global;
void *Thread1(void *x) {
Global = 42;
return x;
}
int main() {
pthread_t t;
pthread_create(&t, NULL, Thread1, NULL);
Global = 43;
pthread_join(t, NULL);
return Global;
}
Runtime flags go in TSAN_OPTIONS (for example halt_on_error=1, history_size=7, log_path=...). Run TSAN_OPTIONS=help=1 ./app for the full list. To skip instrumentation in selected functions, use __attribute__((no_sanitize("thread"))) or an ignorelist with src: / fun: as documented.
In general all code under test should be built with -fsanitize=thread. Non-instrumented shared libraries can miss races or produce false positives. Non-PIE executables are unsupported; the flag behaves like -fPIE / -pie. Static linking of libc/libstdc++ is not supported.
How Do You Read a Race Report?
One-line answer: Under WARNING: ThreadSanitizer: data race, pair the current access (Write/Read of size N) with the Previous write/read stacks and the involved thread’s created at frames to locate unsynchronized accesses to the same address.
A typical report shape from the Clang docs:
WARNING: ThreadSanitizer: data race (pid=...)
Write of size 4 at 0x... by thread T1:
#0 Thread1 tiny_race.c:4 ...
Previous write of size 4 at 0x... by main thread:
#0 main tiny_race.c:10 ...
Thread T1 (running) created at:
#0 pthread_create ...
#1 main tiny_race.c:9 ...
Practical reading order:
- Address and size: two threads (or main and a worker) touch the same location.
- Access kind: plain writes together, or a write and a read without synchronization, are reported as races. Races between atomic and plain accesses are gated by
report_atomic_races(default true). - Stacks and creation: follow the frames and the
pthread_createsite to rebuild the path. - Symbols: you need
-gfor useful file/line info. If you see “failed to restore the stack”, try raisinghistory_size(0–7).
For temporary suppression, use TSAN_OPTIONS=suppressions=/path/to/file.supp or __tsan_default_suppressions with race: patterns—not a substitute for a fix.
Why Must You Not Combine It With ASan?
One-line answer: GCC’s Instrumentation Options state that -fsanitize=address cannot be combined with -fsanitize=thread. Keep memory-error and data-race checks in separate builds and runs.
The two tools use different shadow memory, runtimes, and instrumentation. Putting both on one binary is an unsupported combination. In CI, split an ASan job and a TSan job over the same sources, changing only the sanitize flags.
On Linux, disabling ASLR can make TSan fail to mmap shadow memory (FATAL: ThreadSanitizer can not mmap the shadow memory). GDB disables ASLR by default; for TSan under GDB the docs recommend set disable-randomization off.
TSan’s runtime is for bug detection, not for linking into production binaries (Clang Security Considerations).
FAQ
Q. What about CMake?
A. Put -fsanitize=thread on both compile and link options for the target, and prefer a configuration that already carries -g.
Q. Does it detect deadlocks?
A. detect_deadlocks (default true) covers mutex-related deadlock reports, separate from the data-race focus of this post.