diff options
author | Alexey Samsonov <samsonov@google.com> | 2014-02-14 14:35:48 +0000 |
---|---|---|
committer | Alexey Samsonov <samsonov@google.com> | 2014-02-14 14:35:48 +0000 |
commit | e6a6183e9b8ecf7dec8808fa87577d5b574ca22e (patch) | |
tree | a6e5f3181f2e0c4ffc21f9648c5e9c873f34b330 /compiler-rt/test | |
parent | 9f20d6703479a952465f7db0c2fbd70904c030c2 (diff) | |
download | bcm5719-llvm-e6a6183e9b8ecf7dec8808fa87577d5b574ca22e.tar.gz bcm5719-llvm-e6a6183e9b8ecf7dec8808fa87577d5b574ca22e.zip |
Move TSan lit-tests under test/tsan
llvm-svn: 201414
Diffstat (limited to 'compiler-rt/test')
150 files changed, 4980 insertions, 0 deletions
diff --git a/compiler-rt/test/CMakeLists.txt b/compiler-rt/test/CMakeLists.txt index 590c2d38dbd..72f8aab30b2 100644 --- a/compiler-rt/test/CMakeLists.txt +++ b/compiler-rt/test/CMakeLists.txt @@ -21,6 +21,9 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS) if(MSAN_SUPPORTED_ARCH) add_subdirectory(msan) endif() + if(TSAN_SUPPORTED_ARCH) + add_subdirectory(tsan) + endif() if(UBSAN_SUPPORTED_ARCH) add_subdirectory(ubsan) endif() diff --git a/compiler-rt/test/tsan/CMakeLists.txt b/compiler-rt/test/tsan/CMakeLists.txt new file mode 100644 index 00000000000..8d79c705ddb --- /dev/null +++ b/compiler-rt/test/tsan/CMakeLists.txt @@ -0,0 +1,19 @@ +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + +set(TSAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + tsan) + +if(LLVM_INCLUDE_TESTS) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) + list(APPEND TSAN_TEST_DEPS TsanUnitTests) +endif() + +add_lit_testsuite(check-tsan "Running ThreadSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${TSAN_TEST_DEPS}) +set_target_properties(check-tsan PROPERTIES FOLDER "TSan tests") diff --git a/compiler-rt/test/tsan/Helpers/blacklist.txt b/compiler-rt/test/tsan/Helpers/blacklist.txt new file mode 100644 index 00000000000..22225e542ff --- /dev/null +++ b/compiler-rt/test/tsan/Helpers/blacklist.txt @@ -0,0 +1 @@ +fun:*Blacklisted_Thread2* diff --git a/compiler-rt/test/tsan/Helpers/lit.local.cfg b/compiler-rt/test/tsan/Helpers/lit.local.cfg new file mode 100644 index 00000000000..9246b10352a --- /dev/null +++ b/compiler-rt/test/tsan/Helpers/lit.local.cfg @@ -0,0 +1,2 @@ +# Files in this directory are helper files for other output tests. +config.suffixes = [] diff --git a/compiler-rt/test/tsan/SharedLibs/lit.local.cfg b/compiler-rt/test/tsan/SharedLibs/lit.local.cfg new file mode 100644 index 00000000000..b3677c17a0f --- /dev/null +++ b/compiler-rt/test/tsan/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# Sources in this directory are compiled as shared libraries and used by +# tests in parent directory. + +config.suffixes = [] diff --git a/compiler-rt/test/tsan/SharedLibs/load_shared_lib-so.cc b/compiler-rt/test/tsan/SharedLibs/load_shared_lib-so.cc new file mode 100644 index 00000000000..d05aa6a40d1 --- /dev/null +++ b/compiler-rt/test/tsan/SharedLibs/load_shared_lib-so.cc @@ -0,0 +1,22 @@ +//===----------- load_shared_lib-so.cc --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include <stddef.h> + +int GLOB_SHARED = 0; + +extern "C" +void *write_from_so(void *unused) { + GLOB_SHARED++; + return NULL; +} diff --git a/compiler-rt/test/tsan/Unit/lit.site.cfg.in b/compiler-rt/test/tsan/Unit/lit.site.cfg.in new file mode 100644 index 00000000000..9498105653a --- /dev/null +++ b/compiler-rt/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/compiler-rt/test/tsan/aligned_vs_unaligned_race.cc b/compiler-rt/test/tsan/aligned_vs_unaligned_race.cc new file mode 100644 index 00000000000..f4533d08306 --- /dev/null +++ b/compiler-rt/test/tsan/aligned_vs_unaligned_race.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// Race between an aligned access and an unaligned access, which +// touches the same memory region. +// This is a real race which is not detected by tsan. +// https://code.google.com/p/thread-sanitizer/issues/detail?id=17 +#include <pthread.h> +#include <stdio.h> +#include <stdint.h> + +uint64_t Global[2]; + +void *Thread1(void *x) { + Global[1]++; + return NULL; +} + +void *Thread2(void *x) { + char *p1 = reinterpret_cast<char *>(&Global[0]); + uint64_t *p4 = reinterpret_cast<uint64_t *>(p1 + 1); + (*p4)++; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("Pass\n"); + // CHECK-NOT: ThreadSanitizer: data race + // CHECK: Pass + return 0; +} diff --git a/compiler-rt/test/tsan/allocator_returns_null.cc b/compiler-rt/test/tsan/allocator_returns_null.cc new file mode 100644 index 00000000000..4b5eb5504c2 --- /dev/null +++ b/compiler-rt/test/tsan/allocator_returns_null.cc @@ -0,0 +1,64 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process shoudl crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + fprintf(stderr, "x: %p\n", x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: ThreadSanitizer's allocator is terminating the process + diff --git a/compiler-rt/test/tsan/atomic_free.cc b/compiler-rt/test/tsan/atomic_free.cc new file mode 100644 index 00000000000..87d559362af --- /dev/null +++ b/compiler-rt/test/tsan/atomic_free.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *a) { + __atomic_fetch_add((int*)a, 1, __ATOMIC_SEQ_CST); + return 0; +} + +int main() { + int *a = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, a); + sleep(1); + delete a; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/atomic_free2.cc b/compiler-rt/test/tsan/atomic_free2.cc new file mode 100644 index 00000000000..961ff38c843 --- /dev/null +++ b/compiler-rt/test/tsan/atomic_free2.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *a) { + sleep(1); + __atomic_fetch_add((int*)a, 1, __ATOMIC_SEQ_CST); + return 0; +} + +int main() { + int *a = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, a); + delete a; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: heap-use-after-free diff --git a/compiler-rt/test/tsan/atomic_norace.cc b/compiler-rt/test/tsan/atomic_norace.cc new file mode 100644 index 00000000000..265459b0758 --- /dev/null +++ b/compiler-rt/test/tsan/atomic_norace.cc @@ -0,0 +1,61 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +const int kTestCount = 4; +typedef long long T; +T atomics[kTestCount * 2]; + +void Test(int test, T *p, bool main_thread) { + volatile T sink; + if (test == 0) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + } else if (test == 1) { + if (main_thread) + __atomic_exchange_n(p, 1, __ATOMIC_ACQ_REL); + else + __atomic_exchange_n(p, 1, __ATOMIC_ACQ_REL); + } else if (test == 2) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + __atomic_store_n(p, 1, __ATOMIC_SEQ_CST); + } else if (test == 3) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + sink = *p; + } +} + +void *Thread(void *p) { + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[i], false); + } + sleep(2); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d reverse\n", i); + Test(i, &atomics[kTestCount + i], false); + } + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d\n", i); + Test(i, &atomics[i], true); + } + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[kTestCount + i], true); + } + pthread_join(t, 0); +} + +// CHECK-NOT: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/atomic_race.cc b/compiler-rt/test/tsan/atomic_race.cc new file mode 100644 index 00000000000..0dfe4d93df6 --- /dev/null +++ b/compiler-rt/test/tsan/atomic_race.cc @@ -0,0 +1,80 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> + +const int kTestCount = 4; +typedef long long T; +T atomics[kTestCount * 2]; + +void Test(int test, T *p, bool main_thread) { + volatile T sink; + if (test == 0) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + *p = 42; + } else if (test == 1) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + sink = *p; + } else if (test == 2) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + *p = 42; + } else if (test == 3) { + if (main_thread) + __atomic_store_n(p, 1, __ATOMIC_SEQ_CST); + else + sink = *p; + } +} + +void *Thread(void *p) { + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[i], false); + } + sleep(2); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d reverse\n", i); + Test(i, &atomics[kTestCount + i], false); + } + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d\n", i); + Test(i, &atomics[i], true); + } + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[kTestCount + i], true); + } + pthread_join(t, 0); +} + +// CHECK: Test 0 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 1 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 2 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 3 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 0 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 1 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 2 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 3 reverse +// CHECK: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/atomic_stack.cc b/compiler-rt/test/tsan/atomic_stack.cc new file mode 100644 index 00000000000..841f74b891a --- /dev/null +++ b/compiler-rt/test/tsan/atomic_stack.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + __atomic_fetch_add(&Global, 1, __ATOMIC_RELAXED); + return NULL; +} + +void *Thread2(void *x) { + Global++; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Atomic write of size 4 +// CHECK: #0 __tsan_atomic32_fetch_add +// CHECK: #1 Thread1 diff --git a/compiler-rt/test/tsan/benign_race.cc b/compiler-rt/test/tsan/benign_race.cc new file mode 100644 index 00000000000..a4d4d23c362 --- /dev/null +++ b/compiler-rt/test/tsan/benign_race.cc @@ -0,0 +1,39 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +int WTFGlobal; + +extern "C" { +void AnnotateBenignRaceSized(const char *f, int l, + void *mem, unsigned int size, const char *desc); +void WTFAnnotateBenignRaceSized(const char *f, int l, + void *mem, unsigned int size, + const char *desc); +} + + +void *Thread(void *x) { + Global = 42; + WTFGlobal = 142; + return 0; +} + +int main() { + AnnotateBenignRaceSized(__FILE__, __LINE__, + &Global, sizeof(Global), "Race on Global"); + WTFAnnotateBenignRaceSized(__FILE__, __LINE__, + &WTFGlobal, sizeof(WTFGlobal), + "Race on WTFGlobal"); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + Global = 43; + WTFGlobal = 143; + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/blacklist.cc b/compiler-rt/test/tsan/blacklist.cc new file mode 100644 index 00000000000..5baf926e627 --- /dev/null +++ b/compiler-rt/test/tsan/blacklist.cc @@ -0,0 +1,31 @@ +// Test blacklist functionality for TSan. + +// RUN: %clangxx_tsan -O1 %s \ +// RUN: -fsanitize-blacklist=%p/Helpers/blacklist.txt \ +// RUN: -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int Global; + +void *Thread1(void *x) { + Global++; + return NULL; +} + +void *Blacklisted_Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Blacklisted_Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/cond.c b/compiler-rt/test/tsan/cond.c new file mode 100644 index 00000000000..52c87a413eb --- /dev/null +++ b/compiler-rt/test/tsan/cond.c @@ -0,0 +1,53 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: ThreadSanitizer WARNING: double lock +// CHECK-NOT: ThreadSanitizer WARNING: mutex unlock by another thread +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +pthread_mutex_t m; +pthread_cond_t c; +int x; + +void *thr1(void *p) { + int i; + + for (i = 0; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_cond_signal(&c); + pthread_mutex_unlock(&m); + } + return 0; +} + +void *thr2(void *p) { + int i; + + for (i = 1; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_mutex_unlock(&m); + pthread_cond_broadcast(&c); + } + return 0; +} + +int main() { + pthread_t th1, th2; + + pthread_mutex_init(&m, 0); + pthread_cond_init(&c, 0); + pthread_create(&th1, 0, thr1, 0); + pthread_create(&th2, 0, thr2, 0); + pthread_join(th1, 0); + pthread_join(th2, 0); + fprintf(stderr, "OK\n"); +} diff --git a/compiler-rt/test/tsan/cond_race.cc b/compiler-rt/test/tsan/cond_race.cc new file mode 100644 index 00000000000..1e2acb24327 --- /dev/null +++ b/compiler-rt/test/tsan/cond_race.cc @@ -0,0 +1,36 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// CHECK: ThreadSanitizer: data race +// CHECK: pthread_cond_signal + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +struct Ctx { + pthread_mutex_t m; + pthread_cond_t c; + bool done; +}; + +void *thr(void *p) { + Ctx *c = (Ctx*)p; + pthread_mutex_lock(&c->m); + c->done = true; + pthread_mutex_unlock(&c->m); + pthread_cond_signal(&c->c); + return 0; +} + +int main() { + Ctx *c = new Ctx(); + pthread_mutex_init(&c->m, 0); + pthread_cond_init(&c->c, 0); + pthread_t th; + pthread_create(&th, 0, thr, c); + pthread_mutex_lock(&c->m); + while (!c->done) + pthread_cond_wait(&c->c, &c->m); + pthread_mutex_unlock(&c->m); + delete c; + pthread_join(th, 0); +} diff --git a/compiler-rt/test/tsan/cond_version.c b/compiler-rt/test/tsan/cond_version.c new file mode 100644 index 00000000000..1f966bfacb8 --- /dev/null +++ b/compiler-rt/test/tsan/cond_version.c @@ -0,0 +1,44 @@ +// RUN: %clang_tsan -O1 %s -o %t -lrt && %t 2>&1 | FileCheck %s +// Test that pthread_cond is properly intercepted, +// previously there were issues with versioned symbols. +// CHECK: OK + +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <time.h> +#include <errno.h> + +int main() { + typedef unsigned long long u64; + pthread_mutex_t m; + pthread_cond_t c; + pthread_condattr_t at; + struct timespec ts0, ts1, ts2; + int res; + u64 sleep; + + pthread_mutex_init(&m, 0); + pthread_condattr_init(&at); + pthread_condattr_setclock(&at, CLOCK_MONOTONIC); + pthread_cond_init(&c, &at); + + clock_gettime(CLOCK_MONOTONIC, &ts0); + ts1 = ts0; + ts1.tv_sec += 2; + + pthread_mutex_lock(&m); + do { + res = pthread_cond_timedwait(&c, &m, &ts1); + } while (res == 0); + pthread_mutex_unlock(&m); + + clock_gettime(CLOCK_MONOTONIC, &ts2); + sleep = (u64)ts2.tv_sec * 1000000000 + ts2.tv_nsec - + ((u64)ts0.tv_sec * 1000000000 + ts0.tv_nsec); + if (res != ETIMEDOUT) + exit(printf("bad return value %d, want %d\n", res, ETIMEDOUT)); + if (sleep < 1000000000) + exit(printf("bad sleep duration %lluns, want %dns\n", sleep, 1000000000)); + fprintf(stderr, "OK\n"); +} diff --git a/compiler-rt/test/tsan/deep_stack1.cc b/compiler-rt/test/tsan/deep_stack1.cc new file mode 100644 index 00000000000..3048aa8745b --- /dev/null +++ b/compiler-rt/test/tsan/deep_stack1.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +volatile int X; +volatile int N; +void (*volatile F)(); + +static void foo() { + if (--N == 0) + X = 42; + else + F(); +} + +void *Thread(void *p) { +#ifdef ORDER1 + sleep(1); +#endif + F(); + return 0; +} + +int main() { + N = 50000; + F = foo; + pthread_t t; + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setstacksize(&a, N * 256 + (1 << 20)); + pthread_create(&t, &a, Thread, 0); +#ifdef ORDER2 + sleep(1); +#endif + X = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #100 foo +// We must output suffucuently large stack (at least 100 frames) + diff --git a/compiler-rt/test/tsan/default_options.cc b/compiler-rt/test/tsan/default_options.cc new file mode 100644 index 00000000000..62c6c028f9e --- /dev/null +++ b/compiler-rt/test/tsan/default_options.cc @@ -0,0 +1,32 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" const char *__tsan_default_options() { + return "report_bugs=0"; +} + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/compiler-rt/test/tsan/fd_close_norace.cc b/compiler-rt/test/tsan/fd_close_norace.cc new file mode 100644 index 00000000000..a8b1a6d7b9e --- /dev/null +++ b/compiler-rt/test/tsan/fd_close_norace.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +void *Thread1(void *x) { + int f = open("/dev/random", O_RDONLY); + close(f); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + int f = open("/dev/random", O_RDONLY); + close(f); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + + diff --git a/compiler-rt/test/tsan/fd_close_norace2.cc b/compiler-rt/test/tsan/fd_close_norace2.cc new file mode 100644 index 00000000000..b42b334a27c --- /dev/null +++ b/compiler-rt/test/tsan/fd_close_norace2.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int pipes[2]; + +void *Thread(void *x) { + // wait for shutown signal + while (read(pipes[0], &x, 1) != 1) { + } + close(pipes[0]); + close(pipes[1]); + return 0; +} + +int main() { + if (pipe(pipes)) + return 1; + pthread_t t; + pthread_create(&t, 0, Thread, 0); + // send shutdown signal + while (write(pipes[1], &t, 1) != 1) { + } + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/compiler-rt/test/tsan/fd_dup_norace.cc b/compiler-rt/test/tsan/fd_dup_norace.cc new file mode 100644 index 00000000000..8826f90fc48 --- /dev/null +++ b/compiler-rt/test/tsan/fd_dup_norace.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int fds[2]; + +void *Thread1(void *x) { + char buf; + read(fds[0], &buf, 1); + close(fds[0]); + return 0; +} + +void *Thread2(void *x) { + close(fds[1]); + return 0; +} + +int main() { + fds[0] = open("/dev/random", O_RDONLY); + fds[1] = dup2(fds[0], 100); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/fd_location.cc b/compiler-rt/test/tsan/fd_location.cc new file mode 100644 index 00000000000..2b1e9c56e36 --- /dev/null +++ b/compiler-rt/test/tsan/fd_location.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int fds[2]; + +void *Thread1(void *x) { + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + close(fds[0]); + close(fds[1]); + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is file descriptor {{[0-9]+}} created by main thread at: +// CHECK: #0 pipe +// CHECK: #1 main + diff --git a/compiler-rt/test/tsan/fd_pipe_norace.cc b/compiler-rt/test/tsan/fd_pipe_norace.cc new file mode 100644 index 00000000000..2da69ea2111 --- /dev/null +++ b/compiler-rt/test/tsan/fd_pipe_norace.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int fds[2]; +int X; + +void *Thread1(void *x) { + X = 42; + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + char buf; + while (read(fds[0], &buf, 1) != 1) { + } + X = 43; + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/fd_pipe_race.cc b/compiler-rt/test/tsan/fd_pipe_race.cc new file mode 100644 index 00000000000..4dd2b77861a --- /dev/null +++ b/compiler-rt/test/tsan/fd_pipe_race.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int fds[2]; + +void *Thread1(void *x) { + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + close(fds[0]); + close(fds[1]); + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 8 +// CHECK: #0 close +// CHECK: #1 Thread2 +// CHECK: Previous read of size 8 +// CHECK: #0 write +// CHECK: #1 Thread1 + + diff --git a/compiler-rt/test/tsan/fd_socket_connect_norace.cc b/compiler-rt/test/tsan/fd_socket_connect_norace.cc new file mode 100644 index 00000000000..065299a9c6b --- /dev/null +++ b/compiler-rt/test/tsan/fd_socket_connect_norace.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +struct sockaddr_in addr; +int X; + +void *ClientThread(void *x) { + int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + X = 42; + if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) { + perror("connect"); + exit(1); + } + close(c); + return NULL; +} + +int main() { + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + addr.sin_family = AF_INET; + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + addr.sin_port = INADDR_ANY; + socklen_t len = sizeof(addr); + bind(s, (sockaddr*)&addr, len); + getsockname(s, (sockaddr*)&addr, &len); + listen(s, 10); + pthread_t t; + pthread_create(&t, 0, ClientThread, 0); + int c = accept(s, 0, 0); + X = 42; + pthread_join(t, 0); + close(c); + close(s); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/compiler-rt/test/tsan/fd_socket_norace.cc b/compiler-rt/test/tsan/fd_socket_norace.cc new file mode 100644 index 00000000000..243fc9de223 --- /dev/null +++ b/compiler-rt/test/tsan/fd_socket_norace.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +struct sockaddr_in addr; +int X; + +void *ClientThread(void *x) { + X = 42; + int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) { + perror("connect"); + exit(1); + } + if (send(c, "a", 1, 0) != 1) { + perror("send"); + exit(1); + } + close(c); + return NULL; +} + +int main() { + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + addr.sin_family = AF_INET; + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + addr.sin_port = INADDR_ANY; + socklen_t len = sizeof(addr); + bind(s, (sockaddr*)&addr, len); + getsockname(s, (sockaddr*)&addr, &len); + listen(s, 10); + pthread_t t; + pthread_create(&t, 0, ClientThread, 0); + int c = accept(s, 0, 0); + char buf; + while (read(c, &buf, 1) != 1) { + } + X = 43; + close(c); + close(s); + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/compiler-rt/test/tsan/fd_socketpair_norace.cc b/compiler-rt/test/tsan/fd_socketpair_norace.cc new file mode 100644 index 00000000000..f91e4eca0fe --- /dev/null +++ b/compiler-rt/test/tsan/fd_socketpair_norace.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> + +int fds[2]; +int X; + +void *Thread1(void *x) { + X = 42; + write(fds[1], "a", 1); + close(fds[1]); + return NULL; +} + +void *Thread2(void *x) { + char buf; + while (read(fds[0], &buf, 1) != 1) { + } + X = 43; + close(fds[0]); + return NULL; +} + +int main() { + socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/fd_stdout_race.cc b/compiler-rt/test/tsan/fd_stdout_race.cc new file mode 100644 index 00000000000..4b512bb7887 --- /dev/null +++ b/compiler-rt/test/tsan/fd_stdout_race.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int X; + +void *Thread1(void *x) { + sleep(1); + int f = open("/dev/random", O_RDONLY); + char buf; + read(f, &buf, 1); + close(f); + X = 42; + return NULL; +} + +void *Thread2(void *x) { + X = 43; + write(STDOUT_FILENO, "a", 1); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 +// CHECK: #0 Thread1 +// CHECK: Previous write of size 4 +// CHECK: #0 Thread2 + + diff --git a/compiler-rt/test/tsan/fork_deadlock.cc b/compiler-rt/test/tsan/fork_deadlock.cc new file mode 100644 index 00000000000..09500b5be2c --- /dev/null +++ b/compiler-rt/test/tsan/fork_deadlock.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="atexit_sleep_ms=50" %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +int counter; + +static void *incrementer(void *p) { + for (;;) + __sync_fetch_and_add(&counter, 1); + return 0; +} + +static void *watchdog(void *p) { + sleep(100); + fprintf(stderr, "timed out after 100 seconds\n"); + exit(1); + return 0; +} + +int main() { + pthread_t th1, th2; + pthread_create(&th1, 0, incrementer, 0); + pthread_create(&th2, 0, watchdog, 0); + for (int i = 0; i < 10; i++) { + switch (fork()) { + default: // parent + while (wait(0) < 0) {} + fprintf(stderr, "."); + break; + case 0: // child + __sync_fetch_and_add(&counter, 1); + exit(0); + break; + case -1: // error + fprintf(stderr, "failed to fork (%d)\n", errno); + exit(1); + } + } + fprintf(stderr, "OK\n"); +} + +// CHECK: OK + diff --git a/compiler-rt/test/tsan/fork_multithreaded.cc b/compiler-rt/test/tsan/fork_multithreaded.cc new file mode 100644 index 00000000000..474b53e425a --- /dev/null +++ b/compiler-rt/test/tsan/fork_multithreaded.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s -check-prefix=CHECK-DIE +// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="die_after_fork=0" %t 2>&1 | FileCheck %s -check-prefix=CHECK-NODIE +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +static void *sleeper(void *p) { + sleep(10); + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, sleeper, 0); + switch (fork()) { + default: // parent + while (wait(0) < 0) {} + break; + case 0: // child + { + pthread_t th2; + pthread_create(&th2, 0, sleeper, 0); + exit(0); + break; + } + case -1: // error + fprintf(stderr, "failed to fork (%d)\n", errno); + exit(1); + } + fprintf(stderr, "OK\n"); +} + +// CHECK-DIE: ThreadSanitizer: starting new threads after muti-threaded fork is not supported +// CHECK-DIE: OK + +// CHECK-NODIE-NOT: ThreadSanitizer: starting new threads after muti-threaded fork is not supported +// CHECK-NODIE: OK + diff --git a/compiler-rt/test/tsan/fork_multithreaded3.cc b/compiler-rt/test/tsan/fork_multithreaded3.cc new file mode 100644 index 00000000000..c4ebcce1a39 --- /dev/null +++ b/compiler-rt/test/tsan/fork_multithreaded3.cc @@ -0,0 +1,40 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +static void *racer(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + switch (fork()) { + default: // parent + while (wait(0) < 0) {} + break; + case 0: // child + { + int x = 0; + pthread_t th1, th2; + pthread_create(&th1, 0, racer, &x); + pthread_create(&th2, 0, racer, &x); + pthread_join(th1, 0); + pthread_join(th2, 0); + exit(0); + break; + } + case -1: // error + fprintf(stderr, "failed to fork (%d)\n", errno); + exit(1); + } + fprintf(stderr, "OK\n"); +} + +// CHECK: ThreadSanitizer: data race +// CHECK: OK + diff --git a/compiler-rt/test/tsan/free_race.c b/compiler-rt/test/tsan/free_race.c new file mode 100644 index 00000000000..d1db9fece90 --- /dev/null +++ b/compiler-rt/test/tsan/free_race.c @@ -0,0 +1,49 @@ +// RUN: %clang_tsan -O1 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOZUPP +// RUN: TSAN_OPTIONS="suppressions=%s.supp print_suppressions=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUPP + +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +int *mem; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx); + free(mem); + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_mutex_lock(&mtx); + mem[0] = 42; + pthread_mutex_unlock(&mtx); + return NULL; +} + +int main() { + mem = (int*)malloc(100); + pthread_mutex_init(&mtx, 0); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + pthread_mutex_destroy(&mtx); + return 0; +} + +// CHECK-NOZUPP: WARNING: ThreadSanitizer: heap-use-after-free +// CHECK-NOZUPP: Write of size 4 at {{.*}} by main thread{{.*}}: +// CHECK-NOZUPP: #0 Thread2 +// CHECK-NOZUPP: #1 main +// CHECK-NOZUPP: Previous write of size 8 at {{.*}} by thread T1{{.*}}: +// CHECK-NOZUPP: #0 free +// CHECK-NOZUPP: #{{(1|2)}} Thread1 +// CHECK-NOZUPP: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 +// CHECK-SUPP: ThreadSanitizer: Matched 1 suppressions +// CHECK-SUPP: 1 race:^Thread2$ diff --git a/compiler-rt/test/tsan/free_race.c.supp b/compiler-rt/test/tsan/free_race.c.supp new file mode 100644 index 00000000000..f5d6a4969a4 --- /dev/null +++ b/compiler-rt/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/compiler-rt/test/tsan/free_race2.c b/compiler-rt/test/tsan/free_race2.c new file mode 100644 index 00000000000..2b9a41927a4 --- /dev/null +++ b/compiler-rt/test/tsan/free_race2.c @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <stdlib.h> + +void __attribute__((noinline)) foo(int *mem) { + free(mem); +} + +void __attribute__((noinline)) bar(int *mem) { + mem[0] = 42; +} + +int main() { + int *mem = (int*)malloc(100); + foo(mem); + bar(mem); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: heap-use-after-free +// CHECK: Write of size 4 at {{.*}} by main thread: +// CHECK: #0 bar +// CHECK: #1 main +// CHECK: Previous write of size 8 at {{.*}} by main thread: +// CHECK: #0 free +// CHECK: #{{1|2}} foo +// CHECK: #{{2|3}} main diff --git a/compiler-rt/test/tsan/getline_nohang.cc b/compiler-rt/test/tsan/getline_nohang.cc new file mode 100644 index 00000000000..1dd17f0646a --- /dev/null +++ b/compiler-rt/test/tsan/getline_nohang.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t + +// Make sure TSan doesn't deadlock on a file stream lock at program shutdown. +// See https://code.google.com/p/thread-sanitizer/issues/detail?id=47 +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +void *thread(void *unused) { + char *line = NULL; + size_t size; + int fd[2]; + pipe(fd); + // Forge a non-standard stream to make sure it's not closed. + FILE *stream = fdopen(fd[0], "r"); + while (1) { + volatile int res = getline(&line, &size, stream); + (void)res; + } + return NULL; +} + +int main() { + pthread_t t; + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); + pthread_create(&t, &a, thread, NULL); + pthread_attr_destroy(&a); + fprintf(stderr, "DONE\n"); + return 0; + // ThreadSanitizer used to hang here because of a deadlock on a file stream. +} + +// CHECK: DONE diff --git a/compiler-rt/test/tsan/global_race.cc b/compiler-rt/test/tsan/global_race.cc new file mode 100644 index 00000000000..0e3ce2e5033 --- /dev/null +++ b/compiler-rt/test/tsan/global_race.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +int GlobalData[10]; +int x; +namespace XXX { + struct YYY { + static int ZZZ[10]; + }; + int YYY::ZZZ[10]; +} + +void *Thread(void *a) { + sleep(1); + GlobalData[2] = 42; + x = 1; + XXX::YYY::ZZZ[0] = 1; + return 0; +} + +int main() { + fprintf(stderr, "addr=%p\n", GlobalData); + fprintf(stderr, "addr2=%p\n", &x); + fprintf(stderr, "addr3=%p\n", XXX::YYY::ZZZ); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + GlobalData[2] = 43; + x = 0; + XXX::YYY::ZZZ[0] = 0; + pthread_join(t, 0); +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: addr2=[[ADDR2:0x[0-9,a-f]+]] +// CHECK: addr3=[[ADDR3:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'GlobalData' of size 40 at [[ADDR]] ({{.*}}+0x{{[0-9,a-f]+}}) +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'x' of size 4 at [[ADDR2]] ({{.*}}+0x{{[0-9,a-f]+}}) +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'XXX::YYY::ZZZ' of size 40 at [[ADDR3]] ({{.*}}+0x{{[0-9,a-f]+}}) diff --git a/compiler-rt/test/tsan/halt_on_error.cc b/compiler-rt/test/tsan/halt_on_error.cc new file mode 100644 index 00000000000..fddaffff29a --- /dev/null +++ b/compiler-rt/test/tsan/halt_on_error.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS halt_on_error=1" not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int X; + +void *Thread(void *x) { + X = 42; + return 0; +} + +int main() { + fprintf(stderr, "BEFORE\n"); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + fprintf(stderr, "AFTER\n"); + return 0; +} + +// CHECK: BEFORE +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: AFTER + diff --git a/compiler-rt/test/tsan/heap_race.cc b/compiler-rt/test/tsan/heap_race.cc new file mode 100644 index 00000000000..cc2c1fee532 --- /dev/null +++ b/compiler-rt/test/tsan/heap_race.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> + +void *Thread(void *a) { + ((int*)a)[0]++; + return NULL; +} + +int main() { + int *p = new int(42); + pthread_t t; + pthread_create(&t, NULL, Thread, p); + p[0]++; + pthread_join(t, NULL); + delete p; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/ignore_free.cc b/compiler-rt/test/tsan/ignore_free.cc new file mode 100644 index 00000000000..60369cc1baa --- /dev/null +++ b/compiler-rt/test/tsan/ignore_free.cc @@ -0,0 +1,35 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +void *Thread(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + int *p = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, p); + sleep(1); + AnnotateIgnoreReadsBegin(__FILE__, __LINE__); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + free(p); + AnnotateIgnoreReadsEnd(__FILE__, __LINE__); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + pthread_join(t, 0); + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/compiler-rt/test/tsan/ignore_lib0.cc b/compiler-rt/test/tsan/ignore_lib0.cc new file mode 100644 index 00000000000..ea0f061e609 --- /dev/null +++ b/compiler-rt/test/tsan/ignore_lib0.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib0.so +// RUN: %clangxx_tsan -O1 %s -L%T -lignore_lib0 -o %t +// RUN: echo running w/o suppressions: +// RUN: LD_LIBRARY_PATH=%T not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: LD_LIBRARY_PATH=%T TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// Tests that interceptors coming from a library specified in called_from_lib +// suppression are ignored. + +#ifndef LIB + +extern "C" void libfunc(); + +int main() { + libfunc(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/compiler-rt/test/tsan/ignore_lib0.cc.supp b/compiler-rt/test/tsan/ignore_lib0.cc.supp new file mode 100644 index 00000000000..7728c926b7d --- /dev/null +++ b/compiler-rt/test/tsan/ignore_lib0.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib0.so + diff --git a/compiler-rt/test/tsan/ignore_lib1.cc b/compiler-rt/test/tsan/ignore_lib1.cc new file mode 100644 index 00000000000..c4f2e734413 --- /dev/null +++ b/compiler-rt/test/tsan/ignore_lib1.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: echo running w/o suppressions: +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// Tests that interceptors coming from a dynamically loaded library specified +// in called_from_lib suppression are ignored. + +#ifndef LIB + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib1.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + if (h == 0) + exit(printf("failed to load the library (%d)\n", errno)); + void (*f)() = (void(*)())dlsym(h, "libfunc"); + if (f == 0) + exit(printf("failed to find the func (%d)\n", errno)); + f(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/compiler-rt/test/tsan/ignore_lib1.cc.supp b/compiler-rt/test/tsan/ignore_lib1.cc.supp new file mode 100644 index 00000000000..9f4119ec0bc --- /dev/null +++ b/compiler-rt/test/tsan/ignore_lib1.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib1.so$ + diff --git a/compiler-rt/test/tsan/ignore_lib2.cc b/compiler-rt/test/tsan/ignore_lib2.cc new file mode 100644 index 00000000000..97f9419e4d8 --- /dev/null +++ b/compiler-rt/test/tsan/ignore_lib2.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_0.so +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s + +// Tests that called_from_lib suppression matched against 2 libraries +// causes program crash (this is not supported). + +#ifndef LIB + +#include <dlfcn.h> +#include <stdio.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib0 = std::string(dirname(argv[0])) + "/libignore_lib2_0.so"; + std::string lib1 = std::string(dirname(argv[0])) + "/libignore_lib2_1.so"; + dlopen(lib0.c_str(), RTLD_GLOBAL | RTLD_NOW); + dlopen(lib1.c_str(), RTLD_GLOBAL | RTLD_NOW); + fprintf(stderr, "OK\n"); +} + +#else // #ifdef LIB + +extern "C" void libfunc() { +} + +#endif // #ifdef LIB + +// CHECK: ThreadSanitizer: called_from_lib suppression 'ignore_lib2' is matched against 2 libraries +// CHECK-NOT: OK + diff --git a/compiler-rt/test/tsan/ignore_lib2.cc.supp b/compiler-rt/test/tsan/ignore_lib2.cc.supp new file mode 100644 index 00000000000..1419c71c67e --- /dev/null +++ b/compiler-rt/test/tsan/ignore_lib2.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib2 + diff --git a/compiler-rt/test/tsan/ignore_lib3.cc b/compiler-rt/test/tsan/ignore_lib3.cc new file mode 100644 index 00000000000..8f237fcc81f --- /dev/null +++ b/compiler-rt/test/tsan/ignore_lib3.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib3.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s + +// Tests that unloading of a library matched against called_from_lib suppression +// causes program crash (this is not supported). + +#ifndef LIB + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib3.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + dlclose(h); + fprintf(stderr, "OK\n"); +} + +#else // #ifdef LIB + +extern "C" void libfunc() { +} + +#endif // #ifdef LIB + +// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded +// CHECK-NOT: OK + diff --git a/compiler-rt/test/tsan/ignore_lib3.cc.supp b/compiler-rt/test/tsan/ignore_lib3.cc.supp new file mode 100644 index 00000000000..975dbcef99f --- /dev/null +++ b/compiler-rt/test/tsan/ignore_lib3.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib3.so + diff --git a/compiler-rt/test/tsan/ignore_lib_lib.h b/compiler-rt/test/tsan/ignore_lib_lib.h new file mode 100644 index 00000000000..2bfe84dfc0e --- /dev/null +++ b/compiler-rt/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/compiler-rt/test/tsan/ignore_malloc.cc b/compiler-rt/test/tsan/ignore_malloc.cc new file mode 100644 index 00000000000..63bd4241b59 --- /dev/null +++ b/compiler-rt/test/tsan/ignore_malloc.cc @@ -0,0 +1,38 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +int *g; + +void *Thread(void *a) { + int *p = 0; + while ((p = __atomic_load_n(&g, __ATOMIC_RELAXED)) == 0) + usleep(100); + *p = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + int *p = new int(0); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + __atomic_store_n(&g, p, __ATOMIC_RELAXED); + pthread_join(t, 0); + delete p; + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/compiler-rt/test/tsan/ignore_race.cc b/compiler-rt/test/tsan/ignore_race.cc new file mode 100644 index 00000000000..23d74d0ed84 --- /dev/null +++ b/compiler-rt/test/tsan/ignore_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l); +extern "C" void AnnotateIgnoreWritesEnd(const char *f, int l); +extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l); +extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l); + +void *Thread(void *x) { + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + AnnotateIgnoreReadsBegin(__FILE__, __LINE__); + Global = 42; + AnnotateIgnoreReadsEnd(__FILE__, __LINE__); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + Global = 43; + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/ignore_sync.cc b/compiler-rt/test/tsan/ignore_sync.cc new file mode 100644 index 00000000000..67f2d906d9c --- /dev/null +++ b/compiler-rt/test/tsan/ignore_sync.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" void AnnotateIgnoreSyncBegin(const char*, int); +extern "C" void AnnotateIgnoreSyncEnd(const char*, int); + +int Global; +pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER; + +void *Thread(void *x) { + AnnotateIgnoreSyncBegin(0, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + AnnotateIgnoreSyncEnd(0, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/compiler-rt/test/tsan/inlined_memcpy_race.cc b/compiler-rt/test/tsan/inlined_memcpy_race.cc new file mode 100644 index 00000000000..5dda36e4b9e --- /dev/null +++ b/compiler-rt/test/tsan/inlined_memcpy_race.cc @@ -0,0 +1,55 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int x[4], y[4], z[4]; + +void *MemCpyThread(void *a) { + memcpy((int*)a, z, 16); + return NULL; +} + +void *MemMoveThread(void *a) { + memmove((int*)a, z, 16); + return NULL; +} + +void *MemSetThread(void *a) { + sleep(1); + memset((int*)a, 0, 16); + return NULL; +} + +int main() { + pthread_t t[2]; + // Race on x between memcpy and memset + pthread_create(&t[0], NULL, MemCpyThread, x); + pthread_create(&t[1], NULL, MemSetThread, x); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + // Race on y between memmove and memset + pthread_create(&t[0], NULL, MemMoveThread, y); + pthread_create(&t[1], NULL, MemSetThread, y); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + + printf("PASS\n"); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 memset +// CHECK: #1 MemSetThread +// CHECK: Previous write +// CHECK: #0 memcpy +// CHECK: #1 MemCpyThread + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 memset +// CHECK: #1 MemSetThread +// CHECK: Previous write +// CHECK: #0 memmove +// CHECK: #1 MemMoveThread diff --git a/compiler-rt/test/tsan/interface_atomic_test.c b/compiler-rt/test/tsan/interface_atomic_test.c new file mode 100644 index 00000000000..7f274a0d1d4 --- /dev/null +++ b/compiler-rt/test/tsan/interface_atomic_test.c @@ -0,0 +1,16 @@ +// Test that we can include header with TSan atomic interface. +// RUN: %clang_tsan %s -o %t && %t | FileCheck %s +#include <sanitizer/tsan_interface_atomic.h> +#include <stdio.h> + +int main() { + __tsan_atomic32 a; + __tsan_atomic32_store(&a, 100, __tsan_memory_order_release); + int res = __tsan_atomic32_load(&a, __tsan_memory_order_acquire); + if (res == 100) { + // CHECK: PASS + printf("PASS\n"); + return 0; + } + return 1; +} diff --git a/compiler-rt/test/tsan/java.h b/compiler-rt/test/tsan/java.h new file mode 100644 index 00000000000..7aa0bca32ce --- /dev/null +++ b/compiler-rt/test/tsan/java.h @@ -0,0 +1,20 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" { +typedef unsigned long jptr; // NOLINT +void __tsan_java_preinit(const char *libjvm_path); +void __tsan_java_init(jptr heap_begin, jptr heap_size); +int __tsan_java_fini(); +void __tsan_java_alloc(jptr ptr, jptr size); +void __tsan_java_free(jptr ptr, jptr size); +void __tsan_java_move(jptr src, jptr dst, jptr size); +void __tsan_java_mutex_lock(jptr addr); +void __tsan_java_mutex_unlock(jptr addr); +void __tsan_java_mutex_read_lock(jptr addr); +void __tsan_java_mutex_read_unlock(jptr addr); +void __tsan_java_mutex_lock_rec(jptr addr, int rec); +int __tsan_java_mutex_unlock_rec(jptr addr); +} diff --git a/compiler-rt/test/tsan/java_alloc.cc b/compiler-rt/test/tsan/java_alloc.cc new file mode 100644 index 00000000000..4dbce70c31e --- /dev/null +++ b/compiler-rt/test/tsan/java_alloc.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" + +int const kHeapSize = 1024 * 1024; + +void stress(jptr addr) { + for (jptr sz = 8; sz <= 32; sz <<= 1) { + for (jptr i = 0; i < kHeapSize / 4 / sz; i++) { + __tsan_java_alloc(addr + i * sz, sz); + } + __tsan_java_move(addr, addr + kHeapSize / 2, kHeapSize / 4); + __tsan_java_free(addr + kHeapSize / 2, kHeapSize / 4); + } +} + +void *Thread(void *p) { + stress((jptr)p); + return 0; +} + +int main() { + jptr jheap = (jptr)malloc(kHeapSize); + __tsan_java_init(jheap, kHeapSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)(jheap + kHeapSize / 4)); + stress(jheap); + pthread_join(th, 0); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/java_lock.cc b/compiler-rt/test/tsan/java_lock.cc new file mode 100644 index 00000000000..d9db103504d --- /dev/null +++ b/compiler-rt/test/tsan/java_lock.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" +#include <unistd.h> + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 42; + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + lockaddr = (jptr)jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/java_lock_move.cc b/compiler-rt/test/tsan/java_lock_move.cc new file mode 100644 index 00000000000..48b5a5a88d3 --- /dev/null +++ b/compiler-rt/test/tsan/java_lock_move.cc @@ -0,0 +1,40 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" + +jptr varaddr; +jptr lockaddr; +jptr varaddr2; +jptr lockaddr2; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_lock(lockaddr2); + *(int*)varaddr2 = 42; + __tsan_java_mutex_unlock(lockaddr2); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 64; + int const kMove = 1024; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + lockaddr = (jptr)jheap + 46; + varaddr2 = varaddr + kMove; + lockaddr2 = lockaddr + kMove; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_move(varaddr, varaddr2, kBlockSize); + pthread_join(th, 0); + __tsan_java_free(varaddr2, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/java_lock_rec.cc b/compiler-rt/test/tsan/java_lock_rec.cc new file mode 100644 index 00000000000..5cc80d4a33e --- /dev/null +++ b/compiler-rt/test/tsan/java_lock_rec.cc @@ -0,0 +1,54 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" +#include <unistd.h> + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 42; + int rec = __tsan_java_mutex_unlock_rec(lockaddr); + if (rec != 2) { + printf("FAILED 0 rec=%d\n", rec); + exit(1); + } + sleep(2); + __tsan_java_mutex_lock_rec(lockaddr, rec); + if (*(int*)varaddr != 43) { + printf("FAILED 3 var=%d\n", *(int*)varaddr); + exit(1); + } + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + *(int*)varaddr = 0; + lockaddr = (jptr)jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + sleep(1); + __tsan_java_mutex_lock(lockaddr); + if (*(int*)varaddr != 42) { + printf("FAILED 1 var=%d\n", *(int*)varaddr); + exit(1); + } + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED diff --git a/compiler-rt/test/tsan/java_lock_rec_race.cc b/compiler-rt/test/tsan/java_lock_rec_race.cc new file mode 100644 index 00000000000..a868e260c86 --- /dev/null +++ b/compiler-rt/test/tsan/java_lock_rec_race.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include "java.h" +#include <unistd.h> + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + int rec = __tsan_java_mutex_unlock_rec(lockaddr); + if (rec != 3) { + printf("FAILED 0 rec=%d\n", rec); + exit(1); + } + *(int*)varaddr = 42; + sleep(2); + __tsan_java_mutex_lock_rec(lockaddr, rec); + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + *(int*)varaddr = 0; + lockaddr = (jptr)jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + sleep(1); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED diff --git a/compiler-rt/test/tsan/java_race.cc b/compiler-rt/test/tsan/java_race.cc new file mode 100644 index 00000000000..4841a7db0a9 --- /dev/null +++ b/compiler-rt/test/tsan/java_race.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include "java.h" + +void *Thread(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + pthread_t th; + pthread_create(&th, 0, Thread, jheap); + *(int*)jheap = 43; + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/java_race_move.cc b/compiler-rt/test/tsan/java_race_move.cc new file mode 100644 index 00000000000..6da8a106483 --- /dev/null +++ b/compiler-rt/test/tsan/java_race_move.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include "java.h" + +jptr varaddr; +jptr varaddr2; + +void *Thread(void *p) { + sleep(1); + *(int*)varaddr2 = 42; + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 64; + int const kMove = 1024; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap + 16; + varaddr2 = varaddr + kMove; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + *(int*)varaddr = 43; + __tsan_java_move(varaddr, varaddr2, kBlockSize); + pthread_join(th, 0); + __tsan_java_free(varaddr2, kBlockSize); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/java_rwlock.cc b/compiler-rt/test/tsan/java_rwlock.cc new file mode 100644 index 00000000000..d1f38733ba0 --- /dev/null +++ b/compiler-rt/test/tsan/java_rwlock.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" +#include <unistd.h> + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_read_lock(lockaddr); + *(int*)varaddr = 42; + __tsan_java_mutex_read_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + lockaddr = (jptr)jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/lit.cfg b/compiler-rt/test/tsan/lit.cfg new file mode 100644 index 00000000000..b437dafa460 --- /dev/null +++ b/compiler-rt/test/tsan/lit.cfg @@ -0,0 +1,47 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'ThreadSanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup environment variables for running ThreadSanitizer. +tsan_options = "atexit_sleep_ms=0" + +config.environment['TSAN_OPTIONS'] = tsan_options + +# Setup default compiler flags used with -fsanitize=thread option. +# FIXME: Review the set of required flags and check if it can be reduced. +clang_tsan_cflags = ("-fsanitize=thread " + + "-g " + + "-Wall " + + "-lpthread " + + "-ldl " + + "-m64 ") +clang_tsan_cxxflags = "--driver-mode=g++ " + clang_tsan_cflags +config.substitutions.append( ("%clangxx_tsan ", (" " + config.clang + " " + + clang_tsan_cxxflags + " ")) ) +config.substitutions.append( ("%clang_tsan ", (" " + config.clang + " " + + clang_tsan_cflags + " ")) ) + +# Define CHECK-%os to check for OS-dependent output. +config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# ThreadSanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/compiler-rt/test/tsan/lit.site.cfg.in b/compiler-rt/test/tsan/lit.site.cfg.in new file mode 100644 index 00000000000..aebdd5ebc1f --- /dev/null +++ b/compiler-rt/test/tsan/lit.site.cfg.in @@ -0,0 +1,8 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/compiler-rt/test/tsan/load_shared_lib.cc b/compiler-rt/test/tsan/load_shared_lib.cc new file mode 100644 index 00000000000..d60cd5700a8 --- /dev/null +++ b/compiler-rt/test/tsan/load_shared_lib.cc @@ -0,0 +1,44 @@ +// Check that if the list of shared libraries changes between the two race +// reports, the second report occurring in a new shared library is still +// symbolized correctly. + +// RUN: %clangxx_tsan -O1 %p/SharedLibs/load_shared_lib-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> + +#include <string> + +int GLOB = 0; + +void *write_glob(void *unused) { + GLOB++; + return NULL; +} + +void race_two_threads(void *(*access_callback)(void *unused)) { + pthread_t t1, t2; + pthread_create(&t1, NULL, access_callback, NULL); + pthread_create(&t2, NULL, access_callback, NULL); + pthread_join(t1, NULL); + pthread_join(t2, NULL); +} + +int main(int argc, char *argv[]) { + std::string path = std::string(argv[0]) + std::string("-so.so"); + race_two_threads(write_glob); + // CHECK: write_glob + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + void *(*write_from_so)(void *unused); + *(void **)&write_from_so = dlsym(lib, "write_from_so"); + race_two_threads(write_from_so); + // CHECK: write_from_so + return 0; +} diff --git a/compiler-rt/test/tsan/longjmp.cc b/compiler-rt/test/tsan/longjmp.cc new file mode 100644 index 00000000000..d9ca4ca5e6e --- /dev/null +++ b/compiler-rt/test/tsan/longjmp.cc @@ -0,0 +1,22 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +int foo(jmp_buf env) { + longjmp(env, 42); +} + +int main() { + jmp_buf env; + if (setjmp(env) == 42) { + printf("JUMPED\n"); + return 0; + } + foo(env); + printf("FAILED\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK: JUMPED diff --git a/compiler-rt/test/tsan/longjmp2.cc b/compiler-rt/test/tsan/longjmp2.cc new file mode 100644 index 00000000000..0d551fa19d9 --- /dev/null +++ b/compiler-rt/test/tsan/longjmp2.cc @@ -0,0 +1,24 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +int foo(sigjmp_buf env) { + printf("env=%p\n", env); + siglongjmp(env, 42); +} + +int main() { + sigjmp_buf env; + printf("env=%p\n", env); + if (sigsetjmp(env, 1) == 42) { + printf("JUMPED\n"); + return 0; + } + foo(env); + printf("FAILED\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK: JUMPED diff --git a/compiler-rt/test/tsan/longjmp3.cc b/compiler-rt/test/tsan/longjmp3.cc new file mode 100644 index 00000000000..ae2cfd05fe1 --- /dev/null +++ b/compiler-rt/test/tsan/longjmp3.cc @@ -0,0 +1,48 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +void bar(jmp_buf env) { + volatile int x = 42; + longjmp(env, 42); + x++; +} + +void foo(jmp_buf env) { + volatile int x = 42; + bar(env); + x++; +} + +void badguy() { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + pthread_mutex_destroy(&mtx); +} + +void mymain() { + jmp_buf env; + if (setjmp(env) == 42) { + badguy(); + return; + } + foo(env); + printf("FAILED\n"); +} + +int main() { + volatile int x = 42; + mymain(); + return x; +} + +// CHECK-NOT: FAILED +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 badguy +// CHECK: #2 mymain +// CHECK: #3 main + diff --git a/compiler-rt/test/tsan/longjmp4.cc b/compiler-rt/test/tsan/longjmp4.cc new file mode 100644 index 00000000000..6b0526ef3a6 --- /dev/null +++ b/compiler-rt/test/tsan/longjmp4.cc @@ -0,0 +1,51 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#include <string.h> + +void bar(jmp_buf env) { + volatile int x = 42; + jmp_buf env2; + memcpy(env2, env, sizeof(jmp_buf)); + longjmp(env2, 42); + x++; +} + +void foo(jmp_buf env) { + volatile int x = 42; + bar(env); + x++; +} + +void badguy() { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + pthread_mutex_destroy(&mtx); +} + +void mymain() { + jmp_buf env; + if (setjmp(env) == 42) { + badguy(); + return; + } + foo(env); + printf("FAILED\n"); +} + +int main() { + volatile int x = 42; + mymain(); + return x; +} + +// CHECK-NOT: FAILED +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 badguy +// CHECK: #2 mymain +// CHECK: #3 main + diff --git a/compiler-rt/test/tsan/malloc_hook.cc b/compiler-rt/test/tsan/malloc_hook.cc new file mode 100644 index 00000000000..82eb6900efd --- /dev/null +++ b/compiler-rt/test/tsan/malloc_hook.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> + +static int malloc_count; +static int free_count; + +extern "C" { +void __tsan_malloc_hook(void *ptr, size_t size) { + (void)ptr; + (void)size; + __sync_fetch_and_add(&malloc_count, 1); +} + +void __tsan_free_hook(void *ptr) { + (void)ptr; + __sync_fetch_and_add(&free_count, 1); +} +} + +void *Thread1(void *x) { + ((int*)x)[0]++; + return 0; +} + +void *Thread2(void *x) { + sleep(1); + ((int*)x)[0]++; + return 0; +} + +int main() { + int *x = new int; + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, x); + pthread_create(&t[1], 0, Thread2, x); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + delete x; + if (malloc_count == 0 || free_count == 0) { + fprintf(stderr, "FAILED %d %d\n", malloc_count, free_count); + exit(1); + } + fprintf(stderr, "DONE\n"); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED +// CHECK: DONE diff --git a/compiler-rt/test/tsan/malloc_overflow.cc b/compiler-rt/test/tsan/malloc_overflow.cc new file mode 100644 index 00000000000..afbebc8bec4 --- /dev/null +++ b/compiler-rt/test/tsan/malloc_overflow.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS=allocator_may_return_null=1 %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> + +int main() { + void *p = malloc((size_t)-1); + if (p != 0) + printf("FAIL malloc(-1) = %p\n", p); + p = malloc((size_t)-1 / 2); + if (p != 0) + printf("FAIL malloc(-1/2) = %p\n", p); + p = calloc((size_t)-1, (size_t)-1); + if (p != 0) + printf("FAIL calloc(-1, -1) = %p\n", p); + p = calloc((size_t)-1 / 2, (size_t)-1 / 2); + if (p != 0) + printf("FAIL calloc(-1/2, -1/2) = %p\n", p); + printf("OK\n"); +} + +// CHECK-NOT: FAIL +// CHECK-NOT: failed to allocate diff --git a/compiler-rt/test/tsan/malloc_stack.cc b/compiler-rt/test/tsan/malloc_stack.cc new file mode 100644 index 00000000000..3603497ef31 --- /dev/null +++ b/compiler-rt/test/tsan/malloc_stack.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +_Atomic(int*) p; + +void *thr(void *a) { + sleep(1); + int *pp = __c11_atomic_load(&p, __ATOMIC_RELAXED); + *pp = 42; + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, thr, p); + __c11_atomic_store(&p, new int, __ATOMIC_RELAXED); + pthread_join(th, 0); +} + +// CHECK: data race +// CHECK: Previous write +// CHECK: #0 operator new +// CHECK: Location is heap block +// CHECK: #0 operator new diff --git a/compiler-rt/test/tsan/memcpy_race.cc b/compiler-rt/test/tsan/memcpy_race.cc new file mode 100644 index 00000000000..8f39113674d --- /dev/null +++ b/compiler-rt/test/tsan/memcpy_race.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +char *data = new char[10]; +char *data1 = new char[10]; +char *data2 = new char[10]; + +void *Thread1(void *x) { + static volatile int size = 1; + memcpy(data+5, data1, size); + return NULL; +} + +void *Thread2(void *x) { + static volatile int size = 4; + sleep(1); + memcpy(data+3, data2, size); + return NULL; +} + +int main() { + fprintf(stderr, "addr=%p\n", &data[5]); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 at [[ADDR]] by thread T2: +// CHECK: #0 memcpy +// CHECK: #1 Thread2 +// CHECK: Previous write of size 1 at [[ADDR]] by thread T1: +// CHECK: #0 memcpy +// CHECK: #1 Thread1 diff --git a/compiler-rt/test/tsan/mop_with_offset.cc b/compiler-rt/test/tsan/mop_with_offset.cc new file mode 100644 index 00000000000..2b6a4ff50aa --- /dev/null +++ b/compiler-rt/test/tsan/mop_with_offset.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> + +void *Thread1(void *x) { + int *p = (int*)x; + p[0] = 1; + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + char *p = (char*)x; + p[2] = 1; + return NULL; +} + +int main() { + int *data = new int(42); + fprintf(stderr, "ptr1=%p\n", data); + fprintf(stderr, "ptr2=%p\n", (char*)data + 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, data); + pthread_create(&t[1], NULL, Thread2, data); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + delete data; +} + +// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] +// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 at [[PTR2]] by thread T2: +// CHECK: Previous write of size 4 at [[PTR1]] by thread T1: diff --git a/compiler-rt/test/tsan/mop_with_offset2.cc b/compiler-rt/test/tsan/mop_with_offset2.cc new file mode 100644 index 00000000000..037c4db5f52 --- /dev/null +++ b/compiler-rt/test/tsan/mop_with_offset2.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> + +void *Thread1(void *x) { + sleep(1); + int *p = (int*)x; + p[0] = 1; + return NULL; +} + +void *Thread2(void *x) { + char *p = (char*)x; + p[2] = 1; + return NULL; +} + +int main() { + int *data = new int(42); + fprintf(stderr, "ptr1=%p\n", data); + fprintf(stderr, "ptr2=%p\n", (char*)data + 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, data); + pthread_create(&t[1], NULL, Thread2, data); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + delete data; +} + +// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] +// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at [[PTR1]] by thread T1: +// CHECK: Previous write of size 1 at [[PTR2]] by thread T2: diff --git a/compiler-rt/test/tsan/mutex_cycle2.c b/compiler-rt/test/tsan/mutex_cycle2.c new file mode 100644 index 00000000000..cd1006d4760 --- /dev/null +++ b/compiler-rt/test/tsan/mutex_cycle2.c @@ -0,0 +1,25 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: TSAN_OPTIONS=detect_deadlocks=1 %t 2>&1 | FileCheck %s +#include <pthread.h> + +int main() { + pthread_mutex_t mu1, mu2; + pthread_mutex_init(&mu1, NULL); + pthread_mutex_init(&mu2, NULL); + + // mu1 => mu2 + pthread_mutex_lock(&mu1); + pthread_mutex_lock(&mu2); + pthread_mutex_unlock(&mu2); + pthread_mutex_unlock(&mu1); + + // mu2 => mu1 + pthread_mutex_lock(&mu2); + pthread_mutex_lock(&mu1); + // CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock) + pthread_mutex_unlock(&mu1); + pthread_mutex_unlock(&mu2); + + pthread_mutex_destroy(&mu1); + pthread_mutex_destroy(&mu2); +} diff --git a/compiler-rt/test/tsan/mutex_destroy_locked.cc b/compiler-rt/test/tsan/mutex_destroy_locked.cc new file mode 100644 index 00000000000..9b020d31b94 --- /dev/null +++ b/compiler-rt/test/tsan/mutex_destroy_locked.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int main() { + pthread_mutex_t m; + pthread_mutex_init(&m, 0); + pthread_mutex_lock(&m); + pthread_mutex_destroy(&m); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 main +// CHECK: and: +// CHECK: #0 pthread_mutex_lock +// CHECK: #1 main +// CHECK: Mutex {{.*}} created at: +// CHECK: #0 pthread_mutex_init +// CHECK: #1 main +// CHECK: SUMMARY: ThreadSanitizer: destroy of a locked mutex{{.*}}main diff --git a/compiler-rt/test/tsan/mutex_robust.cc b/compiler-rt/test/tsan/mutex_robust.cc new file mode 100644 index 00000000000..b826616076a --- /dev/null +++ b/compiler-rt/test/tsan/mutex_robust.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +pthread_mutex_t m; + +void *thr(void *p) { + pthread_mutex_lock(&m); + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_lock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a correct code, and tsan must not bark. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/compiler-rt/test/tsan/mutex_robust2.cc b/compiler-rt/test/tsan/mutex_robust2.cc new file mode 100644 index 00000000000..5bd7ff682d1 --- /dev/null +++ b/compiler-rt/test/tsan/mutex_robust2.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +pthread_mutex_t m; +int x; + +void *thr(void *p) { + pthread_mutex_lock(&m); + x = 42; + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_trylock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + x = 43; + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a false positive, tsan must not bark at the data race. +// But currently it does. +// CHECK-NOT: WARNING: ThreadSanitizer WARNING: double lock of mutex +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/compiler-rt/test/tsan/mutexset1.cc b/compiler-rt/test/tsan/mutexset1.cc new file mode 100644 index 00000000000..ca87a7ba047 --- /dev/null +++ b/compiler-rt/test/tsan/mutexset1.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx); + Global++; + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset1.cc:[[@LINE+1]] + pthread_mutex_init(&mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx); +} diff --git a/compiler-rt/test/tsan/mutexset2.cc b/compiler-rt/test/tsan/mutexset2.cc new file mode 100644 index 00000000000..9ccf952b005 --- /dev/null +++ b/compiler-rt/test/tsan/mutexset2.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx); + Global++; + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T2: + // CHECK: Previous write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset2.cc:[[@LINE+1]] + pthread_mutex_init(&mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx); +} diff --git a/compiler-rt/test/tsan/mutexset3.cc b/compiler-rt/test/tsan/mutexset3.cc new file mode 100644 index 00000000000..272ddafb3c4 --- /dev/null +++ b/compiler-rt/test/tsan/mutexset3.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + pthread_mutex_lock(&mtx2); + Global++; + pthread_mutex_unlock(&mtx2); + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+2]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/compiler-rt/test/tsan/mutexset4.cc b/compiler-rt/test/tsan/mutexset4.cc new file mode 100644 index 00000000000..be751fa92bf --- /dev/null +++ b/compiler-rt/test/tsan/mutexset4.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx1); + pthread_mutex_lock(&mtx2); + Global++; + pthread_mutex_unlock(&mtx2); + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T2: + // CHECK: Previous write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]): + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+2]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/compiler-rt/test/tsan/mutexset5.cc b/compiler-rt/test/tsan/mutexset5.cc new file mode 100644 index 00000000000..e013edb4656 --- /dev/null +++ b/compiler-rt/test/tsan/mutexset5.cc @@ -0,0 +1,46 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + Global++; + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_lock(&mtx2); + Global--; + pthread_mutex_unlock(&mtx2); + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2 + // CHECK: (mutexes: write [[M2:M[0-9]+]]): + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+5]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/compiler-rt/test/tsan/mutexset6.cc b/compiler-rt/test/tsan/mutexset6.cc new file mode 100644 index 00000000000..f5e6e66becf --- /dev/null +++ b/compiler-rt/test/tsan/mutexset6.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t mtx1; +pthread_spinlock_t mtx2; +pthread_rwlock_t mtx3; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + Global++; + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_lock(&mtx1); + pthread_mutex_unlock(&mtx1); + pthread_spin_lock(&mtx2); + pthread_rwlock_rdlock(&mtx3); + Global--; + pthread_spin_unlock(&mtx2); + pthread_rwlock_unlock(&mtx3); + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2 + // CHECK: (mutexes: write [[M2:M[0-9]+]], read [[M3:M[0-9]+]]): + // CHECK: Mutex [[M1]] created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+5]] + // CHECK: Mutex [[M2]] created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+4]] + // CHECK: Mutex [[M3]] created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+3]] + pthread_mutex_init(&mtx1, 0); + pthread_spin_init(&mtx2, 0); + pthread_rwlock_init(&mtx3, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_spin_destroy(&mtx2); + pthread_rwlock_destroy(&mtx3); +} diff --git a/compiler-rt/test/tsan/mutexset7.cc b/compiler-rt/test/tsan/mutexset7.cc new file mode 100644 index 00000000000..51451b21549 --- /dev/null +++ b/compiler-rt/test/tsan/mutexset7.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +__thread int huge[1024*1024]; + +void *Thread1(void *x) { + sleep(1); + Global++; + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + Global--; + pthread_mutex_unlock(&mtx); + pthread_mutex_destroy(&mtx); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T1: +// CHECK: Previous write of size 4 at {{.*}} by thread T2 +// CHECK: (mutexes: write [[M1:M[0-9]+]]): +// CHECK: Mutex [[M1]] is already destroyed +// CHECK-NOT: Mutex {{.*}} created at + diff --git a/compiler-rt/test/tsan/mutexset8.cc b/compiler-rt/test/tsan/mutexset8.cc new file mode 100644 index 00000000000..8822b050e93 --- /dev/null +++ b/compiler-rt/test/tsan/mutexset8.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; +pthread_mutex_t *mtx; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(mtx); + Global++; + pthread_mutex_unlock(mtx); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset8.cc + mtx = new pthread_mutex_t; + pthread_mutex_init(mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(mtx); + delete mtx; +} diff --git a/compiler-rt/test/tsan/oob_race.cc b/compiler-rt/test/tsan/oob_race.cc new file mode 100644 index 00000000000..c84e819cbe7 --- /dev/null +++ b/compiler-rt/test/tsan/oob_race.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +const long kOffset = 64*1024; + +void *Thread(void *p) { + sleep(1); + ((char*)p)[-kOffset] = 43; + return 0; +} + +int main() { + char *volatile p0 = new char[16]; + delete[] p0; + char *p = new char[32]; + pthread_t th; + pthread_create(&th, 0, Thread, p); + p[-kOffset] = 42; + pthread_join(th, 0); +} + +// Used to crash with CHECK failed. +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/compiler-rt/test/tsan/printf-1.c b/compiler-rt/test/tsan/printf-1.c new file mode 100644 index 00000000000..a76048e8596 --- /dev/null +++ b/compiler-rt/test/tsan/printf-1.c @@ -0,0 +1,16 @@ +// RUN: %clang_tsan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck %s +// RUN: %t 2>&1 | FileCheck %s + +#include <stdio.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + printf("%c %d %.3f %s\n", c, x, f, s); + return 0; + // Check that printf works fine under Tsan. + // CHECK: 0 12 1.239 34 +} diff --git a/compiler-rt/test/tsan/race_on_barrier.c b/compiler-rt/test/tsan/race_on_barrier.c new file mode 100644 index 00000000000..3c0199dec22 --- /dev/null +++ b/compiler-rt/test/tsan/race_on_barrier.c @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_barrier_t B; +int Global; + +void *Thread1(void *x) { + pthread_barrier_init(&B, 0, 2); + pthread_barrier_wait(&B); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_barrier_wait(&B); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + pthread_barrier_destroy(&B); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/race_on_barrier2.c b/compiler-rt/test/tsan/race_on_barrier2.c new file mode 100644 index 00000000000..62773d43e66 --- /dev/null +++ b/compiler-rt/test/tsan/race_on_barrier2.c @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_barrier_t B; +int Global; + +void *Thread1(void *x) { + if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD) + pthread_barrier_destroy(&B); + return NULL; +} + +void *Thread2(void *x) { + if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD) + pthread_barrier_destroy(&B); + return NULL; +} + +int main() { + pthread_barrier_init(&B, 0, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/race_on_heap.cc b/compiler-rt/test/tsan/race_on_heap.cc new file mode 100644 index 00000000000..a84c0de9655 --- /dev/null +++ b/compiler-rt/test/tsan/race_on_heap.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +void *Thread1(void *p) { + *(int*)p = 42; + return 0; +} + +void *Thread2(void *p) { + *(int*)p = 44; + return 0; +} + +void *alloc() { + return malloc(99); +} + +void *AllocThread(void* arg) { + return alloc(); +} + +int main() { + void *p = 0; + pthread_t t[2]; + pthread_create(&t[0], 0, AllocThread, 0); + pthread_join(t[0], &p); + fprintf(stderr, "addr=%p\n", p); + pthread_create(&t[0], 0, Thread1, (char*)p + 16); + pthread_create(&t[1], 0, Thread2, (char*)p + 16); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + return 0; +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// ... +// CHECK: Location is heap block of size 99 at [[ADDR]] allocated by thread T1: +// CHCEKL #0 malloc +// CHECK: #{{1|2}} alloc +// CHECK: #{{2|3}} AllocThread +// ... +// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at: +// CHECK: #0 pthread_create +// CHECK: #1 main diff --git a/compiler-rt/test/tsan/race_on_mutex.c b/compiler-rt/test/tsan/race_on_mutex.c new file mode 100644 index 00000000000..e6634141483 --- /dev/null +++ b/compiler-rt/test/tsan/race_on_mutex.c @@ -0,0 +1,42 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_mutex_t Mtx; +int Global; + +void *Thread1(void *x) { + pthread_mutex_init(&Mtx, 0); + pthread_mutex_lock(&Mtx); + Global = 42; + pthread_mutex_unlock(&Mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_mutex_lock(&Mtx); + Global = 43; + pthread_mutex_unlock(&Mtx); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&Mtx); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Atomic read of size 1 at {{.*}} by thread T2: +// CHECK-NEXT: #0 pthread_mutex_lock +// CHECK-NEXT: #1 Thread2{{.*}} {{.*}}race_on_mutex.c:20{{(:3)?}} ({{.*}}) +// CHECK: Previous write of size 1 at {{.*}} by thread T1: +// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}}) +// CHECK-NEXT: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}}) diff --git a/compiler-rt/test/tsan/race_on_mutex2.c b/compiler-rt/test/tsan/race_on_mutex2.c new file mode 100644 index 00000000000..80c395e1f9c --- /dev/null +++ b/compiler-rt/test/tsan/race_on_mutex2.c @@ -0,0 +1,24 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread(void *x) { + pthread_mutex_lock((pthread_mutex_t*)x); + pthread_mutex_unlock((pthread_mutex_t*)x); + return 0; +} + +int main() { + pthread_mutex_t Mtx; + pthread_mutex_init(&Mtx, 0); + pthread_t t; + pthread_create(&t, 0, Thread, &Mtx); + sleep(1); + pthread_mutex_destroy(&Mtx); + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/race_on_read.cc b/compiler-rt/test/tsan/race_on_read.cc new file mode 100644 index 00000000000..e870ff9e83c --- /dev/null +++ b/compiler-rt/test/tsan/race_on_read.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +int fd; +char buf; + +void *Thread(void *x) { + sleep(1); + read(fd, &buf, 1); + return NULL; +} + +int main() { + fd = open("/dev/random", O_RDONLY); + if (fd < 0) return 1; + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread, NULL); + pthread_create(&t[1], NULL, Thread, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + close(fd); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 +// CHECK: #0 read +// CHECK: Previous write of size 1 +// CHECK: #0 read diff --git a/compiler-rt/test/tsan/race_on_speculative_load.cc b/compiler-rt/test/tsan/race_on_speculative_load.cc new file mode 100644 index 00000000000..31f0e4f2a31 --- /dev/null +++ b/compiler-rt/test/tsan/race_on_speculative_load.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t | FileCheck %s +// Regtest for https://code.google.com/p/thread-sanitizer/issues/detail?id=40 +// This is a correct program and tsan should not report a race. +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> +int g; +__attribute__((noinline)) +int foo(int cond) { + if (cond) + return g; + return 0; +} +void *Thread1(void *p) { + long res = foo((long)p); + sleep(1); + return (void*) res; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + g = 1; + pthread_join(t, 0); + printf("PASS\n"); + // CHECK: PASS +} diff --git a/compiler-rt/test/tsan/race_on_write.cc b/compiler-rt/test/tsan/race_on_write.cc new file mode 100644 index 00000000000..8a56c8464b9 --- /dev/null +++ b/compiler-rt/test/tsan/race_on_write.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int fd; +char buf; + +void *Thread1(void *x) { + buf = 1; + sleep(1); + return NULL; +} + +void *Thread2(void *x) { + write(fd, &buf, 1); + return NULL; +} + +int main() { + fd = open("/dev/null", O_WRONLY); + if (fd < 0) return 1; + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + sleep(1); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + close(fd); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Read of size 1 +// CHECK: #0 write +// CHECK: Previous write of size 1 +// CHECK: #0 Thread1 diff --git a/compiler-rt/test/tsan/race_with_finished_thread.cc b/compiler-rt/test/tsan/race_with_finished_thread.cc new file mode 100644 index 00000000000..c713c67a398 --- /dev/null +++ b/compiler-rt/test/tsan/race_with_finished_thread.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +// Ensure that we can restore a stack of a finished thread. + +int g_data; + +void __attribute__((noinline)) foobar(int *p) { + *p = 42; +} + +void *Thread1(void *x) { + foobar(&g_data); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + g_data = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T2: +// CHECK: Previous write of size 4 at {{.*}} by thread T1: +// CHECK: #0 foobar +// CHECK: #1 Thread1 +// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at: +// CHECK: #0 pthread_create +// CHECK: #1 main diff --git a/compiler-rt/test/tsan/signal_errno.cc b/compiler-rt/test/tsan/signal_errno.cc new file mode 100644 index 00000000000..651f916ac0c --- /dev/null +++ b/compiler-rt/test/tsan/signal_errno.cc @@ -0,0 +1,50 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> + +pthread_t mainth; +volatile int done; + +static void MyHandler(int, siginfo_t *s, void *c) { + errno = 1; + done = 1; +} + +static void* sendsignal(void *p) { + sleep(1); + pthread_kill(mainth, SIGPROF); + return 0; +} + +static __attribute__((noinline)) void loop() { + while (done == 0) { + volatile char *p = (char*)malloc(1); + p[0] = 0; + free((void*)p); + pthread_yield(); + } +} + +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + loop(); + pthread_join(th, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: signal handler spoils errno +// CHECK: #0 MyHandler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_errno.cc +// CHECK: #1 loop +// CHECK: #2 main +// CHECK: SUMMARY: ThreadSanitizer: signal handler spoils errno{{.*}}MyHandler + diff --git a/compiler-rt/test/tsan/signal_malloc.cc b/compiler-rt/test/tsan/signal_malloc.cc new file mode 100644 index 00000000000..ef180b8a25b --- /dev/null +++ b/compiler-rt/test/tsan/signal_malloc.cc @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +static void handler(int, siginfo_t*, void*) { + // CHECK: WARNING: ThreadSanitizer: signal-unsafe call inside of a signal + // CHECK: #0 malloc + // CHECK: #{{(1|2)}} handler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_malloc.cc:[[@LINE+2]] + // CHECK: SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal{{.*}}handler + volatile char *p = (char*)malloc(1); + p[0] = 0; + free((void*)p); +} + +int main() { + struct sigaction act = {}; + act.sa_sigaction = &handler; + sigaction(SIGPROF, &act, 0); + kill(getpid(), SIGPROF); + sleep(1); + return 0; +} + diff --git a/compiler-rt/test/tsan/sigsuspend.cc b/compiler-rt/test/tsan/sigsuspend.cc new file mode 100644 index 00000000000..503bd5629ed --- /dev/null +++ b/compiler-rt/test/tsan/sigsuspend.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s + +// Always enable asserts. +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include <assert.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <stdio.h> + +static bool signal_handler_ran = false; + +void do_nothing_signal_handler(int signum) { + write(1, "HANDLER\n", 8); + signal_handler_ran = true; +} + +int main() { + const int kSignalToTest = SIGSYS; + assert(SIG_ERR != signal(kSignalToTest, do_nothing_signal_handler)); + sigset_t empty_set; + assert(0 == sigemptyset(&empty_set)); + sigset_t one_signal = empty_set; + assert(0 == sigaddset(&one_signal, kSignalToTest)); + sigset_t old_set; + assert(0 == sigprocmask(SIG_BLOCK, &one_signal, &old_set)); + raise(kSignalToTest); + assert(!signal_handler_ran); + sigset_t all_but_one; + assert(0 == sigfillset(&all_but_one)); + assert(0 == sigdelset(&all_but_one, kSignalToTest)); + sigsuspend(&all_but_one); + assert(signal_handler_ran); + + // Restore the original set. + assert(0 == sigprocmask(SIG_SETMASK, &old_set, NULL)); + printf("DONE"); +} + +// CHECK: HANDLER +// CHECK: DONE diff --git a/compiler-rt/test/tsan/simple_race.c b/compiler-rt/test/tsan/simple_race.c new file mode 100644 index 00000000000..80a83e01a29 --- /dev/null +++ b/compiler-rt/test/tsan/simple_race.c @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/simple_race.cc b/compiler-rt/test/tsan/simple_race.cc new file mode 100644 index 00000000000..47854cfd9a3 --- /dev/null +++ b/compiler-rt/test/tsan/simple_race.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int Global; + +void *Thread1(void *x) { + Global++; + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: SUMMARY: ThreadSanitizer: data race{{.*}}Thread diff --git a/compiler-rt/test/tsan/simple_stack.c b/compiler-rt/test/tsan/simple_stack.c new file mode 100644 index 00000000000..a447e288044 --- /dev/null +++ b/compiler-rt/test/tsan/simple_stack.c @@ -0,0 +1,66 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void __attribute__((noinline)) foo1() { + Global = 42; +} + +void __attribute__((noinline)) bar1() { + volatile int tmp = 42; (void)tmp; + foo1(); +} + +void __attribute__((noinline)) foo2() { + volatile int v = Global; (void)v; +} + +void __attribute__((noinline)) bar2() { + volatile int tmp = 42; (void)tmp; + foo2(); +} + +void *Thread1(void *x) { + sleep(1); + bar1(); + return NULL; +} + +void *Thread2(void *x) { + bar2(); + return NULL; +} + +void StartThread(pthread_t *t, void *(*f)(void*)) { + pthread_create(t, NULL, f, NULL); +} + +int main() { + pthread_t t[2]; + StartThread(&t[0], Thread1); + StartThread(&t[1], Thread2); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1: +// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:9{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:28{{(:3)?}} ({{.*}}) +// CHECK: Previous read of size 4 at {{.*}} by thread T2: +// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:26)?}} ({{.*}}) +// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:33{{(:3)?}} ({{.*}}) +// CHECK: Thread T1 (tid={{.*}}, running) created by main thread at: +// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}}) +// CHECK: Thread T2 ({{.*}}) created by main thread at: +// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}}) diff --git a/compiler-rt/test/tsan/simple_stack2.cc b/compiler-rt/test/tsan/simple_stack2.cc new file mode 100644 index 00000000000..7a034c4cd6e --- /dev/null +++ b/compiler-rt/test/tsan/simple_stack2.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void __attribute__((noinline)) foo1() { + Global = 42; +} + +void __attribute__((noinline)) bar1() { + volatile int tmp = 42; + int tmp2 = tmp; + (void)tmp2; + foo1(); +} + +void __attribute__((noinline)) foo2() { + volatile int tmp = Global; + int tmp2 = tmp; + (void)tmp2; +} + +void __attribute__((noinline)) bar2() { + volatile int tmp = 42; + int tmp2 = tmp; + (void)tmp2; + foo2(); +} + +void *Thread1(void *x) { + sleep(1); + bar1(); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + bar2(); + pthread_join(t, NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1: +// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}}) +// CHECK: Previous read of size 4 at {{.*}} by main thread: +// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:20{{(:28)?}} ({{.*}}) +// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:29{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:41{{(:3)?}} ({{.*}}) diff --git a/compiler-rt/test/tsan/sleep_sync.cc b/compiler-rt/test/tsan/sleep_sync.cc new file mode 100644 index 00000000000..217a52a097c --- /dev/null +++ b/compiler-rt/test/tsan/sleep_sync.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int X = 0; + +void MySleep() { + sleep(1); +} + +void *Thread(void *p) { + MySleep(); // Assume the main thread has done the write. + X = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// ... +// CHECK: As if synchronized via sleep: +// CHECK-NEXT: #0 sleep +// CHECK-NEXT: #1 MySleep +// CHECK-NEXT: #2 Thread diff --git a/compiler-rt/test/tsan/sleep_sync2.cc b/compiler-rt/test/tsan/sleep_sync2.cc new file mode 100644 index 00000000000..e22999279f9 --- /dev/null +++ b/compiler-rt/test/tsan/sleep_sync2.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int X = 0; + +void *Thread(void *p) { + X = 42; + return 0; +} + +int main() { + pthread_t t; + sleep(1); + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: As if synchronized via sleep diff --git a/compiler-rt/test/tsan/stack_race.cc b/compiler-rt/test/tsan/stack_race.cc new file mode 100644 index 00000000000..01ff2e83961 --- /dev/null +++ b/compiler-rt/test/tsan/stack_race.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread(void *a) { + sleep(1); + *(int*)a = 43; + return 0; +} + +int main() { + int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread, &Var); + Var = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is stack of main thread. + diff --git a/compiler-rt/test/tsan/stack_race2.cc b/compiler-rt/test/tsan/stack_race2.cc new file mode 100644 index 00000000000..577f12c95f2 --- /dev/null +++ b/compiler-rt/test/tsan/stack_race2.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread2(void *a) { + sleep(1); + *(int*)a = 43; + return 0; +} + +void *Thread(void *a) { + int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread2, &Var); + Var = 42; + pthread_join(t, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is stack of thread T1. + diff --git a/compiler-rt/test/tsan/static_init1.cc b/compiler-rt/test/tsan/static_init1.cc new file mode 100644 index 00000000000..4faf5bc5474 --- /dev/null +++ b/compiler-rt/test/tsan/static_init1.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +struct P { + int x; + int y; +}; + +void *Thread(void *x) { + static P p = {rand(), rand()}; + if (p.x > RAND_MAX || p.y > RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread, 0); + pthread_create(&t[1], 0, Thread, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/static_init2.cc b/compiler-rt/test/tsan/static_init2.cc new file mode 100644 index 00000000000..96ef821a752 --- /dev/null +++ b/compiler-rt/test/tsan/static_init2.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void foo(Cache *my) { + static Cache *c = my ? my : new Cache(rand()); + if (c->x >= RAND_MAX) + exit(1); +} + +void *Thread(void *x) { + foo(new Cache(rand())); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread, 0); + pthread_create(&t[1], 0, Thread, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/static_init3.cc b/compiler-rt/test/tsan/static_init3.cc new file mode 100644 index 00000000000..70a3c16878c --- /dev/null +++ b/compiler-rt/test/tsan/static_init3.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; +}; + +Cache g_cache; + +Cache *CreateCache() { + g_cache.x = rand(); + return &g_cache; +} + +_Atomic(Cache*) queue; + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + __c11_atomic_store(&queue, c, 0); + return 0; +} + +void *Thread2(void *x) { + Cache *c = 0; + for (;;) { + c = __c11_atomic_load(&queue, 0); + if (c) + break; + sched_yield(); + } + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/static_init4.cc b/compiler-rt/test/tsan/static_init4.cc new file mode 100644 index 00000000000..5ecc39926a2 --- /dev/null +++ b/compiler-rt/test/tsan/static_init4.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +int g_other; + +Cache *CreateCache() { + g_other = rand(); + return new Cache(rand()); +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x == g_other) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/static_init5.cc b/compiler-rt/test/tsan/static_init5.cc new file mode 100644 index 00000000000..1d0ed6d54ca --- /dev/null +++ b/compiler-rt/test/tsan/static_init5.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void *AsyncInit(void *p) { + return new Cache((int)(long)p); +} + +Cache *CreateCache() { + pthread_t t; + pthread_create(&t, 0, AsyncInit, (void*)(long)rand()); + void *res; + pthread_join(t, &res); + return (Cache*)res; +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/static_init6.cc b/compiler-rt/test/tsan/static_init6.cc new file mode 100644 index 00000000000..c9099f9b679 --- /dev/null +++ b/compiler-rt/test/tsan/static_init6.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -static-libstdc++ -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void *AsyncInit(void *p) { + return new Cache((int)(long)p); +} + +Cache *CreateCache() { + pthread_t t; + pthread_create(&t, 0, AsyncInit, (void*)(long)rand()); + void *res; + pthread_join(t, &res); + return (Cache*)res; +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/suppress_same_address.cc b/compiler-rt/test/tsan/suppress_same_address.cc new file mode 100644 index 00000000000..8fbf7b9ed61 --- /dev/null +++ b/compiler-rt/test/tsan/suppress_same_address.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +volatile int X; + +void *Thread1(void *x) { + sleep(1); + X = 42; + X = 66; + X = 78; + return 0; +} + +void *Thread2(void *x) { + X = 11; + X = 99; + X = 73; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Thread2(0); + pthread_join(t, 0); +} + +// CHECK: ThreadSanitizer: reported 1 warnings diff --git a/compiler-rt/test/tsan/suppress_same_stacks.cc b/compiler-rt/test/tsan/suppress_same_stacks.cc new file mode 100644 index 00000000000..f0ab8b30435 --- /dev/null +++ b/compiler-rt/test/tsan/suppress_same_stacks.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> + +volatile int N; // Prevent loop unrolling. +int **data; + +void *Thread1(void *x) { + for (int i = 0; i < N; i++) + data[i][0] = 42; + return 0; +} + +int main() { + N = 4; + data = new int*[N]; + for (int i = 0; i < N; i++) + data[i] = new int; + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Thread1(0); + pthread_join(t, 0); + for (int i = 0; i < N; i++) + delete data[i]; + delete[] data; +} + +// CHECK: ThreadSanitizer: reported 1 warnings diff --git a/compiler-rt/test/tsan/suppressions_global.cc b/compiler-rt/test/tsan/suppressions_global.cc new file mode 100644 index 00000000000..181cb56cf2e --- /dev/null +++ b/compiler-rt/test/tsan/suppressions_global.cc @@ -0,0 +1,29 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +int RacyGlobal; + +void *Thread1(void *x) { + RacyGlobal = 42; + return NULL; +} + +void *Thread2(void *x) { + RacyGlobal = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/compiler-rt/test/tsan/suppressions_global.cc.supp b/compiler-rt/test/tsan/suppressions_global.cc.supp new file mode 100644 index 00000000000..5fa8a2e43a9 --- /dev/null +++ b/compiler-rt/test/tsan/suppressions_global.cc.supp @@ -0,0 +1,2 @@ +race:RacyGlobal + diff --git a/compiler-rt/test/tsan/suppressions_race.cc b/compiler-rt/test/tsan/suppressions_race.cc new file mode 100644 index 00000000000..c88e69bec6a --- /dev/null +++ b/compiler-rt/test/tsan/suppressions_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/compiler-rt/test/tsan/suppressions_race.cc.supp b/compiler-rt/test/tsan/suppressions_race.cc.supp new file mode 100644 index 00000000000..cbdba76ea93 --- /dev/null +++ b/compiler-rt/test/tsan/suppressions_race.cc.supp @@ -0,0 +1,2 @@ +race:Thread1 + diff --git a/compiler-rt/test/tsan/suppressions_race2.cc b/compiler-rt/test/tsan/suppressions_race2.cc new file mode 100644 index 00000000000..57146f96a42 --- /dev/null +++ b/compiler-rt/test/tsan/suppressions_race2.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/compiler-rt/test/tsan/suppressions_race2.cc.supp b/compiler-rt/test/tsan/suppressions_race2.cc.supp new file mode 100644 index 00000000000..b3c4dbc5936 --- /dev/null +++ b/compiler-rt/test/tsan/suppressions_race2.cc.supp @@ -0,0 +1,2 @@ +race:Thread2 + diff --git a/compiler-rt/test/tsan/test_output.sh b/compiler-rt/test/tsan/test_output.sh new file mode 100755 index 00000000000..8a15a673862 --- /dev/null +++ b/compiler-rt/test/tsan/test_output.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +ulimit -s 8192 +set -e # fail on any error + +HERE=$(dirname $0) +TSAN_DIR=$(dirname $0)/../../lib/tsan +BLACKLIST=$HERE/Helpers/blacklist.txt + +# Assume clang and clang++ are in path. +: ${CC:=clang} +: ${CXX:=clang++} +: ${FILECHECK:=FileCheck} + +# TODO: add testing for all of -O0...-O3 +CFLAGS="-fsanitize=thread -fsanitize-blacklist=$BLACKLIST -fPIE -O1 -g -Wall" +LDFLAGS="-pie -lpthread -ldl -lrt -lm -Wl,--whole-archive $TSAN_DIR/rtl/libtsan.a -Wl,--no-whole-archive" + +test_file() { + SRC=$1 + COMPILER=$2 + echo ----- TESTING $(basename $1) + OBJ=$SRC.o + EXE=$SRC.exe + $COMPILER $SRC $CFLAGS -c -o $OBJ + $COMPILER $OBJ $LDFLAGS -o $EXE + RES=$($EXE 2>&1 || true) + printf "%s\n" "$RES" | $FILECHECK $SRC + if [ "$3" == "" ]; then + rm -f $EXE $OBJ + fi +} + +if [ "$1" == "" ]; then + for c in $HERE/*.{c,cc}; do + if [[ $c == */failing_* ]]; then + echo SKIPPING FAILING TEST $c + continue + fi + if [[ $c == */load_shared_lib.cc ]]; then + echo TEST $c is not supported + continue + fi + if [ "`grep "TSAN_OPTIONS" $c`" ]; then + echo SKIPPING $c -- requires TSAN_OPTIONS + continue + fi + COMPILER=$CXX + case $c in + *.c) COMPILER=$CC + esac + test_file $c $COMPILER & + done + for job in `jobs -p`; do + wait $job || exit 1 + done +else + test_file $HERE/$1 $CXX "DUMP" +fi diff --git a/compiler-rt/test/tsan/thread_end_with_ignore.cc b/compiler-rt/test/tsan/thread_end_with_ignore.cc new file mode 100644 index 00000000000..8bb52e3e8c5 --- /dev/null +++ b/compiler-rt/test/tsan/thread_end_with_ignore.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l); + +void *Thread(void *x) { + AnnotateIgnoreReadsBegin("", 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: ThreadSanitizer: thread T1 finished with ignores enabled, created at: +// CHECK: #0 pthread_create +// CHECK: #1 main +// CHECK: Ignore was enabled at: +// CHECK: #0 AnnotateIgnoreReadsBegin +// CHECK: #1 Thread + diff --git a/compiler-rt/test/tsan/thread_end_with_ignore2.cc b/compiler-rt/test/tsan/thread_end_with_ignore2.cc new file mode 100644 index 00000000000..224599c95d7 --- /dev/null +++ b/compiler-rt/test/tsan/thread_end_with_ignore2.cc @@ -0,0 +1,12 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l); + +int main() { + AnnotateIgnoreWritesBegin("", 0); +} + +// CHECK: ThreadSanitizer: main thread finished with ignores enabled +// CHECK: Ignore was enabled at: +// CHECK: #0 AnnotateIgnoreWritesBegin +// CHECK: #1 main + diff --git a/compiler-rt/test/tsan/thread_end_with_ignore3.cc b/compiler-rt/test/tsan/thread_end_with_ignore3.cc new file mode 100644 index 00000000000..bf46eb89b5b --- /dev/null +++ b/compiler-rt/test/tsan/thread_end_with_ignore3.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l); +extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l); + +int main() { + AnnotateIgnoreReadsBegin("", 0); + AnnotateIgnoreReadsBegin("", 0); + AnnotateIgnoreReadsEnd("", 0); + AnnotateIgnoreReadsEnd("", 0); + AnnotateIgnoreReadsBegin("", 0); + AnnotateIgnoreReadsBegin("", 0); + AnnotateIgnoreReadsEnd("", 0); +} + +// CHECK: ThreadSanitizer: main thread finished with ignores enabled +// CHECK: Ignore was enabled at: +// CHECK: #0 AnnotateIgnoreReadsBegin +// CHECK: #1 main {{.*}}thread_end_with_ignore3.cc:10 +// CHECK: Ignore was enabled at: +// CHECK: #0 AnnotateIgnoreReadsBegin +// CHECK: #1 main {{.*}}thread_end_with_ignore3.cc:11 + diff --git a/compiler-rt/test/tsan/thread_leak.c b/compiler-rt/test/tsan/thread_leak.c new file mode 100644 index 00000000000..c5e669e5d99 --- /dev/null +++ b/compiler-rt/test/tsan/thread_leak.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/compiler-rt/test/tsan/thread_leak2.c b/compiler-rt/test/tsan/thread_leak2.c new file mode 100644 index 00000000000..39f6b5e02e3 --- /dev/null +++ b/compiler-rt/test/tsan/thread_leak2.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_detach(t); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/compiler-rt/test/tsan/thread_leak3.c b/compiler-rt/test/tsan/thread_leak3.c new file mode 100644 index 00000000000..5f447dbdbdf --- /dev/null +++ b/compiler-rt/test/tsan/thread_leak3.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: thread leak +// CHECK: SUMMARY: ThreadSanitizer: thread leak{{.*}}main diff --git a/compiler-rt/test/tsan/thread_leak4.c b/compiler-rt/test/tsan/thread_leak4.c new file mode 100644 index 00000000000..f9fad0360d3 --- /dev/null +++ b/compiler-rt/test/tsan/thread_leak4.c @@ -0,0 +1,18 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> + +void *Thread(void *x) { + sleep(10); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/compiler-rt/test/tsan/thread_leak5.c b/compiler-rt/test/tsan/thread_leak5.c new file mode 100644 index 00000000000..c19d6177fe1 --- /dev/null +++ b/compiler-rt/test/tsan/thread_leak5.c @@ -0,0 +1,20 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + volatile int N = 5; // prevent loop unrolling + for (int i = 0; i < N; i++) { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + } + sleep(1); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: thread leak +// CHECK: And 4 more similar thread leaks diff --git a/compiler-rt/test/tsan/thread_name.cc b/compiler-rt/test/tsan/thread_name.cc new file mode 100644 index 00000000000..646ab583624 --- /dev/null +++ b/compiler-rt/test/tsan/thread_name.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" void AnnotateThreadName(const char *f, int l, const char *name); + +int Global; + +void *Thread1(void *x) { + sleep(1); + AnnotateThreadName(__FILE__, __LINE__, "Thread1"); + Global++; + return NULL; +} + +void *Thread2(void *x) { +#if SANITIZER_LINUX && __GLIBC_PREREQ(2, 12) + pthread_setname_np(pthread_self(), "Thread2"); +#else + AnnotateThreadName(__FILE__, __LINE__, "Thread2"); +#endif + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T1 'Thread1' +// CHECK: Thread T2 'Thread2' + diff --git a/compiler-rt/test/tsan/thread_name2.cc b/compiler-rt/test/tsan/thread_name2.cc new file mode 100644 index 00000000000..8c5cb741f61 --- /dev/null +++ b/compiler-rt/test/tsan/thread_name2.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global++; + return 0; +} + +void *Thread2(void *x) { + pthread_setname_np(pthread_self(), "foobar2"); + Global--; + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_setname_np(t[0], "foobar1"); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T1 'foobar1' +// CHECK: Thread T2 'foobar2' + diff --git a/compiler-rt/test/tsan/tiny_race.c b/compiler-rt/test/tsan/tiny_race.c new file mode 100644 index 00000000000..f77e1606c1d --- /dev/null +++ b/compiler-rt/test/tsan/tiny_race.c @@ -0,0 +1,21 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return x; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Global = 43; + pthread_join(t, 0); + return Global; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/tls_race.cc b/compiler-rt/test/tsan/tls_race.cc new file mode 100644 index 00000000000..7b1f38d62de --- /dev/null +++ b/compiler-rt/test/tsan/tls_race.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread(void *a) { + sleep(1); + *(int*)a = 43; + return 0; +} + +int main() { + static __thread int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread, &Var); + Var = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is TLS of main thread. diff --git a/compiler-rt/test/tsan/tls_race2.cc b/compiler-rt/test/tsan/tls_race2.cc new file mode 100644 index 00000000000..2cf44ae54a8 --- /dev/null +++ b/compiler-rt/test/tsan/tls_race2.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +void *Thread2(void *a) { + sleep(1); + *(int*)a = 43; + return 0; +} + +void *Thread(void *a) { + static __thread int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread2, &Var); + Var = 42; + pthread_join(t, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is TLS of thread T1. + diff --git a/compiler-rt/test/tsan/tsan-vs-gvn.cc b/compiler-rt/test/tsan/tsan-vs-gvn.cc new file mode 100644 index 00000000000..40ae724b78e --- /dev/null +++ b/compiler-rt/test/tsan/tsan-vs-gvn.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O3 %s -o %t && %t 2>&1 | FileCheck %s +// +// Check that load widening is not tsan-hostile. +#include <pthread.h> +#include <stdio.h> +#include <string.h> + +struct { + int i; + char c1, c2, c3, c4; +} S; + +int G; + +void *Thread1(void *x) { + G = S.c1 + S.c3; + return NULL; +} + +void *Thread2(void *x) { + S.c2 = 1; + return NULL; +} + +int main() { + pthread_t t[2]; + memset(&S, 123, sizeof(S)); + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: PASS diff --git a/compiler-rt/test/tsan/unaligned_norace.cc b/compiler-rt/test/tsan/unaligned_norace.cc new file mode 100644 index 00000000000..792224b8012 --- /dev/null +++ b/compiler-rt/test/tsan/unaligned_norace.cc @@ -0,0 +1,84 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +uint64_t objs[8*3*3*2][3]; + +extern "C" { +uint16_t __tsan_unaligned_read2(void *addr); +uint32_t __tsan_unaligned_read4(void *addr); +uint64_t __tsan_unaligned_read8(void *addr); +void __tsan_unaligned_write2(void *addr, uint16_t v); +void __tsan_unaligned_write4(void *addr, uint32_t v); +void __tsan_unaligned_write8(void *addr, uint64_t v); +} + +static void access(char *p, int sz, int rw) { + if (rw) { + switch (sz) { + case 0: __tsan_unaligned_write2(p, 0); break; + case 1: __tsan_unaligned_write4(p, 0); break; + case 2: __tsan_unaligned_write8(p, 0); break; + default: exit(1); + } + } else { + switch (sz) { + case 0: __tsan_unaligned_read2(p); break; + case 1: __tsan_unaligned_read4(p); break; + case 2: __tsan_unaligned_read8(p); break; + default: exit(1); + } + } +} + +static int accesssize(int sz) { + switch (sz) { + case 0: return 2; + case 1: return 4; + case 2: return 8; + } + exit(1); +} + +void Test(bool main) { + uint64_t *obj = objs[0]; + for (int off = 0; off < 8; off++) { + for (int sz1 = 0; sz1 < 3; sz1++) { + for (int sz2 = 0; sz2 < 3; sz2++) { + for (int rw = 0; rw < 2; rw++) { + char *p = (char*)obj + off; + if (main) { + // printf("thr=%d off=%d sz1=%d sz2=%d rw=%d p=%p\n", + // main, off, sz1, sz2, rw, p); + access(p, sz1, true); + } else { + p += accesssize(sz1); + // printf("thr=%d off=%d sz1=%d sz2=%d rw=%d p=%p\n", + // main, off, sz1, sz2, rw, p); + access(p, sz2, rw); + } + obj += 3; + } + } + } + } +} + +void *Thread(void *p) { + (void)p; + Test(false); + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, Thread, 0); + Test(true); + pthread_join(th, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: OK diff --git a/compiler-rt/test/tsan/unaligned_race.cc b/compiler-rt/test/tsan/unaligned_race.cc new file mode 100644 index 00000000000..3534ff5d6e0 --- /dev/null +++ b/compiler-rt/test/tsan/unaligned_race.cc @@ -0,0 +1,139 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> + +#define NOINLINE __attribute__((noinline)) + +volatile uint64_t objs[8*2*(2 + 4 + 8)][2]; + +extern "C" { +uint16_t __sanitizer_unaligned_load16(volatile void *addr); +uint32_t __sanitizer_unaligned_load32(volatile void *addr); +uint64_t __sanitizer_unaligned_load64(volatile void *addr); +void __sanitizer_unaligned_store16(volatile void *addr, uint16_t v); +void __sanitizer_unaligned_store32(volatile void *addr, uint32_t v); +void __sanitizer_unaligned_store64(volatile void *addr, uint64_t v); +} + +// All this mess is to generate unique stack for each race, +// otherwise tsan will suppress similar stacks. + +static NOINLINE void access(volatile char *p, int sz, int rw) { + if (rw) { + switch (sz) { + case 0: __sanitizer_unaligned_store16(p, 0); break; + case 1: __sanitizer_unaligned_store32(p, 0); break; + case 2: __sanitizer_unaligned_store64(p, 0); break; + default: exit(1); + } + } else { + switch (sz) { + case 0: __sanitizer_unaligned_load16(p); break; + case 1: __sanitizer_unaligned_load32(p); break; + case 2: __sanitizer_unaligned_load64(p); break; + default: exit(1); + } + } +} + +static int accesssize(int sz) { + switch (sz) { + case 0: return 2; + case 1: return 4; + case 2: return 8; + } + exit(1); +} + +template<int off, int off2> +static NOINLINE void access3(bool main, int sz1, bool rw, volatile char *p) { + p += off; + if (main) { + access(p, sz1, true); + } else { + p += off2; + if (rw) { + *p = 42; + } else { + if (*p == 42) + printf("bingo!\n"); + } + } +} + +template<int off> +static NOINLINE void +access2(bool main, int sz1, int off2, bool rw, volatile char *obj) { + if (off2 == 0) + access3<off, 0>(main, sz1, rw, obj); + else if (off2 == 1) + access3<off, 1>(main, sz1, rw, obj); + else if (off2 == 2) + access3<off, 2>(main, sz1, rw, obj); + else if (off2 == 3) + access3<off, 3>(main, sz1, rw, obj); + else if (off2 == 4) + access3<off, 4>(main, sz1, rw, obj); + else if (off2 == 5) + access3<off, 5>(main, sz1, rw, obj); + else if (off2 == 6) + access3<off, 6>(main, sz1, rw, obj); + else if (off2 == 7) + access3<off, 7>(main, sz1, rw, obj); +} + +static NOINLINE void +access1(bool main, int off, int sz1, int off2, bool rw, char *obj) { + if (off == 0) + access2<0>(main, sz1, off2, rw, obj); + else if (off == 1) + access2<1>(main, sz1, off2, rw, obj); + else if (off == 2) + access2<2>(main, sz1, off2, rw, obj); + else if (off == 3) + access2<3>(main, sz1, off2, rw, obj); + else if (off == 4) + access2<4>(main, sz1, off2, rw, obj); + else if (off == 5) + access2<5>(main, sz1, off2, rw, obj); + else if (off == 6) + access2<6>(main, sz1, off2, rw, obj); + else if (off == 7) + access2<7>(main, sz1, off2, rw, obj); +} + +NOINLINE void Test(bool main) { + volatile uint64_t *obj = objs[0]; + for (int off = 0; off < 8; off++) { + for (int sz1 = 0; sz1 < 3; sz1++) { + for (int off2 = 0; off2 < accesssize(sz1); off2++) { + for (int rw = 0; rw < 2; rw++) { + // printf("thr=%d off=%d sz1=%d off2=%d rw=%d p=%p\n", + // main, off, sz1, off2, rw, obj); + access1(main, off, sz1, off2, rw, (char*)obj); + obj += 2; + } + } + } + } +} + +void *Thread(void *p) { + (void)p; + sleep(1); + Test(false); + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, Thread, 0); + Test(true); + pthread_join(th, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: ThreadSanitizer: reported 224 warnings diff --git a/compiler-rt/test/tsan/user_fopen.cc b/compiler-rt/test/tsan/user_fopen.cc new file mode 100644 index 00000000000..794d598719b --- /dev/null +++ b/compiler-rt/test/tsan/user_fopen.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> + +// defined by tsan. +extern "C" FILE *__interceptor_fopen(const char *file, const char *mode); +extern "C" int __interceptor_fileno(FILE *f); + +extern "C" FILE *fopen(const char *file, const char *mode) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user fopen\n"); + return __interceptor_fopen(file, mode); +} + +extern "C" int fileno(FILE *f) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user fileno\n"); + return 1; +} + +int main() { + FILE *f = fopen("/dev/zero", "r"); + if (f) { + char buf; + fread(&buf, 1, 1, f); + fclose(f); + } +} + +// CHECK: user fopen +// CHECK-NOT: ThreadSanitizer + diff --git a/compiler-rt/test/tsan/user_malloc.cc b/compiler-rt/test/tsan/user_malloc.cc new file mode 100644 index 00000000000..0be6d54fb13 --- /dev/null +++ b/compiler-rt/test/tsan/user_malloc.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdio.h> + +// defined by tsan. +extern "C" void *__interceptor_malloc(unsigned long size); +extern "C" void __interceptor_free(void *p); + +extern "C" void *malloc(unsigned long size) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user malloc\n"); + return __interceptor_malloc(size); +} + +extern "C" void free(void *p) { + __interceptor_free(p); +} + +int main() { + volatile char *p = (char*)malloc(10); + p[0] = 0; + free((void*)p); +} + +// CHECK: user malloc +// CHECK-NOT: ThreadSanitizer + diff --git a/compiler-rt/test/tsan/virtual_inheritance_compile_bug.cc b/compiler-rt/test/tsan/virtual_inheritance_compile_bug.cc new file mode 100644 index 00000000000..2275b8b8d21 --- /dev/null +++ b/compiler-rt/test/tsan/virtual_inheritance_compile_bug.cc @@ -0,0 +1,15 @@ +// Regression test for http://code.google.com/p/thread-sanitizer/issues/detail?id=3. +// The C++ variant is much more compact that the LLVM IR equivalent. + +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdio.h> +struct AAA { virtual long aaa () { return 0; } }; // NOLINT +struct BBB: virtual AAA { unsigned long bbb; }; // NOLINT +struct CCC: virtual AAA { }; +struct DDD: CCC, BBB { DDD(); }; // NOLINT +DDD::DDD() { } +int main() { + DDD d; + printf("OK\n"); +} +// CHECK: OK diff --git a/compiler-rt/test/tsan/vptr_benign_race.cc b/compiler-rt/test/tsan/vptr_benign_race.cc new file mode 100644 index 00000000000..8c9fc596e17 --- /dev/null +++ b/compiler-rt/test/tsan/vptr_benign_race.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { + sem_wait(&sem_); + sem_destroy(&sem_); + } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "PASS\n"); +} +// CHECK: PASS +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/test/tsan/vptr_harmful_race.cc b/compiler-rt/test/tsan/vptr_harmful_race.cc new file mode 100644 index 00000000000..0105c4cedd9 --- /dev/null +++ b/compiler-rt/test/tsan/vptr_harmful_race.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> +#include <unistd.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + sem_wait(&sem_); + sem_destroy(&sem_); + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race on vptr diff --git a/compiler-rt/test/tsan/vptr_harmful_race2.cc b/compiler-rt/test/tsan/vptr_harmful_race2.cc new file mode 100644 index 00000000000..378790c6234 --- /dev/null +++ b/compiler-rt/test/tsan/vptr_harmful_race2.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> +#include <unistd.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + sem_wait(&sem_); + sem_destroy(&sem_); + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + sleep(1); + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race on vptr diff --git a/compiler-rt/test/tsan/write_in_reader_lock.cc b/compiler-rt/test/tsan/write_in_reader_lock.cc new file mode 100644 index 00000000000..e872fe3ff96 --- /dev/null +++ b/compiler-rt/test/tsan/write_in_reader_lock.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +pthread_rwlock_t rwlock; +int GLOB; + +void *Thread1(void *p) { + (void)p; + pthread_rwlock_rdlock(&rwlock); + // Write under reader lock. + sleep(1); + GLOB++; + pthread_rwlock_unlock(&rwlock); + return 0; +} + +int main(int argc, char *argv[]) { + pthread_rwlock_init(&rwlock, NULL); + pthread_rwlock_rdlock(&rwlock); + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + volatile int x = GLOB; + (void)x; + pthread_rwlock_unlock(&rwlock); + pthread_join(t, 0); + pthread_rwlock_destroy(&rwlock); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T1{{.*}}: +// CHECK: #0 Thread1(void*) {{.*}}write_in_reader_lock.cc:13 +// CHECK: Previous read of size 4 at {{.*}} by main thread{{.*}}: +// CHECK: #0 main {{.*}}write_in_reader_lock.cc:23 |