diff options
Diffstat (limited to 'lib/tsan/lit_tests')
53 files changed, 1264 insertions, 38 deletions
diff --git a/lib/tsan/lit_tests/CMakeLists.txt b/lib/tsan/lit_tests/CMakeLists.txt index ff2508dd75afd..53e5015d1bc4d 100644 --- a/lib/tsan/lit_tests/CMakeLists.txt +++ b/lib/tsan/lit_tests/CMakeLists.txt @@ -11,9 +11,8 @@ configure_lit_site_cfg( if(COMPILER_RT_CAN_EXECUTE_TESTS) # Run TSan output tests only if we're sure we can produce working binaries. set(TSAN_TEST_DEPS - clang clang-headers FileCheck count not llvm-symbolizer - ${TSAN_RUNTIME_LIBRARIES} - ) + ${SANITIZER_COMMON_LIT_TEST_DEPS} + ${TSAN_RUNTIME_LIBRARIES}) set(TSAN_TEST_PARAMS tsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg ) diff --git a/lib/tsan/lit_tests/SharedLibs/lit.local.cfg b/lib/tsan/lit_tests/SharedLibs/lit.local.cfg new file mode 100644 index 0000000000000..b3677c17a0f2a --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/SharedLibs/load_shared_lib-so.cc b/lib/tsan/lit_tests/SharedLibs/load_shared_lib-so.cc new file mode 100644 index 0000000000000..d05aa6a40d182 --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/Unit/lit.cfg b/lib/tsan/lit_tests/Unit/lit.cfg index 6688697c0c1b1..0a0dbbfa54950 100644 --- a/lib/tsan/lit_tests/Unit/lit.cfg +++ b/lib/tsan/lit_tests/Unit/lit.cfg @@ -11,9 +11,8 @@ def get_required_attr(config, attr_name): return attr_value # Setup attributes common for all compiler-rt projects. -llvm_src_root = get_required_attr(config, 'llvm_src_root') -compiler_rt_lit_unit_cfg = os.path.join(llvm_src_root, "projects", - "compiler-rt", "lib", +compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') +compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", "lit.common.unit.cfg") lit.load_config(config, compiler_rt_lit_unit_cfg) diff --git a/lib/tsan/lit_tests/Unit/lit.site.cfg.in b/lib/tsan/lit_tests/Unit/lit.site.cfg.in index 23654b9be2eee..6eedc21808765 100644 --- a/lib/tsan/lit_tests/Unit/lit.site.cfg.in +++ b/lib/tsan/lit_tests/Unit/lit.site.cfg.in @@ -1,15 +1,17 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.build_type = "@CMAKE_BUILD_TYPE@" config.llvm_obj_root = "@LLVM_BINARY_DIR@" config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" # LLVM tools dir can be passed in lit parameters, so try to # apply substitution. try: config.llvm_tools_dir = config.llvm_tools_dir % lit.params + config.llvm_build_mode = config.llvm_build_mode % lit.params except KeyError,e: key, = e.args lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) diff --git a/lib/tsan/lit_tests/aligned_vs_unaligned_race.cc b/lib/tsan/lit_tests/aligned_vs_unaligned_race.cc new file mode 100644 index 0000000000000..f4533d08306c5 --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/atomic_free.cc b/lib/tsan/lit_tests/atomic_free.cc new file mode 100644 index 0000000000000..ba9bd5ac4aedd --- /dev/null +++ b/lib/tsan/lit_tests/atomic_free.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/atomic_free2.cc b/lib/tsan/lit_tests/atomic_free2.cc new file mode 100644 index 0000000000000..5517bf7ce9029 --- /dev/null +++ b/lib/tsan/lit_tests/atomic_free2.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/atomic_norace.cc b/lib/tsan/lit_tests/atomic_norace.cc new file mode 100644 index 0000000000000..265459b0758e1 --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/atomic_race.cc b/lib/tsan/lit_tests/atomic_race.cc new file mode 100644 index 0000000000000..360b81238889d --- /dev/null +++ b/lib/tsan/lit_tests/atomic_race.cc @@ -0,0 +1,80 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/atomic_stack.cc b/lib/tsan/lit_tests/atomic_stack.cc new file mode 100644 index 0000000000000..50f6a8a889ca8 --- /dev/null +++ b/lib/tsan/lit_tests/atomic_stack.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/benign_race.cc b/lib/tsan/lit_tests/benign_race.cc new file mode 100644 index 0000000000000..a4d4d23c362af --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/free_race.c b/lib/tsan/lit_tests/free_race.c index 7a2ec0cdbed0c..ff71a4d2116b4 100644 --- a/lib/tsan/lit_tests/free_race.c +++ b/lib/tsan/lit_tests/free_race.c @@ -40,4 +40,5 @@ int main() { // CHECK: #1 main // CHECK: Previous write of size 8 at {{.*}} by thread T1{{.*}}: // CHECK: #0 free -// CHECK: #1 Thread1 +// CHECK: #{{(1|2)}} Thread1 +// CHECK: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 diff --git a/lib/tsan/lit_tests/free_race2.c b/lib/tsan/lit_tests/free_race2.c index 095f82ea08188..f20774b2d8d4a 100644 --- a/lib/tsan/lit_tests/free_race2.c +++ b/lib/tsan/lit_tests/free_race2.c @@ -22,5 +22,5 @@ int main() { // CHECK: #1 main // CHECK: Previous write of size 8 at {{.*}} by main thread: // CHECK: #0 free -// CHECK: #1 foo -// CHECK: #2 main +// CHECK: #{{1|2}} foo +// CHECK: #{{2|3}} main diff --git a/lib/tsan/lit_tests/inlined_memcpy_race.cc b/lib/tsan/lit_tests/inlined_memcpy_race.cc new file mode 100644 index 0000000000000..6efe5a956e9db --- /dev/null +++ b/lib/tsan/lit_tests/inlined_memcpy_race.cc @@ -0,0 +1,55 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/java.h b/lib/tsan/lit_tests/java.h index 7d61f58028645..04094197edb7d 100644 --- a/lib/tsan/lit_tests/java.h +++ b/lib/tsan/lit_tests/java.h @@ -14,4 +14,6 @@ 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/lib/tsan/lit_tests/java_lock.cc b/lib/tsan/lit_tests/java_lock.cc index f66f1e7097fab..d9db103504de1 100644 --- a/lib/tsan/lit_tests/java_lock.cc +++ b/lib/tsan/lit_tests/java_lock.cc @@ -1,10 +1,12 @@ // 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); diff --git a/lib/tsan/lit_tests/java_lock_rec.cc b/lib/tsan/lit_tests/java_lock_rec.cc new file mode 100644 index 0000000000000..5cc80d4a33eff --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/java_lock_rec_race.cc b/lib/tsan/lit_tests/java_lock_rec_race.cc new file mode 100644 index 0000000000000..61626aaddc0d3 --- /dev/null +++ b/lib/tsan/lit_tests/java_lock_rec_race.cc @@ -0,0 +1,48 @@ +// 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); + __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/lib/tsan/lit_tests/java_rwlock.cc b/lib/tsan/lit_tests/java_rwlock.cc index 1e8940afd7d08..d1f38733ba035 100644 --- a/lib/tsan/lit_tests/java_rwlock.cc +++ b/lib/tsan/lit_tests/java_rwlock.cc @@ -1,10 +1,12 @@ // 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); diff --git a/lib/tsan/lit_tests/lit.cfg b/lib/tsan/lit_tests/lit.cfg index 7e2db7b8fd0b0..d483d2fcbdc60 100644 --- a/lib/tsan/lit_tests/lit.cfg +++ b/lib/tsan/lit_tests/lit.cfg @@ -2,6 +2,14 @@ import os +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit.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' @@ -30,14 +38,6 @@ if llvm_src_root is None: if not llvm_config: DisplayNoConfigMessage() - # Validate that llvm-config points to the same source tree. - llvm_src_root = lit.util.capture(["llvm-config", "--src-root"]).strip() - tsan_test_src_root = os.path.join(llvm_src_root, "projects", "compiler-rt", - "lib", "tsan", "lit_tests") - if (os.path.realpath(tsan_test_src_root) != - os.path.realpath(config.test_source_root)): - DisplayNoConfigMessage() - # Find out the presumed location of generated site config. llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() tsan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", @@ -49,8 +49,9 @@ if llvm_src_root is None: raise SystemExit # Setup attributes common for all compiler-rt projects. -compiler_rt_lit_cfg = os.path.join(llvm_src_root, "projects", "compiler-rt", - "lib", "lit.common.cfg") +compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') +compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", + "lit.common.cfg") if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)): lit.fatal("Can't find common compiler-rt lit config at: %r" % compiler_rt_lit_cfg) @@ -69,11 +70,8 @@ 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 " - + "-fPIE " - + "-fno-builtin " + "-g " + "-Wall " - + "-pie " + "-lpthread " + "-ldl ") clang_tsan_cxxflags = "-ccc-cxx " + clang_tsan_cflags diff --git a/lib/tsan/lit_tests/lit.site.cfg.in b/lib/tsan/lit_tests/lit.site.cfg.in index b1c6ccf544ea5..07b521af061f7 100644 --- a/lib/tsan/lit_tests/lit.site.cfg.in +++ b/lib/tsan/lit_tests/lit.site.cfg.in @@ -4,6 +4,7 @@ config.clang = "@LLVM_BINARY_DIR@/bin/clang" config.host_os = "@HOST_OS@" config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.target_triple = "@TARGET_TRIPLE@" diff --git a/lib/tsan/lit_tests/load_shared_lib.cc b/lib/tsan/lit_tests/load_shared_lib.cc new file mode 100644 index 0000000000000..dd6fa0964f4af --- /dev/null +++ b/lib/tsan/lit_tests/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 && %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/lib/tsan/lit_tests/longjmp.cc b/lib/tsan/lit_tests/longjmp.cc new file mode 100644 index 0000000000000..d9ca4ca5e6e9d --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/longjmp2.cc b/lib/tsan/lit_tests/longjmp2.cc new file mode 100644 index 0000000000000..0d551fa19d949 --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/longjmp3.cc b/lib/tsan/lit_tests/longjmp3.cc new file mode 100644 index 0000000000000..87fabd0b3be27 --- /dev/null +++ b/lib/tsan/lit_tests/longjmp3.cc @@ -0,0 +1,48 @@ +// RUN: %clang_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/longjmp4.cc b/lib/tsan/lit_tests/longjmp4.cc new file mode 100644 index 0000000000000..a8764dda5a6bd --- /dev/null +++ b/lib/tsan/lit_tests/longjmp4.cc @@ -0,0 +1,51 @@ +// RUN: %clang_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/malloc_overflow.cc b/lib/tsan/lit_tests/malloc_overflow.cc new file mode 100644 index 0000000000000..19423c5f93f14 --- /dev/null +++ b/lib/tsan/lit_tests/malloc_overflow.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/malloc_stack.cc b/lib/tsan/lit_tests/malloc_stack.cc new file mode 100644 index 0000000000000..c185623ff5ca0 --- /dev/null +++ b/lib/tsan/lit_tests/malloc_stack.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/memcpy_race.cc b/lib/tsan/lit_tests/memcpy_race.cc index 806740dda2418..857728ba05400 100644 --- a/lib/tsan/lit_tests/memcpy_race.cc +++ b/lib/tsan/lit_tests/memcpy_race.cc @@ -10,13 +10,15 @@ char *data1 = new char[10]; char *data2 = new char[10]; void *Thread1(void *x) { - memcpy(data+5, data1, 1); + 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, 4); + memcpy(data+3, data2, size); return NULL; } diff --git a/lib/tsan/lit_tests/mutex_destroy_locked.cc b/lib/tsan/lit_tests/mutex_destroy_locked.cc index 991eaf5426e22..27a04248b172b 100644 --- a/lib/tsan/lit_tests/mutex_destroy_locked.cc +++ b/lib/tsan/lit_tests/mutex_destroy_locked.cc @@ -19,3 +19,4 @@ int 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/lib/tsan/lit_tests/mutexset7.cc b/lib/tsan/lit_tests/mutexset7.cc index 141bde2b5015e..3ec1b52029836 100644 --- a/lib/tsan/lit_tests/mutexset7.cc +++ b/lib/tsan/lit_tests/mutexset7.cc @@ -4,6 +4,7 @@ #include <unistd.h> int Global; +__thread int huge[1024*1024]; void *Thread1(void *x) { sleep(1); diff --git a/lib/tsan/lit_tests/mutexset8.cc b/lib/tsan/lit_tests/mutexset8.cc new file mode 100644 index 0000000000000..6db63f7d16dbd --- /dev/null +++ b/lib/tsan/lit_tests/mutexset8.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/oob_race.cc b/lib/tsan/lit_tests/oob_race.cc new file mode 100644 index 0000000000000..2e7f0593fd8d9 --- /dev/null +++ b/lib/tsan/lit_tests/oob_race.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> + +const long kOffset = 64*1024; + +void *Thread(void *p) { + ((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/lib/tsan/lit_tests/race_on_heap.cc b/lib/tsan/lit_tests/race_on_heap.cc index dc679e8bf3f96..35434eac18506 100644 --- a/lib/tsan/lit_tests/race_on_heap.cc +++ b/lib/tsan/lit_tests/race_on_heap.cc @@ -39,8 +39,8 @@ int main() { // ... // CHECK: Location is heap block of size 99 at [[ADDR]] allocated by thread T1: // CHCEKL #0 malloc -// CHECK: #1 alloc -// CHECK: #2 AllocThread +// CHECK: #{{1|2}} alloc +// CHECK: #{{2|3}} AllocThread // ... // CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at: // CHECK: #0 pthread_create diff --git a/lib/tsan/lit_tests/race_on_mutex.c b/lib/tsan/lit_tests/race_on_mutex.c index de1c2d4160a60..aff32f9bb1a2d 100644 --- a/lib/tsan/lit_tests/race_on_mutex.c +++ b/lib/tsan/lit_tests/race_on_mutex.c @@ -34,7 +34,7 @@ int main() { } // CHECK: WARNING: ThreadSanitizer: data race -// CHECK-NEXT: Read of size 1 at {{.*}} by thread T2: +// 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: diff --git a/lib/tsan/lit_tests/race_on_mutex2.c b/lib/tsan/lit_tests/race_on_mutex2.c new file mode 100644 index 0000000000000..84bef75a34499 --- /dev/null +++ b/lib/tsan/lit_tests/race_on_mutex2.c @@ -0,0 +1,24 @@ +// RUN: %clang_tsan -O1 %s -o %t && %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/lib/tsan/lit_tests/race_on_write.cc b/lib/tsan/lit_tests/race_on_write.cc new file mode 100644 index 0000000000000..f1b0bb1cbd6ee --- /dev/null +++ b/lib/tsan/lit_tests/race_on_write.cc @@ -0,0 +1,39 @@ +// 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 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/lib/tsan/lit_tests/signal_errno.cc b/lib/tsan/lit_tests/signal_errno.cc index af9ccce9045a7..8181555f6f639 100644 --- a/lib/tsan/lit_tests/signal_errno.cc +++ b/lib/tsan/lit_tests/signal_errno.cc @@ -10,7 +10,7 @@ pthread_t mainth; volatile int done; -static void handler(int, siginfo_t *s, void *c) { +static void MyHandler(int, siginfo_t *s, void *c) { errno = 1; done = 1; } @@ -23,7 +23,7 @@ static void* sendsignal(void *p) { int main() { mainth = pthread_self(); struct sigaction act = {}; - act.sa_sigaction = &handler; + act.sa_sigaction = &MyHandler; sigaction(SIGPROF, &act, 0); pthread_t th; pthread_create(&th, 0, sendsignal, 0); @@ -38,5 +38,6 @@ int main() { } // CHECK: WARNING: ThreadSanitizer: signal handler spoils errno -// CHECK: #0 handler(int, siginfo*, void*) {{.*}}signal_errno.cc +// CHECK: #0 MyHandler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_errno.cc +// CHECK: SUMMARY: ThreadSanitizer: signal handler spoils errno{{.*}}MyHandler diff --git a/lib/tsan/lit_tests/signal_malloc.cc b/lib/tsan/lit_tests/signal_malloc.cc index cee997cdb7635..4dbc2f78ab17a 100644 --- a/lib/tsan/lit_tests/signal_malloc.cc +++ b/lib/tsan/lit_tests/signal_malloc.cc @@ -8,7 +8,8 @@ static void handler(int, siginfo_t*, void*) { // CHECK: WARNING: ThreadSanitizer: signal-unsafe call inside of a signal // CHECK: #0 malloc - // CHECK: #1 handler(int, siginfo*, void*) {{.*}}signal_malloc.cc:[[@LINE+1]] + // 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); diff --git a/lib/tsan/lit_tests/simple_race.cc b/lib/tsan/lit_tests/simple_race.cc index ec29c92ee1a83..99cf228ac2f2b 100644 --- a/lib/tsan/lit_tests/simple_race.cc +++ b/lib/tsan/lit_tests/simple_race.cc @@ -23,3 +23,4 @@ int main() { } // CHECK: WARNING: ThreadSanitizer: data race +// CHECK: SUMMARY: ThreadSanitizer: data race{{.*}}Thread diff --git a/lib/tsan/lit_tests/test_output.sh b/lib/tsan/lit_tests/test_output.sh index d21c9a797ad3f..1eedf6eb20a32 100755 --- a/lib/tsan/lit_tests/test_output.sh +++ b/lib/tsan/lit_tests/test_output.sh @@ -6,12 +6,13 @@ set -e # fail on any error ROOTDIR=$(dirname $0)/.. BLACKLIST=$ROOTDIR/lit_tests/Helpers/blacklist.txt -# Assuming clang is in path. -CC=clang -CXX=clang++ +# 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 -fno-builtin -Wall" +CFLAGS="-fsanitize=thread -fsanitize-blacklist=$BLACKLIST -fPIE -O1 -g -Wall" LDFLAGS="-pie -lpthread -ldl $ROOTDIR/rtl/libtsan.a" test_file() { @@ -23,7 +24,7 @@ test_file() { $COMPILER $SRC $CFLAGS -c -o $OBJ $COMPILER $OBJ $LDFLAGS -o $EXE RES=$($EXE 2>&1 || true) - printf "%s\n" "$RES" | FileCheck $SRC + printf "%s\n" "$RES" | $FILECHECK $SRC if [ "$3" == "" ]; then rm -f $EXE $OBJ fi @@ -35,6 +36,10 @@ if [ "$1" == "" ]; then echo SKIPPING FAILING TEST $c continue fi + if [[ $c == */load_shared_lib.cc ]]; then + echo TEST $c is not supported + continue + fi COMPILER=$CXX case $c in *.c) COMPILER=$CC diff --git a/lib/tsan/lit_tests/thread_end_with_ignore.cc b/lib/tsan/lit_tests/thread_end_with_ignore.cc new file mode 100644 index 0000000000000..960a477c5ad33 --- /dev/null +++ b/lib/tsan/lit_tests/thread_end_with_ignore.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %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 + diff --git a/lib/tsan/lit_tests/thread_end_with_ignore2.cc b/lib/tsan/lit_tests/thread_end_with_ignore2.cc new file mode 100644 index 0000000000000..8f743ae2f4a4a --- /dev/null +++ b/lib/tsan/lit_tests/thread_end_with_ignore2.cc @@ -0,0 +1,9 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l); + +int main() { + AnnotateIgnoreWritesBegin("", 0); +} + +// CHECK: ThreadSanitizer: thread T0 finished with ignores enabled + diff --git a/lib/tsan/lit_tests/thread_leak3.c b/lib/tsan/lit_tests/thread_leak3.c index c48219fe73fae..3577164cad4a7 100644 --- a/lib/tsan/lit_tests/thread_leak3.c +++ b/lib/tsan/lit_tests/thread_leak3.c @@ -1,5 +1,6 @@ // RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s #include <pthread.h> +#include <unistd.h> void *Thread(void *x) { return 0; @@ -8,7 +9,9 @@ void *Thread(void *x) { 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/lib/tsan/lit_tests/thread_leak4.c b/lib/tsan/lit_tests/thread_leak4.c new file mode 100644 index 0000000000000..f9fad0360d34b --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/thread_leak5.c b/lib/tsan/lit_tests/thread_leak5.c new file mode 100644 index 0000000000000..fc72b149ec250 --- /dev/null +++ b/lib/tsan/lit_tests/thread_leak5.c @@ -0,0 +1,19 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <unistd.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + for (int i = 0; i < 5; 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/lib/tsan/lit_tests/thread_name.cc b/lib/tsan/lit_tests/thread_name.cc index 0ca0b17699765..37f308ffbc0cf 100644 --- a/lib/tsan/lit_tests/thread_name.cc +++ b/lib/tsan/lit_tests/thread_name.cc @@ -15,7 +15,11 @@ void *Thread1(void *x) { } 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; } diff --git a/lib/tsan/lit_tests/tsan-vs-gvn.cc b/lib/tsan/lit_tests/tsan-vs-gvn.cc new file mode 100644 index 0000000000000..40ae724b78e12 --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/unaligned_norace.cc b/lib/tsan/lit_tests/unaligned_norace.cc new file mode 100644 index 0000000000000..792224b80126b --- /dev/null +++ b/lib/tsan/lit_tests/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/lib/tsan/lit_tests/unaligned_race.cc b/lib/tsan/lit_tests/unaligned_race.cc new file mode 100644 index 0000000000000..18bed8555cc5e --- /dev/null +++ b/lib/tsan/lit_tests/unaligned_race.cc @@ -0,0 +1,135 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> + +uint64_t objs[8*2*(2 + 4 + 8)][2]; + +extern "C" { +uint16_t __sanitizer_unaligned_load16(void *addr); +uint32_t __sanitizer_unaligned_load32(void *addr); +uint64_t __sanitizer_unaligned_load64(void *addr); +void __sanitizer_unaligned_store16(void *addr, uint16_t v); +void __sanitizer_unaligned_store32(void *addr, uint32_t v); +void __sanitizer_unaligned_store64(void *addr, uint64_t v); +} + +// All this mess is to generate unique stack for each race, +// otherwise tsan will suppress similar stacks. + +static void access(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 void access3(bool main, int sz1, bool rw, 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 void access2(bool main, int sz1, int off2, bool rw, 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 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); +} + +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 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/lib/tsan/lit_tests/vptr_harmful_race.cc b/lib/tsan/lit_tests/vptr_harmful_race.cc index f51ba7ee57f0b..76d31c00ad4f8 100644 --- a/lib/tsan/lit_tests/vptr_harmful_race.cc +++ b/lib/tsan/lit_tests/vptr_harmful_race.cc @@ -2,6 +2,7 @@ #include <pthread.h> #include <semaphore.h> #include <stdio.h> +#include <unistd.h> struct A { A() { @@ -34,6 +35,7 @@ void *Thread1(void *x) { } void *Thread2(void *x) { + sleep(1); delete obj; return NULL; } @@ -46,4 +48,4 @@ int main() { pthread_join(t[1], NULL); } -// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: WARNING: ThreadSanitizer: data race on vptr diff --git a/lib/tsan/lit_tests/vptr_harmful_race2.cc b/lib/tsan/lit_tests/vptr_harmful_race2.cc new file mode 100644 index 0000000000000..d7e1d19a11bd3 --- /dev/null +++ b/lib/tsan/lit_tests/vptr_harmful_race2.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> +#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 |