CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
333 lines
11 KiB
ReStructuredText
333 lines
11 KiB
ReStructuredText
ThreadSanitizer
|
|
===============
|
|
|
|
Introduction
|
|
------------
|
|
|
|
ThreadSanitizer is a tool that detects data races. It consists of a compiler
|
|
instrumentation module and a run-time library. Typical slowdown introduced by
|
|
ThreadSanitizer is about **5x-15x**. Typical memory overhead introduced by
|
|
ThreadSanitizer is about **5x-10x**.
|
|
|
|
How to build
|
|
------------
|
|
|
|
Build LLVM/Clang with `CMake <https://llvm.org/docs/CMake.html>`_.
|
|
|
|
Supported Platforms
|
|
-------------------
|
|
|
|
ThreadSanitizer is supported on the following OS:
|
|
|
|
* Android aarch64, x86_64
|
|
* Darwin arm64, x86_64
|
|
* FreeBSD
|
|
* Linux aarch64, x86_64, powerpc64, powerpc64le
|
|
* NetBSD
|
|
|
|
Support for other 64-bit architectures is possible, contributions are welcome.
|
|
Support for 32-bit platforms is problematic and is not planned.
|
|
|
|
Usage
|
|
-----
|
|
|
|
Simply compile and link your program with ``-fsanitize=thread``. To get a
|
|
reasonable performance add ``-O1`` or higher. Use ``-g`` to get file names
|
|
and line numbers in the warning messages.
|
|
|
|
Example:
|
|
|
|
.. code-block:: console
|
|
|
|
% cat projects/compiler-rt/lib/tsan/lit_tests/tiny_race.c
|
|
#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;
|
|
}
|
|
|
|
$ clang -fsanitize=thread -g -O1 tiny_race.c
|
|
|
|
If a bug is detected, the program will print an error message to stderr.
|
|
Currently, ThreadSanitizer symbolizes its output using an external
|
|
``addr2line`` process (this will be fixed in future).
|
|
|
|
.. code-block:: bash
|
|
|
|
% ./a.out
|
|
WARNING: ThreadSanitizer: data race (pid=19219)
|
|
Write of size 4 at 0x7fcf47b21bc0 by thread T1:
|
|
#0 Thread1 tiny_race.c:4 (exe+0x00000000a360)
|
|
|
|
Previous write of size 4 at 0x7fcf47b21bc0 by main thread:
|
|
#0 main tiny_race.c:10 (exe+0x00000000a3b4)
|
|
|
|
Thread T1 (running) created at:
|
|
#0 pthread_create tsan_interceptors.cc:705 (exe+0x00000000c790)
|
|
#1 main tiny_race.c:9 (exe+0x00000000a3a4)
|
|
|
|
``__has_feature(thread_sanitizer)``
|
|
------------------------------------
|
|
|
|
In some cases one may need to execute different code depending on whether
|
|
ThreadSanitizer is enabled.
|
|
:ref:`\_\_has\_feature <langext-__has_feature-__has_extension>` can be used for
|
|
this purpose.
|
|
|
|
.. code-block:: c
|
|
|
|
#if defined(__has_feature)
|
|
# if __has_feature(thread_sanitizer)
|
|
// code that builds only under ThreadSanitizer
|
|
# endif
|
|
#endif
|
|
|
|
``__attribute__((no_sanitize("thread")))``
|
|
-----------------------------------------------
|
|
|
|
Some code should not be instrumented by ThreadSanitizer. One may use the
|
|
function attribute ``no_sanitize("thread")`` to disable instrumentation of plain
|
|
(non-atomic) loads/stores in a particular function. ThreadSanitizer still
|
|
instruments such functions to avoid false positives and provide meaningful stack
|
|
traces. This attribute may not be supported by other compilers, so we suggest
|
|
to use it together with ``__has_feature(thread_sanitizer)``.
|
|
|
|
``__attribute__((disable_sanitizer_instrumentation))``
|
|
--------------------------------------------------------
|
|
|
|
The ``disable_sanitizer_instrumentation`` attribute can be applied to functions
|
|
to prevent all kinds of instrumentation. As a result, it may introduce false
|
|
positives and incorrect stack traces. Therefore, it should be used with care,
|
|
and only if absolutely required; for example for certain code that cannot
|
|
tolerate any instrumentation and resulting side-effects. This attribute
|
|
overrides ``no_sanitize("thread")``.
|
|
|
|
Interaction of Inlining with Disabling Sanitizer Instrumentation
|
|
-----------------------------------------------------------------
|
|
|
|
* A `no_sanitize` function will not be inlined heuristically by the compiler into a sanitized function.
|
|
* An `always_inline` function will adopt the instrumentation status of the function it is inlined into.
|
|
* Forcibly combining `no_sanitize` and ``__attribute__((always_inline))`` is not supported, and will often lead to unexpected results. To avoid mixing these attributes, use:
|
|
|
|
.. code-block:: c
|
|
|
|
// Note, __has_feature test for sanitizers is deprecated, and Clang will support __SANITIZE_<sanitizer>__ similar to GCC.
|
|
#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__) || ... <other sanitizers>
|
|
#define ALWAYS_INLINE_IF_UNINSTRUMENTED
|
|
#else
|
|
#define ALWAYS_INLINE_IF_UNINSTRUMENTED __attribute__((always_inline))
|
|
#endif
|
|
|
|
Explicit Sanitizer Checks with ``__builtin_allow_sanitize_check``
|
|
-----------------------------------------------------------------
|
|
|
|
The ``__builtin_allow_sanitize_check("thread")`` builtin can be used to
|
|
conditionally execute code depending on whether ThreadSanitizer checks are
|
|
enabled and permitted by the current policy (after inlining). This is
|
|
particularly useful for inserting explicit, sanitizer-specific checks around
|
|
operations like syscalls or inline assembly, which might otherwise be unchecked
|
|
by the sanitizer.
|
|
|
|
Example:
|
|
|
|
.. code-block:: c
|
|
|
|
void __tsan_read8(void *);
|
|
|
|
inline __attribute__((always_inline))
|
|
void my_helper(void *addr) {
|
|
if (__builtin_allow_sanitize_check("thread"))
|
|
__tsan_read8(addr);
|
|
// ... actual logic, e.g. inline assembly ...
|
|
asm volatile ("..." : : "r" (addr) : "memory");
|
|
}
|
|
|
|
void instrumented_function() {
|
|
...
|
|
my_helper(&shared_data); // checks are active
|
|
...
|
|
}
|
|
|
|
__attribute__((no_sanitize("thread")))
|
|
void uninstrumented_function() {
|
|
...
|
|
my_helper(&shared_data); // checks are skipped
|
|
...
|
|
}
|
|
|
|
Ignorelist
|
|
----------
|
|
|
|
ThreadSanitizer supports ``src`` and ``fun`` entity types in
|
|
:doc:`SanitizerSpecialCaseList`, that can be used to suppress data race reports
|
|
in the specified source files or functions. Unlike functions marked with
|
|
``no_sanitize("thread")`` attribute, ignored functions are not instrumented
|
|
at all. This can lead to false positives due to missed synchronization via
|
|
atomic operations and missed stack frames in reports.
|
|
|
|
Limitations
|
|
-----------
|
|
|
|
* ThreadSanitizer uses more real memory than a native run. At the default
|
|
settings the memory overhead is 5x plus 1Mb per each thread. Settings with 3x
|
|
(less accurate analysis) and 9x (more accurate analysis) overhead are also
|
|
available.
|
|
* ThreadSanitizer maps (but does not reserve) a lot of virtual address space.
|
|
This means that tools like ``ulimit`` may not work as usually expected.
|
|
* Libc/libstdc++ static linking is not supported.
|
|
* Non-position-independent executables are not supported. Therefore, the
|
|
``fsanitize=thread`` flag will cause Clang to act as though the ``-fPIE``
|
|
flag had been supplied if compiling without ``-fPIC``, and as though the
|
|
``-pie`` flag had been supplied if linking an executable.
|
|
|
|
Security Considerations
|
|
-----------------------
|
|
|
|
ThreadSanitizer is a bug detection tool and its runtime is not meant to be
|
|
linked against production executables. While it may be useful for testing,
|
|
ThreadSanitizer's runtime was not developed with security-sensitive
|
|
constraints in mind and may compromise the security of the resulting executable.
|
|
|
|
Current Status
|
|
--------------
|
|
|
|
ThreadSanitizer is in beta stage. It is known to work on large C++ programs
|
|
using pthreads, but we do not promise anything (yet). C++11 threading is
|
|
supported with llvm libc++. The test suite is integrated into CMake build
|
|
and can be run with ``make check-tsan`` command.
|
|
|
|
We are actively working on enhancing the tool --- stay tuned. Any help,
|
|
especially in the form of minimized standalone tests is more than welcome.
|
|
|
|
Adaptive Delay
|
|
--------------
|
|
|
|
Overview
|
|
~~~~~~~~
|
|
|
|
Adaptive Delay is an optional ThreadSanitizer feature that injects delays at
|
|
synchronization points to explore novel thread interleavings and increase the
|
|
likelihood of exposing data races. By perturbing thread scheduling, adaptive
|
|
delay creates more opportunities for concurrent accesses to shared data,
|
|
improving race detection.
|
|
|
|
Adaptive delay is particularly useful for:
|
|
|
|
* Detecting races in rarely-executed thread interleavings or code paths
|
|
* Testing parallel data structures and algorithms
|
|
|
|
When enabled, adaptive delay maintains a configurable time budget to balance
|
|
race exposure against performance overhead. The delays can be
|
|
|
|
* random amount of spin cycles
|
|
* a single yield to the OS scheduler
|
|
* random usleep
|
|
|
|
The strategy prioritizes high-value synchronization points:
|
|
|
|
* Relaxed atomic operations receive cheap delays (spin cycles) with low sampling
|
|
* Synchronizing atomic operations (acquire/release/seq_cst) receive moderate
|
|
delays with higher sampling
|
|
* Mutex and thread lifecycle operations receive the longest delays with highest
|
|
sampling
|
|
|
|
The delays focus on synchronization points with clear happens-before relationships,
|
|
as those are most likely to expose data races.
|
|
|
|
Enabling Adaptive Delay
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Adaptive delay is disabled by default. Enable it by setting the
|
|
``enable_adaptive_delay`` flag:
|
|
|
|
.. code-block:: console
|
|
|
|
$ TSAN_OPTIONS=enable_adaptive_delay=1 ./myapp
|
|
|
|
Configuration Options
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. list-table:: Adaptive Delay Options
|
|
:name: adaptive-delay-options-table
|
|
:header-rows: 1
|
|
:widths: 35 10 15 40
|
|
|
|
* - Flag
|
|
- Type
|
|
- Default
|
|
- Description
|
|
* - ``enable_adaptive_delay``
|
|
- bool
|
|
- false
|
|
- Enable adaptive delay injection to expose data races.
|
|
* - ``adaptive_delay_aggressiveness``
|
|
- int
|
|
- 25
|
|
- Controls delay injection intensity for race detection. Higher values inject
|
|
more delays to expose races. Value must be greater than 0. Suggested values:
|
|
10 (minimal), 50 (moderate), 200 (aggressive). This is a tuning parameter;
|
|
actual overhead varies by workload and platform.
|
|
* - ``adaptive_delay_relaxed_sample_rate``
|
|
- int
|
|
- 10000
|
|
- Sample 1 in N relaxed atomic operations for delay injection. Relaxed atomics
|
|
have minimal synchronization, so sampling helps avoid excessive overhead.
|
|
* - ``adaptive_delay_sync_atomic_sample_rate``
|
|
- int
|
|
- 100
|
|
- Sample 1 in N acquire/release/seq_cst atomic operations for delay injection.
|
|
These synchronizing atomics are more likely to expose races, so are sampled
|
|
more often.
|
|
* - ``adaptive_delay_mutex_sample_rate``
|
|
- int
|
|
- 10
|
|
- Sample 1 in N mutex/condition variable operations for delay injection. Mutex
|
|
ops are high-value synchronization points and are sampled frequently.
|
|
* - ``adaptive_delay_max_atomic``
|
|
- string
|
|
- ``"sleep_us=50"``
|
|
- Maximum delay for atomic operations. Format: ``"spin=N"`` (N spin cycles,
|
|
1 <= N <= 10,000), ``"yield"`` (one yield to the OS), or ``"sleep_us=N"``
|
|
(up to N microseconds). The delay is randomly chosen up to the specified
|
|
maximum N.
|
|
* - ``adaptive_delay_max_sync``
|
|
- string
|
|
- ``"sleep_us=500"``
|
|
- Maximum delay for synchronization operations (mutex and thread lifecycle
|
|
operations). Format: same as ``adaptive_delay_max_atomic``. Typically set
|
|
longer than atomic delays since these operations involve waking blocked threads
|
|
and may be more likely to expose races.
|
|
|
|
Examples
|
|
~~~~~~~~
|
|
|
|
Enable adaptive delay with moderate aggressiveness:
|
|
|
|
.. code-block:: console
|
|
|
|
$ TSAN_OPTIONS=enable_adaptive_delay=1:adaptive_delay_aggressiveness=50 ./myapp
|
|
|
|
Enable aggressive delay injection:
|
|
|
|
.. code-block:: console
|
|
|
|
$ TSAN_OPTIONS=enable_adaptive_delay=1:adaptive_delay_aggressiveness=200 ./myapp
|
|
|
|
Increase sampling frequency for mutex operations:
|
|
|
|
.. code-block:: console
|
|
|
|
$ TSAN_OPTIONS=enable_adaptive_delay=1:adaptive_delay_mutex_sample_rate=5 ./myapp
|
|
|
|
More Information
|
|
----------------
|
|
`<https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual>`_
|