线程安全检查器¶
简介¶
线程安全检查器是一个检测数据竞争的工具。它包含一个编译器插桩模块和一个运行时库。线程安全检查器通常会导致约 **5x-15x** 的性能下降。线程安全检查器通常会导致约 **5x-10x** 的内存开销。
如何构建¶
使用 CMake 构建 LLVM/Clang。
支持的平台¶
线程安全检查器在以下操作系统上受支持
Android aarch64, x86_64
Darwin arm64, x86_64
FreeBSD
Linux aarch64, x86_64, powerpc64, powerpc64le
NetBSD
对其他 64 位架构的支持是可能的,欢迎贡献。对 32 位平台的支持存在问题,目前没有计划。
用法¶
只需使用 -fsanitize=thread
编译和链接您的程序。为了获得合理的性能,请添加 -O1
或更高。使用 -g
在警告消息中获取文件名和行号。
示例
% 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
如果检测到错误,程序将向 stderr 打印错误消息。目前,线程安全检查器使用外部 addr2line
进程来符号化其输出(将在将来修复)。
% ./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)
¶
在某些情况下,可能需要根据是否启用了线程安全检查器来执行不同的代码。 __has_feature 可用于此目的。
#if defined(__has_feature)
# if __has_feature(thread_sanitizer)
// code that builds only under ThreadSanitizer
# endif
#endif
__attribute__((no_sanitize("thread")))
¶
某些代码不应受线程安全检查器的插桩。可以使用函数属性 no_sanitize("thread")
来禁用对特定函数中简单(非原子)的加载/存储的插桩。线程安全检查器仍然会对这些函数进行插桩,以避免误报并提供有意义的堆栈跟踪。该属性可能不受其他编译器的支持,因此建议将其与 __has_feature(thread_sanitizer)
一起使用。
__attribute__((disable_sanitizer_instrumentation))
¶
disable_sanitizer_instrumentation
属性可以应用于函数,以防止所有类型的插桩。因此,它可能会导致误报和错误的堆栈跟踪。因此,应谨慎使用,并且仅在绝对必要时使用;例如,对于某些无法容忍任何插桩及其产生的副作用的代码。该属性会覆盖 no_sanitize("thread")
。
忽略列表¶
线程安全检查器在 Sanitizer 特殊情况列表 中支持 src
和 fun
实体类型,这些类型可用于抑制在指定源文件或函数中的数据竞争报告。与使用 no_sanitize("thread")
属性标记的函数不同,被忽略的函数根本不会被插桩。由于通过原子操作错过了同步以及报告中错过了堆栈帧,这会导致误报。
局限性¶
线程安全检查器比原生运行使用更多的实际内存。在默认设置下,内存开销为 5x 加上每个线程 1Mb。还提供 3x(不太准确的分析)和 9x(更准确的分析)开销设置。
线程安全检查器会映射(但不保留)大量虚拟地址空间。这意味着像
ulimit
这样的工具可能不会像预期的那样工作。不支持 Libc/libstdc++ 静态链接。
不支持非位置无关的可执行文件。因此,如果在没有
-fPIC
的情况下编译,则fsanitize=thread
标志会导致 Clang 就像提供了-fPIE
标志一样,如果链接可执行文件,则就像提供了-pie
标志一样。
安全注意事项¶
线程安全检查器是一个错误检测工具,其运行时不打算与生产可执行文件链接。虽然它可能对测试有用,但线程安全检查器的运行时并非针对安全敏感的约束而开发,并且可能会损害结果可执行文件的安全性。
当前状态¶
线程安全检查器处于测试阶段。已知它适用于使用 pthreads 的大型 C++ 程序,但我们不承诺任何事情(目前)。C++11 线程在 llvm libc++ 中受支持。测试套件已集成到 CMake 构建中,可以使用 make check-tsan
命令运行。
我们正在积极努力改进该工具 - 请持续关注。任何帮助,特别是以最小化的独立测试的形式,都非常受欢迎。
更多信息¶
https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual