summaryrefslogtreecommitdiff
path: root/test/tsan
diff options
context:
space:
mode:
authorAlexey Samsonov <samsonov@google.com>2014-02-14 14:35:48 +0000
committerAlexey Samsonov <samsonov@google.com>2014-02-14 14:35:48 +0000
commite951fe4eec98de20b9b843f97fec2cca84c48b9c (patch)
treee5d5d3dbf11683bfde3cfd829ccea8ddbbc0dd0e /test/tsan
parentba2d0d7b386f38c97eee11749f2fc75449c2b253 (diff)
Move TSan lit-tests under test/tsan
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@201414 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/tsan')
-rw-r--r--test/tsan/CMakeLists.txt19
-rw-r--r--test/tsan/Helpers/blacklist.txt1
-rw-r--r--test/tsan/Helpers/lit.local.cfg2
-rw-r--r--test/tsan/SharedLibs/lit.local.cfg4
-rw-r--r--test/tsan/SharedLibs/load_shared_lib-so.cc22
-rw-r--r--test/tsan/Unit/lit.site.cfg.in14
-rw-r--r--test/tsan/aligned_vs_unaligned_race.cc34
-rw-r--r--test/tsan/allocator_returns_null.cc64
-rw-r--r--test/tsan/atomic_free.cc19
-rw-r--r--test/tsan/atomic_free2.cc19
-rw-r--r--test/tsan/atomic_norace.cc61
-rw-r--r--test/tsan/atomic_race.cc80
-rw-r--r--test/tsan/atomic_stack.cc29
-rw-r--r--test/tsan/benign_race.cc39
-rw-r--r--test/tsan/blacklist.cc31
-rw-r--r--test/tsan/cond.c53
-rw-r--r--test/tsan/cond_race.cc36
-rw-r--r--test/tsan/cond_version.c44
-rw-r--r--test/tsan/deep_stack1.cc44
-rw-r--r--test/tsan/default_options.cc32
-rw-r--r--test/tsan/fd_close_norace.cc33
-rw-r--r--test/tsan/fd_close_norace2.cc30
-rw-r--r--test/tsan/fd_dup_norace.cc34
-rw-r--r--test/tsan/fd_location.cc33
-rw-r--r--test/tsan/fd_pipe_norace.cc33
-rw-r--r--test/tsan/fd_pipe_race.cc37
-rw-r--r--test/tsan/fd_socket_connect_norace.cc45
-rw-r--r--test/tsan/fd_socket_norace.cc52
-rw-r--r--test/tsan/fd_socketpair_norace.cc37
-rw-r--r--test/tsan/fd_stdout_race.cc41
-rw-r--r--test/tsan/fork_deadlock.cc48
-rw-r--r--test/tsan/fork_multithreaded.cc42
-rw-r--r--test/tsan/fork_multithreaded3.cc40
-rw-r--r--test/tsan/free_race.c49
-rw-r--r--test/tsan/free_race.c.supp2
-rw-r--r--test/tsan/free_race2.c26
-rw-r--r--test/tsan/getline_nohang.cc35
-rw-r--r--test/tsan/global_race.cc44
-rw-r--r--test/tsan/halt_on_error.cc25
-rw-r--r--test/tsan/heap_race.cc20
-rw-r--r--test/tsan/ignore_free.cc35
-rw-r--r--test/tsan/ignore_lib0.cc30
-rw-r--r--test/tsan/ignore_lib0.cc.supp2
-rw-r--r--test/tsan/ignore_lib1.cc42
-rw-r--r--test/tsan/ignore_lib1.cc.supp2
-rw-r--r--test/tsan/ignore_lib2.cc33
-rw-r--r--test/tsan/ignore_lib2.cc.supp2
-rw-r--r--test/tsan/ignore_lib3.cc33
-rw-r--r--test/tsan/ignore_lib3.cc.supp2
-rw-r--r--test/tsan/ignore_lib_lib.h25
-rw-r--r--test/tsan/ignore_malloc.cc38
-rw-r--r--test/tsan/ignore_race.cc31
-rw-r--r--test/tsan/ignore_sync.cc30
-rw-r--r--test/tsan/inlined_memcpy_race.cc55
-rw-r--r--test/tsan/interface_atomic_test.c16
-rw-r--r--test/tsan/java.h20
-rw-r--r--test/tsan/java_alloc.cc32
-rw-r--r--test/tsan/java_lock.cc35
-rw-r--r--test/tsan/java_lock_move.cc40
-rw-r--r--test/tsan/java_lock_rec.cc54
-rw-r--r--test/tsan/java_lock_rec_race.cc48
-rw-r--r--test/tsan/java_race.cc23
-rw-r--r--test/tsan/java_race_move.cc31
-rw-r--r--test/tsan/java_rwlock.cc35
-rw-r--r--test/tsan/lit.cfg47
-rw-r--r--test/tsan/lit.site.cfg.in8
-rw-r--r--test/tsan/load_shared_lib.cc44
-rw-r--r--test/tsan/longjmp.cc22
-rw-r--r--test/tsan/longjmp2.cc24
-rw-r--r--test/tsan/longjmp3.cc48
-rw-r--r--test/tsan/longjmp4.cc51
-rw-r--r--test/tsan/malloc_hook.cc52
-rw-r--r--test/tsan/malloc_overflow.cc23
-rw-r--r--test/tsan/malloc_stack.cc25
-rw-r--r--test/tsan/memcpy_race.cc42
-rw-r--r--test/tsan/mop_with_offset.cc36
-rw-r--r--test/tsan/mop_with_offset2.cc36
-rw-r--r--test/tsan/mutex_cycle2.c25
-rw-r--r--test/tsan/mutex_destroy_locked.cc22
-rw-r--r--test/tsan/mutex_robust.cc36
-rw-r--r--test/tsan/mutex_robust2.cc41
-rw-r--r--test/tsan/mutexset1.cc37
-rw-r--r--test/tsan/mutexset2.cc37
-rw-r--r--test/tsan/mutexset3.cc45
-rw-r--r--test/tsan/mutexset4.cc45
-rw-r--r--test/tsan/mutexset5.cc46
-rw-r--r--test/tsan/mutexset6.cc53
-rw-r--r--test/tsan/mutexset7.cc39
-rw-r--r--test/tsan/mutexset8.cc39
-rw-r--r--test/tsan/oob_race.cc26
-rw-r--r--test/tsan/printf-1.c16
-rw-r--r--test/tsan/race_on_barrier.c31
-rw-r--r--test/tsan/race_on_barrier2.c31
-rw-r--r--test/tsan/race_on_heap.cc47
-rw-r--r--test/tsan/race_on_mutex.c42
-rw-r--r--test/tsan/race_on_mutex2.c24
-rw-r--r--test/tsan/race_on_read.cc34
-rw-r--r--test/tsan/race_on_speculative_load.cc27
-rw-r--r--test/tsan/race_on_write.cc39
-rw-r--r--test/tsan/race_with_finished_thread.cc43
-rw-r--r--test/tsan/signal_errno.cc50
-rw-r--r--test/tsan/signal_malloc.cc26
-rw-r--r--test/tsan/sigsuspend.cc44
-rw-r--r--test/tsan/simple_race.c26
-rw-r--r--test/tsan/simple_race.cc26
-rw-r--r--test/tsan/simple_stack.c66
-rw-r--r--test/tsan/simple_stack2.cc53
-rw-r--r--test/tsan/sleep_sync.cc30
-rw-r--r--test/tsan/sleep_sync2.cc22
-rw-r--r--test/tsan/stack_race.cc22
-rw-r--r--test/tsan/stack_race2.cc29
-rw-r--r--test/tsan/static_init1.cc27
-rw-r--r--test/tsan/static_init2.cc33
-rw-r--r--test/tsan/static_init3.cc47
-rw-r--r--test/tsan/static_init4.cc37
-rw-r--r--test/tsan/static_init5.cc42
-rw-r--r--test/tsan/static_init6.cc42
-rw-r--r--test/tsan/suppress_same_address.cc29
-rw-r--r--test/tsan/suppress_same_stacks.cc27
-rw-r--r--test/tsan/suppressions_global.cc29
-rw-r--r--test/tsan/suppressions_global.cc.supp2
-rw-r--r--test/tsan/suppressions_race.cc31
-rw-r--r--test/tsan/suppressions_race.cc.supp2
-rw-r--r--test/tsan/suppressions_race2.cc31
-rw-r--r--test/tsan/suppressions_race2.cc.supp2
-rwxr-xr-xtest/tsan/test_output.sh59
-rw-r--r--test/tsan/thread_end_with_ignore.cc24
-rw-r--r--test/tsan/thread_end_with_ignore2.cc12
-rw-r--r--test/tsan/thread_end_with_ignore3.cc22
-rw-r--r--test/tsan/thread_leak.c17
-rw-r--r--test/tsan/thread_leak2.c17
-rw-r--r--test/tsan/thread_leak3.c17
-rw-r--r--test/tsan/thread_leak4.c18
-rw-r--r--test/tsan/thread_leak5.c20
-rw-r--r--test/tsan/thread_name.cc38
-rw-r--r--test/tsan/thread_name2.cc32
-rw-r--r--test/tsan/tiny_race.c21
-rw-r--r--test/tsan/tls_race.cc21
-rw-r--r--test/tsan/tls_race2.cc29
-rw-r--r--test/tsan/tsan-vs-gvn.cc38
-rw-r--r--test/tsan/unaligned_norace.cc84
-rw-r--r--test/tsan/unaligned_race.cc139
-rw-r--r--test/tsan/user_fopen.cc34
-rw-r--r--test/tsan/user_malloc.cc27
-rw-r--r--test/tsan/virtual_inheritance_compile_bug.cc15
-rw-r--r--test/tsan/vptr_benign_race.cc51
-rw-r--r--test/tsan/vptr_harmful_race.cc51
-rw-r--r--test/tsan/vptr_harmful_race2.cc51
-rw-r--r--test/tsan/write_in_reader_lock.cc35
149 files changed, 4977 insertions, 0 deletions
diff --git a/test/tsan/CMakeLists.txt b/test/tsan/CMakeLists.txt
new file mode 100644
index 000000000..8d79c705d
--- /dev/null
+++ b/test/tsan/CMakeLists.txt
@@ -0,0 +1,19 @@
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
+
+set(TSAN_TEST_DEPS
+ ${SANITIZER_COMMON_LIT_TEST_DEPS}
+ tsan)
+
+if(LLVM_INCLUDE_TESTS)
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg)
+ list(APPEND TSAN_TEST_DEPS TsanUnitTests)
+endif()
+
+add_lit_testsuite(check-tsan "Running ThreadSanitizer tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${TSAN_TEST_DEPS})
+set_target_properties(check-tsan PROPERTIES FOLDER "TSan tests")
diff --git a/test/tsan/Helpers/blacklist.txt b/test/tsan/Helpers/blacklist.txt
new file mode 100644
index 000000000..22225e542
--- /dev/null
+++ b/test/tsan/Helpers/blacklist.txt
@@ -0,0 +1 @@
+fun:*Blacklisted_Thread2*
diff --git a/test/tsan/Helpers/lit.local.cfg b/test/tsan/Helpers/lit.local.cfg
new file mode 100644
index 000000000..9246b1035
--- /dev/null
+++ b/test/tsan/Helpers/lit.local.cfg
@@ -0,0 +1,2 @@
+# Files in this directory are helper files for other output tests.
+config.suffixes = []
diff --git a/test/tsan/SharedLibs/lit.local.cfg b/test/tsan/SharedLibs/lit.local.cfg
new file mode 100644
index 000000000..b3677c17a
--- /dev/null
+++ b/test/tsan/SharedLibs/lit.local.cfg
@@ -0,0 +1,4 @@
+# Sources in this directory are compiled as shared libraries and used by
+# tests in parent directory.
+
+config.suffixes = []
diff --git a/test/tsan/SharedLibs/load_shared_lib-so.cc b/test/tsan/SharedLibs/load_shared_lib-so.cc
new file mode 100644
index 000000000..d05aa6a40
--- /dev/null
+++ b/test/tsan/SharedLibs/load_shared_lib-so.cc
@@ -0,0 +1,22 @@
+//===----------- load_shared_lib-so.cc --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stddef.h>
+
+int GLOB_SHARED = 0;
+
+extern "C"
+void *write_from_so(void *unused) {
+ GLOB_SHARED++;
+ return NULL;
+}
diff --git a/test/tsan/Unit/lit.site.cfg.in b/test/tsan/Unit/lit.site.cfg.in
new file mode 100644
index 000000000..949810565
--- /dev/null
+++ b/test/tsan/Unit/lit.site.cfg.in
@@ -0,0 +1,14 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+# Load common config for all compiler-rt unit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured")
+
+# Setup config name.
+config.name = 'ThreadSanitizer-Unit'
+
+# Setup test source and exec root. For unit tests, we define
+# it as build directory with ASan unit tests.
+# FIXME: De-hardcode this path.
+config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/tsan/tests"
+config.test_source_root = config.test_exec_root
diff --git a/test/tsan/aligned_vs_unaligned_race.cc b/test/tsan/aligned_vs_unaligned_race.cc
new file mode 100644
index 000000000..f4533d083
--- /dev/null
+++ b/test/tsan/aligned_vs_unaligned_race.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// Race between an aligned access and an unaligned access, which
+// touches the same memory region.
+// This is a real race which is not detected by tsan.
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=17
+#include <pthread.h>
+#include <stdio.h>
+#include <stdint.h>
+
+uint64_t Global[2];
+
+void *Thread1(void *x) {
+ Global[1]++;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ char *p1 = reinterpret_cast<char *>(&Global[0]);
+ uint64_t *p4 = reinterpret_cast<uint64_t *>(p1 + 1);
+ (*p4)++;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("Pass\n");
+ // CHECK-NOT: ThreadSanitizer: data race
+ // CHECK: Pass
+ return 0;
+}
diff --git a/test/tsan/allocator_returns_null.cc b/test/tsan/allocator_returns_null.cc
new file mode 100644
index 000000000..4b5eb5504
--- /dev/null
+++ b/test/tsan/allocator_returns_null.cc
@@ -0,0 +1,64 @@
+// Test the behavior of malloc/calloc/realloc when the allocation size is huge.
+// By default (allocator_may_return_null=0) the process shoudl crash.
+// With allocator_may_return_null=1 the allocator should return 0.
+//
+// RUN: %clangxx_tsan -O0 %s -o %t
+// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <limits>
+int main(int argc, char **argv) {
+ volatile size_t size = std::numeric_limits<size_t>::max() - 10000;
+ assert(argc == 2);
+ char *x = 0;
+ if (!strcmp(argv[1], "malloc")) {
+ fprintf(stderr, "malloc:\n");
+ x = (char*)malloc(size);
+ }
+ if (!strcmp(argv[1], "calloc")) {
+ fprintf(stderr, "calloc:\n");
+ x = (char*)calloc(size / 4, 4);
+ }
+
+ if (!strcmp(argv[1], "calloc-overflow")) {
+ fprintf(stderr, "calloc-overflow:\n");
+ volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max();
+ size_t kArraySize = 4096;
+ volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
+ x = (char*)calloc(kArraySize, kArraySize2);
+ }
+
+ if (!strcmp(argv[1], "realloc")) {
+ fprintf(stderr, "realloc:\n");
+ x = (char*)realloc(0, size);
+ }
+ if (!strcmp(argv[1], "realloc-after-malloc")) {
+ fprintf(stderr, "realloc-after-malloc:\n");
+ char *t = (char*)malloc(100);
+ *t = 42;
+ x = (char*)realloc(t, size);
+ assert(*t == 42);
+ }
+ fprintf(stderr, "x: %p\n", x);
+ return x != 0;
+}
+// CHECK-mCRASH: malloc:
+// CHECK-mCRASH: ThreadSanitizer's allocator is terminating the process
+// CHECK-cCRASH: calloc:
+// CHECK-cCRASH: ThreadSanitizer's allocator is terminating the process
+// CHECK-coCRASH: calloc-overflow:
+// CHECK-coCRASH: ThreadSanitizer's allocator is terminating the process
+// CHECK-rCRASH: realloc:
+// CHECK-rCRASH: ThreadSanitizer's allocator is terminating the process
+// CHECK-mrCRASH: realloc-after-malloc:
+// CHECK-mrCRASH: ThreadSanitizer's allocator is terminating the process
+
diff --git a/test/tsan/atomic_free.cc b/test/tsan/atomic_free.cc
new file mode 100644
index 000000000..87d559362
--- /dev/null
+++ b/test/tsan/atomic_free.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+void *Thread(void *a) {
+ __atomic_fetch_add((int*)a, 1, __ATOMIC_SEQ_CST);
+ return 0;
+}
+
+int main() {
+ int *a = new int(0);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, a);
+ sleep(1);
+ delete a;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/atomic_free2.cc b/test/tsan/atomic_free2.cc
new file mode 100644
index 000000000..961ff38c8
--- /dev/null
+++ b/test/tsan/atomic_free2.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+void *Thread(void *a) {
+ sleep(1);
+ __atomic_fetch_add((int*)a, 1, __ATOMIC_SEQ_CST);
+ return 0;
+}
+
+int main() {
+ int *a = new int(0);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, a);
+ delete a;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: heap-use-after-free
diff --git a/test/tsan/atomic_norace.cc b/test/tsan/atomic_norace.cc
new file mode 100644
index 000000000..265459b07
--- /dev/null
+++ b/test/tsan/atomic_norace.cc
@@ -0,0 +1,61 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+const int kTestCount = 4;
+typedef long long T;
+T atomics[kTestCount * 2];
+
+void Test(int test, T *p, bool main_thread) {
+ volatile T sink;
+ if (test == 0) {
+ if (main_thread)
+ __atomic_fetch_add(p, 1, __ATOMIC_RELAXED);
+ else
+ __atomic_fetch_add(p, 1, __ATOMIC_RELAXED);
+ } else if (test == 1) {
+ if (main_thread)
+ __atomic_exchange_n(p, 1, __ATOMIC_ACQ_REL);
+ else
+ __atomic_exchange_n(p, 1, __ATOMIC_ACQ_REL);
+ } else if (test == 2) {
+ if (main_thread)
+ sink = __atomic_load_n(p, __ATOMIC_SEQ_CST);
+ else
+ __atomic_store_n(p, 1, __ATOMIC_SEQ_CST);
+ } else if (test == 3) {
+ if (main_thread)
+ sink = __atomic_load_n(p, __ATOMIC_SEQ_CST);
+ else
+ sink = *p;
+ }
+}
+
+void *Thread(void *p) {
+ for (int i = 0; i < kTestCount; i++) {
+ Test(i, &atomics[i], false);
+ }
+ sleep(2);
+ for (int i = 0; i < kTestCount; i++) {
+ fprintf(stderr, "Test %d reverse\n", i);
+ Test(i, &atomics[kTestCount + i], false);
+ }
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ sleep(1);
+ for (int i = 0; i < kTestCount; i++) {
+ fprintf(stderr, "Test %d\n", i);
+ Test(i, &atomics[i], true);
+ }
+ for (int i = 0; i < kTestCount; i++) {
+ Test(i, &atomics[kTestCount + i], true);
+ }
+ pthread_join(t, 0);
+}
+
+// CHECK-NOT: ThreadSanitizer: data race
diff --git a/test/tsan/atomic_race.cc b/test/tsan/atomic_race.cc
new file mode 100644
index 000000000..0dfe4d93d
--- /dev/null
+++ b/test/tsan/atomic_race.cc
@@ -0,0 +1,80 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+
+const int kTestCount = 4;
+typedef long long T;
+T atomics[kTestCount * 2];
+
+void Test(int test, T *p, bool main_thread) {
+ volatile T sink;
+ if (test == 0) {
+ if (main_thread)
+ __atomic_fetch_add(p, 1, __ATOMIC_RELAXED);
+ else
+ *p = 42;
+ } else if (test == 1) {
+ if (main_thread)
+ __atomic_fetch_add(p, 1, __ATOMIC_RELAXED);
+ else
+ sink = *p;
+ } else if (test == 2) {
+ if (main_thread)
+ sink = __atomic_load_n(p, __ATOMIC_SEQ_CST);
+ else
+ *p = 42;
+ } else if (test == 3) {
+ if (main_thread)
+ __atomic_store_n(p, 1, __ATOMIC_SEQ_CST);
+ else
+ sink = *p;
+ }
+}
+
+void *Thread(void *p) {
+ for (int i = 0; i < kTestCount; i++) {
+ Test(i, &atomics[i], false);
+ }
+ sleep(2);
+ for (int i = 0; i < kTestCount; i++) {
+ fprintf(stderr, "Test %d reverse\n", i);
+ Test(i, &atomics[kTestCount + i], false);
+ }
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ sleep(1);
+ for (int i = 0; i < kTestCount; i++) {
+ fprintf(stderr, "Test %d\n", i);
+ Test(i, &atomics[i], true);
+ }
+ for (int i = 0; i < kTestCount; i++) {
+ Test(i, &atomics[kTestCount + i], true);
+ }
+ pthread_join(t, 0);
+}
+
+// CHECK: Test 0
+// CHECK: ThreadSanitizer: data race
+// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic
+// CHECK: Test 1
+// CHECK: ThreadSanitizer: data race
+// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic
+// CHECK: Test 2
+// CHECK: ThreadSanitizer: data race
+// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic
+// CHECK: Test 3
+// CHECK: ThreadSanitizer: data race
+// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic
+// CHECK: Test 0 reverse
+// CHECK: ThreadSanitizer: data race
+// CHECK: Test 1 reverse
+// CHECK: ThreadSanitizer: data race
+// CHECK: Test 2 reverse
+// CHECK: ThreadSanitizer: data race
+// CHECK: Test 3 reverse
+// CHECK: ThreadSanitizer: data race
diff --git a/test/tsan/atomic_stack.cc b/test/tsan/atomic_stack.cc
new file mode 100644
index 000000000..841f74b89
--- /dev/null
+++ b/test/tsan/atomic_stack.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ __atomic_fetch_add(&Global, 1, __ATOMIC_RELAXED);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global++;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Atomic write of size 4
+// CHECK: #0 __tsan_atomic32_fetch_add
+// CHECK: #1 Thread1
diff --git a/test/tsan/benign_race.cc b/test/tsan/benign_race.cc
new file mode 100644
index 000000000..a4d4d23c3
--- /dev/null
+++ b/test/tsan/benign_race.cc
@@ -0,0 +1,39 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+int WTFGlobal;
+
+extern "C" {
+void AnnotateBenignRaceSized(const char *f, int l,
+ void *mem, unsigned int size, const char *desc);
+void WTFAnnotateBenignRaceSized(const char *f, int l,
+ void *mem, unsigned int size,
+ const char *desc);
+}
+
+
+void *Thread(void *x) {
+ Global = 42;
+ WTFGlobal = 142;
+ return 0;
+}
+
+int main() {
+ AnnotateBenignRaceSized(__FILE__, __LINE__,
+ &Global, sizeof(Global), "Race on Global");
+ WTFAnnotateBenignRaceSized(__FILE__, __LINE__,
+ &WTFGlobal, sizeof(WTFGlobal),
+ "Race on WTFGlobal");
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ sleep(1);
+ Global = 43;
+ WTFGlobal = 143;
+ pthread_join(t, 0);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/blacklist.cc b/test/tsan/blacklist.cc
new file mode 100644
index 000000000..5baf926e6
--- /dev/null
+++ b/test/tsan/blacklist.cc
@@ -0,0 +1,31 @@
+// Test blacklist functionality for TSan.
+
+// RUN: %clangxx_tsan -O1 %s \
+// RUN: -fsanitize-blacklist=%p/Helpers/blacklist.txt \
+// RUN: -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ Global++;
+ return NULL;
+}
+
+void *Blacklisted_Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Blacklisted_Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("PASS\n");
+ return 0;
+}
+
+// CHECK-NOT: ThreadSanitizer: data race
diff --git a/test/tsan/cond.c b/test/tsan/cond.c
new file mode 100644
index 000000000..52c87a413
--- /dev/null
+++ b/test/tsan/cond.c
@@ -0,0 +1,53 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: ThreadSanitizer WARNING: double lock
+// CHECK-NOT: ThreadSanitizer WARNING: mutex unlock by another thread
+// CHECK: OK
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+pthread_mutex_t m;
+pthread_cond_t c;
+int x;
+
+void *thr1(void *p) {
+ int i;
+
+ for (i = 0; i < 10; i += 2) {
+ pthread_mutex_lock(&m);
+ while (x != i)
+ pthread_cond_wait(&c, &m);
+ x = i + 1;
+ pthread_cond_signal(&c);
+ pthread_mutex_unlock(&m);
+ }
+ return 0;
+}
+
+void *thr2(void *p) {
+ int i;
+
+ for (i = 1; i < 10; i += 2) {
+ pthread_mutex_lock(&m);
+ while (x != i)
+ pthread_cond_wait(&c, &m);
+ x = i + 1;
+ pthread_mutex_unlock(&m);
+ pthread_cond_broadcast(&c);
+ }
+ return 0;
+}
+
+int main() {
+ pthread_t th1, th2;
+
+ pthread_mutex_init(&m, 0);
+ pthread_cond_init(&c, 0);
+ pthread_create(&th1, 0, thr1, 0);
+ pthread_create(&th2, 0, thr2, 0);
+ pthread_join(th1, 0);
+ pthread_join(th2, 0);
+ fprintf(stderr, "OK\n");
+}
diff --git a/test/tsan/cond_race.cc b/test/tsan/cond_race.cc
new file mode 100644
index 000000000..1e2acb243
--- /dev/null
+++ b/test/tsan/cond_race.cc
@@ -0,0 +1,36 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+// CHECK: ThreadSanitizer: data race
+// CHECK: pthread_cond_signal
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+struct Ctx {
+ pthread_mutex_t m;
+ pthread_cond_t c;
+ bool done;
+};
+
+void *thr(void *p) {
+ Ctx *c = (Ctx*)p;
+ pthread_mutex_lock(&c->m);
+ c->done = true;
+ pthread_mutex_unlock(&c->m);
+ pthread_cond_signal(&c->c);
+ return 0;
+}
+
+int main() {
+ Ctx *c = new Ctx();
+ pthread_mutex_init(&c->m, 0);
+ pthread_cond_init(&c->c, 0);
+ pthread_t th;
+ pthread_create(&th, 0, thr, c);
+ pthread_mutex_lock(&c->m);
+ while (!c->done)
+ pthread_cond_wait(&c->c, &c->m);
+ pthread_mutex_unlock(&c->m);
+ delete c;
+ pthread_join(th, 0);
+}
diff --git a/test/tsan/cond_version.c b/test/tsan/cond_version.c
new file mode 100644
index 000000000..1f966bfac
--- /dev/null
+++ b/test/tsan/cond_version.c
@@ -0,0 +1,44 @@
+// RUN: %clang_tsan -O1 %s -o %t -lrt && %t 2>&1 | FileCheck %s
+// Test that pthread_cond is properly intercepted,
+// previously there were issues with versioned symbols.
+// CHECK: OK
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <time.h>
+#include <errno.h>
+
+int main() {
+ typedef unsigned long long u64;
+ pthread_mutex_t m;
+ pthread_cond_t c;
+ pthread_condattr_t at;
+ struct timespec ts0, ts1, ts2;
+ int res;
+ u64 sleep;
+
+ pthread_mutex_init(&m, 0);
+ pthread_condattr_init(&at);
+ pthread_condattr_setclock(&at, CLOCK_MONOTONIC);
+ pthread_cond_init(&c, &at);
+
+ clock_gettime(CLOCK_MONOTONIC, &ts0);
+ ts1 = ts0;
+ ts1.tv_sec += 2;
+
+ pthread_mutex_lock(&m);
+ do {
+ res = pthread_cond_timedwait(&c, &m, &ts1);
+ } while (res == 0);
+ pthread_mutex_unlock(&m);
+
+ clock_gettime(CLOCK_MONOTONIC, &ts2);
+ sleep = (u64)ts2.tv_sec * 1000000000 + ts2.tv_nsec -
+ ((u64)ts0.tv_sec * 1000000000 + ts0.tv_nsec);
+ if (res != ETIMEDOUT)
+ exit(printf("bad return value %d, want %d\n", res, ETIMEDOUT));
+ if (sleep < 1000000000)
+ exit(printf("bad sleep duration %lluns, want %dns\n", sleep, 1000000000));
+ fprintf(stderr, "OK\n");
+}
diff --git a/test/tsan/deep_stack1.cc b/test/tsan/deep_stack1.cc
new file mode 100644
index 000000000..3048aa874
--- /dev/null
+++ b/test/tsan/deep_stack1.cc
@@ -0,0 +1,44 @@
+// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && not %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+volatile int X;
+volatile int N;
+void (*volatile F)();
+
+static void foo() {
+ if (--N == 0)
+ X = 42;
+ else
+ F();
+}
+
+void *Thread(void *p) {
+#ifdef ORDER1
+ sleep(1);
+#endif
+ F();
+ return 0;
+}
+
+int main() {
+ N = 50000;
+ F = foo;
+ pthread_t t;
+ pthread_attr_t a;
+ pthread_attr_init(&a);
+ pthread_attr_setstacksize(&a, N * 256 + (1 << 20));
+ pthread_create(&t, &a, Thread, 0);
+#ifdef ORDER2
+ sleep(1);
+#endif
+ X = 43;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: #100 foo
+// We must output suffucuently large stack (at least 100 frames)
+
diff --git a/test/tsan/default_options.cc b/test/tsan/default_options.cc
new file mode 100644
index 000000000..62c6c028f
--- /dev/null
+++ b/test/tsan/default_options.cc
@@ -0,0 +1,32 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+extern "C" const char *__tsan_default_options() {
+ return "report_bugs=0";
+}
+
+int Global;
+
+void *Thread1(void *x) {
+ Global = 42;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global = 43;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/fd_close_norace.cc b/test/tsan/fd_close_norace.cc
new file mode 100644
index 000000000..a8b1a6d7b
--- /dev/null
+++ b/test/tsan/fd_close_norace.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+void *Thread1(void *x) {
+ int f = open("/dev/random", O_RDONLY);
+ close(f);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ int f = open("/dev/random", O_RDONLY);
+ close(f);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
+
diff --git a/test/tsan/fd_close_norace2.cc b/test/tsan/fd_close_norace2.cc
new file mode 100644
index 000000000..b42b334a2
--- /dev/null
+++ b/test/tsan/fd_close_norace2.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int pipes[2];
+
+void *Thread(void *x) {
+ // wait for shutown signal
+ while (read(pipes[0], &x, 1) != 1) {
+ }
+ close(pipes[0]);
+ close(pipes[1]);
+ return 0;
+}
+
+int main() {
+ if (pipe(pipes))
+ return 1;
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ // send shutdown signal
+ while (write(pipes[1], &t, 1) != 1) {
+ }
+ pthread_join(t, 0);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: OK
diff --git a/test/tsan/fd_dup_norace.cc b/test/tsan/fd_dup_norace.cc
new file mode 100644
index 000000000..8826f90fc
--- /dev/null
+++ b/test/tsan/fd_dup_norace.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int fds[2];
+
+void *Thread1(void *x) {
+ char buf;
+ read(fds[0], &buf, 1);
+ close(fds[0]);
+ return 0;
+}
+
+void *Thread2(void *x) {
+ close(fds[1]);
+ return 0;
+}
+
+int main() {
+ fds[0] = open("/dev/random", O_RDONLY);
+ fds[1] = dup2(fds[0], 100);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/fd_location.cc b/test/tsan/fd_location.cc
new file mode 100644
index 000000000..2b1e9c56e
--- /dev/null
+++ b/test/tsan/fd_location.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int fds[2];
+
+void *Thread1(void *x) {
+ write(fds[1], "a", 1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ close(fds[0]);
+ close(fds[1]);
+ return NULL;
+}
+
+int main() {
+ pipe(fds);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is file descriptor {{[0-9]+}} created by main thread at:
+// CHECK: #0 pipe
+// CHECK: #1 main
+
diff --git a/test/tsan/fd_pipe_norace.cc b/test/tsan/fd_pipe_norace.cc
new file mode 100644
index 000000000..2da69ea21
--- /dev/null
+++ b/test/tsan/fd_pipe_norace.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int fds[2];
+int X;
+
+void *Thread1(void *x) {
+ X = 42;
+ write(fds[1], "a", 1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ char buf;
+ while (read(fds[0], &buf, 1) != 1) {
+ }
+ X = 43;
+ return NULL;
+}
+
+int main() {
+ pipe(fds);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/fd_pipe_race.cc b/test/tsan/fd_pipe_race.cc
new file mode 100644
index 000000000..4dd2b7786
--- /dev/null
+++ b/test/tsan/fd_pipe_race.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int fds[2];
+
+void *Thread1(void *x) {
+ write(fds[1], "a", 1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ close(fds[0]);
+ close(fds[1]);
+ return NULL;
+}
+
+int main() {
+ pipe(fds);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 8
+// CHECK: #0 close
+// CHECK: #1 Thread2
+// CHECK: Previous read of size 8
+// CHECK: #0 write
+// CHECK: #1 Thread1
+
+
diff --git a/test/tsan/fd_socket_connect_norace.cc b/test/tsan/fd_socket_connect_norace.cc
new file mode 100644
index 000000000..065299a9c
--- /dev/null
+++ b/test/tsan/fd_socket_connect_norace.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+struct sockaddr_in addr;
+int X;
+
+void *ClientThread(void *x) {
+ int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ X = 42;
+ if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) {
+ perror("connect");
+ exit(1);
+ }
+ close(c);
+ return NULL;
+}
+
+int main() {
+ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ addr.sin_family = AF_INET;
+ inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
+ addr.sin_port = INADDR_ANY;
+ socklen_t len = sizeof(addr);
+ bind(s, (sockaddr*)&addr, len);
+ getsockname(s, (sockaddr*)&addr, &len);
+ listen(s, 10);
+ pthread_t t;
+ pthread_create(&t, 0, ClientThread, 0);
+ int c = accept(s, 0, 0);
+ X = 42;
+ pthread_join(t, 0);
+ close(c);
+ close(s);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
diff --git a/test/tsan/fd_socket_norace.cc b/test/tsan/fd_socket_norace.cc
new file mode 100644
index 000000000..243fc9de2
--- /dev/null
+++ b/test/tsan/fd_socket_norace.cc
@@ -0,0 +1,52 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+struct sockaddr_in addr;
+int X;
+
+void *ClientThread(void *x) {
+ X = 42;
+ int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) {
+ perror("connect");
+ exit(1);
+ }
+ if (send(c, "a", 1, 0) != 1) {
+ perror("send");
+ exit(1);
+ }
+ close(c);
+ return NULL;
+}
+
+int main() {
+ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ addr.sin_family = AF_INET;
+ inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
+ addr.sin_port = INADDR_ANY;
+ socklen_t len = sizeof(addr);
+ bind(s, (sockaddr*)&addr, len);
+ getsockname(s, (sockaddr*)&addr, &len);
+ listen(s, 10);
+ pthread_t t;
+ pthread_create(&t, 0, ClientThread, 0);
+ int c = accept(s, 0, 0);
+ char buf;
+ while (read(c, &buf, 1) != 1) {
+ }
+ X = 43;
+ close(c);
+ close(s);
+ pthread_join(t, 0);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
diff --git a/test/tsan/fd_socketpair_norace.cc b/test/tsan/fd_socketpair_norace.cc
new file mode 100644
index 000000000..f91e4eca0
--- /dev/null
+++ b/test/tsan/fd_socketpair_norace.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int fds[2];
+int X;
+
+void *Thread1(void *x) {
+ X = 42;
+ write(fds[1], "a", 1);
+ close(fds[1]);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ char buf;
+ while (read(fds[0], &buf, 1) != 1) {
+ }
+ X = 43;
+ close(fds[0]);
+ return NULL;
+}
+
+int main() {
+ socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/fd_stdout_race.cc b/test/tsan/fd_stdout_race.cc
new file mode 100644
index 000000000..4b512bb78
--- /dev/null
+++ b/test/tsan/fd_stdout_race.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int X;
+
+void *Thread1(void *x) {
+ sleep(1);
+ int f = open("/dev/random", O_RDONLY);
+ char buf;
+ read(f, &buf, 1);
+ close(f);
+ X = 42;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ X = 43;
+ write(STDOUT_FILENO, "a", 1);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4
+// CHECK: #0 Thread1
+// CHECK: Previous write of size 4
+// CHECK: #0 Thread2
+
+
diff --git a/test/tsan/fork_deadlock.cc b/test/tsan/fork_deadlock.cc
new file mode 100644
index 000000000..09500b5be
--- /dev/null
+++ b/test/tsan/fork_deadlock.cc
@@ -0,0 +1,48 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="atexit_sleep_ms=50" %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int counter;
+
+static void *incrementer(void *p) {
+ for (;;)
+ __sync_fetch_and_add(&counter, 1);
+ return 0;
+}
+
+static void *watchdog(void *p) {
+ sleep(100);
+ fprintf(stderr, "timed out after 100 seconds\n");
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t th1, th2;
+ pthread_create(&th1, 0, incrementer, 0);
+ pthread_create(&th2, 0, watchdog, 0);
+ for (int i = 0; i < 10; i++) {
+ switch (fork()) {
+ default: // parent
+ while (wait(0) < 0) {}
+ fprintf(stderr, ".");
+ break;
+ case 0: // child
+ __sync_fetch_and_add(&counter, 1);
+ exit(0);
+ break;
+ case -1: // error
+ fprintf(stderr, "failed to fork (%d)\n", errno);
+ exit(1);
+ }
+ }
+ fprintf(stderr, "OK\n");
+}
+
+// CHECK: OK
+
diff --git a/test/tsan/fork_multithreaded.cc b/test/tsan/fork_multithreaded.cc
new file mode 100644
index 000000000..474b53e42
--- /dev/null
+++ b/test/tsan/fork_multithreaded.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s -check-prefix=CHECK-DIE
+// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="die_after_fork=0" %t 2>&1 | FileCheck %s -check-prefix=CHECK-NODIE
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static void *sleeper(void *p) {
+ sleep(10);
+ return 0;
+}
+
+int main() {
+ pthread_t th;
+ pthread_create(&th, 0, sleeper, 0);
+ switch (fork()) {
+ default: // parent
+ while (wait(0) < 0) {}
+ break;
+ case 0: // child
+ {
+ pthread_t th2;
+ pthread_create(&th2, 0, sleeper, 0);
+ exit(0);
+ break;
+ }
+ case -1: // error
+ fprintf(stderr, "failed to fork (%d)\n", errno);
+ exit(1);
+ }
+ fprintf(stderr, "OK\n");
+}
+
+// CHECK-DIE: ThreadSanitizer: starting new threads after muti-threaded fork is not supported
+// CHECK-DIE: OK
+
+// CHECK-NODIE-NOT: ThreadSanitizer: starting new threads after muti-threaded fork is not supported
+// CHECK-NODIE: OK
+
diff --git a/test/tsan/fork_multithreaded3.cc b/test/tsan/fork_multithreaded3.cc
new file mode 100644
index 000000000..c4ebcce1a
--- /dev/null
+++ b/test/tsan/fork_multithreaded3.cc
@@ -0,0 +1,40 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static void *racer(void *p) {
+ *(int*)p = 42;
+ return 0;
+}
+
+int main() {
+ switch (fork()) {
+ default: // parent
+ while (wait(0) < 0) {}
+ break;
+ case 0: // child
+ {
+ int x = 0;
+ pthread_t th1, th2;
+ pthread_create(&th1, 0, racer, &x);
+ pthread_create(&th2, 0, racer, &x);
+ pthread_join(th1, 0);
+ pthread_join(th2, 0);
+ exit(0);
+ break;
+ }
+ case -1: // error
+ fprintf(stderr, "failed to fork (%d)\n", errno);
+ exit(1);
+ }
+ fprintf(stderr, "OK\n");
+}
+
+// CHECK: ThreadSanitizer: data race
+// CHECK: OK
+
diff --git a/test/tsan/free_race.c b/test/tsan/free_race.c
new file mode 100644
index 000000000..d1db9fece
--- /dev/null
+++ b/test/tsan/free_race.c
@@ -0,0 +1,49 @@
+// RUN: %clang_tsan -O1 %s -o %t
+// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOZUPP
+// RUN: TSAN_OPTIONS="suppressions=%s.supp print_suppressions=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUPP
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+int *mem;
+pthread_mutex_t mtx;
+
+void *Thread1(void *x) {
+ pthread_mutex_lock(&mtx);
+ free(mem);
+ pthread_mutex_unlock(&mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx);
+ mem[0] = 42;
+ pthread_mutex_unlock(&mtx);
+ return NULL;
+}
+
+int main() {
+ mem = (int*)malloc(100);
+ pthread_mutex_init(&mtx, 0);
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ Thread2(0);
+ pthread_join(t, NULL);
+ pthread_mutex_destroy(&mtx);
+ return 0;
+}
+
+// CHECK-NOZUPP: WARNING: ThreadSanitizer: heap-use-after-free
+// CHECK-NOZUPP: Write of size 4 at {{.*}} by main thread{{.*}}:
+// CHECK-NOZUPP: #0 Thread2
+// CHECK-NOZUPP: #1 main
+// CHECK-NOZUPP: Previous write of size 8 at {{.*}} by thread T1{{.*}}:
+// CHECK-NOZUPP: #0 free
+// CHECK-NOZUPP: #{{(1|2)}} Thread1
+// CHECK-NOZUPP: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2
+// CHECK-SUPP: ThreadSanitizer: Matched 1 suppressions
+// CHECK-SUPP: 1 race:^Thread2$
diff --git a/test/tsan/free_race.c.supp b/test/tsan/free_race.c.supp
new file mode 100644
index 000000000..f5d6a4969
--- /dev/null
+++ b/test/tsan/free_race.c.supp
@@ -0,0 +1,2 @@
+# Suppression for a use-after-free in free_race.c
+race:^Thread2$
diff --git a/test/tsan/free_race2.c b/test/tsan/free_race2.c
new file mode 100644
index 000000000..2b9a41927
--- /dev/null
+++ b/test/tsan/free_race2.c
@@ -0,0 +1,26 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+
+void __attribute__((noinline)) foo(int *mem) {
+ free(mem);
+}
+
+void __attribute__((noinline)) bar(int *mem) {
+ mem[0] = 42;
+}
+
+int main() {
+ int *mem = (int*)malloc(100);
+ foo(mem);
+ bar(mem);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: heap-use-after-free
+// CHECK: Write of size 4 at {{.*}} by main thread:
+// CHECK: #0 bar
+// CHECK: #1 main
+// CHECK: Previous write of size 8 at {{.*}} by main thread:
+// CHECK: #0 free
+// CHECK: #{{1|2}} foo
+// CHECK: #{{2|3}} main
diff --git a/test/tsan/getline_nohang.cc b/test/tsan/getline_nohang.cc
new file mode 100644
index 000000000..1dd17f064
--- /dev/null
+++ b/test/tsan/getline_nohang.cc
@@ -0,0 +1,35 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t
+
+// Make sure TSan doesn't deadlock on a file stream lock at program shutdown.
+// See https://code.google.com/p/thread-sanitizer/issues/detail?id=47
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *thread(void *unused) {
+ char *line = NULL;
+ size_t size;
+ int fd[2];
+ pipe(fd);
+ // Forge a non-standard stream to make sure it's not closed.
+ FILE *stream = fdopen(fd[0], "r");
+ while (1) {
+ volatile int res = getline(&line, &size, stream);
+ (void)res;
+ }
+ return NULL;
+}
+
+int main() {
+ pthread_t t;
+ pthread_attr_t a;
+ pthread_attr_init(&a);
+ pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
+ pthread_create(&t, &a, thread, NULL);
+ pthread_attr_destroy(&a);
+ fprintf(stderr, "DONE\n");
+ return 0;
+ // ThreadSanitizer used to hang here because of a deadlock on a file stream.
+}
+
+// CHECK: DONE
diff --git a/test/tsan/global_race.cc b/test/tsan/global_race.cc
new file mode 100644
index 000000000..0e3ce2e50
--- /dev/null
+++ b/test/tsan/global_race.cc
@@ -0,0 +1,44 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+int GlobalData[10];
+int x;
+namespace XXX {
+ struct YYY {
+ static int ZZZ[10];
+ };
+ int YYY::ZZZ[10];
+}
+
+void *Thread(void *a) {
+ sleep(1);
+ GlobalData[2] = 42;
+ x = 1;
+ XXX::YYY::ZZZ[0] = 1;
+ return 0;
+}
+
+int main() {
+ fprintf(stderr, "addr=%p\n", GlobalData);
+ fprintf(stderr, "addr2=%p\n", &x);
+ fprintf(stderr, "addr3=%p\n", XXX::YYY::ZZZ);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ GlobalData[2] = 43;
+ x = 0;
+ XXX::YYY::ZZZ[0] = 0;
+ pthread_join(t, 0);
+}
+
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: addr2=[[ADDR2:0x[0-9,a-f]+]]
+// CHECK: addr3=[[ADDR3:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is global 'GlobalData' of size 40 at [[ADDR]] ({{.*}}+0x{{[0-9,a-f]+}})
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is global 'x' of size 4 at [[ADDR2]] ({{.*}}+0x{{[0-9,a-f]+}})
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is global 'XXX::YYY::ZZZ' of size 40 at [[ADDR3]] ({{.*}}+0x{{[0-9,a-f]+}})
diff --git a/test/tsan/halt_on_error.cc b/test/tsan/halt_on_error.cc
new file mode 100644
index 000000000..fddaffff2
--- /dev/null
+++ b/test/tsan/halt_on_error.cc
@@ -0,0 +1,25 @@
+// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS halt_on_error=1" not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+int X;
+
+void *Thread(void *x) {
+ X = 42;
+ return 0;
+}
+
+int main() {
+ fprintf(stderr, "BEFORE\n");
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ X = 43;
+ pthread_join(t, 0);
+ fprintf(stderr, "AFTER\n");
+ return 0;
+}
+
+// CHECK: BEFORE
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: AFTER
+
diff --git a/test/tsan/heap_race.cc b/test/tsan/heap_race.cc
new file mode 100644
index 000000000..cc2c1fee5
--- /dev/null
+++ b/test/tsan/heap_race.cc
@@ -0,0 +1,20 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+
+void *Thread(void *a) {
+ ((int*)a)[0]++;
+ return NULL;
+}
+
+int main() {
+ int *p = new int(42);
+ pthread_t t;
+ pthread_create(&t, NULL, Thread, p);
+ p[0]++;
+ pthread_join(t, NULL);
+ delete p;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/ignore_free.cc b/test/tsan/ignore_free.cc
new file mode 100644
index 000000000..60369cc1b
--- /dev/null
+++ b/test/tsan/ignore_free.cc
@@ -0,0 +1,35 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" {
+void AnnotateIgnoreReadsBegin(const char *f, int l);
+void AnnotateIgnoreReadsEnd(const char *f, int l);
+void AnnotateIgnoreWritesBegin(const char *f, int l);
+void AnnotateIgnoreWritesEnd(const char *f, int l);
+}
+
+void *Thread(void *p) {
+ *(int*)p = 42;
+ return 0;
+}
+
+int main() {
+ int *p = new int(0);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, p);
+ sleep(1);
+ AnnotateIgnoreReadsBegin(__FILE__, __LINE__);
+ AnnotateIgnoreWritesBegin(__FILE__, __LINE__);
+ free(p);
+ AnnotateIgnoreReadsEnd(__FILE__, __LINE__);
+ AnnotateIgnoreWritesEnd(__FILE__, __LINE__);
+ pthread_join(t, 0);
+ fprintf(stderr, "OK\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: OK
diff --git a/test/tsan/ignore_lib0.cc b/test/tsan/ignore_lib0.cc
new file mode 100644
index 000000000..ea0f061e6
--- /dev/null
+++ b/test/tsan/ignore_lib0.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib0.so
+// RUN: %clangxx_tsan -O1 %s -L%T -lignore_lib0 -o %t
+// RUN: echo running w/o suppressions:
+// RUN: LD_LIBRARY_PATH=%T not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: echo running with suppressions:
+// RUN: LD_LIBRARY_PATH=%T TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
+
+// Tests that interceptors coming from a library specified in called_from_lib
+// suppression are ignored.
+
+#ifndef LIB
+
+extern "C" void libfunc();
+
+int main() {
+ libfunc();
+}
+
+#else // #ifdef LIB
+
+#include "ignore_lib_lib.h"
+
+#endif // #ifdef LIB
+
+// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race
+// CHECK-NOSUPP: OK
+
+// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-WITHSUPP: OK
+
diff --git a/test/tsan/ignore_lib0.cc.supp b/test/tsan/ignore_lib0.cc.supp
new file mode 100644
index 000000000..7728c926b
--- /dev/null
+++ b/test/tsan/ignore_lib0.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:/libignore_lib0.so
+
diff --git a/test/tsan/ignore_lib1.cc b/test/tsan/ignore_lib1.cc
new file mode 100644
index 000000000..c4f2e7344
--- /dev/null
+++ b/test/tsan/ignore_lib1.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib1.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: echo running w/o suppressions:
+// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: echo running with suppressions:
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
+
+// Tests that interceptors coming from a dynamically loaded library specified
+// in called_from_lib suppression are ignored.
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+ std::string lib = std::string(dirname(argv[0])) + "/libignore_lib1.so";
+ void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
+ if (h == 0)
+ exit(printf("failed to load the library (%d)\n", errno));
+ void (*f)() = (void(*)())dlsym(h, "libfunc");
+ if (f == 0)
+ exit(printf("failed to find the func (%d)\n", errno));
+ f();
+}
+
+#else // #ifdef LIB
+
+#include "ignore_lib_lib.h"
+
+#endif // #ifdef LIB
+
+// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race
+// CHECK-NOSUPP: OK
+
+// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-WITHSUPP: OK
+
diff --git a/test/tsan/ignore_lib1.cc.supp b/test/tsan/ignore_lib1.cc.supp
new file mode 100644
index 000000000..9f4119ec0
--- /dev/null
+++ b/test/tsan/ignore_lib1.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:/libignore_lib1.so$
+
diff --git a/test/tsan/ignore_lib2.cc b/test/tsan/ignore_lib2.cc
new file mode 100644
index 000000000..97f9419e4
--- /dev/null
+++ b/test/tsan/ignore_lib2.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_0.so
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_1.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s
+
+// Tests that called_from_lib suppression matched against 2 libraries
+// causes program crash (this is not supported).
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+ std::string lib0 = std::string(dirname(argv[0])) + "/libignore_lib2_0.so";
+ std::string lib1 = std::string(dirname(argv[0])) + "/libignore_lib2_1.so";
+ dlopen(lib0.c_str(), RTLD_GLOBAL | RTLD_NOW);
+ dlopen(lib1.c_str(), RTLD_GLOBAL | RTLD_NOW);
+ fprintf(stderr, "OK\n");
+}
+
+#else // #ifdef LIB
+
+extern "C" void libfunc() {
+}
+
+#endif // #ifdef LIB
+
+// CHECK: ThreadSanitizer: called_from_lib suppression 'ignore_lib2' is matched against 2 libraries
+// CHECK-NOT: OK
+
diff --git a/test/tsan/ignore_lib2.cc.supp b/test/tsan/ignore_lib2.cc.supp
new file mode 100644
index 000000000..1419c71c6
--- /dev/null
+++ b/test/tsan/ignore_lib2.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:ignore_lib2
+
diff --git a/test/tsan/ignore_lib3.cc b/test/tsan/ignore_lib3.cc
new file mode 100644
index 000000000..8f237fcc8
--- /dev/null
+++ b/test/tsan/ignore_lib3.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib3.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s
+
+// Tests that unloading of a library matched against called_from_lib suppression
+// causes program crash (this is not supported).
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+ std::string lib = std::string(dirname(argv[0])) + "/libignore_lib3.so";
+ void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
+ dlclose(h);
+ fprintf(stderr, "OK\n");
+}
+
+#else // #ifdef LIB
+
+extern "C" void libfunc() {
+}
+
+#endif // #ifdef LIB
+
+// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
+// CHECK-NOT: OK
+
diff --git a/test/tsan/ignore_lib3.cc.supp b/test/tsan/ignore_lib3.cc.supp
new file mode 100644
index 000000000..975dbcef9
--- /dev/null
+++ b/test/tsan/ignore_lib3.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:ignore_lib3.so
+
diff --git a/test/tsan/ignore_lib_lib.h b/test/tsan/ignore_lib_lib.h
new file mode 100644
index 000000000..2bfe84dfc
--- /dev/null
+++ b/test/tsan/ignore_lib_lib.h
@@ -0,0 +1,25 @@
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *volatile mem;
+volatile int len;
+
+void *Thread(void *p) {
+ while ((p = __atomic_load_n(&mem, __ATOMIC_ACQUIRE)) == 0)
+ usleep(100);
+ memset(p, 0, len);
+ return 0;
+}
+
+extern "C" void libfunc() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ len = 10;
+ __atomic_store_n(&mem, malloc(len), __ATOMIC_RELEASE);
+ pthread_join(t, 0);
+ free(mem);
+ fprintf(stderr, "OK\n");
+}
diff --git a/test/tsan/ignore_malloc.cc b/test/tsan/ignore_malloc.cc
new file mode 100644
index 000000000..63bd4241b
--- /dev/null
+++ b/test/tsan/ignore_malloc.cc
@@ -0,0 +1,38 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" {
+void AnnotateIgnoreReadsBegin(const char *f, int l);
+void AnnotateIgnoreReadsEnd(const char *f, int l);
+void AnnotateIgnoreWritesBegin(const char *f, int l);
+void AnnotateIgnoreWritesEnd(const char *f, int l);
+}
+
+int *g;
+
+void *Thread(void *a) {
+ int *p = 0;
+ while ((p = __atomic_load_n(&g, __ATOMIC_RELAXED)) == 0)
+ usleep(100);
+ *p = 42;
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ AnnotateIgnoreWritesBegin(__FILE__, __LINE__);
+ int *p = new int(0);
+ AnnotateIgnoreWritesEnd(__FILE__, __LINE__);
+ __atomic_store_n(&g, p, __ATOMIC_RELAXED);
+ pthread_join(t, 0);
+ delete p;
+ fprintf(stderr, "OK\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: OK
diff --git a/test/tsan/ignore_race.cc b/test/tsan/ignore_race.cc
new file mode 100644
index 000000000..23d74d0ed
--- /dev/null
+++ b/test/tsan/ignore_race.cc
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l);
+extern "C" void AnnotateIgnoreWritesEnd(const char *f, int l);
+extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l);
+extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l);
+
+void *Thread(void *x) {
+ AnnotateIgnoreWritesBegin(__FILE__, __LINE__);
+ AnnotateIgnoreReadsBegin(__FILE__, __LINE__);
+ Global = 42;
+ AnnotateIgnoreReadsEnd(__FILE__, __LINE__);
+ AnnotateIgnoreWritesEnd(__FILE__, __LINE__);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ sleep(1);
+ Global = 43;
+ pthread_join(t, 0);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/ignore_sync.cc b/test/tsan/ignore_sync.cc
new file mode 100644
index 000000000..67f2d906d
--- /dev/null
+++ b/test/tsan/ignore_sync.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+extern "C" void AnnotateIgnoreSyncBegin(const char*, int);
+extern "C" void AnnotateIgnoreSyncEnd(const char*, int);
+
+int Global;
+pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void *Thread(void *x) {
+ AnnotateIgnoreSyncBegin(0, 0);
+ pthread_mutex_lock(&Mutex);
+ Global++;
+ pthread_mutex_unlock(&Mutex);
+ AnnotateIgnoreSyncEnd(0, 0);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_mutex_lock(&Mutex);
+ Global++;
+ pthread_mutex_unlock(&Mutex);
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+
diff --git a/test/tsan/inlined_memcpy_race.cc b/test/tsan/inlined_memcpy_race.cc
new file mode 100644
index 000000000..5dda36e4b
--- /dev/null
+++ b/test/tsan/inlined_memcpy_race.cc
@@ -0,0 +1,55 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int x[4], y[4], z[4];
+
+void *MemCpyThread(void *a) {
+ memcpy((int*)a, z, 16);
+ return NULL;
+}
+
+void *MemMoveThread(void *a) {
+ memmove((int*)a, z, 16);
+ return NULL;
+}
+
+void *MemSetThread(void *a) {
+ sleep(1);
+ memset((int*)a, 0, 16);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ // Race on x between memcpy and memset
+ pthread_create(&t[0], NULL, MemCpyThread, x);
+ pthread_create(&t[1], NULL, MemSetThread, x);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ // Race on y between memmove and memset
+ pthread_create(&t[0], NULL, MemMoveThread, y);
+ pthread_create(&t[1], NULL, MemSetThread, y);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+
+ printf("PASS\n");
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: #0 memset
+// CHECK: #1 MemSetThread
+// CHECK: Previous write
+// CHECK: #0 memcpy
+// CHECK: #1 MemCpyThread
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: #0 memset
+// CHECK: #1 MemSetThread
+// CHECK: Previous write
+// CHECK: #0 memmove
+// CHECK: #1 MemMoveThread
diff --git a/test/tsan/interface_atomic_test.c b/test/tsan/interface_atomic_test.c
new file mode 100644
index 000000000..7f274a0d1
--- /dev/null
+++ b/test/tsan/interface_atomic_test.c
@@ -0,0 +1,16 @@
+// Test that we can include header with TSan atomic interface.
+// RUN: %clang_tsan %s -o %t && %t | FileCheck %s
+#include <sanitizer/tsan_interface_atomic.h>
+#include <stdio.h>
+
+int main() {
+ __tsan_atomic32 a;
+ __tsan_atomic32_store(&a, 100, __tsan_memory_order_release);
+ int res = __tsan_atomic32_load(&a, __tsan_memory_order_acquire);
+ if (res == 100) {
+ // CHECK: PASS
+ printf("PASS\n");
+ return 0;
+ }
+ return 1;
+}
diff --git a/test/tsan/java.h b/test/tsan/java.h
new file mode 100644
index 000000000..7aa0bca32
--- /dev/null
+++ b/test/tsan/java.h
@@ -0,0 +1,20 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" {
+typedef unsigned long jptr; // NOLINT
+void __tsan_java_preinit(const char *libjvm_path);
+void __tsan_java_init(jptr heap_begin, jptr heap_size);
+int __tsan_java_fini();
+void __tsan_java_alloc(jptr ptr, jptr size);
+void __tsan_java_free(jptr ptr, jptr size);
+void __tsan_java_move(jptr src, jptr dst, jptr size);
+void __tsan_java_mutex_lock(jptr addr);
+void __tsan_java_mutex_unlock(jptr addr);
+void __tsan_java_mutex_read_lock(jptr addr);
+void __tsan_java_mutex_read_unlock(jptr addr);
+void __tsan_java_mutex_lock_rec(jptr addr, int rec);
+int __tsan_java_mutex_unlock_rec(jptr addr);
+}
diff --git a/test/tsan/java_alloc.cc b/test/tsan/java_alloc.cc
new file mode 100644
index 000000000..4dbce70c3
--- /dev/null
+++ b/test/tsan/java_alloc.cc
@@ -0,0 +1,32 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+
+int const kHeapSize = 1024 * 1024;
+
+void stress(jptr addr) {
+ for (jptr sz = 8; sz <= 32; sz <<= 1) {
+ for (jptr i = 0; i < kHeapSize / 4 / sz; i++) {
+ __tsan_java_alloc(addr + i * sz, sz);
+ }
+ __tsan_java_move(addr, addr + kHeapSize / 2, kHeapSize / 4);
+ __tsan_java_free(addr + kHeapSize / 2, kHeapSize / 4);
+ }
+}
+
+void *Thread(void *p) {
+ stress((jptr)p);
+ return 0;
+}
+
+int main() {
+ jptr jheap = (jptr)malloc(kHeapSize);
+ __tsan_java_init(jheap, kHeapSize);
+ pthread_t th;
+ pthread_create(&th, 0, Thread, (void*)(jheap + kHeapSize / 4));
+ stress(jheap);
+ pthread_join(th, 0);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/java_lock.cc b/test/tsan/java_lock.cc
new file mode 100644
index 000000000..d9db10350
--- /dev/null
+++ b/test/tsan/java_lock.cc
@@ -0,0 +1,35 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+#include <unistd.h>
+
+jptr varaddr;
+jptr lockaddr;
+
+void *Thread(void *p) {
+ sleep(1);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 42;
+ __tsan_java_mutex_unlock(lockaddr);
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap;
+ lockaddr = (jptr)jheap + 8;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 43;
+ __tsan_java_mutex_unlock(lockaddr);
+ pthread_join(th, 0);
+ __tsan_java_free((jptr)jheap, kBlockSize);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/java_lock_move.cc b/test/tsan/java_lock_move.cc
new file mode 100644
index 000000000..48b5a5a88
--- /dev/null
+++ b/test/tsan/java_lock_move.cc
@@ -0,0 +1,40 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+
+jptr varaddr;
+jptr lockaddr;
+jptr varaddr2;
+jptr lockaddr2;
+
+void *Thread(void *p) {
+ sleep(1);
+ __tsan_java_mutex_lock(lockaddr2);
+ *(int*)varaddr2 = 42;
+ __tsan_java_mutex_unlock(lockaddr2);
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 64;
+ int const kMove = 1024;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap;
+ lockaddr = (jptr)jheap + 46;
+ varaddr2 = varaddr + kMove;
+ lockaddr2 = lockaddr + kMove;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 43;
+ __tsan_java_mutex_unlock(lockaddr);
+ __tsan_java_move(varaddr, varaddr2, kBlockSize);
+ pthread_join(th, 0);
+ __tsan_java_free(varaddr2, kBlockSize);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/java_lock_rec.cc b/test/tsan/java_lock_rec.cc
new file mode 100644
index 000000000..5cc80d4a3
--- /dev/null
+++ b/test/tsan/java_lock_rec.cc
@@ -0,0 +1,54 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+#include <unistd.h>
+
+jptr varaddr;
+jptr lockaddr;
+
+void *Thread(void *p) {
+ __tsan_java_mutex_lock(lockaddr);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 42;
+ int rec = __tsan_java_mutex_unlock_rec(lockaddr);
+ if (rec != 2) {
+ printf("FAILED 0 rec=%d\n", rec);
+ exit(1);
+ }
+ sleep(2);
+ __tsan_java_mutex_lock_rec(lockaddr, rec);
+ if (*(int*)varaddr != 43) {
+ printf("FAILED 3 var=%d\n", *(int*)varaddr);
+ exit(1);
+ }
+ __tsan_java_mutex_unlock(lockaddr);
+ __tsan_java_mutex_unlock(lockaddr);
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap;
+ *(int*)varaddr = 0;
+ lockaddr = (jptr)jheap + 8;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ sleep(1);
+ __tsan_java_mutex_lock(lockaddr);
+ if (*(int*)varaddr != 42) {
+ printf("FAILED 1 var=%d\n", *(int*)varaddr);
+ exit(1);
+ }
+ *(int*)varaddr = 43;
+ __tsan_java_mutex_unlock(lockaddr);
+ pthread_join(th, 0);
+ __tsan_java_free((jptr)jheap, kBlockSize);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAILED
diff --git a/test/tsan/java_lock_rec_race.cc b/test/tsan/java_lock_rec_race.cc
new file mode 100644
index 000000000..a868e260c
--- /dev/null
+++ b/test/tsan/java_lock_rec_race.cc
@@ -0,0 +1,48 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include "java.h"
+#include <unistd.h>
+
+jptr varaddr;
+jptr lockaddr;
+
+void *Thread(void *p) {
+ __tsan_java_mutex_lock(lockaddr);
+ __tsan_java_mutex_lock(lockaddr);
+ __tsan_java_mutex_lock(lockaddr);
+ int rec = __tsan_java_mutex_unlock_rec(lockaddr);
+ if (rec != 3) {
+ printf("FAILED 0 rec=%d\n", rec);
+ exit(1);
+ }
+ *(int*)varaddr = 42;
+ sleep(2);
+ __tsan_java_mutex_lock_rec(lockaddr, rec);
+ __tsan_java_mutex_unlock(lockaddr);
+ __tsan_java_mutex_unlock(lockaddr);
+ __tsan_java_mutex_unlock(lockaddr);
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap;
+ *(int*)varaddr = 0;
+ lockaddr = (jptr)jheap + 8;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ sleep(1);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 43;
+ __tsan_java_mutex_unlock(lockaddr);
+ pthread_join(th, 0);
+ __tsan_java_free((jptr)jheap, kBlockSize);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAILED
diff --git a/test/tsan/java_race.cc b/test/tsan/java_race.cc
new file mode 100644
index 000000000..4841a7db0
--- /dev/null
+++ b/test/tsan/java_race.cc
@@ -0,0 +1,23 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include "java.h"
+
+void *Thread(void *p) {
+ *(int*)p = 42;
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ pthread_t th;
+ pthread_create(&th, 0, Thread, jheap);
+ *(int*)jheap = 43;
+ pthread_join(th, 0);
+ __tsan_java_free((jptr)jheap, kBlockSize);
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/java_race_move.cc b/test/tsan/java_race_move.cc
new file mode 100644
index 000000000..6da8a1064
--- /dev/null
+++ b/test/tsan/java_race_move.cc
@@ -0,0 +1,31 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include "java.h"
+
+jptr varaddr;
+jptr varaddr2;
+
+void *Thread(void *p) {
+ sleep(1);
+ *(int*)varaddr2 = 42;
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 64;
+ int const kMove = 1024;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap + 16;
+ varaddr2 = varaddr + kMove;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ *(int*)varaddr = 43;
+ __tsan_java_move(varaddr, varaddr2, kBlockSize);
+ pthread_join(th, 0);
+ __tsan_java_free(varaddr2, kBlockSize);
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/java_rwlock.cc b/test/tsan/java_rwlock.cc
new file mode 100644
index 000000000..d1f38733b
--- /dev/null
+++ b/test/tsan/java_rwlock.cc
@@ -0,0 +1,35 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+#include <unistd.h>
+
+jptr varaddr;
+jptr lockaddr;
+
+void *Thread(void *p) {
+ sleep(1);
+ __tsan_java_mutex_read_lock(lockaddr);
+ *(int*)varaddr = 42;
+ __tsan_java_mutex_read_unlock(lockaddr);
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap;
+ lockaddr = (jptr)jheap + 8;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 43;
+ __tsan_java_mutex_unlock(lockaddr);
+ pthread_join(th, 0);
+ __tsan_java_free((jptr)jheap, kBlockSize);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/lit.cfg b/test/tsan/lit.cfg
new file mode 100644
index 000000000..b437dafa4
--- /dev/null
+++ b/test/tsan/lit.cfg
@@ -0,0 +1,47 @@
+# -*- Python -*-
+
+import os
+
+def get_required_attr(config, attr_name):
+ attr_value = getattr(config, attr_name, None)
+ if not attr_value:
+ lit_config.fatal(
+ "No attribute %r in test configuration! You may need to run "
+ "tests from your build directory or add this attribute "
+ "to lit.site.cfg " % attr_name)
+ return attr_value
+
+# Setup config name.
+config.name = 'ThreadSanitizer'
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+# Setup environment variables for running ThreadSanitizer.
+tsan_options = "atexit_sleep_ms=0"
+
+config.environment['TSAN_OPTIONS'] = tsan_options
+
+# Setup default compiler flags used with -fsanitize=thread option.
+# FIXME: Review the set of required flags and check if it can be reduced.
+clang_tsan_cflags = ("-fsanitize=thread "
+ + "-g "
+ + "-Wall "
+ + "-lpthread "
+ + "-ldl "
+ + "-m64 ")
+clang_tsan_cxxflags = "--driver-mode=g++ " + clang_tsan_cflags
+config.substitutions.append( ("%clangxx_tsan ", (" " + config.clang + " " +
+ clang_tsan_cxxflags + " ")) )
+config.substitutions.append( ("%clang_tsan ", (" " + config.clang + " " +
+ clang_tsan_cflags + " ")) )
+
+# Define CHECK-%os to check for OS-dependent output.
+config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os)))
+
+# Default test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp']
+
+# ThreadSanitizer tests are currently supported on Linux only.
+if config.host_os not in ['Linux']:
+ config.unsupported = True
diff --git a/test/tsan/lit.site.cfg.in b/test/tsan/lit.site.cfg.in
new file mode 100644
index 000000000..aebdd5ebc
--- /dev/null
+++ b/test/tsan/lit.site.cfg.in
@@ -0,0 +1,8 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+# Load common config for all compiler-rt lit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
diff --git a/test/tsan/load_shared_lib.cc b/test/tsan/load_shared_lib.cc
new file mode 100644
index 000000000..d60cd5700
--- /dev/null
+++ b/test/tsan/load_shared_lib.cc
@@ -0,0 +1,44 @@
+// Check that if the list of shared libraries changes between the two race
+// reports, the second report occurring in a new shared library is still
+// symbolized correctly.
+
+// RUN: %clangxx_tsan -O1 %p/SharedLibs/load_shared_lib-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+
+#include <string>
+
+int GLOB = 0;
+
+void *write_glob(void *unused) {
+ GLOB++;
+ return NULL;
+}
+
+void race_two_threads(void *(*access_callback)(void *unused)) {
+ pthread_t t1, t2;
+ pthread_create(&t1, NULL, access_callback, NULL);
+ pthread_create(&t2, NULL, access_callback, NULL);
+ pthread_join(t1, NULL);
+ pthread_join(t2, NULL);
+}
+
+int main(int argc, char *argv[]) {
+ std::string path = std::string(argv[0]) + std::string("-so.so");
+ race_two_threads(write_glob);
+ // CHECK: write_glob
+ void *lib = dlopen(path.c_str(), RTLD_NOW);
+ if (!lib) {
+ printf("error in dlopen(): %s\n", dlerror());
+ return 1;
+ }
+ void *(*write_from_so)(void *unused);
+ *(void **)&write_from_so = dlsym(lib, "write_from_so");
+ race_two_threads(write_from_so);
+ // CHECK: write_from_so
+ return 0;
+}
diff --git a/test/tsan/longjmp.cc b/test/tsan/longjmp.cc
new file mode 100644
index 000000000..d9ca4ca5e
--- /dev/null
+++ b/test/tsan/longjmp.cc
@@ -0,0 +1,22 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+int foo(jmp_buf env) {
+ longjmp(env, 42);
+}
+
+int main() {
+ jmp_buf env;
+ if (setjmp(env) == 42) {
+ printf("JUMPED\n");
+ return 0;
+ }
+ foo(env);
+ printf("FAILED\n");
+ return 0;
+}
+
+// CHECK-NOT: FAILED
+// CHECK: JUMPED
diff --git a/test/tsan/longjmp2.cc b/test/tsan/longjmp2.cc
new file mode 100644
index 000000000..0d551fa19
--- /dev/null
+++ b/test/tsan/longjmp2.cc
@@ -0,0 +1,24 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+int foo(sigjmp_buf env) {
+ printf("env=%p\n", env);
+ siglongjmp(env, 42);
+}
+
+int main() {
+ sigjmp_buf env;
+ printf("env=%p\n", env);
+ if (sigsetjmp(env, 1) == 42) {
+ printf("JUMPED\n");
+ return 0;
+ }
+ foo(env);
+ printf("FAILED\n");
+ return 0;
+}
+
+// CHECK-NOT: FAILED
+// CHECK: JUMPED
diff --git a/test/tsan/longjmp3.cc b/test/tsan/longjmp3.cc
new file mode 100644
index 000000000..ae2cfd05f
--- /dev/null
+++ b/test/tsan/longjmp3.cc
@@ -0,0 +1,48 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+void bar(jmp_buf env) {
+ volatile int x = 42;
+ longjmp(env, 42);
+ x++;
+}
+
+void foo(jmp_buf env) {
+ volatile int x = 42;
+ bar(env);
+ x++;
+}
+
+void badguy() {
+ pthread_mutex_t mtx;
+ pthread_mutex_init(&mtx, 0);
+ pthread_mutex_lock(&mtx);
+ pthread_mutex_destroy(&mtx);
+}
+
+void mymain() {
+ jmp_buf env;
+ if (setjmp(env) == 42) {
+ badguy();
+ return;
+ }
+ foo(env);
+ printf("FAILED\n");
+}
+
+int main() {
+ volatile int x = 42;
+ mymain();
+ return x;
+}
+
+// CHECK-NOT: FAILED
+// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex
+// CHECK: #0 pthread_mutex_destroy
+// CHECK: #1 badguy
+// CHECK: #2 mymain
+// CHECK: #3 main
+
diff --git a/test/tsan/longjmp4.cc b/test/tsan/longjmp4.cc
new file mode 100644
index 000000000..6b0526ef3
--- /dev/null
+++ b/test/tsan/longjmp4.cc
@@ -0,0 +1,51 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <string.h>
+
+void bar(jmp_buf env) {
+ volatile int x = 42;
+ jmp_buf env2;
+ memcpy(env2, env, sizeof(jmp_buf));
+ longjmp(env2, 42);
+ x++;
+}
+
+void foo(jmp_buf env) {
+ volatile int x = 42;
+ bar(env);
+ x++;
+}
+
+void badguy() {
+ pthread_mutex_t mtx;
+ pthread_mutex_init(&mtx, 0);
+ pthread_mutex_lock(&mtx);
+ pthread_mutex_destroy(&mtx);
+}
+
+void mymain() {
+ jmp_buf env;
+ if (setjmp(env) == 42) {
+ badguy();
+ return;
+ }
+ foo(env);
+ printf("FAILED\n");
+}
+
+int main() {
+ volatile int x = 42;
+ mymain();
+ return x;
+}
+
+// CHECK-NOT: FAILED
+// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex
+// CHECK: #0 pthread_mutex_destroy
+// CHECK: #1 badguy
+// CHECK: #2 mymain
+// CHECK: #3 main
+
diff --git a/test/tsan/malloc_hook.cc b/test/tsan/malloc_hook.cc
new file mode 100644
index 000000000..82eb6900e
--- /dev/null
+++ b/test/tsan/malloc_hook.cc
@@ -0,0 +1,52 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+static int malloc_count;
+static int free_count;
+
+extern "C" {
+void __tsan_malloc_hook(void *ptr, size_t size) {
+ (void)ptr;
+ (void)size;
+ __sync_fetch_and_add(&malloc_count, 1);
+}
+
+void __tsan_free_hook(void *ptr) {
+ (void)ptr;
+ __sync_fetch_and_add(&free_count, 1);
+}
+}
+
+void *Thread1(void *x) {
+ ((int*)x)[0]++;
+ return 0;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ ((int*)x)[0]++;
+ return 0;
+}
+
+int main() {
+ int *x = new int;
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, x);
+ pthread_create(&t[1], 0, Thread2, x);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ delete x;
+ if (malloc_count == 0 || free_count == 0) {
+ fprintf(stderr, "FAILED %d %d\n", malloc_count, free_count);
+ exit(1);
+ }
+ fprintf(stderr, "DONE\n");
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAILED
+// CHECK: DONE
diff --git a/test/tsan/malloc_overflow.cc b/test/tsan/malloc_overflow.cc
new file mode 100644
index 000000000..afbebc8be
--- /dev/null
+++ b/test/tsan/malloc_overflow.cc
@@ -0,0 +1,23 @@
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: TSAN_OPTIONS=allocator_may_return_null=1 %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+ void *p = malloc((size_t)-1);
+ if (p != 0)
+ printf("FAIL malloc(-1) = %p\n", p);
+ p = malloc((size_t)-1 / 2);
+ if (p != 0)
+ printf("FAIL malloc(-1/2) = %p\n", p);
+ p = calloc((size_t)-1, (size_t)-1);
+ if (p != 0)
+ printf("FAIL calloc(-1, -1) = %p\n", p);
+ p = calloc((size_t)-1 / 2, (size_t)-1 / 2);
+ if (p != 0)
+ printf("FAIL calloc(-1/2, -1/2) = %p\n", p);
+ printf("OK\n");
+}
+
+// CHECK-NOT: FAIL
+// CHECK-NOT: failed to allocate
diff --git a/test/tsan/malloc_stack.cc b/test/tsan/malloc_stack.cc
new file mode 100644
index 000000000..3603497ef
--- /dev/null
+++ b/test/tsan/malloc_stack.cc
@@ -0,0 +1,25 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+_Atomic(int*) p;
+
+void *thr(void *a) {
+ sleep(1);
+ int *pp = __c11_atomic_load(&p, __ATOMIC_RELAXED);
+ *pp = 42;
+ return 0;
+}
+
+int main() {
+ pthread_t th;
+ pthread_create(&th, 0, thr, p);
+ __c11_atomic_store(&p, new int, __ATOMIC_RELAXED);
+ pthread_join(th, 0);
+}
+
+// CHECK: data race
+// CHECK: Previous write
+// CHECK: #0 operator new
+// CHECK: Location is heap block
+// CHECK: #0 operator new
diff --git a/test/tsan/memcpy_race.cc b/test/tsan/memcpy_race.cc
new file mode 100644
index 000000000..8f3911367
--- /dev/null
+++ b/test/tsan/memcpy_race.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+char *data = new char[10];
+char *data1 = new char[10];
+char *data2 = new char[10];
+
+void *Thread1(void *x) {
+ static volatile int size = 1;
+ memcpy(data+5, data1, size);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ static volatile int size = 4;
+ sleep(1);
+ memcpy(data+3, data2, size);
+ return NULL;
+}
+
+int main() {
+ fprintf(stderr, "addr=%p\n", &data[5]);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 1 at [[ADDR]] by thread T2:
+// CHECK: #0 memcpy
+// CHECK: #1 Thread2
+// CHECK: Previous write of size 1 at [[ADDR]] by thread T1:
+// CHECK: #0 memcpy
+// CHECK: #1 Thread1
diff --git a/test/tsan/mop_with_offset.cc b/test/tsan/mop_with_offset.cc
new file mode 100644
index 000000000..2b6a4ff50
--- /dev/null
+++ b/test/tsan/mop_with_offset.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *Thread1(void *x) {
+ int *p = (int*)x;
+ p[0] = 1;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ char *p = (char*)x;
+ p[2] = 1;
+ return NULL;
+}
+
+int main() {
+ int *data = new int(42);
+ fprintf(stderr, "ptr1=%p\n", data);
+ fprintf(stderr, "ptr2=%p\n", (char*)data + 2);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, data);
+ pthread_create(&t[1], NULL, Thread2, data);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ delete data;
+}
+
+// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]]
+// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 1 at [[PTR2]] by thread T2:
+// CHECK: Previous write of size 4 at [[PTR1]] by thread T1:
diff --git a/test/tsan/mop_with_offset2.cc b/test/tsan/mop_with_offset2.cc
new file mode 100644
index 000000000..037c4db5f
--- /dev/null
+++ b/test/tsan/mop_with_offset2.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *Thread1(void *x) {
+ sleep(1);
+ int *p = (int*)x;
+ p[0] = 1;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ char *p = (char*)x;
+ p[2] = 1;
+ return NULL;
+}
+
+int main() {
+ int *data = new int(42);
+ fprintf(stderr, "ptr1=%p\n", data);
+ fprintf(stderr, "ptr2=%p\n", (char*)data + 2);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, data);
+ pthread_create(&t[1], NULL, Thread2, data);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ delete data;
+}
+
+// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]]
+// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4 at [[PTR1]] by thread T1:
+// CHECK: Previous write of size 1 at [[PTR2]] by thread T2:
diff --git a/test/tsan/mutex_cycle2.c b/test/tsan/mutex_cycle2.c
new file mode 100644
index 000000000..cd1006d47
--- /dev/null
+++ b/test/tsan/mutex_cycle2.c
@@ -0,0 +1,25 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: TSAN_OPTIONS=detect_deadlocks=1 %t 2>&1 | FileCheck %s
+#include <pthread.h>
+
+int main() {
+ pthread_mutex_t mu1, mu2;
+ pthread_mutex_init(&mu1, NULL);
+ pthread_mutex_init(&mu2, NULL);
+
+ // mu1 => mu2
+ pthread_mutex_lock(&mu1);
+ pthread_mutex_lock(&mu2);
+ pthread_mutex_unlock(&mu2);
+ pthread_mutex_unlock(&mu1);
+
+ // mu2 => mu1
+ pthread_mutex_lock(&mu2);
+ pthread_mutex_lock(&mu1);
+ // CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock)
+ pthread_mutex_unlock(&mu1);
+ pthread_mutex_unlock(&mu2);
+
+ pthread_mutex_destroy(&mu1);
+ pthread_mutex_destroy(&mu2);
+}
diff --git a/test/tsan/mutex_destroy_locked.cc b/test/tsan/mutex_destroy_locked.cc
new file mode 100644
index 000000000..9b020d31b
--- /dev/null
+++ b/test/tsan/mutex_destroy_locked.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+int main() {
+ pthread_mutex_t m;
+ pthread_mutex_init(&m, 0);
+ pthread_mutex_lock(&m);
+ pthread_mutex_destroy(&m);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex
+// CHECK: #0 pthread_mutex_destroy
+// CHECK: #1 main
+// CHECK: and:
+// CHECK: #0 pthread_mutex_lock
+// CHECK: #1 main
+// CHECK: Mutex {{.*}} created at:
+// CHECK: #0 pthread_mutex_init
+// CHECK: #1 main
+// CHECK: SUMMARY: ThreadSanitizer: destroy of a locked mutex{{.*}}main
diff --git a/test/tsan/mutex_robust.cc b/test/tsan/mutex_robust.cc
new file mode 100644
index 000000000..b82661607
--- /dev/null
+++ b/test/tsan/mutex_robust.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+pthread_mutex_t m;
+
+void *thr(void *p) {
+ pthread_mutex_lock(&m);
+ return 0;
+}
+
+int main() {
+ pthread_mutexattr_t a;
+ pthread_mutexattr_init(&a);
+ pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST);
+ pthread_mutex_init(&m, &a);
+ pthread_t th;
+ pthread_create(&th, 0, thr, 0);
+ sleep(1);
+ if (pthread_mutex_lock(&m) != EOWNERDEAD) {
+ fprintf(stderr, "not EOWNERDEAD\n");
+ exit(1);
+ }
+ pthread_join(th, 0);
+ fprintf(stderr, "DONE\n");
+}
+
+// This is a correct code, and tsan must not bark.
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK-NOT: EOWNERDEAD
+// CHECK: DONE
+// CHECK-NOT: WARNING: ThreadSanitizer
+
diff --git a/test/tsan/mutex_robust2.cc b/test/tsan/mutex_robust2.cc
new file mode 100644
index 000000000..5bd7ff682
--- /dev/null
+++ b/test/tsan/mutex_robust2.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+pthread_mutex_t m;
+int x;
+
+void *thr(void *p) {
+ pthread_mutex_lock(&m);
+ x = 42;
+ return 0;
+}
+
+int main() {
+ pthread_mutexattr_t a;
+ pthread_mutexattr_init(&a);
+ pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST);
+ pthread_mutex_init(&m, &a);
+ pthread_t th;
+ pthread_create(&th, 0, thr, 0);
+ sleep(1);
+ if (pthread_mutex_trylock(&m) != EOWNERDEAD) {
+ fprintf(stderr, "not EOWNERDEAD\n");
+ exit(1);
+ }
+ x = 43;
+ pthread_join(th, 0);
+ fprintf(stderr, "DONE\n");
+}
+
+// This is a false positive, tsan must not bark at the data race.
+// But currently it does.
+// CHECK-NOT: WARNING: ThreadSanitizer WARNING: double lock of mutex
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: EOWNERDEAD
+// CHECK: DONE
+// CHECK-NOT: WARNING: ThreadSanitizer
+
diff --git a/test/tsan/mutexset1.cc b/test/tsan/mutexset1.cc
new file mode 100644
index 000000000..ca87a7ba0
--- /dev/null
+++ b/test/tsan/mutexset1.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx);
+ Global++;
+ pthread_mutex_unlock(&mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2:
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset1.cc:[[@LINE+1]]
+ pthread_mutex_init(&mtx, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx);
+}
diff --git a/test/tsan/mutexset2.cc b/test/tsan/mutexset2.cc
new file mode 100644
index 000000000..9ccf952b0
--- /dev/null
+++ b/test/tsan/mutexset2.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx;
+
+void *Thread1(void *x) {
+ pthread_mutex_lock(&mtx);
+ Global++;
+ pthread_mutex_unlock(&mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T2:
+ // CHECK: Previous write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset2.cc:[[@LINE+1]]
+ pthread_mutex_init(&mtx, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx);
+}
diff --git a/test/tsan/mutexset3.cc b/test/tsan/mutexset3.cc
new file mode 100644
index 000000000..272ddafb3
--- /dev/null
+++ b/test/tsan/mutexset3.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx1;
+pthread_mutex_t mtx2;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx1);
+ pthread_mutex_lock(&mtx2);
+ Global++;
+ pthread_mutex_unlock(&mtx2);
+ pthread_mutex_unlock(&mtx1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2:
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+2]]
+ pthread_mutex_init(&mtx1, 0);
+ pthread_mutex_init(&mtx2, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx1);
+ pthread_mutex_destroy(&mtx2);
+}
diff --git a/test/tsan/mutexset4.cc b/test/tsan/mutexset4.cc
new file mode 100644
index 000000000..be751fa92
--- /dev/null
+++ b/test/tsan/mutexset4.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx1;
+pthread_mutex_t mtx2;
+
+void *Thread1(void *x) {
+ pthread_mutex_lock(&mtx1);
+ pthread_mutex_lock(&mtx2);
+ Global++;
+ pthread_mutex_unlock(&mtx2);
+ pthread_mutex_unlock(&mtx1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T2:
+ // CHECK: Previous write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]):
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+2]]
+ pthread_mutex_init(&mtx1, 0);
+ pthread_mutex_init(&mtx2, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx1);
+ pthread_mutex_destroy(&mtx2);
+}
diff --git a/test/tsan/mutexset5.cc b/test/tsan/mutexset5.cc
new file mode 100644
index 000000000..e013edb46
--- /dev/null
+++ b/test/tsan/mutexset5.cc
@@ -0,0 +1,46 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx1;
+pthread_mutex_t mtx2;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx1);
+ Global++;
+ pthread_mutex_unlock(&mtx1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ pthread_mutex_lock(&mtx2);
+ Global--;
+ pthread_mutex_unlock(&mtx2);
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2
+ // CHECK: (mutexes: write [[M2:M[0-9]+]]):
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+5]]
+ pthread_mutex_init(&mtx1, 0);
+ pthread_mutex_init(&mtx2, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx1);
+ pthread_mutex_destroy(&mtx2);
+}
diff --git a/test/tsan/mutexset6.cc b/test/tsan/mutexset6.cc
new file mode 100644
index 000000000..f5e6e66be
--- /dev/null
+++ b/test/tsan/mutexset6.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx1;
+pthread_spinlock_t mtx2;
+pthread_rwlock_t mtx3;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx1);
+ Global++;
+ pthread_mutex_unlock(&mtx1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ pthread_mutex_lock(&mtx1);
+ pthread_mutex_unlock(&mtx1);
+ pthread_spin_lock(&mtx2);
+ pthread_rwlock_rdlock(&mtx3);
+ Global--;
+ pthread_spin_unlock(&mtx2);
+ pthread_rwlock_unlock(&mtx3);
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2
+ // CHECK: (mutexes: write [[M2:M[0-9]+]], read [[M3:M[0-9]+]]):
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+5]]
+ // CHECK: Mutex [[M2]] created at:
+ // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M3]] created at:
+ // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+3]]
+ pthread_mutex_init(&mtx1, 0);
+ pthread_spin_init(&mtx2, 0);
+ pthread_rwlock_init(&mtx3, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx1);
+ pthread_spin_destroy(&mtx2);
+ pthread_rwlock_destroy(&mtx3);
+}
diff --git a/test/tsan/mutexset7.cc b/test/tsan/mutexset7.cc
new file mode 100644
index 000000000..51451b215
--- /dev/null
+++ b/test/tsan/mutexset7.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+__thread int huge[1024*1024];
+
+void *Thread1(void *x) {
+ sleep(1);
+ Global++;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ pthread_mutex_t mtx;
+ pthread_mutex_init(&mtx, 0);
+ pthread_mutex_lock(&mtx);
+ Global--;
+ pthread_mutex_unlock(&mtx);
+ pthread_mutex_destroy(&mtx);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4 at {{.*}} by thread T1:
+// CHECK: Previous write of size 4 at {{.*}} by thread T2
+// CHECK: (mutexes: write [[M1:M[0-9]+]]):
+// CHECK: Mutex [[M1]] is already destroyed
+// CHECK-NOT: Mutex {{.*}} created at
+
diff --git a/test/tsan/mutexset8.cc b/test/tsan/mutexset8.cc
new file mode 100644
index 000000000..8822b050e
--- /dev/null
+++ b/test/tsan/mutexset8.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t *mtx;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(mtx);
+ Global++;
+ pthread_mutex_unlock(mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2:
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset8.cc
+ mtx = new pthread_mutex_t;
+ pthread_mutex_init(mtx, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(mtx);
+ delete mtx;
+}
diff --git a/test/tsan/oob_race.cc b/test/tsan/oob_race.cc
new file mode 100644
index 000000000..c84e819cb
--- /dev/null
+++ b/test/tsan/oob_race.cc
@@ -0,0 +1,26 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+const long kOffset = 64*1024;
+
+void *Thread(void *p) {
+ sleep(1);
+ ((char*)p)[-kOffset] = 43;
+ return 0;
+}
+
+int main() {
+ char *volatile p0 = new char[16];
+ delete[] p0;
+ char *p = new char[32];
+ pthread_t th;
+ pthread_create(&th, 0, Thread, p);
+ p[-kOffset] = 42;
+ pthread_join(th, 0);
+}
+
+// Used to crash with CHECK failed.
+// CHECK: WARNING: ThreadSanitizer: data race
+
diff --git a/test/tsan/printf-1.c b/test/tsan/printf-1.c
new file mode 100644
index 000000000..a76048e85
--- /dev/null
+++ b/test/tsan/printf-1.c
@@ -0,0 +1,16 @@
+// RUN: %clang_tsan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=check_printf=1 %t 2>&1 | FileCheck %s
+// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck %s
+// RUN: %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+int main() {
+ volatile char c = '0';
+ volatile int x = 12;
+ volatile float f = 1.239;
+ volatile char s[] = "34";
+ printf("%c %d %.3f %s\n", c, x, f, s);
+ return 0;
+ // Check that printf works fine under Tsan.
+ // CHECK: 0 12 1.239 34
+}
diff --git a/test/tsan/race_on_barrier.c b/test/tsan/race_on_barrier.c
new file mode 100644
index 000000000..3c0199dec
--- /dev/null
+++ b/test/tsan/race_on_barrier.c
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+pthread_barrier_t B;
+int Global;
+
+void *Thread1(void *x) {
+ pthread_barrier_init(&B, 0, 2);
+ pthread_barrier_wait(&B);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ pthread_barrier_wait(&B);
+ return NULL;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ Thread2(0);
+ pthread_join(t, NULL);
+ pthread_barrier_destroy(&B);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/race_on_barrier2.c b/test/tsan/race_on_barrier2.c
new file mode 100644
index 000000000..62773d43e
--- /dev/null
+++ b/test/tsan/race_on_barrier2.c
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+pthread_barrier_t B;
+int Global;
+
+void *Thread1(void *x) {
+ if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD)
+ pthread_barrier_destroy(&B);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD)
+ pthread_barrier_destroy(&B);
+ return NULL;
+}
+
+int main() {
+ pthread_barrier_init(&B, 0, 2);
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ Thread2(0);
+ pthread_join(t, NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/race_on_heap.cc b/test/tsan/race_on_heap.cc
new file mode 100644
index 000000000..a84c0de96
--- /dev/null
+++ b/test/tsan/race_on_heap.cc
@@ -0,0 +1,47 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+void *Thread1(void *p) {
+ *(int*)p = 42;
+ return 0;
+}
+
+void *Thread2(void *p) {
+ *(int*)p = 44;
+ return 0;
+}
+
+void *alloc() {
+ return malloc(99);
+}
+
+void *AllocThread(void* arg) {
+ return alloc();
+}
+
+int main() {
+ void *p = 0;
+ pthread_t t[2];
+ pthread_create(&t[0], 0, AllocThread, 0);
+ pthread_join(t[0], &p);
+ fprintf(stderr, "addr=%p\n", p);
+ pthread_create(&t[0], 0, Thread1, (char*)p + 16);
+ pthread_create(&t[1], 0, Thread2, (char*)p + 16);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ return 0;
+}
+
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// ...
+// CHECK: Location is heap block of size 99 at [[ADDR]] allocated by thread T1:
+// CHCEKL #0 malloc
+// CHECK: #{{1|2}} alloc
+// CHECK: #{{2|3}} AllocThread
+// ...
+// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at:
+// CHECK: #0 pthread_create
+// CHECK: #1 main
diff --git a/test/tsan/race_on_mutex.c b/test/tsan/race_on_mutex.c
new file mode 100644
index 000000000..e66341414
--- /dev/null
+++ b/test/tsan/race_on_mutex.c
@@ -0,0 +1,42 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+pthread_mutex_t Mtx;
+int Global;
+
+void *Thread1(void *x) {
+ pthread_mutex_init(&Mtx, 0);
+ pthread_mutex_lock(&Mtx);
+ Global = 42;
+ pthread_mutex_unlock(&Mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&Mtx);
+ Global = 43;
+ pthread_mutex_unlock(&Mtx);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&Mtx);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NEXT: Atomic read of size 1 at {{.*}} by thread T2:
+// CHECK-NEXT: #0 pthread_mutex_lock
+// CHECK-NEXT: #1 Thread2{{.*}} {{.*}}race_on_mutex.c:20{{(:3)?}} ({{.*}})
+// CHECK: Previous write of size 1 at {{.*}} by thread T1:
+// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}})
+// CHECK-NEXT: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}})
diff --git a/test/tsan/race_on_mutex2.c b/test/tsan/race_on_mutex2.c
new file mode 100644
index 000000000..80c395e1f
--- /dev/null
+++ b/test/tsan/race_on_mutex2.c
@@ -0,0 +1,24 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+void *Thread(void *x) {
+ pthread_mutex_lock((pthread_mutex_t*)x);
+ pthread_mutex_unlock((pthread_mutex_t*)x);
+ return 0;
+}
+
+int main() {
+ pthread_mutex_t Mtx;
+ pthread_mutex_init(&Mtx, 0);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, &Mtx);
+ sleep(1);
+ pthread_mutex_destroy(&Mtx);
+ pthread_join(t, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/race_on_read.cc b/test/tsan/race_on_read.cc
new file mode 100644
index 000000000..e870ff9e8
--- /dev/null
+++ b/test/tsan/race_on_read.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+int fd;
+char buf;
+
+void *Thread(void *x) {
+ sleep(1);
+ read(fd, &buf, 1);
+ return NULL;
+}
+
+int main() {
+ fd = open("/dev/random", O_RDONLY);
+ if (fd < 0) return 1;
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread, NULL);
+ pthread_create(&t[1], NULL, Thread, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ close(fd);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 1
+// CHECK: #0 read
+// CHECK: Previous write of size 1
+// CHECK: #0 read
diff --git a/test/tsan/race_on_speculative_load.cc b/test/tsan/race_on_speculative_load.cc
new file mode 100644
index 000000000..31f0e4f2a
--- /dev/null
+++ b/test/tsan/race_on_speculative_load.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t | FileCheck %s
+// Regtest for https://code.google.com/p/thread-sanitizer/issues/detail?id=40
+// This is a correct program and tsan should not report a race.
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+int g;
+__attribute__((noinline))
+int foo(int cond) {
+ if (cond)
+ return g;
+ return 0;
+}
+void *Thread1(void *p) {
+ long res = foo((long)p);
+ sleep(1);
+ return (void*) res;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread1, 0);
+ g = 1;
+ pthread_join(t, 0);
+ printf("PASS\n");
+ // CHECK: PASS
+}
diff --git a/test/tsan/race_on_write.cc b/test/tsan/race_on_write.cc
new file mode 100644
index 000000000..8a56c8464
--- /dev/null
+++ b/test/tsan/race_on_write.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int fd;
+char buf;
+
+void *Thread1(void *x) {
+ buf = 1;
+ sleep(1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ write(fd, &buf, 1);
+ return NULL;
+}
+
+int main() {
+ fd = open("/dev/null", O_WRONLY);
+ if (fd < 0) return 1;
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ sleep(1);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ close(fd);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Read of size 1
+// CHECK: #0 write
+// CHECK: Previous write of size 1
+// CHECK: #0 Thread1
diff --git a/test/tsan/race_with_finished_thread.cc b/test/tsan/race_with_finished_thread.cc
new file mode 100644
index 000000000..c713c67a3
--- /dev/null
+++ b/test/tsan/race_with_finished_thread.cc
@@ -0,0 +1,43 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+// Ensure that we can restore a stack of a finished thread.
+
+int g_data;
+
+void __attribute__((noinline)) foobar(int *p) {
+ *p = 42;
+}
+
+void *Thread1(void *x) {
+ foobar(&g_data);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ g_data = 43;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4 at {{.*}} by thread T2:
+// CHECK: Previous write of size 4 at {{.*}} by thread T1:
+// CHECK: #0 foobar
+// CHECK: #1 Thread1
+// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at:
+// CHECK: #0 pthread_create
+// CHECK: #1 main
diff --git a/test/tsan/signal_errno.cc b/test/tsan/signal_errno.cc
new file mode 100644
index 000000000..651f916ac
--- /dev/null
+++ b/test/tsan/signal_errno.cc
@@ -0,0 +1,50 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+pthread_t mainth;
+volatile int done;
+
+static void MyHandler(int, siginfo_t *s, void *c) {
+ errno = 1;
+ done = 1;
+}
+
+static void* sendsignal(void *p) {
+ sleep(1);
+ pthread_kill(mainth, SIGPROF);
+ return 0;
+}
+
+static __attribute__((noinline)) void loop() {
+ while (done == 0) {
+ volatile char *p = (char*)malloc(1);
+ p[0] = 0;
+ free((void*)p);
+ pthread_yield();
+ }
+}
+
+int main() {
+ mainth = pthread_self();
+ struct sigaction act = {};
+ act.sa_sigaction = &MyHandler;
+ sigaction(SIGPROF, &act, 0);
+ pthread_t th;
+ pthread_create(&th, 0, sendsignal, 0);
+ loop();
+ pthread_join(th, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: signal handler spoils errno
+// CHECK: #0 MyHandler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_errno.cc
+// CHECK: #1 loop
+// CHECK: #2 main
+// CHECK: SUMMARY: ThreadSanitizer: signal handler spoils errno{{.*}}MyHandler
+
diff --git a/test/tsan/signal_malloc.cc b/test/tsan/signal_malloc.cc
new file mode 100644
index 000000000..ef180b8a2
--- /dev/null
+++ b/test/tsan/signal_malloc.cc
@@ -0,0 +1,26 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static void handler(int, siginfo_t*, void*) {
+ // CHECK: WARNING: ThreadSanitizer: signal-unsafe call inside of a signal
+ // CHECK: #0 malloc
+ // CHECK: #{{(1|2)}} handler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_malloc.cc:[[@LINE+2]]
+ // CHECK: SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal{{.*}}handler
+ volatile char *p = (char*)malloc(1);
+ p[0] = 0;
+ free((void*)p);
+}
+
+int main() {
+ struct sigaction act = {};
+ act.sa_sigaction = &handler;
+ sigaction(SIGPROF, &act, 0);
+ kill(getpid(), SIGPROF);
+ sleep(1);
+ return 0;
+}
+
diff --git a/test/tsan/sigsuspend.cc b/test/tsan/sigsuspend.cc
new file mode 100644
index 000000000..503bd5629
--- /dev/null
+++ b/test/tsan/sigsuspend.cc
@@ -0,0 +1,44 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+
+// Always enable asserts.
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+
+static bool signal_handler_ran = false;
+
+void do_nothing_signal_handler(int signum) {
+ write(1, "HANDLER\n", 8);
+ signal_handler_ran = true;
+}
+
+int main() {
+ const int kSignalToTest = SIGSYS;
+ assert(SIG_ERR != signal(kSignalToTest, do_nothing_signal_handler));
+ sigset_t empty_set;
+ assert(0 == sigemptyset(&empty_set));
+ sigset_t one_signal = empty_set;
+ assert(0 == sigaddset(&one_signal, kSignalToTest));
+ sigset_t old_set;
+ assert(0 == sigprocmask(SIG_BLOCK, &one_signal, &old_set));
+ raise(kSignalToTest);
+ assert(!signal_handler_ran);
+ sigset_t all_but_one;
+ assert(0 == sigfillset(&all_but_one));
+ assert(0 == sigdelset(&all_but_one, kSignalToTest));
+ sigsuspend(&all_but_one);
+ assert(signal_handler_ran);
+
+ // Restore the original set.
+ assert(0 == sigprocmask(SIG_SETMASK, &old_set, NULL));
+ printf("DONE");
+}
+
+// CHECK: HANDLER
+// CHECK: DONE
diff --git a/test/tsan/simple_race.c b/test/tsan/simple_race.c
new file mode 100644
index 000000000..80a83e01a
--- /dev/null
+++ b/test/tsan/simple_race.c
@@ -0,0 +1,26 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ Global = 42;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global = 43;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/simple_race.cc b/test/tsan/simple_race.cc
new file mode 100644
index 000000000..47854cfd9
--- /dev/null
+++ b/test/tsan/simple_race.cc
@@ -0,0 +1,26 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ Global++;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: SUMMARY: ThreadSanitizer: data race{{.*}}Thread
diff --git a/test/tsan/simple_stack.c b/test/tsan/simple_stack.c
new file mode 100644
index 000000000..a447e2880
--- /dev/null
+++ b/test/tsan/simple_stack.c
@@ -0,0 +1,66 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void __attribute__((noinline)) foo1() {
+ Global = 42;
+}
+
+void __attribute__((noinline)) bar1() {
+ volatile int tmp = 42; (void)tmp;
+ foo1();
+}
+
+void __attribute__((noinline)) foo2() {
+ volatile int v = Global; (void)v;
+}
+
+void __attribute__((noinline)) bar2() {
+ volatile int tmp = 42; (void)tmp;
+ foo2();
+}
+
+void *Thread1(void *x) {
+ sleep(1);
+ bar1();
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ bar2();
+ return NULL;
+}
+
+void StartThread(pthread_t *t, void *(*f)(void*)) {
+ pthread_create(t, NULL, f, NULL);
+}
+
+int main() {
+ pthread_t t[2];
+ StartThread(&t[0], Thread1);
+ StartThread(&t[1], Thread2);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1:
+// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:9{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:28{{(:3)?}} ({{.*}})
+// CHECK: Previous read of size 4 at {{.*}} by thread T2:
+// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:26)?}} ({{.*}})
+// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:33{{(:3)?}} ({{.*}})
+// CHECK: Thread T1 (tid={{.*}}, running) created by main thread at:
+// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
+// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}})
+// CHECK: Thread T2 ({{.*}}) created by main thread at:
+// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
+// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}})
diff --git a/test/tsan/simple_stack2.cc b/test/tsan/simple_stack2.cc
new file mode 100644
index 000000000..7a034c4cd
--- /dev/null
+++ b/test/tsan/simple_stack2.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void __attribute__((noinline)) foo1() {
+ Global = 42;
+}
+
+void __attribute__((noinline)) bar1() {
+ volatile int tmp = 42;
+ int tmp2 = tmp;
+ (void)tmp2;
+ foo1();
+}
+
+void __attribute__((noinline)) foo2() {
+ volatile int tmp = Global;
+ int tmp2 = tmp;
+ (void)tmp2;
+}
+
+void __attribute__((noinline)) bar2() {
+ volatile int tmp = 42;
+ int tmp2 = tmp;
+ (void)tmp2;
+ foo2();
+}
+
+void *Thread1(void *x) {
+ sleep(1);
+ bar1();
+ return NULL;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ bar2();
+ pthread_join(t, NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1:
+// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}})
+// CHECK: Previous read of size 4 at {{.*}} by main thread:
+// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:20{{(:28)?}} ({{.*}})
+// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:29{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:41{{(:3)?}} ({{.*}})
diff --git a/test/tsan/sleep_sync.cc b/test/tsan/sleep_sync.cc
new file mode 100644
index 000000000..217a52a09
--- /dev/null
+++ b/test/tsan/sleep_sync.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+int X = 0;
+
+void MySleep() {
+ sleep(1);
+}
+
+void *Thread(void *p) {
+ MySleep(); // Assume the main thread has done the write.
+ X = 42;
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ X = 43;
+ pthread_join(t, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// ...
+// CHECK: As if synchronized via sleep:
+// CHECK-NEXT: #0 sleep
+// CHECK-NEXT: #1 MySleep
+// CHECK-NEXT: #2 Thread
diff --git a/test/tsan/sleep_sync2.cc b/test/tsan/sleep_sync2.cc
new file mode 100644
index 000000000..e22999279
--- /dev/null
+++ b/test/tsan/sleep_sync2.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+int X = 0;
+
+void *Thread(void *p) {
+ X = 42;
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ sleep(1);
+ pthread_create(&t, 0, Thread, 0);
+ X = 43;
+ pthread_join(t, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: As if synchronized via sleep
diff --git a/test/tsan/stack_race.cc b/test/tsan/stack_race.cc
new file mode 100644
index 000000000..01ff2e839
--- /dev/null
+++ b/test/tsan/stack_race.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <unistd.h>
+
+void *Thread(void *a) {
+ sleep(1);
+ *(int*)a = 43;
+ return 0;
+}
+
+int main() {
+ int Var = 42;
+ pthread_t t;
+ pthread_create(&t, 0, Thread, &Var);
+ Var = 43;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is stack of main thread.
+
diff --git a/test/tsan/stack_race2.cc b/test/tsan/stack_race2.cc
new file mode 100644
index 000000000..577f12c95
--- /dev/null
+++ b/test/tsan/stack_race2.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <unistd.h>
+
+void *Thread2(void *a) {
+ sleep(1);
+ *(int*)a = 43;
+ return 0;
+}
+
+void *Thread(void *a) {
+ int Var = 42;
+ pthread_t t;
+ pthread_create(&t, 0, Thread2, &Var);
+ Var = 42;
+ pthread_join(t, 0);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is stack of thread T1.
+
diff --git a/test/tsan/static_init1.cc b/test/tsan/static_init1.cc
new file mode 100644
index 000000000..4faf5bc54
--- /dev/null
+++ b/test/tsan/static_init1.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+struct P {
+ int x;
+ int y;
+};
+
+void *Thread(void *x) {
+ static P p = {rand(), rand()};
+ if (p.x > RAND_MAX || p.y > RAND_MAX)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread, 0);
+ pthread_create(&t[1], 0, Thread, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ printf("PASS\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/static_init2.cc b/test/tsan/static_init2.cc
new file mode 100644
index 000000000..96ef821a7
--- /dev/null
+++ b/test/tsan/static_init2.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+struct Cache {
+ int x;
+ explicit Cache(int x)
+ : x(x) {
+ }
+};
+
+void foo(Cache *my) {
+ static Cache *c = my ? my : new Cache(rand());
+ if (c->x >= RAND_MAX)
+ exit(1);
+}
+
+void *Thread(void *x) {
+ foo(new Cache(rand()));
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread, 0);
+ pthread_create(&t[1], 0, Thread, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ printf("PASS\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/static_init3.cc b/test/tsan/static_init3.cc
new file mode 100644
index 000000000..70a3c1687
--- /dev/null
+++ b/test/tsan/static_init3.cc
@@ -0,0 +1,47 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct Cache {
+ int x;
+};
+
+Cache g_cache;
+
+Cache *CreateCache() {
+ g_cache.x = rand();
+ return &g_cache;
+}
+
+_Atomic(Cache*) queue;
+
+void *Thread1(void *x) {
+ static Cache *c = CreateCache();
+ __c11_atomic_store(&queue, c, 0);
+ return 0;
+}
+
+void *Thread2(void *x) {
+ Cache *c = 0;
+ for (;;) {
+ c = __c11_atomic_load(&queue, 0);
+ if (c)
+ break;
+ sched_yield();
+ }
+ if (c->x >= RAND_MAX)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread2, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/static_init4.cc b/test/tsan/static_init4.cc
new file mode 100644
index 000000000..5ecc39926
--- /dev/null
+++ b/test/tsan/static_init4.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct Cache {
+ int x;
+ explicit Cache(int x)
+ : x(x) {
+ }
+};
+
+int g_other;
+
+Cache *CreateCache() {
+ g_other = rand();
+ return new Cache(rand());
+}
+
+void *Thread1(void *x) {
+ static Cache *c = CreateCache();
+ if (c->x == g_other)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread1, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ printf("PASS\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/static_init5.cc b/test/tsan/static_init5.cc
new file mode 100644
index 000000000..1d0ed6d54
--- /dev/null
+++ b/test/tsan/static_init5.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct Cache {
+ int x;
+ explicit Cache(int x)
+ : x(x) {
+ }
+};
+
+void *AsyncInit(void *p) {
+ return new Cache((int)(long)p);
+}
+
+Cache *CreateCache() {
+ pthread_t t;
+ pthread_create(&t, 0, AsyncInit, (void*)(long)rand());
+ void *res;
+ pthread_join(t, &res);
+ return (Cache*)res;
+}
+
+void *Thread1(void *x) {
+ static Cache *c = CreateCache();
+ if (c->x >= RAND_MAX)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread1, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ printf("PASS\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/static_init6.cc b/test/tsan/static_init6.cc
new file mode 100644
index 000000000..c9099f9b6
--- /dev/null
+++ b/test/tsan/static_init6.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -static-libstdc++ -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct Cache {
+ int x;
+ explicit Cache(int x)
+ : x(x) {
+ }
+};
+
+void *AsyncInit(void *p) {
+ return new Cache((int)(long)p);
+}
+
+Cache *CreateCache() {
+ pthread_t t;
+ pthread_create(&t, 0, AsyncInit, (void*)(long)rand());
+ void *res;
+ pthread_join(t, &res);
+ return (Cache*)res;
+}
+
+void *Thread1(void *x) {
+ static Cache *c = CreateCache();
+ if (c->x >= RAND_MAX)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread1, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ printf("PASS\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/suppress_same_address.cc b/test/tsan/suppress_same_address.cc
new file mode 100644
index 000000000..8fbf7b9ed
--- /dev/null
+++ b/test/tsan/suppress_same_address.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+volatile int X;
+
+void *Thread1(void *x) {
+ sleep(1);
+ X = 42;
+ X = 66;
+ X = 78;
+ return 0;
+}
+
+void *Thread2(void *x) {
+ X = 11;
+ X = 99;
+ X = 73;
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread1, 0);
+ Thread2(0);
+ pthread_join(t, 0);
+}
+
+// CHECK: ThreadSanitizer: reported 1 warnings
diff --git a/test/tsan/suppress_same_stacks.cc b/test/tsan/suppress_same_stacks.cc
new file mode 100644
index 000000000..f0ab8b304
--- /dev/null
+++ b/test/tsan/suppress_same_stacks.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+
+volatile int N; // Prevent loop unrolling.
+int **data;
+
+void *Thread1(void *x) {
+ for (int i = 0; i < N; i++)
+ data[i][0] = 42;
+ return 0;
+}
+
+int main() {
+ N = 4;
+ data = new int*[N];
+ for (int i = 0; i < N; i++)
+ data[i] = new int;
+ pthread_t t;
+ pthread_create(&t, 0, Thread1, 0);
+ Thread1(0);
+ pthread_join(t, 0);
+ for (int i = 0; i < N; i++)
+ delete data[i];
+ delete[] data;
+}
+
+// CHECK: ThreadSanitizer: reported 1 warnings
diff --git a/test/tsan/suppressions_global.cc b/test/tsan/suppressions_global.cc
new file mode 100644
index 000000000..181cb56cf
--- /dev/null
+++ b/test/tsan/suppressions_global.cc
@@ -0,0 +1,29 @@
+// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+int RacyGlobal;
+
+void *Thread1(void *x) {
+ RacyGlobal = 42;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ RacyGlobal = 43;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+ return 0;
+}
+
+// CHECK-NOT: failed to open suppressions file
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
diff --git a/test/tsan/suppressions_global.cc.supp b/test/tsan/suppressions_global.cc.supp
new file mode 100644
index 000000000..5fa8a2e43
--- /dev/null
+++ b/test/tsan/suppressions_global.cc.supp
@@ -0,0 +1,2 @@
+race:RacyGlobal
+
diff --git a/test/tsan/suppressions_race.cc b/test/tsan/suppressions_race.cc
new file mode 100644
index 000000000..c88e69bec
--- /dev/null
+++ b/test/tsan/suppressions_race.cc
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ Global = 42;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global = 43;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+ return 0;
+}
+
+// CHECK-NOT: failed to open suppressions file
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
diff --git a/test/tsan/suppressions_race.cc.supp b/test/tsan/suppressions_race.cc.supp
new file mode 100644
index 000000000..cbdba76ea
--- /dev/null
+++ b/test/tsan/suppressions_race.cc.supp
@@ -0,0 +1,2 @@
+race:Thread1
+
diff --git a/test/tsan/suppressions_race2.cc b/test/tsan/suppressions_race2.cc
new file mode 100644
index 000000000..57146f96a
--- /dev/null
+++ b/test/tsan/suppressions_race2.cc
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ Global = 42;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ Global = 43;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+ return 0;
+}
+
+// CHECK-NOT: failed to open suppressions file
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
diff --git a/test/tsan/suppressions_race2.cc.supp b/test/tsan/suppressions_race2.cc.supp
new file mode 100644
index 000000000..b3c4dbc59
--- /dev/null
+++ b/test/tsan/suppressions_race2.cc.supp
@@ -0,0 +1,2 @@
+race:Thread2
+
diff --git a/test/tsan/test_output.sh b/test/tsan/test_output.sh
new file mode 100755
index 000000000..8a15a6738
--- /dev/null
+++ b/test/tsan/test_output.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+ulimit -s 8192
+set -e # fail on any error
+
+HERE=$(dirname $0)
+TSAN_DIR=$(dirname $0)/../../lib/tsan
+BLACKLIST=$HERE/Helpers/blacklist.txt
+
+# Assume clang and clang++ are in path.
+: ${CC:=clang}
+: ${CXX:=clang++}
+: ${FILECHECK:=FileCheck}
+
+# TODO: add testing for all of -O0...-O3
+CFLAGS="-fsanitize=thread -fsanitize-blacklist=$BLACKLIST -fPIE -O1 -g -Wall"
+LDFLAGS="-pie -lpthread -ldl -lrt -lm -Wl,--whole-archive $TSAN_DIR/rtl/libtsan.a -Wl,--no-whole-archive"
+
+test_file() {
+ SRC=$1
+ COMPILER=$2
+ echo ----- TESTING $(basename $1)
+ OBJ=$SRC.o
+ EXE=$SRC.exe
+ $COMPILER $SRC $CFLAGS -c -o $OBJ
+ $COMPILER $OBJ $LDFLAGS -o $EXE
+ RES=$($EXE 2>&1 || true)
+ printf "%s\n" "$RES" | $FILECHECK $SRC
+ if [ "$3" == "" ]; then
+ rm -f $EXE $OBJ
+ fi
+}
+
+if [ "$1" == "" ]; then
+ for c in $HERE/*.{c,cc}; do
+ if [[ $c == */failing_* ]]; then
+ echo SKIPPING FAILING TEST $c
+ continue
+ fi
+ if [[ $c == */load_shared_lib.cc ]]; then
+ echo TEST $c is not supported
+ continue
+ fi
+ if [ "`grep "TSAN_OPTIONS" $c`" ]; then
+ echo SKIPPING $c -- requires TSAN_OPTIONS
+ continue
+ fi
+ COMPILER=$CXX
+ case $c in
+ *.c) COMPILER=$CC
+ esac
+ test_file $c $COMPILER &
+ done
+ for job in `jobs -p`; do
+ wait $job || exit 1
+ done
+else
+ test_file $HERE/$1 $CXX "DUMP"
+fi
diff --git a/test/tsan/thread_end_with_ignore.cc b/test/tsan/thread_end_with_ignore.cc
new file mode 100644
index 000000000..8bb52e3e8
--- /dev/null
+++ b/test/tsan/thread_end_with_ignore.cc
@@ -0,0 +1,24 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l);
+
+void *Thread(void *x) {
+ AnnotateIgnoreReadsBegin("", 0);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_join(t, 0);
+}
+
+// CHECK: ThreadSanitizer: thread T1 finished with ignores enabled, created at:
+// CHECK: #0 pthread_create
+// CHECK: #1 main
+// CHECK: Ignore was enabled at:
+// CHECK: #0 AnnotateIgnoreReadsBegin
+// CHECK: #1 Thread
+
diff --git a/test/tsan/thread_end_with_ignore2.cc b/test/tsan/thread_end_with_ignore2.cc
new file mode 100644
index 000000000..224599c95
--- /dev/null
+++ b/test/tsan/thread_end_with_ignore2.cc
@@ -0,0 +1,12 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l);
+
+int main() {
+ AnnotateIgnoreWritesBegin("", 0);
+}
+
+// CHECK: ThreadSanitizer: main thread finished with ignores enabled
+// CHECK: Ignore was enabled at:
+// CHECK: #0 AnnotateIgnoreWritesBegin
+// CHECK: #1 main
+
diff --git a/test/tsan/thread_end_with_ignore3.cc b/test/tsan/thread_end_with_ignore3.cc
new file mode 100644
index 000000000..bf46eb89b
--- /dev/null
+++ b/test/tsan/thread_end_with_ignore3.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l);
+extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l);
+
+int main() {
+ AnnotateIgnoreReadsBegin("", 0);
+ AnnotateIgnoreReadsBegin("", 0);
+ AnnotateIgnoreReadsEnd("", 0);
+ AnnotateIgnoreReadsEnd("", 0);
+ AnnotateIgnoreReadsBegin("", 0);
+ AnnotateIgnoreReadsBegin("", 0);
+ AnnotateIgnoreReadsEnd("", 0);
+}
+
+// CHECK: ThreadSanitizer: main thread finished with ignores enabled
+// CHECK: Ignore was enabled at:
+// CHECK: #0 AnnotateIgnoreReadsBegin
+// CHECK: #1 main {{.*}}thread_end_with_ignore3.cc:10
+// CHECK: Ignore was enabled at:
+// CHECK: #0 AnnotateIgnoreReadsBegin
+// CHECK: #1 main {{.*}}thread_end_with_ignore3.cc:11
+
diff --git a/test/tsan/thread_leak.c b/test/tsan/thread_leak.c
new file mode 100644
index 000000000..c5e669e5d
--- /dev/null
+++ b/test/tsan/thread_leak.c
@@ -0,0 +1,17 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_join(t, 0);
+ printf("PASS\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
diff --git a/test/tsan/thread_leak2.c b/test/tsan/thread_leak2.c
new file mode 100644
index 000000000..39f6b5e02
--- /dev/null
+++ b/test/tsan/thread_leak2.c
@@ -0,0 +1,17 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_detach(t);
+ printf("PASS\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
diff --git a/test/tsan/thread_leak3.c b/test/tsan/thread_leak3.c
new file mode 100644
index 000000000..5f447dbdb
--- /dev/null
+++ b/test/tsan/thread_leak3.c
@@ -0,0 +1,17 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ sleep(1);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: thread leak
+// CHECK: SUMMARY: ThreadSanitizer: thread leak{{.*}}main
diff --git a/test/tsan/thread_leak4.c b/test/tsan/thread_leak4.c
new file mode 100644
index 000000000..f9fad0360
--- /dev/null
+++ b/test/tsan/thread_leak4.c
@@ -0,0 +1,18 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+
+void *Thread(void *x) {
+ sleep(10);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ printf("OK\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
diff --git a/test/tsan/thread_leak5.c b/test/tsan/thread_leak5.c
new file mode 100644
index 000000000..c19d6177f
--- /dev/null
+++ b/test/tsan/thread_leak5.c
@@ -0,0 +1,20 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ volatile int N = 5; // prevent loop unrolling
+ for (int i = 0; i < N; i++) {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ }
+ sleep(1);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: thread leak
+// CHECK: And 4 more similar thread leaks
diff --git a/test/tsan/thread_name.cc b/test/tsan/thread_name.cc
new file mode 100644
index 000000000..646ab5836
--- /dev/null
+++ b/test/tsan/thread_name.cc
@@ -0,0 +1,38 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" void AnnotateThreadName(const char *f, int l, const char *name);
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ AnnotateThreadName(__FILE__, __LINE__, "Thread1");
+ Global++;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+#if SANITIZER_LINUX && __GLIBC_PREREQ(2, 12)
+ pthread_setname_np(pthread_self(), "Thread2");
+#else
+ AnnotateThreadName(__FILE__, __LINE__, "Thread2");
+#endif
+ Global--;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Thread T1 'Thread1'
+// CHECK: Thread T2 'Thread2'
+
diff --git a/test/tsan/thread_name2.cc b/test/tsan/thread_name2.cc
new file mode 100644
index 000000000..8c5cb741f
--- /dev/null
+++ b/test/tsan/thread_name2.cc
@@ -0,0 +1,32 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ Global++;
+ return 0;
+}
+
+void *Thread2(void *x) {
+ pthread_setname_np(pthread_self(), "foobar2");
+ Global--;
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread2, 0);
+ pthread_setname_np(t[0], "foobar1");
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Thread T1 'foobar1'
+// CHECK: Thread T2 'foobar2'
+
diff --git a/test/tsan/tiny_race.c b/test/tsan/tiny_race.c
new file mode 100644
index 000000000..f77e1606c
--- /dev/null
+++ b/test/tsan/tiny_race.c
@@ -0,0 +1,21 @@
+// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ Global = 42;
+ return x;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread1, 0);
+ Global = 43;
+ pthread_join(t, 0);
+ return Global;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/tls_race.cc b/test/tsan/tls_race.cc
new file mode 100644
index 000000000..7b1f38d62
--- /dev/null
+++ b/test/tsan/tls_race.cc
@@ -0,0 +1,21 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <unistd.h>
+
+void *Thread(void *a) {
+ sleep(1);
+ *(int*)a = 43;
+ return 0;
+}
+
+int main() {
+ static __thread int Var = 42;
+ pthread_t t;
+ pthread_create(&t, 0, Thread, &Var);
+ Var = 43;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is TLS of main thread.
diff --git a/test/tsan/tls_race2.cc b/test/tsan/tls_race2.cc
new file mode 100644
index 000000000..2cf44ae54
--- /dev/null
+++ b/test/tsan/tls_race2.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <unistd.h>
+
+void *Thread2(void *a) {
+ sleep(1);
+ *(int*)a = 43;
+ return 0;
+}
+
+void *Thread(void *a) {
+ static __thread int Var = 42;
+ pthread_t t;
+ pthread_create(&t, 0, Thread2, &Var);
+ Var = 42;
+ pthread_join(t, 0);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is TLS of thread T1.
+
diff --git a/test/tsan/tsan-vs-gvn.cc b/test/tsan/tsan-vs-gvn.cc
new file mode 100644
index 000000000..40ae724b7
--- /dev/null
+++ b/test/tsan/tsan-vs-gvn.cc
@@ -0,0 +1,38 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O3 %s -o %t && %t 2>&1 | FileCheck %s
+//
+// Check that load widening is not tsan-hostile.
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+struct {
+ int i;
+ char c1, c2, c3, c4;
+} S;
+
+int G;
+
+void *Thread1(void *x) {
+ G = S.c1 + S.c3;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ S.c2 = 1;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ memset(&S, 123, sizeof(S));
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("PASS\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: PASS
diff --git a/test/tsan/unaligned_norace.cc b/test/tsan/unaligned_norace.cc
new file mode 100644
index 000000000..792224b80
--- /dev/null
+++ b/test/tsan/unaligned_norace.cc
@@ -0,0 +1,84 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+uint64_t objs[8*3*3*2][3];
+
+extern "C" {
+uint16_t __tsan_unaligned_read2(void *addr);
+uint32_t __tsan_unaligned_read4(void *addr);
+uint64_t __tsan_unaligned_read8(void *addr);
+void __tsan_unaligned_write2(void *addr, uint16_t v);
+void __tsan_unaligned_write4(void *addr, uint32_t v);
+void __tsan_unaligned_write8(void *addr, uint64_t v);
+}
+
+static void access(char *p, int sz, int rw) {
+ if (rw) {
+ switch (sz) {
+ case 0: __tsan_unaligned_write2(p, 0); break;
+ case 1: __tsan_unaligned_write4(p, 0); break;
+ case 2: __tsan_unaligned_write8(p, 0); break;
+ default: exit(1);
+ }
+ } else {
+ switch (sz) {
+ case 0: __tsan_unaligned_read2(p); break;
+ case 1: __tsan_unaligned_read4(p); break;
+ case 2: __tsan_unaligned_read8(p); break;
+ default: exit(1);
+ }
+ }
+}
+
+static int accesssize(int sz) {
+ switch (sz) {
+ case 0: return 2;
+ case 1: return 4;
+ case 2: return 8;
+ }
+ exit(1);
+}
+
+void Test(bool main) {
+ uint64_t *obj = objs[0];
+ for (int off = 0; off < 8; off++) {
+ for (int sz1 = 0; sz1 < 3; sz1++) {
+ for (int sz2 = 0; sz2 < 3; sz2++) {
+ for (int rw = 0; rw < 2; rw++) {
+ char *p = (char*)obj + off;
+ if (main) {
+ // printf("thr=%d off=%d sz1=%d sz2=%d rw=%d p=%p\n",
+ // main, off, sz1, sz2, rw, p);
+ access(p, sz1, true);
+ } else {
+ p += accesssize(sz1);
+ // printf("thr=%d off=%d sz1=%d sz2=%d rw=%d p=%p\n",
+ // main, off, sz1, sz2, rw, p);
+ access(p, sz2, rw);
+ }
+ obj += 3;
+ }
+ }
+ }
+ }
+}
+
+void *Thread(void *p) {
+ (void)p;
+ Test(false);
+ return 0;
+}
+
+int main() {
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ Test(true);
+ pthread_join(th, 0);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer:
+// CHECK: OK
diff --git a/test/tsan/unaligned_race.cc b/test/tsan/unaligned_race.cc
new file mode 100644
index 000000000..3534ff5d6
--- /dev/null
+++ b/test/tsan/unaligned_race.cc
@@ -0,0 +1,139 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#define NOINLINE __attribute__((noinline))
+
+volatile uint64_t objs[8*2*(2 + 4 + 8)][2];
+
+extern "C" {
+uint16_t __sanitizer_unaligned_load16(volatile void *addr);
+uint32_t __sanitizer_unaligned_load32(volatile void *addr);
+uint64_t __sanitizer_unaligned_load64(volatile void *addr);
+void __sanitizer_unaligned_store16(volatile void *addr, uint16_t v);
+void __sanitizer_unaligned_store32(volatile void *addr, uint32_t v);
+void __sanitizer_unaligned_store64(volatile void *addr, uint64_t v);
+}
+
+// All this mess is to generate unique stack for each race,
+// otherwise tsan will suppress similar stacks.
+
+static NOINLINE void access(volatile char *p, int sz, int rw) {
+ if (rw) {
+ switch (sz) {
+ case 0: __sanitizer_unaligned_store16(p, 0); break;
+ case 1: __sanitizer_unaligned_store32(p, 0); break;
+ case 2: __sanitizer_unaligned_store64(p, 0); break;
+ default: exit(1);
+ }
+ } else {
+ switch (sz) {
+ case 0: __sanitizer_unaligned_load16(p); break;
+ case 1: __sanitizer_unaligned_load32(p); break;
+ case 2: __sanitizer_unaligned_load64(p); break;
+ default: exit(1);
+ }
+ }
+}
+
+static int accesssize(int sz) {
+ switch (sz) {
+ case 0: return 2;
+ case 1: return 4;
+ case 2: return 8;
+ }
+ exit(1);
+}
+
+template<int off, int off2>
+static NOINLINE void access3(bool main, int sz1, bool rw, volatile char *p) {
+ p += off;
+ if (main) {
+ access(p, sz1, true);
+ } else {
+ p += off2;
+ if (rw) {
+ *p = 42;
+ } else {
+ if (*p == 42)
+ printf("bingo!\n");
+ }
+ }
+}
+
+template<int off>
+static NOINLINE void
+access2(bool main, int sz1, int off2, bool rw, volatile char *obj) {
+ if (off2 == 0)
+ access3<off, 0>(main, sz1, rw, obj);
+ else if (off2 == 1)
+ access3<off, 1>(main, sz1, rw, obj);
+ else if (off2 == 2)
+ access3<off, 2>(main, sz1, rw, obj);
+ else if (off2 == 3)
+ access3<off, 3>(main, sz1, rw, obj);
+ else if (off2 == 4)
+ access3<off, 4>(main, sz1, rw, obj);
+ else if (off2 == 5)
+ access3<off, 5>(main, sz1, rw, obj);
+ else if (off2 == 6)
+ access3<off, 6>(main, sz1, rw, obj);
+ else if (off2 == 7)
+ access3<off, 7>(main, sz1, rw, obj);
+}
+
+static NOINLINE void
+access1(bool main, int off, int sz1, int off2, bool rw, char *obj) {
+ if (off == 0)
+ access2<0>(main, sz1, off2, rw, obj);
+ else if (off == 1)
+ access2<1>(main, sz1, off2, rw, obj);
+ else if (off == 2)
+ access2<2>(main, sz1, off2, rw, obj);
+ else if (off == 3)
+ access2<3>(main, sz1, off2, rw, obj);
+ else if (off == 4)
+ access2<4>(main, sz1, off2, rw, obj);
+ else if (off == 5)
+ access2<5>(main, sz1, off2, rw, obj);
+ else if (off == 6)
+ access2<6>(main, sz1, off2, rw, obj);
+ else if (off == 7)
+ access2<7>(main, sz1, off2, rw, obj);
+}
+
+NOINLINE void Test(bool main) {
+ volatile uint64_t *obj = objs[0];
+ for (int off = 0; off < 8; off++) {
+ for (int sz1 = 0; sz1 < 3; sz1++) {
+ for (int off2 = 0; off2 < accesssize(sz1); off2++) {
+ for (int rw = 0; rw < 2; rw++) {
+ // printf("thr=%d off=%d sz1=%d off2=%d rw=%d p=%p\n",
+ // main, off, sz1, off2, rw, obj);
+ access1(main, off, sz1, off2, rw, (char*)obj);
+ obj += 2;
+ }
+ }
+ }
+ }
+}
+
+void *Thread(void *p) {
+ (void)p;
+ sleep(1);
+ Test(false);
+ return 0;
+}
+
+int main() {
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ Test(true);
+ pthread_join(th, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: ThreadSanitizer: reported 224 warnings
diff --git a/test/tsan/user_fopen.cc b/test/tsan/user_fopen.cc
new file mode 100644
index 000000000..794d59871
--- /dev/null
+++ b/test/tsan/user_fopen.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+
+// defined by tsan.
+extern "C" FILE *__interceptor_fopen(const char *file, const char *mode);
+extern "C" int __interceptor_fileno(FILE *f);
+
+extern "C" FILE *fopen(const char *file, const char *mode) {
+ static int first = 0;
+ if (__sync_lock_test_and_set(&first, 1) == 0)
+ printf("user fopen\n");
+ return __interceptor_fopen(file, mode);
+}
+
+extern "C" int fileno(FILE *f) {
+ static int first = 0;
+ if (__sync_lock_test_and_set(&first, 1) == 0)
+ printf("user fileno\n");
+ return 1;
+}
+
+int main() {
+ FILE *f = fopen("/dev/zero", "r");
+ if (f) {
+ char buf;
+ fread(&buf, 1, 1, f);
+ fclose(f);
+ }
+}
+
+// CHECK: user fopen
+// CHECK-NOT: ThreadSanitizer
+
diff --git a/test/tsan/user_malloc.cc b/test/tsan/user_malloc.cc
new file mode 100644
index 000000000..0be6d54fb
--- /dev/null
+++ b/test/tsan/user_malloc.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdio.h>
+
+// defined by tsan.
+extern "C" void *__interceptor_malloc(unsigned long size);
+extern "C" void __interceptor_free(void *p);
+
+extern "C" void *malloc(unsigned long size) {
+ static int first = 0;
+ if (__sync_lock_test_and_set(&first, 1) == 0)
+ printf("user malloc\n");
+ return __interceptor_malloc(size);
+}
+
+extern "C" void free(void *p) {
+ __interceptor_free(p);
+}
+
+int main() {
+ volatile char *p = (char*)malloc(10);
+ p[0] = 0;
+ free((void*)p);
+}
+
+// CHECK: user malloc
+// CHECK-NOT: ThreadSanitizer
+
diff --git a/test/tsan/virtual_inheritance_compile_bug.cc b/test/tsan/virtual_inheritance_compile_bug.cc
new file mode 100644
index 000000000..2275b8b8d
--- /dev/null
+++ b/test/tsan/virtual_inheritance_compile_bug.cc
@@ -0,0 +1,15 @@
+// Regression test for http://code.google.com/p/thread-sanitizer/issues/detail?id=3.
+// The C++ variant is much more compact that the LLVM IR equivalent.
+
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdio.h>
+struct AAA { virtual long aaa () { return 0; } }; // NOLINT
+struct BBB: virtual AAA { unsigned long bbb; }; // NOLINT
+struct CCC: virtual AAA { };
+struct DDD: CCC, BBB { DDD(); }; // NOLINT
+DDD::DDD() { }
+int main() {
+ DDD d;
+ printf("OK\n");
+}
+// CHECK: OK
diff --git a/test/tsan/vptr_benign_race.cc b/test/tsan/vptr_benign_race.cc
new file mode 100644
index 000000000..8c9fc596e
--- /dev/null
+++ b/test/tsan/vptr_benign_race.cc
@@ -0,0 +1,51 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+
+struct A {
+ A() {
+ sem_init(&sem_, 0, 0);
+ }
+ virtual void F() {
+ }
+ void Done() {
+ sem_post(&sem_);
+ }
+ virtual ~A() {
+ }
+ sem_t sem_;
+};
+
+struct B : A {
+ virtual void F() {
+ }
+ virtual ~B() {
+ sem_wait(&sem_);
+ sem_destroy(&sem_);
+ }
+};
+
+static A *obj = new B;
+
+void *Thread1(void *x) {
+ obj->F();
+ obj->Done();
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ delete obj;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ fprintf(stderr, "PASS\n");
+}
+// CHECK: PASS
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/test/tsan/vptr_harmful_race.cc b/test/tsan/vptr_harmful_race.cc
new file mode 100644
index 000000000..0105c4ced
--- /dev/null
+++ b/test/tsan/vptr_harmful_race.cc
@@ -0,0 +1,51 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <unistd.h>
+
+struct A {
+ A() {
+ sem_init(&sem_, 0, 0);
+ }
+ virtual void F() {
+ }
+ void Done() {
+ sem_post(&sem_);
+ }
+ virtual ~A() {
+ sem_wait(&sem_);
+ sem_destroy(&sem_);
+ }
+ sem_t sem_;
+};
+
+struct B : A {
+ virtual void F() {
+ }
+ virtual ~B() { }
+};
+
+static A *obj = new B;
+
+void *Thread1(void *x) {
+ obj->F();
+ obj->Done();
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ delete obj;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race on vptr
diff --git a/test/tsan/vptr_harmful_race2.cc b/test/tsan/vptr_harmful_race2.cc
new file mode 100644
index 000000000..378790c62
--- /dev/null
+++ b/test/tsan/vptr_harmful_race2.cc
@@ -0,0 +1,51 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <unistd.h>
+
+struct A {
+ A() {
+ sem_init(&sem_, 0, 0);
+ }
+ virtual void F() {
+ }
+ void Done() {
+ sem_post(&sem_);
+ }
+ virtual ~A() {
+ sem_wait(&sem_);
+ sem_destroy(&sem_);
+ }
+ sem_t sem_;
+};
+
+struct B : A {
+ virtual void F() {
+ }
+ virtual ~B() { }
+};
+
+static A *obj = new B;
+
+void *Thread1(void *x) {
+ sleep(1);
+ obj->F();
+ obj->Done();
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ delete obj;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race on vptr
diff --git a/test/tsan/write_in_reader_lock.cc b/test/tsan/write_in_reader_lock.cc
new file mode 100644
index 000000000..e872fe3ff
--- /dev/null
+++ b/test/tsan/write_in_reader_lock.cc
@@ -0,0 +1,35 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+pthread_rwlock_t rwlock;
+int GLOB;
+
+void *Thread1(void *p) {
+ (void)p;
+ pthread_rwlock_rdlock(&rwlock);
+ // Write under reader lock.
+ sleep(1);
+ GLOB++;
+ pthread_rwlock_unlock(&rwlock);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ pthread_rwlock_init(&rwlock, NULL);
+ pthread_rwlock_rdlock(&rwlock);
+ pthread_t t;
+ pthread_create(&t, 0, Thread1, 0);
+ volatile int x = GLOB;
+ (void)x;
+ pthread_rwlock_unlock(&rwlock);
+ pthread_join(t, 0);
+ pthread_rwlock_destroy(&rwlock);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4 at {{.*}} by thread T1{{.*}}:
+// CHECK: #0 Thread1(void*) {{.*}}write_in_reader_lock.cc:13
+// CHECK: Previous read of size 4 at {{.*}} by main thread{{.*}}:
+// CHECK: #0 main {{.*}}write_in_reader_lock.cc:23