| 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>`_ |