aboutsummaryrefslogtreecommitdiff
path: root/test/tsan
diff options
context:
space:
mode:
Diffstat (limited to 'test/tsan')
-rw-r--r--test/tsan/CMakeLists.txt27
-rw-r--r--test/tsan/Linux/lit.local.cfg9
-rw-r--r--test/tsan/Linux/mutex_robust.cc36
-rw-r--r--test/tsan/Linux/mutex_robust2.cc41
-rw-r--r--test/tsan/Linux/user_fopen.cc34
-rw-r--r--test/tsan/Linux/user_malloc.cc27
-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/atexit.cc29
-rw-r--r--test/tsan/atexit2.cc26
-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/barrier.cc37
-rw-r--r--test/tsan/bench.h59
-rw-r--r--test/tsan/bench_acquire_only.cc19
-rw-r--r--test/tsan/bench_acquire_release.cc18
-rw-r--r--test/tsan/bench_local_mutex.cc27
-rw-r--r--test/tsan/bench_mutex.cc28
-rw-r--r--test/tsan/bench_release_only.cc23
-rw-r--r--test/tsan/bench_rwmutex.cc25
-rw-r--r--test/tsan/bench_shadow_flush.cc48
-rw-r--r--test/tsan/bench_single_writer.cc23
-rw-r--r--test/tsan/bench_ten_mutexes.cc26
-rw-r--r--test/tsan/benign_race.cc39
-rw-r--r--test/tsan/blacklist.cc30
-rw-r--r--test/tsan/blacklist2.cc49
-rw-r--r--test/tsan/cond.c53
-rw-r--r--test/tsan/cond_cancel.c37
-rw-r--r--test/tsan/cond_race.cc40
-rw-r--r--test/tsan/cond_version.c44
-rw-r--r--test/tsan/deadlock_detector_stress_test.cc596
-rw-r--r--test/tsan/deep_stack1.cc44
-rw-r--r--test/tsan/default_options.cc32
-rwxr-xr-xtest/tsan/deflake.bash17
-rw-r--r--test/tsan/dlclose.cc58
-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_atexit.cc37
-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.cc39
-rw-r--r--test/tsan/global_race.cc28
-rw-r--r--test/tsan/global_race2.cc28
-rw-r--r--test/tsan/global_race3.cc32
-rw-r--r--test/tsan/halt_on_error.cc27
-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.cc38
-rw-r--r--test/tsan/inlined_memcpy_race2.cc38
-rw-r--r--test/tsan/interface_atomic_test.c16
-rw-r--r--test/tsan/java.h21
-rw-r--r--test/tsan/java_alloc.cc38
-rw-r--r--test/tsan/java_finalizer.cc27
-rw-r--r--test/tsan/java_lock.cc36
-rw-r--r--test/tsan/java_lock_move.cc41
-rw-r--r--test/tsan/java_lock_rec.cc55
-rw-r--r--test/tsan/java_lock_rec_race.cc49
-rw-r--r--test/tsan/java_move_overlap.cc72
-rw-r--r--test/tsan/java_move_overlap_race.cc53
-rw-r--r--test/tsan/java_race.cc25
-rw-r--r--test/tsan/java_race_move.cc33
-rw-r--r--test/tsan/java_rwlock.cc36
-rw-r--r--test/tsan/libcxx/lit.local.cfg10
-rw-r--r--test/tsan/libcxx/std_shared_ptr.cc24
-rw-r--r--test/tsan/lit.cfg65
-rw-r--r--test/tsan/lit.site.cfg.in10
-rw-r--r--test/tsan/load_shared_lib.cc66
-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_overflow.cc23
-rw-r--r--test/tsan/malloc_stack.cc25
-rw-r--r--test/tsan/map32bit.cc41
-rw-r--r--test/tsan/memcpy_race.cc42
-rw-r--r--test/tsan/mmap_large.cc20
-rw-r--r--test/tsan/mop_with_offset.cc36
-rw-r--r--test/tsan/mop_with_offset2.cc36
-rw-r--r--test/tsan/must_deadlock.cc50
-rw-r--r--test/tsan/mutex_bad_read_lock.cc19
-rw-r--r--test/tsan/mutex_bad_read_unlock.cc20
-rw-r--r--test/tsan/mutex_bad_unlock.cc18
-rw-r--r--test/tsan/mutex_cycle2.c35
-rw-r--r--test/tsan/mutex_destroy_locked.cc22
-rw-r--r--test/tsan/mutex_double_lock.cc29
-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.cc40
-rw-r--r--test/tsan/mutexset8.cc39
-rw-r--r--test/tsan/printf-1.c16
-rw-r--r--test/tsan/process_sleep.h7
-rw-r--r--test/tsan/pthread_atfork_deadlock.c33
-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_puts.cc29
-rw-r--r--test/tsan/race_on_read.cc41
-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.cc49
-rw-r--r--test/tsan/signal_longjmp.cc69
-rw-r--r--test/tsan/signal_malloc.cc26
-rw-r--r--test/tsan/signal_recursive.cc132
-rw-r--r--test/tsan/signal_sync.cc58
-rw-r--r--test/tsan/signal_write.cc27
-rw-r--r--test/tsan/sigsuspend.cc44
-rw-r--r--test/tsan/simple_race.c29
-rw-r--r--test/tsan/simple_race.cc29
-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/sunrpc.cc25
-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.sh66
-rw-r--r--test/tsan/thread_detach.c20
-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.cc47
-rw-r--r--test/tsan/thread_name2.cc36
-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/vfork.cc51
-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/vptr_harmful_race3.cc53
-rw-r--r--test/tsan/vptr_harmful_race4.cc34
-rw-r--r--test/tsan/write_in_reader_lock.cc35
190 files changed, 7165 insertions, 0 deletions
diff --git a/test/tsan/CMakeLists.txt b/test/tsan/CMakeLists.txt
new file mode 100644
index 000000000000..29c0821b3c5a
--- /dev/null
+++ b/test/tsan/CMakeLists.txt
@@ -0,0 +1,27 @@
+set(TSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
+if(NOT COMPILER_RT_STANDALONE_BUILD)
+ list(APPEND TSAN_TEST_DEPS tsan)
+endif()
+if(COMPILER_RT_HAS_LIBCXX_SOURCES AND
+ COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang")
+ list(APPEND TSAN_TEST_DEPS libcxx_tsan)
+ set(TSAN_HAS_LIBCXX True)
+else()
+ set(TSAN_HAS_LIBCXX False)
+endif()
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
+
+if(COMPILER_RT_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/Linux/lit.local.cfg b/test/tsan/Linux/lit.local.cfg
new file mode 100644
index 000000000000..57271b8078a4
--- /dev/null
+++ b/test/tsan/Linux/lit.local.cfg
@@ -0,0 +1,9 @@
+def getRoot(config):
+ if not config.parent:
+ return config
+ return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os not in ['Linux']:
+ config.unsupported = True
diff --git a/test/tsan/Linux/mutex_robust.cc b/test/tsan/Linux/mutex_robust.cc
new file mode 100644
index 000000000000..5ca5e70d49a7
--- /dev/null
+++ b/test/tsan/Linux/mutex_robust.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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/Linux/mutex_robust2.cc b/test/tsan/Linux/mutex_robust2.cc
new file mode 100644
index 000000000000..0914c1763604
--- /dev/null
+++ b/test/tsan/Linux/mutex_robust2.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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/Linux/user_fopen.cc b/test/tsan/Linux/user_fopen.cc
new file mode 100644
index 000000000000..c0ff267ff88b
--- /dev/null
+++ b/test/tsan/Linux/user_fopen.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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/Linux/user_malloc.cc b/test/tsan/Linux/user_malloc.cc
new file mode 100644
index 000000000000..c671bfcdd17a
--- /dev/null
+++ b/test/tsan/Linux/user_malloc.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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/Unit/lit.site.cfg.in b/test/tsan/Unit/lit.site.cfg.in
new file mode 100644
index 000000000000..9498105653a1
--- /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 000000000000..f82542ed2b85
--- /dev/null
+++ b/test/tsan/aligned_vs_unaligned_race.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..cde706bc8a1d
--- /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 should crash.
+// With allocator_may_return_null=1 the allocator should return 0.
+//
+// RUN: %clangxx_tsan -O0 %s -o %t
+// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH
+// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %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/atexit.cc b/test/tsan/atexit.cc
new file mode 100644
index 000000000000..69acb4dd783f
--- /dev/null
+++ b/test/tsan/atexit.cc
@@ -0,0 +1,29 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+class Logger {
+ public:
+ Logger() {
+ fprintf(stderr, "Logger ctor\n");
+ }
+
+ ~Logger() {
+ fprintf(stderr, "Logger dtor\n");
+ }
+};
+
+Logger logger;
+
+void log_from_atexit() {
+ fprintf(stderr, "In log_from_atexit\n");
+}
+
+int main() {
+ atexit(log_from_atexit);
+}
+
+// CHECK: Logger ctor
+// CHECK: In log_from_atexit
+// CHECK: Logger dtor
diff --git a/test/tsan/atexit2.cc b/test/tsan/atexit2.cc
new file mode 100644
index 000000000000..6f74c5f9f6e5
--- /dev/null
+++ b/test/tsan/atexit2.cc
@@ -0,0 +1,26 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int n;
+const int N = 10000;
+
+static void atexit1() {
+ n++;
+}
+
+static void atexit0() {
+ fprintf(stderr, "run count: %d\n", n);
+}
+
+int main() {
+ atexit(atexit0);
+ for (int i = 0; i < N; i++)
+ atexit(atexit1);
+}
+
+// CHECK-NOT: FATAL: ThreadSanitizer
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: run count: 10000
+
diff --git a/test/tsan/atomic_free.cc b/test/tsan/atomic_free.cc
new file mode 100644
index 000000000000..1dcf887c41d5
--- /dev/null
+++ b/test/tsan/atomic_free.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..c50be6bba940
--- /dev/null
+++ b/test/tsan/atomic_free2.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..d9ccda58883c
--- /dev/null
+++ b/test/tsan/atomic_norace.cc
@@ -0,0 +1,61 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..9cee8ed8272f
--- /dev/null
+++ b/test/tsan/atomic_race.cc
@@ -0,0 +1,80 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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(4);
+ 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 000000000000..7e3176f8e784
--- /dev/null
+++ b/test/tsan/atomic_stack.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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/barrier.cc b/test/tsan/barrier.cc
new file mode 100644
index 000000000000..d8c2b6ffe514
--- /dev/null
+++ b/test/tsan/barrier.cc
@@ -0,0 +1,37 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// CHECK-NOT: ThreadSanitizer: data race
+// CHECK: DONE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+
+const int kSize = 4;
+volatile int kIter = 10; // prevent unwinding
+int data[2][kSize];
+pthread_barrier_t barrier;
+
+void *thr(void *p) {
+ int idx = (int)(long)p;
+ for (int i = 0; i < kIter; i++) {
+ int *prev = data[i % 2];
+ int *curr = data[(i + 1) % 2];
+ int left = idx - 1 >= 0 ? prev[idx - 1] : 0;
+ int right = idx + 1 < kSize ? prev[idx + 1] : 0;
+ curr[idx] = (left + right) / 2;
+ pthread_barrier_wait(&barrier);
+ }
+ return 0;
+}
+
+int main() {
+ pthread_barrier_init(&barrier, 0, kSize);
+ pthread_t th[kSize];
+ for (int i = 0; i < kSize; i++)
+ pthread_create(&th[i], 0, thr, (void*)(long)i);
+ for (int i = 0; i < kSize; i++)
+ pthread_join(th[i], 0);
+ pthread_barrier_destroy(&barrier);
+ fprintf(stderr, "DONE\n");
+}
diff --git a/test/tsan/bench.h b/test/tsan/bench.h
new file mode 100644
index 000000000000..5ae0dd813db9
--- /dev/null
+++ b/test/tsan/bench.h
@@ -0,0 +1,59 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+
+int bench_nthread;
+int bench_niter;
+int grow_clock_var;
+pthread_barrier_t glow_clock_barrier;
+
+void bench(); // defined by user
+void start_thread_group(int nth, void(*f)(int tid));
+void grow_clock_worker(int tid);
+
+int main(int argc, char **argv) {
+ bench_nthread = 2;
+ if (argc > 1)
+ bench_nthread = atoi(argv[1]);
+ bench_niter = 100;
+ if (argc > 2)
+ bench_niter = atoi(argv[2]);
+
+ // Grow thread's clock.
+ int clock_size = 10;
+ if (argc > 1)
+ clock_size = 1000;
+ pthread_barrier_init(&glow_clock_barrier, 0, clock_size);
+ start_thread_group(clock_size, grow_clock_worker);
+ pthread_barrier_destroy(&glow_clock_barrier);
+ __atomic_load_n(&grow_clock_var, __ATOMIC_ACQUIRE);
+
+ timespec tp0;
+ clock_gettime(CLOCK_MONOTONIC, &tp0);
+ bench();
+ timespec tp1;
+ clock_gettime(CLOCK_MONOTONIC, &tp1);
+ unsigned long long t =
+ (tp1.tv_sec * 1000000000ULL + tp1.tv_nsec) -
+ (tp0.tv_sec * 1000000000ULL + tp0.tv_nsec);
+ fprintf(stderr, "%llu ns/iter\n", t / bench_niter);
+ fprintf(stderr, "DONE\n");
+}
+
+void start_thread_group(int nth, void(*f)(int tid)) {
+ pthread_t *th = (pthread_t*)malloc(nth * sizeof(pthread_t));
+ for (int i = 0; i < nth; i++)
+ pthread_create(&th[i], 0, (void*(*)(void*))f, (void*)(long)i);
+ for (int i = 0; i < nth; i++)
+ pthread_join(th[i], 0);
+}
+
+void grow_clock_worker(int tid) {
+ int res = pthread_barrier_wait(&glow_clock_barrier);
+ if (res == PTHREAD_BARRIER_SERIAL_THREAD)
+ __atomic_store_n(&grow_clock_var, 0, __ATOMIC_RELEASE);
+}
+
diff --git a/test/tsan/bench_acquire_only.cc b/test/tsan/bench_acquire_only.cc
new file mode 100644
index 000000000000..5cd6bd74ebee
--- /dev/null
+++ b/test/tsan/bench_acquire_only.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include "bench.h"
+
+int x;
+
+void thread(int tid) {
+ for (int i = 0; i < bench_niter; i++)
+ __atomic_load_n(&x, __ATOMIC_ACQUIRE);
+}
+
+void bench() {
+ __atomic_store_n(&x, 0, __ATOMIC_RELEASE);
+ start_thread_group(bench_nthread, thread);
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/bench_acquire_release.cc b/test/tsan/bench_acquire_release.cc
new file mode 100644
index 000000000000..9e53a7b26efa
--- /dev/null
+++ b/test/tsan/bench_acquire_release.cc
@@ -0,0 +1,18 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include "bench.h"
+
+int x;
+
+void thread(int tid) {
+ for (int i = 0; i < bench_niter; i++)
+ __atomic_fetch_add(&x, 1, __ATOMIC_ACQ_REL);
+}
+
+void bench() {
+ start_thread_group(bench_nthread, thread);
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/bench_local_mutex.cc b/test/tsan/bench_local_mutex.cc
new file mode 100644
index 000000000000..0fa1db0c883c
--- /dev/null
+++ b/test/tsan/bench_local_mutex.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include "bench.h"
+
+pthread_mutex_t *mtx;
+const int kStride = 16;
+
+void thread(int tid) {
+ for (int i = 0; i < bench_niter; i++) {
+ pthread_mutex_lock(&mtx[tid * kStride]);
+ pthread_mutex_unlock(&mtx[tid * kStride]);
+ }
+}
+
+void bench() {
+ mtx = (pthread_mutex_t*)malloc(bench_nthread * kStride * sizeof(*mtx));
+ for (int i = 0; i < bench_nthread; i++) {
+ pthread_mutex_init(&mtx[i * kStride], 0);
+ pthread_mutex_lock(&mtx[i * kStride]);
+ pthread_mutex_unlock(&mtx[i * kStride]);
+ }
+ start_thread_group(bench_nthread, thread);
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/bench_mutex.cc b/test/tsan/bench_mutex.cc
new file mode 100644
index 000000000000..324d53fd7f28
--- /dev/null
+++ b/test/tsan/bench_mutex.cc
@@ -0,0 +1,28 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include "bench.h"
+
+pthread_mutex_t mtx;
+pthread_cond_t cv;
+int x;
+
+void thread(int tid) {
+ for (int i = 0; i < bench_niter; i++) {
+ pthread_mutex_lock(&mtx);
+ while (x != i * 2 + tid)
+ pthread_cond_wait(&cv, &mtx);
+ x++;
+ pthread_cond_signal(&cv);
+ pthread_mutex_unlock(&mtx);
+ }
+}
+
+void bench() {
+ pthread_mutex_init(&mtx, 0);
+ pthread_cond_init(&cv, 0);
+ start_thread_group(2, thread);
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/bench_release_only.cc b/test/tsan/bench_release_only.cc
new file mode 100644
index 000000000000..0a86f73f249e
--- /dev/null
+++ b/test/tsan/bench_release_only.cc
@@ -0,0 +1,23 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include "bench.h"
+
+int *x;
+const int kStride = 32;
+
+void thread(int tid) {
+ __atomic_load_n(&x[tid * kStride], __ATOMIC_ACQUIRE);
+ for (int i = 0; i < bench_niter; i++)
+ __atomic_store_n(&x[tid * kStride], 0, __ATOMIC_RELEASE);
+}
+
+void bench() {
+ x = (int*)malloc(bench_nthread * kStride * sizeof(x[0]));
+ for (int i = 0; i < bench_nthread; i++)
+ __atomic_store_n(&x[i * kStride], 0, __ATOMIC_RELEASE);
+ start_thread_group(bench_nthread, thread);
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/bench_rwmutex.cc b/test/tsan/bench_rwmutex.cc
new file mode 100644
index 000000000000..818ee8c82bc1
--- /dev/null
+++ b/test/tsan/bench_rwmutex.cc
@@ -0,0 +1,25 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include "bench.h"
+
+pthread_rwlock_t mtx;
+
+void thread(int tid) {
+ for (int i = 0; i < bench_niter; i++) {
+ pthread_rwlock_rdlock(&mtx);
+ pthread_rwlock_unlock(&mtx);
+ }
+}
+
+void bench() {
+ pthread_rwlock_init(&mtx, 0);
+ pthread_rwlock_wrlock(&mtx);
+ pthread_rwlock_unlock(&mtx);
+ pthread_rwlock_rdlock(&mtx);
+ pthread_rwlock_unlock(&mtx);
+ start_thread_group(bench_nthread, thread);
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/bench_shadow_flush.cc b/test/tsan/bench_shadow_flush.cc
new file mode 100644
index 000000000000..0f412bbe82f9
--- /dev/null
+++ b/test/tsan/bench_shadow_flush.cc
@@ -0,0 +1,48 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/mman.h>
+
+const long kSmallPage = 4 << 10;
+const long kLargePage = 2 << 20;
+const long kStride = 1 << 10;
+
+typedef unsigned long uptr;
+
+int main(int argc, const char **argv) {
+ uptr mem_size = 4 << 20;
+ if (argc > 1)
+ mem_size = (uptr)atoi(argv[1]) << 20;
+ uptr stride = kSmallPage;
+ if (argc > 2)
+ stride = (uptr)atoi(argv[2]) << 10;
+ int niter = 1;
+ if (argc > 3)
+ niter = atoi(argv[3]);
+ int stride2 = 1;
+ if (argc > 4)
+ stride2 = atoi(argv[4]);
+
+ uptr sz = mem_size + stride2 * kStride + kLargePage;
+ void *p = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+ uptr a = ((uptr)p + kLargePage - 1) & ~(kLargePage - 1);
+ volatile char *mem = (volatile char *)a;
+
+ for (int i = 0; i < niter; i++) {
+ for (uptr off = 0; off < mem_size; off += stride) {
+ for (uptr off2 = 0; off2 < stride2; off2++)
+ mem[off + off2 * kStride] = 42;
+ }
+ }
+
+ fprintf(stderr, "DONE\n");
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/bench_single_writer.cc b/test/tsan/bench_single_writer.cc
new file mode 100644
index 000000000000..0d3810a03ad0
--- /dev/null
+++ b/test/tsan/bench_single_writer.cc
@@ -0,0 +1,23 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include "bench.h"
+
+int x;
+
+void thread(int tid) {
+ if (tid == 0) {
+ for (int i = 0; i < bench_niter; i++)
+ __atomic_store_n(&x, 0, __ATOMIC_RELEASE);
+ } else {
+ for (int i = 0; i < bench_niter; i++)
+ __atomic_load_n(&x, __ATOMIC_ACQUIRE);
+ }
+}
+
+void bench() {
+ start_thread_group(bench_nthread, thread);
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/bench_ten_mutexes.cc b/test/tsan/bench_ten_mutexes.cc
new file mode 100644
index 000000000000..876f1365ee43
--- /dev/null
+++ b/test/tsan/bench_ten_mutexes.cc
@@ -0,0 +1,26 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include "bench.h"
+
+const int kMutex = 10;
+pthread_mutex_t mtx[kMutex];
+
+void thread(int tid) {
+ for (int i = 0; i < bench_niter; i++) {
+ int idx = (i % kMutex);
+ if (tid == 0)
+ idx = kMutex - idx - 1;
+ pthread_mutex_lock(&mtx[idx]);
+ pthread_mutex_unlock(&mtx[idx]);
+ }
+}
+
+void bench() {
+ for (int i = 0; i < kMutex; i++)
+ pthread_mutex_init(&mtx[i], 0);
+ start_thread_group(2, thread);
+}
+
+// CHECK: DONE
+
diff --git a/test/tsan/benign_race.cc b/test/tsan/benign_race.cc
new file mode 100644
index 000000000000..b6cba19aa96e
--- /dev/null
+++ b/test/tsan/benign_race.cc
@@ -0,0 +1,39 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..d6ca383cb758
--- /dev/null
+++ b/test/tsan/blacklist.cc
@@ -0,0 +1,30 @@
+// Test blacklist functionality for TSan.
+
+// RUN: echo "fun:*Blacklisted_Thread2*" > %t.blacklist
+// RUN: %clangxx_tsan -O1 %s -fsanitize-blacklist=%t.blacklist -o %t && %run %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/blacklist2.cc b/test/tsan/blacklist2.cc
new file mode 100644
index 000000000000..1092561e55bb
--- /dev/null
+++ b/test/tsan/blacklist2.cc
@@ -0,0 +1,49 @@
+// Test that blacklisted functions are still contained in the stack trace.
+
+// RUN: echo "fun:*Blacklisted_Thread2*" > %t.blacklist
+// RUN: echo "fun:*CallTouchGlobal*" >> %t.blacklist
+
+// RUN: %clangxx_tsan -O1 %s -fsanitize-blacklist=%t.blacklist -o %t
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ // CHECK: ThreadSanitizer: data race
+ // CHECK: Write of size 4
+ // CHECK: #0 Thread1{{.*}}blacklist2.cc:[[@LINE+1]]
+ Global++;
+ return NULL;
+}
+
+void TouchGlobal() {
+ // CHECK: Previous write of size 4
+ // CHECK: #0 TouchGlobal{{.*}}blacklist2.cc:[[@LINE+1]]
+ Global--;
+}
+
+void CallTouchGlobal() {
+ // CHECK: #1 CallTouchGlobal{{.*}}blacklist2.cc:[[@LINE+1]]
+ TouchGlobal();
+}
+
+void *Blacklisted_Thread2(void *x) {
+ Global--;
+ // CHECK: #2 Blacklisted_Thread2{{.*}}blacklist2.cc:[[@LINE+1]]
+ CallTouchGlobal();
+ 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;
+}
diff --git a/test/tsan/cond.c b/test/tsan/cond.c
new file mode 100644
index 000000000000..05ea672c6c1c
--- /dev/null
+++ b/test/tsan/cond.c
@@ -0,0 +1,53 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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_cancel.c b/test/tsan/cond_cancel.c
new file mode 100644
index 000000000000..397cad4b1838
--- /dev/null
+++ b/test/tsan/cond_cancel.c
@@ -0,0 +1,37 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// CHECK-NOT: WARNING
+// CHECK: OK
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+
+pthread_mutex_t m;
+pthread_cond_t c;
+int x;
+
+void *thr1(void *p) {
+ pthread_mutex_lock(&m);
+ pthread_cleanup_push((void(*)(void *arg))pthread_mutex_unlock, &m);
+ while (x == 0)
+ pthread_cond_wait(&c, &m);
+ pthread_cleanup_pop(1);
+ return 0;
+}
+
+int main() {
+ pthread_t th;
+
+ pthread_mutex_init(&m, 0);
+ pthread_cond_init(&c, 0);
+
+ pthread_create(&th, 0, thr1, 0);
+ sleep(1); // let it block on cond var
+ pthread_cancel(th);
+
+ pthread_join(th, 0);
+ pthread_mutex_lock(&m);
+ pthread_mutex_unlock(&m);
+ fprintf(stderr, "OK\n");
+}
diff --git a/test/tsan/cond_race.cc b/test/tsan/cond_race.cc
new file mode 100644
index 000000000000..fa42fafca4d2
--- /dev/null
+++ b/test/tsan/cond_race.cc
@@ -0,0 +1,40 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// CHECK-NOT: unlock of unlocked mutex
+// CHECK: ThreadSanitizer: data race
+// CHECK: pthread_cond_signal
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.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);
+ // w/o this sleep, it can be reported as use-after-free
+ sleep(1);
+ 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 000000000000..2282c3ad738d
--- /dev/null
+++ b/test/tsan/cond_version.c
@@ -0,0 +1,44 @@
+// RUN: %clang_tsan -O1 %s -o %t -lrt && %run %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/deadlock_detector_stress_test.cc b/test/tsan/deadlock_detector_stress_test.cc
new file mode 100644
index 000000000000..53624782e07b
--- /dev/null
+++ b/test/tsan/deadlock_detector_stress_test.cc
@@ -0,0 +1,596 @@
+// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadMutex
+// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND
+// TSAN_OPTIONS="detect_deadlocks=1 second_deadlock_stack=1" %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND
+// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadSpinLock
+// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s
+// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRWLock
+// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RD
+// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRecursiveMutex
+// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-REC
+#include <pthread.h>
+#undef NDEBUG
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <new>
+
+#ifndef LockType
+#define LockType PthreadMutex
+#endif
+
+// You can optionally pass [test_number [iter_count]] on command line.
+static int test_number = -1;
+static int iter_count = 100000;
+
+class PthreadMutex {
+ public:
+ explicit PthreadMutex(bool recursive = false) {
+ if (recursive) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ assert(0 == pthread_mutex_init(&mu_, &attr));
+ } else {
+ assert(0 == pthread_mutex_init(&mu_, 0));
+ }
+ }
+ ~PthreadMutex() {
+ assert(0 == pthread_mutex_destroy(&mu_));
+ (void)padding_;
+ }
+ static bool supports_read_lock() { return false; }
+ static bool supports_recursive_lock() { return false; }
+ void lock() { assert(0 == pthread_mutex_lock(&mu_)); }
+ void unlock() { assert(0 == pthread_mutex_unlock(&mu_)); }
+ bool try_lock() { return 0 == pthread_mutex_trylock(&mu_); }
+ void rdlock() { assert(0); }
+ void rdunlock() { assert(0); }
+ bool try_rdlock() { assert(0); }
+
+ private:
+ pthread_mutex_t mu_;
+ char padding_[64 - sizeof(pthread_mutex_t)];
+};
+
+class PthreadRecursiveMutex : public PthreadMutex {
+ public:
+ PthreadRecursiveMutex() : PthreadMutex(true) { }
+ static bool supports_recursive_lock() { return true; }
+};
+
+class PthreadSpinLock {
+ public:
+ PthreadSpinLock() { assert(0 == pthread_spin_init(&mu_, 0)); }
+ ~PthreadSpinLock() {
+ assert(0 == pthread_spin_destroy(&mu_));
+ (void)padding_;
+ }
+ static bool supports_read_lock() { return false; }
+ static bool supports_recursive_lock() { return false; }
+ void lock() { assert(0 == pthread_spin_lock(&mu_)); }
+ void unlock() { assert(0 == pthread_spin_unlock(&mu_)); }
+ bool try_lock() { return 0 == pthread_spin_trylock(&mu_); }
+ void rdlock() { assert(0); }
+ void rdunlock() { assert(0); }
+ bool try_rdlock() { assert(0); }
+
+ private:
+ pthread_spinlock_t mu_;
+ char padding_[64 - sizeof(pthread_spinlock_t)];
+};
+
+class PthreadRWLock {
+ public:
+ PthreadRWLock() { assert(0 == pthread_rwlock_init(&mu_, 0)); }
+ ~PthreadRWLock() {
+ assert(0 == pthread_rwlock_destroy(&mu_));
+ (void)padding_;
+ }
+ static bool supports_read_lock() { return true; }
+ static bool supports_recursive_lock() { return false; }
+ void lock() { assert(0 == pthread_rwlock_wrlock(&mu_)); }
+ void unlock() { assert(0 == pthread_rwlock_unlock(&mu_)); }
+ bool try_lock() { return 0 == pthread_rwlock_trywrlock(&mu_); }
+ void rdlock() { assert(0 == pthread_rwlock_rdlock(&mu_)); }
+ void rdunlock() { assert(0 == pthread_rwlock_unlock(&mu_)); }
+ bool try_rdlock() { return 0 == pthread_rwlock_tryrdlock(&mu_); }
+
+ private:
+ pthread_rwlock_t mu_;
+ char padding_[64 - sizeof(pthread_rwlock_t)];
+};
+
+class LockTest {
+ public:
+ LockTest() : n_(), locks_() {}
+ void Init(size_t n) {
+ n_ = n;
+ locks_ = new LockType*[n_];
+ for (size_t i = 0; i < n_; i++)
+ locks_[i] = new LockType;
+ }
+ ~LockTest() {
+ for (size_t i = 0; i < n_; i++)
+ delete locks_[i];
+ delete [] locks_;
+ }
+ void L(size_t i) {
+ assert(i < n_);
+ locks_[i]->lock();
+ }
+
+ void U(size_t i) {
+ assert(i < n_);
+ locks_[i]->unlock();
+ }
+
+ void RL(size_t i) {
+ assert(i < n_);
+ locks_[i]->rdlock();
+ }
+
+ void RU(size_t i) {
+ assert(i < n_);
+ locks_[i]->rdunlock();
+ }
+
+ void *A(size_t i) {
+ assert(i < n_);
+ return locks_[i];
+ }
+
+ bool T(size_t i) {
+ assert(i < n_);
+ return locks_[i]->try_lock();
+ }
+
+ // Simple lock order onversion.
+ void Test1() {
+ if (test_number > 0 && test_number != 1) return;
+ fprintf(stderr, "Starting Test1\n");
+ // CHECK: Starting Test1
+ Init(5);
+ fprintf(stderr, "Expecting lock inversion: %p %p\n", A(0), A(1));
+ // CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]]
+ Lock_0_1();
+ Lock_1_0();
+ // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock)
+ // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ([[A1]]) => [[M2:M[0-9]+]] ([[A2]]) => [[M1]]
+ // CHECK: Mutex [[M2]] acquired here while holding mutex [[M1]]
+ // CHECK: #0 pthread_
+ // CHECK-SECOND: Mutex [[M1]] previously acquired by the same thread here:
+ // CHECK-SECOND: #0 pthread_
+ // CHECK-NOT-SECOND: second_deadlock_stack=1 to get more informative warning message
+ // CHECK-NOT-SECOND-NOT: #0 pthread_
+ // CHECK: Mutex [[M1]] acquired here while holding mutex [[M2]]
+ // CHECK: #0 pthread_
+ // CHECK-SECOND: Mutex [[M2]] previously acquired by the same thread here:
+ // CHECK-SECOND: #0 pthread_
+ // CHECK-NOT-SECOND-NOT: #0 pthread_
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ }
+
+ // Simple lock order inversion with 3 locks.
+ void Test2() {
+ if (test_number > 0 && test_number != 2) return;
+ fprintf(stderr, "Starting Test2\n");
+ // CHECK: Starting Test2
+ Init(5);
+ fprintf(stderr, "Expecting lock inversion: %p %p %p\n", A(0), A(1), A(2));
+ // CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]] [[A3:0x[a-f0-9]*]]
+ Lock2(0, 1);
+ Lock2(1, 2);
+ Lock2(2, 0);
+ // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock)
+ // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ([[A1]]) => [[M2:M[0-9]+]] ([[A2]]) => [[M3:M[0-9]+]] ([[A3]]) => [[M1]]
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ }
+
+ // Lock order inversion with lots of new locks created (but not used)
+ // between. Since the new locks are not used we should still detect the
+ // deadlock.
+ void Test3() {
+ if (test_number > 0 && test_number != 3) return;
+ fprintf(stderr, "Starting Test3\n");
+ // CHECK: Starting Test3
+ Init(5);
+ Lock_0_1();
+ L(2);
+ CreateAndDestroyManyLocks();
+ U(2);
+ Lock_1_0();
+ // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock)
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ }
+
+ // lock l0=>l1; then create and use lots of locks; then lock l1=>l0.
+ // The deadlock epoch should have changed and we should not report anything.
+ void Test4() {
+ if (test_number > 0 && test_number != 4) return;
+ fprintf(stderr, "Starting Test4\n");
+ // CHECK: Starting Test4
+ Init(5);
+ Lock_0_1();
+ L(2);
+ CreateLockUnlockAndDestroyManyLocks();
+ U(2);
+ Lock_1_0();
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ }
+
+ void Test5() {
+ if (test_number > 0 && test_number != 5) return;
+ fprintf(stderr, "Starting Test5\n");
+ // CHECK: Starting Test5
+ Init(5);
+ RunThreads(&LockTest::Lock_0_1, &LockTest::Lock_1_0);
+ // CHECK: WARNING: ThreadSanitizer: lock-order-inversion
+ // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ({{.*}}) => [[M2:M[0-9]+]] ({{.*}}) => [[M1]]
+ // CHECK: Mutex [[M2]] acquired here while holding mutex [[M1]] in thread [[T1:T[0-9]+]]
+ // CHECK: Mutex [[M1]] acquired here while holding mutex [[M2]] in thread [[T2:T[0-9]+]]
+ // CHECK: Thread [[T1]] {{.*}} created by main thread
+ // CHECK: Thread [[T2]] {{.*}} created by main thread
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ }
+
+ void Test6() {
+ if (test_number > 0 && test_number != 6) return;
+ fprintf(stderr, "Starting Test6: 3 threads lock/unlock private mutexes\n");
+ // CHECK: Starting Test6
+ Init(100);
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ RunThreads(&LockTest::Lock1_Loop_0, &LockTest::Lock1_Loop_1,
+ &LockTest::Lock1_Loop_2);
+ }
+
+ void Test7() {
+ if (test_number > 0 && test_number != 7) return;
+ fprintf(stderr, "Starting Test7\n");
+ // CHECK: Starting Test7
+ Init(10);
+ L(0); T(1); U(1); U(0);
+ T(1); L(0); U(1); U(0);
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ fprintf(stderr, "No cycle: 0=>1\n");
+ // CHECK: No cycle: 0=>1
+
+ T(2); L(3); U(3); U(2);
+ L(3); T(2); U(3); U(2);
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ fprintf(stderr, "No cycle: 2=>3\n");
+ // CHECK: No cycle: 2=>3
+
+ T(4); L(5); U(4); U(5);
+ L(5); L(4); U(4); U(5);
+ // CHECK: WARNING: ThreadSanitizer: lock-order-inversion
+ fprintf(stderr, "Have cycle: 4=>5\n");
+ // CHECK: Have cycle: 4=>5
+
+ L(7); L(6); U(6); U(7);
+ T(6); L(7); U(6); U(7);
+ // CHECK: WARNING: ThreadSanitizer: lock-order-inversion
+ fprintf(stderr, "Have cycle: 6=>7\n");
+ // CHECK: Have cycle: 6=>7
+ }
+
+ void Test8() {
+ if (test_number > 0 && test_number != 8) return;
+ if (!LockType::supports_read_lock()) return;
+ fprintf(stderr, "Starting Test8\n");
+ Init(5);
+ // CHECK-RD: Starting Test8
+ RL(0); L(1); RU(0); U(1);
+ L(1); RL(0); RU(0); U(1);
+ // CHECK-RD: WARNING: ThreadSanitizer: lock-order-inversion
+ fprintf(stderr, "Have cycle: 0=>1\n");
+ // CHECK-RD: Have cycle: 0=>1
+
+ RL(2); RL(3); RU(2); RU(3);
+ RL(3); RL(2); RU(2); RU(3);
+ // CHECK-RD: WARNING: ThreadSanitizer: lock-order-inversion
+ fprintf(stderr, "Have cycle: 2=>3\n");
+ // CHECK-RD: Have cycle: 2=>3
+ }
+
+ void Test9() {
+ if (test_number > 0 && test_number != 9) return;
+ if (!LockType::supports_recursive_lock()) return;
+ fprintf(stderr, "Starting Test9\n");
+ // CHECK-REC: Starting Test9
+ Init(5);
+ L(0); L(0); L(0); L(1); U(1); U(0); U(0); U(0);
+ L(1); L(1); L(1); L(0); U(0); U(1); U(1); U(1);
+ // CHECK-REC: WARNING: ThreadSanitizer: lock-order-inversion
+ }
+
+ void Test10() {
+ if (test_number > 0 && test_number != 10) return;
+ fprintf(stderr, "Starting Test10: 4 threads lock/unlock 4 private mutexes, one under another\n");
+ // CHECK: Starting Test10
+ Init(100);
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ RunThreads(&LockTest::Test10_Thread1, &LockTest::Test10_Thread2,
+ &LockTest::Test10_Thread3, &LockTest::Test10_Thread4);
+ }
+ void Test10_Thread1() { Test10_Thread(0); }
+ void Test10_Thread2() { Test10_Thread(10); }
+ void Test10_Thread3() { Test10_Thread(20); }
+ void Test10_Thread4() { Test10_Thread(30); }
+ void Test10_Thread(size_t m) {
+ for (int i = 0; i < iter_count; i++) {
+ L(m + 0);
+ L(m + 1);
+ L(m + 2);
+ L(m + 3);
+ U(m + 3);
+ U(m + 2);
+ U(m + 1);
+ U(m + 0);
+ }
+ }
+
+ void Test11() {
+ if (test_number > 0 && test_number != 11) return;
+ fprintf(stderr, "Starting Test11: 4 threads lock/unlock 4 private mutexes, all under another private mutex\n");
+ // CHECK: Starting Test11
+ Init(500);
+ // CHECK-NOT: WARNING: ThreadSanitizer:
+ RunThreads(&LockTest::Test11_Thread1, &LockTest::Test11_Thread2,
+ &LockTest::Test11_Thread3, &LockTest::Test11_Thread4);
+ }
+ void Test11_Thread1() { Test10_Thread(0); }
+ void Test11_Thread2() { Test10_Thread(10); }
+ void Test11_Thread3() { Test10_Thread(20); }
+ void Test11_Thread4() { Test10_Thread(30); }
+ void Test11_Thread(size_t m) {
+ for (int i = 0; i < iter_count; i++) {
+ L(m);
+ L(m + 100);
+ U(m + 100);
+ L(m + 200);
+ U(m + 200);
+ L(m + 300);
+ U(m + 300);
+ L(m + 400);
+ U(m + 500);
+ U(m);
+ }
+ }
+
+ void Test12() {
+ if (test_number > 0 && test_number != 12) return;
+ if (!LockType::supports_read_lock()) return;
+ fprintf(stderr, "Starting Test12: 4 threads read lock/unlock 4 shared mutexes, one under another\n");
+ // CHECK-RD: Starting Test12
+ Init(500);
+ // CHECK-RD-NOT: WARNING: ThreadSanitizer:
+ RunThreads(&LockTest::Test12_Thread, &LockTest::Test12_Thread,
+ &LockTest::Test12_Thread, &LockTest::Test12_Thread);
+ }
+ void Test12_Thread() {
+ for (int i = 0; i < iter_count; i++) {
+ RL(000);
+ RL(100);
+ RL(200);
+ RL(300);
+ RU(300);
+ RU(200);
+ RU(100);
+ RU(000);
+ }
+ }
+
+ void Test13() {
+ if (test_number > 0 && test_number != 13) return;
+ if (!LockType::supports_read_lock()) return;
+ fprintf(stderr, "Starting Test13: 4 threads read lock/unlock 4 shared mutexes, all under another shared mutex\n");
+ // CHECK-RD: Starting Test13
+ Init(500);
+ // CHECK-RD-NOT: WARNING: ThreadSanitizer:
+ RunThreads(&LockTest::Test13_Thread, &LockTest::Test13_Thread,
+ &LockTest::Test13_Thread, &LockTest::Test13_Thread);
+ }
+ void Test13_Thread() {
+ for (int i = 0; i < iter_count; i++) {
+ RL(0);
+ RL(100);
+ RU(100);
+ RL(200);
+ RU(200);
+ RL(300);
+ RU(300);
+ RL(400);
+ RU(400);
+ RU(0);
+ }
+ }
+
+ void Test14() {
+ if (test_number > 0 && test_number != 14) return;
+ fprintf(stderr, "Starting Test14: create lots of locks in 4 threads\n");
+ Init(10);
+ // CHECK-RD: Starting Test14
+ RunThreads(&LockTest::CreateAndDestroyLocksLoop,
+ &LockTest::CreateAndDestroyLocksLoop,
+ &LockTest::CreateAndDestroyLocksLoop,
+ &LockTest::CreateAndDestroyLocksLoop);
+ }
+
+ void Test15() {
+ if (test_number > 0 && test_number != 15) return;
+ if (!LockType::supports_read_lock()) return;
+ fprintf(stderr, "Starting Test15: recursive rlock\n");
+ // DISABLEDCHECK-RD: Starting Test15
+ Init(5);
+ RL(0); RL(0); RU(0); RU(0); // Recusrive reader lock.
+ RL(0); RL(0); RL(0); RU(0); RU(0); RU(0); // Recusrive reader lock.
+ }
+
+ // More detailed output test.
+ void Test16() {
+ if (test_number > 0 && test_number != 16) return;
+ fprintf(stderr, "Starting Test16: detailed output test with two locks\n");
+ // CHECK: Starting Test16
+ // CHECK: WARNING: ThreadSanitizer: lock-order-inversion
+ // CHECK: acquired here while holding mutex
+ // CHECK: LockTest::Acquire1
+ // CHECK-NEXT: LockTest::Acquire_0_then_1
+ // CHECK-SECOND: previously acquired by the same thread here
+ // CHECK-SECOND: LockTest::Acquire0
+ // CHECK-SECOND-NEXT: LockTest::Acquire_0_then_1
+ // CHECK: acquired here while holding mutex
+ // CHECK: LockTest::Acquire0
+ // CHECK-NEXT: LockTest::Acquire_1_then_0
+ // CHECK-SECOND: previously acquired by the same thread here
+ // CHECK-SECOND: LockTest::Acquire1
+ // CHECK-SECOND-NEXT: LockTest::Acquire_1_then_0
+ Init(5);
+ Acquire_0_then_1();
+ U(0); U(1);
+ Acquire_1_then_0();
+ U(0); U(1);
+ }
+
+ // More detailed output test.
+ void Test17() {
+ if (test_number > 0 && test_number != 17) return;
+ fprintf(stderr, "Starting Test17: detailed output test with three locks\n");
+ // CHECK: Starting Test17
+ // CHECK: WARNING: ThreadSanitizer: lock-order-inversion
+ // CHECK: LockTest::Acquire1
+ // CHECK-NEXT: LockTest::Acquire_0_then_1
+ // CHECK: LockTest::Acquire2
+ // CHECK-NEXT: LockTest::Acquire_1_then_2
+ // CHECK: LockTest::Acquire0
+ // CHECK-NEXT: LockTest::Acquire_2_then_0
+ Init(5);
+ Acquire_0_then_1();
+ U(0); U(1);
+ Acquire_1_then_2();
+ U(1); U(2);
+ Acquire_2_then_0();
+ U(0); U(2);
+ }
+
+ __attribute__((noinline)) void Acquire2() { L(2); }
+ __attribute__((noinline)) void Acquire1() { L(1); }
+ __attribute__((noinline)) void Acquire0() { L(0); }
+ __attribute__((noinline)) void Acquire_1_then_0() { Acquire1(); Acquire0(); }
+ __attribute__((noinline)) void Acquire_0_then_1() { Acquire0(); Acquire1(); }
+ __attribute__((noinline)) void Acquire_1_then_2() { Acquire1(); Acquire2(); }
+ __attribute__((noinline)) void Acquire_2_then_0() { Acquire2(); Acquire0(); }
+
+ // This test creates, locks, unlocks and destroys lots of mutexes.
+ void Test18() {
+ if (test_number > 0 && test_number != 18) return;
+ fprintf(stderr, "Starting Test18: create, lock and destroy 4 locks; all in "
+ "4 threads in a loop\n");
+ RunThreads(&LockTest::Test18_Thread, &LockTest::Test18_Thread,
+ &LockTest::Test18_Thread, &LockTest::Test18_Thread);
+ }
+
+ void Test18_Thread() {
+ LockType *l = new LockType[4];
+ for (size_t i = 0; i < iter_count / 100; i++) {
+ for (int i = 0; i < 4; i++) l[i].lock();
+ for (int i = 0; i < 4; i++) l[i].unlock();
+ for (int i = 0; i < 4; i++) l[i].~LockType();
+ for (int i = 0; i < 4; i++) new ((void*)&l[i]) LockType();
+ }
+ delete [] l;
+ }
+
+ private:
+ void Lock2(size_t l1, size_t l2) { L(l1); L(l2); U(l2); U(l1); }
+ void Lock_0_1() { Lock2(0, 1); }
+ void Lock_1_0() { sleep(1); Lock2(1, 0); }
+ void Lock1_Loop(size_t i, size_t n_iter) {
+ for (size_t it = 0; it < n_iter; it++) {
+ // if ((it & (it - 1)) == 0) fprintf(stderr, "%zd", i);
+ L(i);
+ U(i);
+ }
+ // fprintf(stderr, "\n");
+ }
+ void Lock1_Loop_0() { Lock1_Loop(0, iter_count); }
+ void Lock1_Loop_1() { Lock1_Loop(10, iter_count); }
+ void Lock1_Loop_2() { Lock1_Loop(20, iter_count); }
+
+ void CreateAndDestroyManyLocks() {
+ LockType *create_many_locks_but_never_acquire =
+ new LockType[kDeadlockGraphSize];
+ (void)create_many_locks_but_never_acquire;
+ delete [] create_many_locks_but_never_acquire;
+ }
+
+ void CreateAndDestroyLocksLoop() {
+ for (size_t it = 0; it <= iter_count; it++) {
+ LockType some_locks[10];
+ (void)some_locks;
+ }
+ }
+
+ void CreateLockUnlockAndDestroyManyLocks() {
+ LockType many_locks[kDeadlockGraphSize];
+ for (size_t i = 0; i < kDeadlockGraphSize; i++) {
+ many_locks[i].lock();
+ many_locks[i].unlock();
+ }
+ }
+
+ // LockTest Member function callback.
+ struct CB {
+ void (LockTest::*f)();
+ LockTest *lt;
+ };
+
+ // Thread function with CB.
+ static void *Thread(void *param) {
+ CB *cb = (CB*)param;
+ (cb->lt->*cb->f)();
+ return NULL;
+ }
+
+ void RunThreads(void (LockTest::*f1)(), void (LockTest::*f2)(),
+ void (LockTest::*f3)() = 0, void (LockTest::*f4)() = 0) {
+ const int kNumThreads = 4;
+ pthread_t t[kNumThreads];
+ CB cb[kNumThreads] = {{f1, this}, {f2, this}, {f3, this}, {f4, this}};
+ for (int i = 0; i < kNumThreads && cb[i].f; i++)
+ pthread_create(&t[i], 0, Thread, &cb[i]);
+ for (int i = 0; i < kNumThreads && cb[i].f; i++)
+ pthread_join(t[i], 0);
+ }
+
+ static const size_t kDeadlockGraphSize = 4096;
+ size_t n_;
+ LockType **locks_;
+};
+
+int main(int argc, char **argv) {
+ if (argc > 1)
+ test_number = atoi(argv[1]);
+ if (argc > 2)
+ iter_count = atoi(argv[2]);
+ LockTest().Test1();
+ LockTest().Test2();
+ LockTest().Test3();
+ LockTest().Test4();
+ LockTest().Test5();
+ LockTest().Test6();
+ LockTest().Test7();
+ LockTest().Test8();
+ LockTest().Test9();
+ LockTest().Test10();
+ LockTest().Test11();
+ LockTest().Test12();
+ LockTest().Test13();
+ LockTest().Test14();
+ LockTest().Test15();
+ LockTest().Test16();
+ LockTest().Test17();
+ LockTest().Test18();
+ fprintf(stderr, "ALL-DONE\n");
+ // CHECK: ALL-DONE
+}
diff --git a/test/tsan/deep_stack1.cc b/test/tsan/deep_stack1.cc
new file mode 100644
index 000000000000..1d00a0e856d5
--- /dev/null
+++ b/test/tsan/deep_stack1.cc
@@ -0,0 +1,44 @@
+// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && %deflake %run %t | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && %deflake %run %t | 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 000000000000..77bdcd5086ba
--- /dev/null
+++ b/test/tsan/default_options.cc
@@ -0,0 +1,32 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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/deflake.bash b/test/tsan/deflake.bash
new file mode 100755
index 000000000000..9731fa53e589
--- /dev/null
+++ b/test/tsan/deflake.bash
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# This script is used to deflake inherently flaky tsan tests.
+# It is invoked from lit tests as:
+# %deflake mybinary
+# which is then substituted by lit to:
+# $(dirname %s)/deflake.bash mybinary
+# The script runs the target program up to 10 times,
+# until it fails (i.e. produces a race report).
+
+for i in $(seq 1 10); do
+ OUT=`$@ 2>&1`
+ if [[ $? != 0 ]]; then
+ echo "$OUT"
+ exit 0
+ fi
+done
+exit 1
diff --git a/test/tsan/dlclose.cc b/test/tsan/dlclose.cc
new file mode 100644
index 000000000000..1a93fe6617e1
--- /dev/null
+++ b/test/tsan/dlclose.cc
@@ -0,0 +1,58 @@
+// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// If we mention TSAN_OPTIONS, the test won't run from test_output.sh script.
+
+// Test case for
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=80
+
+#ifdef BUILD_SO
+
+#include <stdio.h>
+
+extern "C"
+void sofunc() {
+ fprintf(stderr, "HELLO FROM SO\n");
+}
+
+#else // BUILD_SO
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string>
+
+void *lib;
+void *lib2;
+
+struct Closer {
+ ~Closer() {
+ dlclose(lib);
+ fprintf(stderr, "CLOSED SO\n");
+ }
+};
+static Closer c;
+
+int main(int argc, char *argv[]) {
+ lib = dlopen((std::string(argv[0]) + std::string("-so.so")).c_str(),
+ RTLD_NOW|RTLD_NODELETE);
+ if (lib == 0) {
+ printf("error in dlopen: %s\n", dlerror());
+ return 1;
+ }
+ void *f = dlsym(lib, "sofunc");
+ if (f == 0) {
+ printf("error in dlsym: %s\n", dlerror());
+ return 1;
+ }
+ ((void(*)())f)();
+ return 0;
+}
+
+#endif // BUILD_SO
+
+// CHECK: HELLO FROM SO
+// CHECK-NOT: Inconsistency detected by ld.so
+// CHECK: CLOSED SO
+
diff --git a/test/tsan/fd_close_norace.cc b/test/tsan/fd_close_norace.cc
new file mode 100644
index 000000000000..7238d64b432b
--- /dev/null
+++ b/test/tsan/fd_close_norace.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..bf94fd5512b3
--- /dev/null
+++ b/test/tsan/fd_close_norace2.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..5045325b22b5
--- /dev/null
+++ b/test/tsan/fd_dup_norace.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..535329e06409
--- /dev/null
+++ b/test/tsan/fd_location.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..b434703d782a
--- /dev/null
+++ b/test/tsan/fd_pipe_norace.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..88c4ed4aa398
--- /dev/null
+++ b/test/tsan/fd_pipe_race.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..ab2a950f17d6
--- /dev/null
+++ b/test/tsan/fd_socket_connect_norace.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..0f41c4357354
--- /dev/null
+++ b/test/tsan/fd_socket_norace.cc
@@ -0,0 +1,52 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..a455d44a3965
--- /dev/null
+++ b/test/tsan/fd_socketpair_norace.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..d6a2c7c796f1
--- /dev/null
+++ b/test/tsan/fd_stdout_race.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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_atexit.cc b/test/tsan/fork_atexit.cc
new file mode 100644
index 000000000000..6801d3ffff7e
--- /dev/null
+++ b/test/tsan/fork_atexit.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="atexit_sleep_ms=50" %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+void foo() {
+ printf("CHILD ATEXIT\n");
+}
+
+void *worker(void *unused) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, NULL, worker, NULL);
+ int pid = fork();
+ if (pid == 0) {
+ // child
+ atexit(foo);
+ fprintf(stderr, "CHILD DONE\n");
+ } else {
+ pthread_join(t, 0);
+ if (waitpid(pid, 0, 0) == -1) {
+ perror("waitpid");
+ exit(1);
+ }
+ fprintf(stderr, "PARENT DONE\n");
+ }
+}
+
+// CHECK: CHILD DONE
+// CHECK: CHILD ATEXIT
+// CHECK: PARENT DONE
diff --git a/test/tsan/fork_deadlock.cc b/test/tsan/fork_deadlock.cc
new file mode 100644
index 000000000000..cc5b12214cf9
--- /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" %run %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 000000000000..5176a14d6a65
--- /dev/null
+++ b/test/tsan/fork_multithreaded.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-DIE
+// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="die_after_fork=0" %run %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 multi-threaded fork is not supported
+// CHECK-DIE: OK
+
+// CHECK-NODIE-NOT: ThreadSanitizer: starting new threads after multi-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 000000000000..a651b3c18b4e
--- /dev/null
+++ b/test/tsan/fork_multithreaded3.cc
@@ -0,0 +1,40 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..663d7bcf2eb9
--- /dev/null
+++ b/test/tsan/free_race.c
@@ -0,0 +1,49 @@
+// RUN: %clang_tsan -O1 %s -o %t
+// RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOZUPP
+// RUN: TSAN_OPTIONS="suppressions=%s.supp print_suppressions=1" %run %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 000000000000..f5d6a4969a41
--- /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 000000000000..de6b2ae1fcbb
--- /dev/null
+++ b/test/tsan/free_race2.c
@@ -0,0 +1,26 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..89afbe1a66a8
--- /dev/null
+++ b/test/tsan/getline_nohang.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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
+#ifdef __FreeBSD__
+#define _WITH_GETLINE // to declare getline()
+#endif
+
+#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 000000000000..e12bb1d220f8
--- /dev/null
+++ b/test/tsan/global_race.cc
@@ -0,0 +1,28 @@
+// RUN: %clangxx_tsan -O1 %s -o %T/global_race.cc.exe && %deflake %run %T/global_race.cc.exe | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+int GlobalData[10];
+
+void *Thread(void *a) {
+ sleep(1);
+ GlobalData[2] = 42;
+ return 0;
+}
+
+int main() {
+ // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
+ // match to the format used in the diagnotic message.
+ fprintf(stderr, "addr=0x%012lx\n", (unsigned long) GlobalData);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ GlobalData[2] = 43;
+ pthread_join(t, 0);
+}
+
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is global 'GlobalData' of size 40 at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}})
+
diff --git a/test/tsan/global_race2.cc b/test/tsan/global_race2.cc
new file mode 100644
index 000000000000..ac994cc0f4c4
--- /dev/null
+++ b/test/tsan/global_race2.cc
@@ -0,0 +1,28 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+int x;
+
+void *Thread(void *a) {
+ sleep(1);
+ x = 1;
+ return 0;
+}
+
+int main() {
+ // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
+ // match to the format used in the diagnotic message.
+ fprintf(stderr, "addr2=0x%012lx\n", (unsigned long) &x);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ x = 0;
+ pthread_join(t, 0);
+}
+
+// CHECK: addr2=[[ADDR2: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]+}})
+
diff --git a/test/tsan/global_race3.cc b/test/tsan/global_race3.cc
new file mode 100644
index 000000000000..a3222bb3d8cb
--- /dev/null
+++ b/test/tsan/global_race3.cc
@@ -0,0 +1,32 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+namespace XXX {
+ struct YYY {
+ static int ZZZ[10];
+ };
+ int YYY::ZZZ[10];
+}
+
+void *Thread(void *a) {
+ sleep(1);
+ XXX::YYY::ZZZ[0] = 1;
+ return 0;
+}
+
+int main() {
+ // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
+ // match to the format used in the diagnotic message.
+ fprintf(stderr, "addr3=0x%012lx\n", (unsigned long) XXX::YYY::ZZZ);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ XXX::YYY::ZZZ[0] = 0;
+ pthread_join(t, 0);
+}
+
+// CHECK: addr3=[[ADDR3: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 000000000000..3c55c60a457e
--- /dev/null
+++ b/test/tsan/halt_on_error.cc
@@ -0,0 +1,27 @@
+// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS halt_on_error=1" %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int X;
+
+void *Thread(void *x) {
+ sleep(1);
+ 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 000000000000..c3da68f42658
--- /dev/null
+++ b/test/tsan/heap_race.cc
@@ -0,0 +1,20 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..1df6dce2f5e0
--- /dev/null
+++ b/test/tsan/ignore_free.cc
@@ -0,0 +1,35 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..fe1a35558015
--- /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${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: echo running with suppressions:
+// RUN: LD_LIBRARY_PATH=%T${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %run %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 000000000000..7728c926b7de
--- /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 000000000000..30a9994b94f5
--- /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: %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: echo running with suppressions:
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %run %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 000000000000..9f4119ec0bc4
--- /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 000000000000..23a0872feabe
--- /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" %deflake %run %t | 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 000000000000..1419c71c67ef
--- /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 000000000000..137109ea6cb4
--- /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" %deflake %run %t | 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 000000000000..975dbcef99fe
--- /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 000000000000..2bfe84dfc0ec
--- /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 000000000000..0f1fb5e3dfdc
--- /dev/null
+++ b/test/tsan/ignore_malloc.cc
@@ -0,0 +1,38 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..c6e067fabec0
--- /dev/null
+++ b/test/tsan/ignore_race.cc
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..ae24a8c49e75
--- /dev/null
+++ b/test/tsan/ignore_sync.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..a95576a83c9a
--- /dev/null
+++ b/test/tsan/inlined_memcpy_race.cc
@@ -0,0 +1,38 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int x[4], z[4];
+
+void *MemCpyThread(void *a) {
+ memcpy((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);
+ 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
+
diff --git a/test/tsan/inlined_memcpy_race2.cc b/test/tsan/inlined_memcpy_race2.cc
new file mode 100644
index 000000000000..63b560f02269
--- /dev/null
+++ b/test/tsan/inlined_memcpy_race2.cc
@@ -0,0 +1,38 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int y[4], z[4];
+
+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 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 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 000000000000..18d860ea02e2
--- /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 && %run %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 000000000000..d986d08be060
--- /dev/null
+++ b/test/tsan/java.h
@@ -0,0 +1,21 @@
+#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_finalize();
+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 000000000000..4a606f7940d3
--- /dev/null
+++ b/test/tsan/java_alloc.cc
@@ -0,0 +1,38 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ pthread_t th;
+ pthread_create(&th, 0, Thread, (void*)(jheap + kHeapSize / 4));
+ stress(jheap);
+ pthread_join(th, 0);
+ if (__tsan_java_fini() != 0) {
+ printf("FAILED\n");
+ return 1;
+ }
+ printf("DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAILED
+// CHECK: DONE
diff --git a/test/tsan/java_finalizer.cc b/test/tsan/java_finalizer.cc
new file mode 100644
index 000000000000..d5c6a22d1e16
--- /dev/null
+++ b/test/tsan/java_finalizer.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include "java.h"
+
+void *Thread(void *p) {
+ sleep(1);
+ __tsan_java_finalize();
+ *(int*)p = 42;
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ jptr jheap = (jptr)malloc(kHeapSize + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc(jheap, kBlockSize);
+ pthread_t th;
+ pthread_create(&th, 0, Thread, (void*)jheap);
+ *(int*)jheap = 43;
+ pthread_join(th, 0);
+ __tsan_java_free(jheap, kBlockSize);
+ fprintf(stderr, "DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/java_lock.cc b/test/tsan/java_lock.cc
new file mode 100644
index 000000000000..36a0f8b7bff3
--- /dev/null
+++ b/test/tsan/java_lock.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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;
+ jptr jheap = (jptr)malloc(kHeapSize + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc(jheap, kBlockSize);
+ varaddr = jheap;
+ lockaddr = 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(jheap, kBlockSize);
+ fprintf(stderr, "DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/java_lock_move.cc b/test/tsan/java_lock_move.cc
new file mode 100644
index 000000000000..19c3e35d6633
--- /dev/null
+++ b/test/tsan/java_lock_move.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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;
+ jptr jheap = (jptr)malloc(kHeapSize + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ const int kBlockSize = 64;
+ int const kMove = 1024;
+ __tsan_java_alloc(jheap, kBlockSize);
+ varaddr = jheap;
+ lockaddr = 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("DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/java_lock_rec.cc b/test/tsan/java_lock_rec.cc
new file mode 100644
index 000000000000..2b0ab0eb92d0
--- /dev/null
+++ b/test/tsan/java_lock_rec.cc
@@ -0,0 +1,55 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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;
+ jptr jheap = (jptr)malloc(kHeapSize + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc(jheap, kBlockSize);
+ varaddr = jheap;
+ *(int*)varaddr = 0;
+ lockaddr = 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(jheap, kBlockSize);
+ printf("DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAILED
+// CHECK: DONE
diff --git a/test/tsan/java_lock_rec_race.cc b/test/tsan/java_lock_rec_race.cc
new file mode 100644
index 000000000000..841aa396360d
--- /dev/null
+++ b/test/tsan/java_lock_rec_race.cc
@@ -0,0 +1,49 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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;
+ jptr jheap = (jptr)malloc(kHeapSize + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc(jheap, kBlockSize);
+ varaddr = jheap;
+ *(int*)varaddr = 0;
+ lockaddr = 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(jheap, kBlockSize);
+ printf("DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAILED
+// CHECK: DONE
diff --git a/test/tsan/java_move_overlap.cc b/test/tsan/java_move_overlap.cc
new file mode 100644
index 000000000000..12955b4ba0de
--- /dev/null
+++ b/test/tsan/java_move_overlap.cc
@@ -0,0 +1,72 @@
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %run %t arg 2>&1 | FileCheck %s
+#include "java.h"
+
+jptr varaddr1_old;
+jptr varaddr2_old;
+jptr lockaddr1_old;
+jptr lockaddr2_old;
+jptr varaddr1_new;
+jptr varaddr2_new;
+jptr lockaddr1_new;
+jptr lockaddr2_new;
+
+void *Thread(void *p) {
+ sleep(1);
+ __tsan_java_mutex_lock(lockaddr1_new);
+ *(char*)varaddr1_new = 43;
+ __tsan_java_mutex_unlock(lockaddr1_new);
+ __tsan_java_mutex_lock(lockaddr2_new);
+ *(char*)varaddr2_new = 43;
+ __tsan_java_mutex_unlock(lockaddr2_new);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ jheap = (char*)jheap + 8;
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 64;
+ int const kMove = 32;
+ varaddr1_old = (jptr)jheap;
+ lockaddr1_old = (jptr)jheap + 1;
+ varaddr2_old = (jptr)jheap + kBlockSize - 1;
+ lockaddr2_old = (jptr)jheap + kBlockSize - 16;
+ varaddr1_new = varaddr1_old + kMove;
+ lockaddr1_new = lockaddr1_old + kMove;
+ varaddr2_new = varaddr2_old + kMove;
+ lockaddr2_new = lockaddr2_old + kMove;
+ if (argc > 1) {
+ // Move memory backwards.
+ varaddr1_old += kMove;
+ lockaddr1_old += kMove;
+ varaddr2_old += kMove;
+ lockaddr2_old += kMove;
+ varaddr1_new -= kMove;
+ lockaddr1_new -= kMove;
+ varaddr2_new -= kMove;
+ lockaddr2_new -= kMove;
+ }
+ __tsan_java_alloc(varaddr1_old, kBlockSize);
+
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+
+ __tsan_java_mutex_lock(lockaddr1_old);
+ *(char*)varaddr1_old = 43;
+ __tsan_java_mutex_unlock(lockaddr1_old);
+ __tsan_java_mutex_lock(lockaddr2_old);
+ *(char*)varaddr2_old = 43;
+ __tsan_java_mutex_unlock(lockaddr2_old);
+
+ __tsan_java_move(varaddr1_old, varaddr1_new, kBlockSize);
+ pthread_join(th, 0);
+ __tsan_java_free(varaddr1_new, kBlockSize);
+ printf("DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/java_move_overlap_race.cc b/test/tsan/java_move_overlap_race.cc
new file mode 100644
index 000000000000..2b3769be6117
--- /dev/null
+++ b/test/tsan/java_move_overlap_race.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: %deflake %run %t | FileCheck %s
+// RUN: %deflake %run %t arg | FileCheck %s
+#include "java.h"
+
+jptr varaddr1_old;
+jptr varaddr2_old;
+jptr varaddr1_new;
+jptr varaddr2_new;
+
+void *Thread(void *p) {
+ sleep(1);
+ *(int*)varaddr1_new = 43;
+ *(int*)varaddr2_new = 43;
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ jheap = (char*)jheap + 8;
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 64;
+ int const kMove = 32;
+ varaddr1_old = (jptr)jheap;
+ varaddr2_old = (jptr)jheap + kBlockSize - 1;
+ varaddr1_new = varaddr1_old + kMove;
+ varaddr2_new = varaddr2_old + kMove;
+ if (argc > 1) {
+ // Move memory backwards.
+ varaddr1_old += kMove;
+ varaddr2_old += kMove;
+ varaddr1_new -= kMove;
+ varaddr2_new -= kMove;
+ }
+ __tsan_java_alloc(varaddr1_old, kBlockSize);
+
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+
+ *(int*)varaddr1_old = 43;
+ *(int*)varaddr2_old = 43;
+
+ __tsan_java_move(varaddr1_old, varaddr1_new, kBlockSize);
+ pthread_join(th, 0);
+ __tsan_java_free(varaddr1_new, kBlockSize);
+ printf("DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/java_race.cc b/test/tsan/java_race.cc
new file mode 100644
index 000000000000..ede058e85d8a
--- /dev/null
+++ b/test/tsan/java_race.cc
@@ -0,0 +1,25 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include "java.h"
+
+void *Thread(void *p) {
+ *(int*)p = 42;
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ jptr jheap = (jptr)malloc(kHeapSize + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc(jheap, kBlockSize);
+ pthread_t th;
+ pthread_create(&th, 0, Thread, (void*)jheap);
+ *(int*)jheap = 43;
+ pthread_join(th, 0);
+ __tsan_java_free(jheap, kBlockSize);
+ fprintf(stderr, "DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/java_race_move.cc b/test/tsan/java_race_move.cc
new file mode 100644
index 000000000000..8a51be92aa10
--- /dev/null
+++ b/test/tsan/java_race_move.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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;
+ jptr jheap = (jptr)malloc(kHeapSize + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ const int kBlockSize = 64;
+ int const kMove = 1024;
+ __tsan_java_alloc(jheap, kBlockSize);
+ varaddr = 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);
+ fprintf(stderr, "DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/java_rwlock.cc b/test/tsan/java_rwlock.cc
new file mode 100644
index 000000000000..b03afa6e092d
--- /dev/null
+++ b/test/tsan/java_rwlock.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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;
+ jptr jheap = (jptr)malloc(kHeapSize + 8) + 8;
+ __tsan_java_init(jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc(jheap, kBlockSize);
+ varaddr = jheap;
+ lockaddr = 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(jheap, kBlockSize);
+ printf("DONE\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
diff --git a/test/tsan/libcxx/lit.local.cfg b/test/tsan/libcxx/lit.local.cfg
new file mode 100644
index 000000000000..202b44ee116d
--- /dev/null
+++ b/test/tsan/libcxx/lit.local.cfg
@@ -0,0 +1,10 @@
+def getRoot(config):
+ if not config.parent:
+ return config
+ return getRoot(config.parent)
+
+root = getRoot(config)
+
+if not root.has_libcxx:
+ config.unsupported = True
+
diff --git a/test/tsan/libcxx/std_shared_ptr.cc b/test/tsan/libcxx/std_shared_ptr.cc
new file mode 100644
index 000000000000..191a17cc798d
--- /dev/null
+++ b/test/tsan/libcxx/std_shared_ptr.cc
@@ -0,0 +1,24 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <memory>
+#include <thread>
+
+int main() {
+ int v1 = 0;
+ int v2 = 0;
+ std::thread t1;
+ std::thread t2;
+
+ {
+ auto thingy = std::make_shared<int>(42);
+ t1 = std::thread([thingy, &v1] { v1 = *thingy; });
+ t2 = std::thread([thingy, &v2] { v2 = *thingy; });
+ }
+
+ t1.join();
+ t2.join();
+ printf("%d %d\n", v1, v2);
+ // CHECK-NOT: ThreadSanitizer: data race
+ // CHECK: 42 42
+ return 0;
+}
diff --git a/test/tsan/lit.cfg b/test/tsan/lit.cfg
new file mode 100644
index 000000000000..d27500f8e3ea
--- /dev/null
+++ b/test/tsan/lit.cfg
@@ -0,0 +1,65 @@
+# -*- 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
+
+# GCC driver doesn't add necessary compile/link flags with -fsanitize=thread.
+if config.compiler_id == 'GNU':
+ extra_cflags = ["-fPIE", "-pthread", "-ldl", "-lstdc++", "-lrt", "-pie"]
+else:
+ extra_cflags = []
+
+# Setup default compiler flags used with -fsanitize=thread option.
+clang_tsan_cflags = ["-fsanitize=thread",
+ "-Wall",
+ "-m64"] + config.debug_info_flags + extra_cflags
+clang_tsan_cxxflags = config.cxx_mode_flags + clang_tsan_cflags
+# Add additional flags if we're using instrumented libc++.
+if config.has_libcxx:
+ # FIXME: Dehardcode this path somehow.
+ libcxx_path = os.path.join(config.compiler_rt_obj_root, "lib",
+ "tsan", "libcxx_tsan")
+ libcxx_incdir = os.path.join(libcxx_path, "include", "c++", "v1")
+ libcxx_libdir = os.path.join(libcxx_path, "lib")
+ libcxx_so = os.path.join(libcxx_libdir, "libc++.so")
+ clang_tsan_cxxflags += ["-std=c++11",
+ "-I%s" % libcxx_incdir,
+ libcxx_so,
+ "-Wl,-rpath=%s" % libcxx_libdir]
+
+def build_invocation(compile_flags):
+ return " " + " ".join([config.clang] + compile_flags) + " "
+
+config.substitutions.append( ("%clang_tsan ", build_invocation(clang_tsan_cflags)) )
+config.substitutions.append( ("%clangxx_tsan ", build_invocation(clang_tsan_cxxflags)) )
+
+# Define CHECK-%os to check for OS-dependent output.
+config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os)))
+
+config.substitutions.append( ("%deflake ", os.path.join(os.path.dirname(__file__), "deflake.bash")) )
+
+# Default test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp']
+
+# ThreadSanitizer tests are currently supported on FreeBSD and Linux only.
+if config.host_os not in ['FreeBSD', '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 000000000000..5190b211177d
--- /dev/null
+++ b/test/tsan/lit.site.cfg.in
@@ -0,0 +1,10 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.has_libcxx = @TSAN_HAS_LIBCXX@
+
+# 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 000000000000..a27dc1cc6ffb
--- /dev/null
+++ b/test/tsan/load_shared_lib.cc
@@ -0,0 +1,66 @@
+// 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 %s -DBUILD_SO -fPIC -shared -o %t-so.so
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+
+#ifdef BUILD_SO
+
+#include <stddef.h>
+#include <unistd.h>
+
+int GLOB_SHARED = 0;
+
+extern "C"
+void *write_from_so(void *unused) {
+ if (unused)
+ sleep(1);
+ GLOB_SHARED++;
+ return NULL;
+}
+
+#else // BUILD_SO
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+#include <string>
+
+int GLOB = 0;
+
+void *write_glob(void *unused) {
+ if (unused)
+ sleep(1);
+ GLOB++;
+ return NULL;
+}
+
+void race_two_threads(void *(*access_callback)(void *unused)) {
+ pthread_t t1, t2;
+ pthread_create(&t1, NULL, access_callback, (void*)1);
+ 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;
+}
+
+#endif // BUILD_SO
diff --git a/test/tsan/longjmp.cc b/test/tsan/longjmp.cc
new file mode 100644
index 000000000000..d7371c5e4069
--- /dev/null
+++ b/test/tsan/longjmp.cc
@@ -0,0 +1,22 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..546019b2d11a
--- /dev/null
+++ b/test/tsan/longjmp2.cc
@@ -0,0 +1,24 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..71d964dbbed9
--- /dev/null
+++ b/test/tsan/longjmp3.cc
@@ -0,0 +1,48 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..15330f5d83c8
--- /dev/null
+++ b/test/tsan/longjmp4.cc
@@ -0,0 +1,51 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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_overflow.cc b/test/tsan/malloc_overflow.cc
new file mode 100644
index 000000000000..dadc94484f01
--- /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 %run %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 000000000000..6027360754aa
--- /dev/null
+++ b/test/tsan/malloc_stack.cc
@@ -0,0 +1,25 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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/map32bit.cc b/test/tsan/map32bit.cc
new file mode 100644
index 000000000000..3a76fa2f6d62
--- /dev/null
+++ b/test/tsan/map32bit.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+// Test for issue:
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=5
+
+void *Thread(void *ptr) {
+ *(int*)ptr = 42;
+ return 0;
+}
+
+int main() {
+ void *ptr = mmap(0, 128 << 10, PROT_READ|PROT_WRITE,
+ MAP_32BIT|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ fprintf(stderr, "ptr=%p\n", ptr);
+ if (ptr == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %d\n", errno);
+ return 1;
+ }
+ if ((uintptr_t)ptr >= (1ull << 32)) {
+ fprintf(stderr, "ptr is too high\n");
+ return 1;
+ }
+ pthread_t t;
+ pthread_create(&t, 0, Thread, ptr);
+ sleep(1);
+ *(int*)ptr = 42;
+ pthread_join(t, 0);
+ munmap(ptr, 128 << 10);
+ fprintf(stderr, "DONE\n");
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
+
diff --git a/test/tsan/memcpy_race.cc b/test/tsan/memcpy_race.cc
new file mode 100644
index 000000000000..8ec8e0a3e6a5
--- /dev/null
+++ b/test/tsan/memcpy_race.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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/mmap_large.cc b/test/tsan/mmap_large.cc
new file mode 100644
index 000000000000..e715ea666231
--- /dev/null
+++ b/test/tsan/mmap_large.cc
@@ -0,0 +1,20 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+int main() {
+ const size_t kLog2Size = 39;
+ const uintptr_t kLocation = 0x40ULL << kLog2Size;
+ void *p = mmap(
+ reinterpret_cast<void*>(kLocation),
+ 1ULL << kLog2Size,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,
+ -1, 0);
+ fprintf(stderr, "DONE %p %d\n", p, errno);
+ return p == MAP_FAILED;
+}
+
+// CHECK: DONE
diff --git a/test/tsan/mop_with_offset.cc b/test/tsan/mop_with_offset.cc
new file mode 100644
index 000000000000..e44c78b7d1b4
--- /dev/null
+++ b/test/tsan/mop_with_offset.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..a465d5f09471
--- /dev/null
+++ b/test/tsan/mop_with_offset2.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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/must_deadlock.cc b/test/tsan/must_deadlock.cc
new file mode 100644
index 000000000000..1409800e9b39
--- /dev/null
+++ b/test/tsan/must_deadlock.cc
@@ -0,0 +1,50 @@
+// Test that the deadlock detector can find a deadlock that actually happened.
+// Currently we will fail to report such a deadlock because we check for
+// cycles in lock-order graph after pthread_mutex_lock.
+
+// RUN: %clangxx_tsan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// XFAIL: *
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+pthread_mutex_t mu1, mu2;
+pthread_barrier_t barrier;
+
+void *Thread(void *p) {
+ // mu2 => mu1
+ pthread_mutex_lock(&mu2);
+ pthread_barrier_wait(&barrier);
+ pthread_mutex_lock(&mu1);
+ // CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock)
+ pthread_mutex_unlock(&mu1);
+ pthread_mutex_unlock(&mu2);
+ return p;
+}
+
+int main() {
+ pthread_mutex_init(&mu1, NULL);
+ pthread_mutex_init(&mu2, NULL);
+ pthread_barrier_init(&barrier, 0, 2);
+
+ fprintf(stderr, "This test is going to deadlock and die in 3 seconds\n");
+ alarm(3);
+
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+
+ // mu1 => mu2
+ pthread_mutex_lock(&mu1);
+ pthread_barrier_wait(&barrier);
+ pthread_mutex_lock(&mu2);
+ pthread_mutex_unlock(&mu2);
+ pthread_mutex_unlock(&mu1);
+
+ pthread_join(t, 0);
+
+ pthread_mutex_destroy(&mu1);
+ pthread_mutex_destroy(&mu2);
+ pthread_barrier_destroy(&barrier);
+ fprintf(stderr, "FAILED\n");
+}
diff --git a/test/tsan/mutex_bad_read_lock.cc b/test/tsan/mutex_bad_read_lock.cc
new file mode 100644
index 000000000000..84a2976d53e4
--- /dev/null
+++ b/test/tsan/mutex_bad_read_lock.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+extern "C" void AnnotateRWLockAcquired(const char *f, int l, void *m, long rw);
+
+int main() {
+ int m = 0;
+ AnnotateRWLockAcquired(__FILE__, __LINE__, &m, 1);
+ AnnotateRWLockAcquired(__FILE__, __LINE__, &m, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: read lock of a write locked mutex
+// CHECK: #0 AnnotateRWLockAcquired
+// CHECK: #1 main
+// CHECK: Location is stack of main thread.
+// CHECK: Mutex {{.*}}) created at:
+// CHECK: #0 AnnotateRWLockAcquired
+// CHECK: #1 main
+// CHECK: SUMMARY: ThreadSanitizer: read lock of a write locked mutex
+
diff --git a/test/tsan/mutex_bad_read_unlock.cc b/test/tsan/mutex_bad_read_unlock.cc
new file mode 100644
index 000000000000..dcee51599d55
--- /dev/null
+++ b/test/tsan/mutex_bad_read_unlock.cc
@@ -0,0 +1,20 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+extern "C" void AnnotateRWLockAcquired(const char *f, int l, void *m, long rw);
+extern "C" void AnnotateRWLockReleased(const char *f, int l, void *m, long rw);
+
+int main() {
+ int m = 0;
+ AnnotateRWLockAcquired(__FILE__, __LINE__, &m, 1);
+ AnnotateRWLockReleased(__FILE__, __LINE__, &m, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: read unlock of a write locked mutex
+// CHECK: #0 AnnotateRWLockReleased
+// CHECK: #1 main
+// CHECK: Location is stack of main thread.
+// CHECK: Mutex {{.*}}) created at:
+// CHECK: #0 AnnotateRWLockAcquired
+// CHECK: #1 main
+// CHECK: SUMMARY: ThreadSanitizer: read unlock of a write locked mutex
+
diff --git a/test/tsan/mutex_bad_unlock.cc b/test/tsan/mutex_bad_unlock.cc
new file mode 100644
index 000000000000..6b483cf17eda
--- /dev/null
+++ b/test/tsan/mutex_bad_unlock.cc
@@ -0,0 +1,18 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+extern "C" void AnnotateRWLockReleased(const char *f, int l, void *m, long rw);
+
+int main() {
+ int m = 0;
+ AnnotateRWLockReleased(__FILE__, __LINE__, &m, 1);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: unlock of an unlocked mutex (or by a wrong thread)
+// CHECK: #0 AnnotateRWLockReleased
+// CHECK: #1 main
+// CHECK: Location is stack of main thread.
+// CHECK: Mutex {{.*}} created at:
+// CHECK: #0 AnnotateRWLockReleased
+// CHECK: #1 main
+// CHECK: SUMMARY: ThreadSanitizer: unlock of an unlocked mutex (or by a wrong thread)
+
diff --git a/test/tsan/mutex_cycle2.c b/test/tsan/mutex_cycle2.c
new file mode 100644
index 000000000000..031830d5ec63
--- /dev/null
+++ b/test/tsan/mutex_cycle2.c
@@ -0,0 +1,35 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: TSAN_OPTIONS=detect_deadlocks=1 not %run %t 2>&1 | FileCheck %s
+// RUN: TSAN_OPTIONS=detect_deadlocks=0 %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED
+// RUN: echo "deadlock:main" > %t.sup
+// RUN: TSAN_OPTIONS="suppressions=%t.sup" %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED
+// RUN: echo "deadlock:zzzz" > %t.sup
+// RUN: TSAN_OPTIONS="suppressions=%t.sup" not %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.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)
+ // DISABLED-NOT: ThreadSanitizer
+ // DISABLED: PASS
+ pthread_mutex_unlock(&mu1);
+ pthread_mutex_unlock(&mu2);
+
+ pthread_mutex_destroy(&mu1);
+ pthread_mutex_destroy(&mu2);
+ fprintf(stderr, "PASS\n");
+}
diff --git a/test/tsan/mutex_destroy_locked.cc b/test/tsan/mutex_destroy_locked.cc
new file mode 100644
index 000000000000..b81905ec68fd
--- /dev/null
+++ b/test/tsan/mutex_destroy_locked.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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_double_lock.cc b/test/tsan/mutex_double_lock.cc
new file mode 100644
index 000000000000..c1bebf73706e
--- /dev/null
+++ b/test/tsan/mutex_double_lock.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+extern "C" void AnnotateRWLockAcquired(const char *f, int l, void *m, long rw);
+
+void *ThreadFunc(void *m) {
+ AnnotateRWLockAcquired(__FILE__, __LINE__, m, 1);
+ return 0;
+}
+
+int main() {
+ int m = 0;
+ AnnotateRWLockAcquired(__FILE__, __LINE__, &m, 1);
+ pthread_t th;
+ pthread_create(&th, 0, ThreadFunc, &m);
+ pthread_join(th, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: double lock of a mutex
+// CHECK: #0 AnnotateRWLockAcquired
+// CHECK: #1 ThreadFunc
+// CHECK: Location is stack of main thread.
+// CHECK: Mutex {{.*}} created at:
+// CHECK: #0 AnnotateRWLockAcquired
+// CHECK: #1 main
+// CHECK: SUMMARY: ThreadSanitizer: double lock of a mutex {{.*}}mutex_double_lock.cc{{.*}}ThreadFunc
+
diff --git a/test/tsan/mutexset1.cc b/test/tsan/mutexset1.cc
new file mode 100644
index 000000000000..72964edfb1e7
--- /dev/null
+++ b/test/tsan/mutexset1.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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]] (0x{{.*}}) 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 000000000000..01a5f5df6e94
--- /dev/null
+++ b/test/tsan/mutexset2.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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]] (0x{{.*}}) 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 000000000000..e14bb1111e32
--- /dev/null
+++ b/test/tsan/mutexset3.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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]] (0x{{.*}}) created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] (0x{{.*}}) 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 000000000000..db860e005d67
--- /dev/null
+++ b/test/tsan/mutexset4.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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]] (0x{{.*}}) created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] (0x{{.*}}) 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 000000000000..e1cc2fcacf62
--- /dev/null
+++ b/test/tsan/mutexset5.cc
@@ -0,0 +1,46 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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]] (0x{{.*}}) created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] (0x{{.*}}) 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 000000000000..07dcc0a7394d
--- /dev/null
+++ b/test/tsan/mutexset6.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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]] (0x{{.*}}) created at:
+ // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+5]]
+ // CHECK: Mutex [[M2]] (0x{{.*}}) created at:
+ // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M3]] (0x{{.*}}) 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 000000000000..12174844c799
--- /dev/null
+++ b/test/tsan/mutexset7.cc
@@ -0,0 +1,40 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 = new pthread_mutex_t;
+ pthread_mutex_init(mtx, 0);
+ pthread_mutex_lock(mtx);
+ Global--;
+ pthread_mutex_unlock(mtx);
+ pthread_mutex_destroy(mtx);
+ delete 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 000000000000..3e1ab8c5a744
--- /dev/null
+++ b/test/tsan/mutexset8.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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]] (0x{{.*}}) 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/printf-1.c b/test/tsan/printf-1.c
new file mode 100644
index 000000000000..9116c956e30e
--- /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 %run %t 2>&1 | FileCheck %s
+// RUN: ASAN_OPTIONS=check_printf=0 %run %t 2>&1 | FileCheck %s
+// RUN: %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/process_sleep.h b/test/tsan/process_sleep.h
new file mode 100644
index 000000000000..5938a42bf8b2
--- /dev/null
+++ b/test/tsan/process_sleep.h
@@ -0,0 +1,7 @@
+#include <time.h>
+
+static void process_sleep(int sec) {
+ clock_t beg = clock();
+ while((clock() - beg) / CLOCKS_PER_SEC < sec)
+ usleep(100);
+}
diff --git a/test/tsan/pthread_atfork_deadlock.c b/test/tsan/pthread_atfork_deadlock.c
new file mode 100644
index 000000000000..0f33b9022e5f
--- /dev/null
+++ b/test/tsan/pthread_atfork_deadlock.c
@@ -0,0 +1,33 @@
+// RUN: %clang_tsan -O1 %s -lpthread -o %t && %deflake %run %t | FileCheck %s
+// Regression test for
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=61
+// When the data race was reported, pthread_atfork() handler used to be
+// executed which caused another race report in the same thread, which resulted
+// in a deadlock.
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int glob = 0;
+
+void *worker(void *unused) {
+ sleep(1);
+ glob++;
+ return NULL;
+}
+
+void atfork() {
+ fprintf(stderr, "ATFORK\n");
+ glob++;
+}
+
+int main() {
+ pthread_atfork(atfork, NULL, NULL);
+ pthread_t t;
+ pthread_create(&t, NULL, worker, NULL);
+ glob++;
+ pthread_join(t, NULL);
+ // CHECK: ThreadSanitizer: data race
+ // CHECK-NOT: ATFORK
+ return 0;
+}
diff --git a/test/tsan/race_on_barrier.c b/test/tsan/race_on_barrier.c
new file mode 100644
index 000000000000..99b18fe4d8e6
--- /dev/null
+++ b/test/tsan/race_on_barrier.c
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..98c028e19fdd
--- /dev/null
+++ b/test/tsan/race_on_barrier2.c
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..a66e0c4f93f7
--- /dev/null
+++ b/test/tsan/race_on_heap.cc
@@ -0,0 +1,47 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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:
+// CHCEK: #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 000000000000..b4adeeb4df72
--- /dev/null
+++ b/test/tsan/race_on_mutex.c
@@ -0,0 +1,42 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..1796d0c6480b
--- /dev/null
+++ b/test/tsan/race_on_mutex2.c
@@ -0,0 +1,24 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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_puts.cc b/test/tsan/race_on_puts.cc
new file mode 100644
index 000000000000..1f2b4db836ed
--- /dev/null
+++ b/test/tsan/race_on_puts.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+char s[] = "abracadabra";
+
+void *Thread0(void *p) {
+ puts(s);
+ return 0;
+}
+
+void *Thread1(void *p) {
+ s[3] = 'z';
+ return 0;
+}
+
+int main() {
+ pthread_t th[2];
+ pthread_create(&th[0], 0, Thread0, 0);
+ pthread_create(&th[1], 0, Thread1, 0);
+ pthread_join(th[0], 0);
+ pthread_join(th[1], 0);
+ fprintf(stderr, "DONE");
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: DONE
+
diff --git a/test/tsan/race_on_read.cc b/test/tsan/race_on_read.cc
new file mode 100644
index 000000000000..1ec0522b9035
--- /dev/null
+++ b/test/tsan/race_on_read.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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>
+#include <errno.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) {
+ fprintf(stderr, "failed to open /dev/random (%d)\n", errno);
+ 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);
+ fprintf(stderr, "DONE\n");
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 1
+// CHECK: #0 read
+// CHECK: Previous write of size 1
+// CHECK: #0 read
+// CHECK: DONE
+
diff --git a/test/tsan/race_on_speculative_load.cc b/test/tsan/race_on_speculative_load.cc
new file mode 100644
index 000000000000..f816db9e8853
--- /dev/null
+++ b/test/tsan/race_on_speculative_load.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..484bbb7ae022
--- /dev/null
+++ b/test/tsan/race_on_write.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..d28760093c42
--- /dev/null
+++ b/test/tsan/race_with_finished_thread.cc
@@ -0,0 +1,43 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..1fa20f36810b
--- /dev/null
+++ b/test/tsan/signal_errno.cc
@@ -0,0 +1,49 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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: main
+// CHECK: SUMMARY: ThreadSanitizer: signal handler spoils errno{{.*}}MyHandler
+
diff --git a/test/tsan/signal_longjmp.cc b/test/tsan/signal_longjmp.cc
new file mode 100644
index 000000000000..84b0682dcbaf
--- /dev/null
+++ b/test/tsan/signal_longjmp.cc
@@ -0,0 +1,69 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// Test case for longjumping out of signal handler:
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=75
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/mman.h>
+
+sigjmp_buf fault_jmp;
+volatile int fault_expected;
+
+void sigfault_handler(int sig) {
+ if (!fault_expected)
+ abort();
+
+ /* just return from sighandler to proper place */
+ fault_expected = 0;
+ siglongjmp(fault_jmp, 1);
+}
+
+#define MUST_FAULT(code) do { \
+ fault_expected = 1; \
+ if (!sigsetjmp(fault_jmp, 1)) { \
+ code; /* should pagefault -> sihandler does longjmp */ \
+ fprintf(stderr, "%s not faulted\n", #code); \
+ abort(); \
+ } else { \
+ fprintf(stderr, "%s faulted ok\n", #code); \
+ } \
+} while (0)
+
+int main() {
+ struct sigaction act;
+ act.sa_handler = sigfault_handler;
+ act.sa_flags = 0;
+ if (sigemptyset(&act.sa_mask)) {
+ perror("sigemptyset");
+ exit(1);
+ }
+
+ if (sigaction(SIGSEGV, &act, NULL)) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ void *mem = mmap(0, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+
+ MUST_FAULT(((volatile int *volatile)mem)[0] = 0);
+ MUST_FAULT(((volatile int *volatile)mem)[1] = 1);
+ MUST_FAULT(((volatile int *volatile)mem)[3] = 1);
+
+ // Ensure that tsan does not think that we are
+ // in a signal handler.
+ void *volatile p = malloc(10);
+ ((volatile int*)p)[1] = 1;
+ free((void*)p);
+
+ munmap(p, 4096);
+
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: DONE
diff --git a/test/tsan/signal_malloc.cc b/test/tsan/signal_malloc.cc
new file mode 100644
index 000000000000..06932fba42db
--- /dev/null
+++ b/test/tsan/signal_malloc.cc
@@ -0,0 +1,26 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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/signal_recursive.cc b/test/tsan/signal_recursive.cc
new file mode 100644
index 000000000000..bbb6807586a5
--- /dev/null
+++ b/test/tsan/signal_recursive.cc
@@ -0,0 +1,132 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// Test case for recursive signal handlers, adopted from:
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=71
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "process_sleep.h"
+
+static const int kSigSuspend = SIGUSR1;
+static const int kSigRestart = SIGUSR2;
+static sigset_t g_suspend_handler_mask;
+
+static sem_t g_thread_suspend_ack_sem;
+
+static bool g_busy_thread_received_restart;
+
+static volatile bool g_busy_thread_garbage_collected;
+
+static void SaveRegistersInStack() {
+ // Mono walks thread stacks to detect unreferenced objects.
+ // If last object reference is kept in register the object will be collected
+ // This is why threads can't be suspended with something like pthread_suspend
+};
+
+static void fail(const char *what) {
+ fprintf(stderr, "FAILED: %s (errno=%d)\n", what, errno);
+ exit(1);
+}
+
+static void SuspendHandler(int sig) {
+ int old_errno = errno;
+ SaveRegistersInStack();
+ // Acknowledge that thread is saved and suspended
+ if (sem_post(&g_thread_suspend_ack_sem) != 0)
+ fail("sem_post failed");
+
+ do {
+ g_busy_thread_received_restart = false;
+ if (sigsuspend(&g_suspend_handler_mask) != -1 || errno != EINTR)
+ fail("sigsuspend failed");
+ } while (!g_busy_thread_received_restart);
+
+ // Acknowledge that thread restarted
+ if (sem_post(&g_thread_suspend_ack_sem) != 0)
+ fail("sem_post failed");
+
+ g_busy_thread_garbage_collected = true;
+
+ errno = old_errno;
+}
+
+static void RestartHandler(int sig) {
+ g_busy_thread_received_restart = true;
+}
+
+static void StopWorld(pthread_t thread) {
+ if (pthread_kill(thread, kSigSuspend) != 0)
+ fail("pthread_kill failed");
+
+ while (sem_wait(&g_thread_suspend_ack_sem) != 0) {
+ if (errno != EINTR)
+ fail("sem_wait failed");
+ }
+}
+
+static void StartWorld(pthread_t thread) {
+ if (pthread_kill(thread, kSigRestart) != 0)
+ fail("pthread_kill failed");
+
+ while (sem_wait(&g_thread_suspend_ack_sem) != 0) {
+ if (errno != EINTR)
+ fail("sem_wait failed");
+ }
+}
+
+static void CollectGarbage(pthread_t thread) {
+ StopWorld(thread);
+ // Walk stacks
+ process_sleep(1);
+ StartWorld(thread);
+}
+
+static void Init() {
+ if (sigfillset(&g_suspend_handler_mask) != 0)
+ fail("sigfillset failed");
+ if (sigdelset(&g_suspend_handler_mask, kSigRestart) != 0)
+ fail("sigdelset failed");
+ if (sem_init(&g_thread_suspend_ack_sem, 0, 0) != 0)
+ fail("sem_init failed");
+
+ struct sigaction act = {};
+ act.sa_flags = SA_RESTART;
+ sigfillset(&act.sa_mask);
+ act.sa_handler = &SuspendHandler;
+ if (sigaction(kSigSuspend, &act, NULL) != 0)
+ fail("sigaction failed");
+ act.sa_handler = &RestartHandler;
+ if (sigaction(kSigRestart, &act, NULL) != 0)
+ fail("sigaction failed");
+}
+
+void* BusyThread(void *arg) {
+ (void)arg;
+ while (!g_busy_thread_garbage_collected) {
+ usleep(100); // Tsan deadlocks without these sleeps
+ }
+ return NULL;
+}
+
+int main(int argc, const char *argv[]) {
+ Init();
+ pthread_t busy_thread;
+ if (pthread_create(&busy_thread, NULL, &BusyThread, NULL) != 0)
+ fail("pthread_create failed");
+ CollectGarbage(busy_thread);
+ if (pthread_join(busy_thread, 0) != 0)
+ fail("pthread_join failed");
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: FAILED
+// CHECK-NOT: ThreadSanitizer CHECK failed
+// CHECK-NOT: WARNING: ThreadSanitizer:
+// CHECK: DONE
diff --git a/test/tsan/signal_sync.cc b/test/tsan/signal_sync.cc
new file mode 100644
index 000000000000..15387b754dfb
--- /dev/null
+++ b/test/tsan/signal_sync.cc
@@ -0,0 +1,58 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+
+volatile int X;
+
+static void handler(int sig) {
+ (void)sig;
+ if (X != 42)
+ printf("bad");
+}
+
+static void* thr(void *p) {
+ for (int i = 0; i != 1000; i++)
+ usleep(1000);
+ return 0;
+}
+
+int main() {
+ const int kThreads = 10;
+ pthread_t th[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&th[i], 0, thr, 0);
+
+ X = 42;
+
+ struct sigaction act = {};
+ act.sa_handler = &handler;
+ if (sigaction(SIGPROF, &act, 0)) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ itimerval t;
+ t.it_value.tv_sec = 0;
+ t.it_value.tv_usec = 10;
+ t.it_interval = t.it_value;
+ if (setitimer(ITIMER_PROF, &t, 0)) {
+ perror("setitimer");
+ exit(1);
+ }
+
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(th[i], 0);
+
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer:
+// CHECK: DONE
+// CHECK-NOT: WARNING: ThreadSanitizer:
diff --git a/test/tsan/signal_write.cc b/test/tsan/signal_write.cc
new file mode 100644
index 000000000000..626d87a7acc7
--- /dev/null
+++ b/test/tsan/signal_write.cc
@@ -0,0 +1,27 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static void handler(int, siginfo_t*, void*) {
+ const char *str = "HELLO FROM SIGNAL\n";
+ write(2, str, strlen(str));
+}
+
+int main() {
+ struct sigaction act = {};
+ act.sa_sigaction = &handler;
+ sigaction(SIGPROF, &act, 0);
+ kill(getpid(), SIGPROF);
+ sleep(1);
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: HELLO FROM SIGNAL
+// CHECK: DONE
+
diff --git a/test/tsan/sigsuspend.cc b/test/tsan/sigsuspend.cc
new file mode 100644
index 000000000000..a5930d4e87aa
--- /dev/null
+++ b/test/tsan/sigsuspend.cc
@@ -0,0 +1,44 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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\n");
+}
+
+// CHECK: HANDLER
+// CHECK: DONE
diff --git a/test/tsan/simple_race.c b/test/tsan/simple_race.c
new file mode 100644
index 000000000000..7b60c5ec249e
--- /dev/null
+++ b/test/tsan/simple_race.c
@@ -0,0 +1,29 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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);
+ 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 000000000000..f711bb5d114d
--- /dev/null
+++ b/test/tsan/simple_race.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ 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);
+ return 0;
+}
+
+// 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 000000000000..87367033b4c9
--- /dev/null
+++ b/test/tsan/simple_stack.c
@@ -0,0 +1,66 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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{{(:3)?}} ({{.*}})
+// 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 000000000000..b07d863e4008
--- /dev/null
+++ b/test/tsan/simple_stack2.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx_tsan -O1 %s -o %T/simple_stack2.cc.exe && %deflake %run %T/simple_stack2.cc.exe | 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)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK: Previous read of size 4 at {{.*}} by main thread:
+// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:20{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:29{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:41{{(:3)?}} (simple_stack2.cc.exe+{{.*}})
diff --git a/test/tsan/sleep_sync.cc b/test/tsan/sleep_sync.cc
new file mode 100644
index 000000000000..c7614e16bf3e
--- /dev/null
+++ b/test/tsan/sleep_sync.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..4e616992ecc9
--- /dev/null
+++ b/test/tsan/sleep_sync2.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..2e02f46a281f
--- /dev/null
+++ b/test/tsan/stack_race.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..818db367bab6
--- /dev/null
+++ b/test/tsan/stack_race2.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..3e5fb14ba44b
--- /dev/null
+++ b/test/tsan/static_init1.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..667aed1343dc
--- /dev/null
+++ b/test/tsan/static_init2.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..3b9fe62ae2bc
--- /dev/null
+++ b/test/tsan/static_init3.cc
@@ -0,0 +1,47 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..85835a2520f7
--- /dev/null
+++ b/test/tsan/static_init4.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..961e3a3b6329
--- /dev/null
+++ b/test/tsan/static_init5.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..77253eac173f
--- /dev/null
+++ b/test/tsan/static_init6.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -static-libstdc++ -O1 %s -o %t && %run %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/sunrpc.cc b/test/tsan/sunrpc.cc
new file mode 100644
index 000000000000..579816d64098
--- /dev/null
+++ b/test/tsan/sunrpc.cc
@@ -0,0 +1,25 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <pthread.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <stdio.h>
+
+void *thr(void *p) {
+ XDR xdrs;
+ char buf[100];
+ xdrmem_create(&xdrs, buf, sizeof(buf), XDR_ENCODE);
+ xdr_destroy(&xdrs);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ pthread_t th[2];
+ pthread_create(&th[0], 0, thr, 0);
+ pthread_create(&th[1], 0, thr, 0);
+ pthread_join(th[0], 0);
+ pthread_join(th[1], 0);
+ printf("DONE\n");
+ // CHECK: DONE
+ return 0;
+}
diff --git a/test/tsan/suppress_same_address.cc b/test/tsan/suppress_same_address.cc
new file mode 100644
index 000000000000..df19da1cc7ae
--- /dev/null
+++ b/test/tsan/suppress_same_address.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..9305650eaa17
--- /dev/null
+++ b/test/tsan/suppress_same_stacks.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..c808a63d9e84
--- /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" %run %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 000000000000..5fa8a2e43a93
--- /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 000000000000..1d72874d9586
--- /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" %run %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 000000000000..cbdba76ea93a
--- /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 000000000000..4ababddf6311
--- /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" %run %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 000000000000..b3c4dbc59363
--- /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 000000000000..bce0fe8b5511
--- /dev/null
+++ b/test/tsan/test_output.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+ulimit -s 8192
+set -e # fail on any error
+
+HERE=$(dirname $0)
+TSAN_DIR=$(dirname $0)/../../lib/tsan
+
+# Assume clang and clang++ are in path.
+: ${CC:=clang}
+: ${CXX:=clang++}
+: ${FILECHECK:=FileCheck}
+
+# TODO: add testing for all of -O0...-O3
+CFLAGS="-fsanitize=thread -O2 -g -Wall"
+LDFLAGS="-pthread -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 [[ $c == */*blacklist*.cc ]]; then
+ echo TEST $c is not supported
+ continue
+ fi
+ if [ "`grep "TSAN_OPTIONS" $c`" ]; then
+ echo SKIPPING $c -- requires TSAN_OPTIONS
+ continue
+ fi
+ if [ "`grep "XFAIL" $c`" ]; then
+ echo SKIPPING $c -- has XFAIL
+ 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_detach.c b/test/tsan/thread_detach.c
new file mode 100644
index 000000000000..32cf641b141a
--- /dev/null
+++ b/test/tsan/thread_detach.c
@@ -0,0 +1,20 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *Thread(void *x) {
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ sleep(1);
+ pthread_detach(t);
+ printf("PASS\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
+// CHECK: PASS
diff --git a/test/tsan/thread_end_with_ignore.cc b/test/tsan/thread_end_with_ignore.cc
new file mode 100644
index 000000000000..79bb08d64bcb
--- /dev/null
+++ b/test/tsan/thread_end_with_ignore.cc
@@ -0,0 +1,24 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..9387ea488d5a
--- /dev/null
+++ b/test/tsan/thread_end_with_ignore2.cc
@@ -0,0 +1,12 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..55688b2a543f
--- /dev/null
+++ b/test/tsan/thread_end_with_ignore3.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..9b850dd4b567
--- /dev/null
+++ b/test/tsan/thread_leak.c
@@ -0,0 +1,17 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..fc2942b2a05d
--- /dev/null
+++ b/test/tsan/thread_leak2.c
@@ -0,0 +1,17 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..f4db484219a0
--- /dev/null
+++ b/test/tsan/thread_leak3.c
@@ -0,0 +1,17 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..0d3b8307000a
--- /dev/null
+++ b/test/tsan/thread_leak4.c
@@ -0,0 +1,18 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %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 000000000000..ca244a9f24e1
--- /dev/null
+++ b/test/tsan/thread_leak5.c
@@ -0,0 +1,20 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..a790c668c084
--- /dev/null
+++ b/test/tsan/thread_name.cc
@@ -0,0 +1,47 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#if defined(__linux__)
+#define USE_PTHREAD_SETNAME_NP __GLIBC_PREREQ(2, 12)
+#elif defined(__FreeBSD__)
+#include <pthread_np.h>
+#define USE_PTHREAD_SETNAME_NP 1
+#define pthread_setname_np pthread_set_name_np
+#else
+#define USE_PTHREAD_SETNAME_NP 0
+#endif
+
+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 USE_PTHREAD_SETNAME_NP
+ 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 000000000000..6a3dafe9c763
--- /dev/null
+++ b/test/tsan/thread_name2.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#if defined(__FreeBSD__)
+#include <pthread_np.h>
+#define pthread_setname_np pthread_set_name_np
+#endif
+
+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 000000000000..c10eab15c5a9
--- /dev/null
+++ b/test/tsan/tiny_race.c
@@ -0,0 +1,21 @@
+// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..18589347e806
--- /dev/null
+++ b/test/tsan/tls_race.cc
@@ -0,0 +1,21 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..0ca629ada5cc
--- /dev/null
+++ b/test/tsan/tls_race2.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..950f5d30d4da
--- /dev/null
+++ b/test/tsan/tsan-vs-gvn.cc
@@ -0,0 +1,38 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O2 %s -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O3 %s -o %t && %run %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 000000000000..20cb545f7426
--- /dev/null
+++ b/test/tsan/unaligned_norace.cc
@@ -0,0 +1,84 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..6e9b5a33f0da
--- /dev/null
+++ b/test/tsan/unaligned_race.cc
@@ -0,0 +1,139 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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/vfork.cc b/test/tsan/vfork.cc
new file mode 100644
index 000000000000..5ae1dd1ababd
--- /dev/null
+++ b/test/tsan/vfork.cc
@@ -0,0 +1,51 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.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);
+ int pid = vfork();
+ if (pid < 0) {
+ printf("FAIL to vfork\n");
+ exit(1);
+ }
+ if (pid == 0) { // child
+ // Closing of fds must not affect parent process.
+ // Strictly saying this is undefined behavior, because vfork child is not
+ // allowed to call any functions other than exec/exit. But this is what
+ // openjdk does.
+ close(fds[0]);
+ close(fds[1]);
+ _exit(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);
+ printf("DONE\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: FAIL to vfork
+// CHECK: DONE
diff --git a/test/tsan/virtual_inheritance_compile_bug.cc b/test/tsan/virtual_inheritance_compile_bug.cc
new file mode 100644
index 000000000000..2a50c2e88d01
--- /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 && %run %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 000000000000..92a2b326e717
--- /dev/null
+++ b/test/tsan/vptr_benign_race.cc
@@ -0,0 +1,51 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %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 000000000000..68e12e8e7e89
--- /dev/null
+++ b/test/tsan/vptr_harmful_race.cc
@@ -0,0 +1,51 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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 000000000000..aa53bbb90fcf
--- /dev/null
+++ b/test/tsan/vptr_harmful_race2.cc
@@ -0,0 +1,51 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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/vptr_harmful_race3.cc b/test/tsan/vptr_harmful_race3.cc
new file mode 100644
index 000000000000..ac6ea94e51eb
--- /dev/null
+++ b/test/tsan/vptr_harmful_race3.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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;
+static void (A::*fn)() = &A::F;
+
+void *Thread1(void *x) {
+ sleep(1);
+ (obj->*fn)();
+ 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/vptr_harmful_race4.cc b/test/tsan/vptr_harmful_race4.cc
new file mode 100644
index 000000000000..969c9d58a016
--- /dev/null
+++ b/test/tsan/vptr_harmful_race4.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+struct A {
+ virtual void F() {
+ }
+
+ virtual ~A() {
+ }
+};
+
+struct B : A {
+ virtual void F() {
+ }
+};
+
+void *Thread(void *x) {
+ sleep(1);
+ ((A*)x)->F();
+ return 0;
+}
+
+int main() {
+ A *obj = new B;
+ pthread_t t;
+ pthread_create(&t, 0, Thread, obj);
+ delete obj;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: heap-use-after-free (virtual call vs free)
+
diff --git a/test/tsan/write_in_reader_lock.cc b/test/tsan/write_in_reader_lock.cc
new file mode 100644
index 000000000000..55882139b153
--- /dev/null
+++ b/test/tsan/write_in_reader_lock.cc
@@ -0,0 +1,35 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | 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