天天看点

使用Clang作为编译器 —— ThreadSanitizer

ThreadSanitizer

      • 1. 介绍
      • 2. 如何构建
      • 3. 支持的平台
      • 4. 使用(Usage)
          • 4.1 __has_feature(thread_sanitizer)
          • 4.2 __attribute__((no_sanitize("thread")))
      • 5. 黑名单(Blacklist)
      • 6. 限制
      • 7. 当前的状态
      • 8. 更多的信息

本文为译文,点击 此处查看原文。

1. 介绍

ThreadSanitizer

是一个检测数据竞争的工具。它由编译器检测模块和运行时库组成。

ThreadSanitizer

引入的典型减速是 5x-15x 左右。

ThreadSanitizer

引入的典型内存开销约为5x-10x。

2. 如何构建

使用 CMake 构建 LLVM/Clang。

3. 支持的平台

ThreadSanitizer

支持以下操作系统:

  • Android aarch64 x86_64
  • Darwin arm64 x86_64
  • FreeBSD
  • Linux aarch64, x86_64, powerpc64, powerpc64le
  • NetBSD

支持其他64位体系结构是可能的,欢迎贡献。对32位平台的支持存在问题,而且没有计划。

4. 使用(Usage)

只需用

-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
           

如果检测到一个 bug,程序将向 stderr 打印一条错误消息。目前,

ThreadSanitizer

使用外部

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)
           
4.1 __has_feature(thread_sanitizer)

在某些情况下,可能需要执行不同的代码,这取决于是否启用了

ThreadSanitizer

。可以为此目的使用_has_feature。

#if defined(__has_feature)
#  if __has_feature(thread_sanitizer)
// code that builds only under ThreadSanitizer
#  endif
#endif
           
4.2 attribute((no_sanitize(“thread”)))

有些代码不应该用

ThreadSanitizer

检测。可以使用函数属性

no_sanitize(“thread”)

禁用特定函数中普通(非原子)加载/存储的检测。

ThreadSanitizer

仍然使用这些函数来避免误报,并提供有意义的堆栈跟踪。其他编译器可能不支持此属性,因此我们建议将其与

__has_feature(thread_sanitizer)

一起使用。

5. 黑名单(Blacklist)

ThreadSanitizer

支持 Sanitizer special case list 中的

src

fun

实体类型,可以用于在指定的源文件或函数中禁止数据竞争报告。与标记为

no_sanitize(“thread”)

属性的函数不同,黑名单函数根本不插桩。这可能导致误报,原因是通过原子操作错过了同步和报告中错过了堆栈帧。

6. 限制

  • ThreadSanitizer

    比本机运行使用更多的实际内存。在默认设置下,每个线程的内存开销是5x + 1Mb。使用3x(不太精确的分析)和9x(更精确的分析)开销的设置也是可用的。
  • ThreadSanitizer

    映射(但不保留)大量的虚拟地址空间。这意味着像

    ulimit

    这样的工具可能不会像通常预期的那样工作。
  • 不支持

    Libc/libstdc++

    静态链接。
  • 不支持非位置独立的可执行程序。因此,如果编译时没有

    -fPIC

    fsanitize=thread

    标志将导致 Clang 的行为,就好像已经提供了

    -fPIE

    标志一样,如果链接一个可执行文件,就好像已经提供了

    -pie

    标志一样。

7. 当前的状态

ThreadSanitizer

还在测试阶段。众所周知,它可以在使用 pthread 的大型 C++ 程序上工作,但是我们(目前)还没有做出任何承诺。C++ 11 线程由 llvm libc++ 支持。测试套件集成到 CMake build 中,可以使用

make check-tsan

命令运行。

我们正在积极地改进这个工具 —— 请继续关注。任何帮助,特别是以最小化的独立测试的形式,都是非常受欢迎的。

8. 更多的信息

https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual