diff options
Diffstat (limited to 'test/tsan')
-rw-r--r-- | test/tsan/CMakeLists.txt | 4 | ||||
-rw-r--r-- | test/tsan/Darwin/xpc-race.mm | 53 | ||||
-rw-r--r-- | test/tsan/Linux/double_race.cc | 52 | ||||
-rw-r--r-- | test/tsan/Linux/user_malloc.cc | 7 | ||||
-rw-r--r-- | test/tsan/allocator_returns_null.cc | 12 | ||||
-rw-r--r-- | test/tsan/atexit3.cc | 41 | ||||
-rw-r--r-- | test/tsan/cond_cancel.c | 2 | ||||
-rw-r--r-- | test/tsan/custom_mutex.h | 10 | ||||
-rw-r--r-- | test/tsan/custom_mutex4.cc | 33 | ||||
-rw-r--r-- | test/tsan/custom_mutex5.cc | 33 | ||||
-rw-r--r-- | test/tsan/lit.cfg | 11 | ||||
-rw-r--r-- | test/tsan/map32bit.cc | 5 | ||||
-rw-r--r-- | test/tsan/signal_pause.cc | 35 | ||||
-rw-r--r-- | test/tsan/strerror_r.cc | 3 | ||||
-rw-r--r-- | test/tsan/thread_name.cc | 8 | ||||
-rw-r--r-- | test/tsan/thread_name2.cc | 10 | ||||
-rw-r--r-- | test/tsan/tls_race.cc | 1 | ||||
-rw-r--r-- | test/tsan/tls_race2.cc | 1 |
18 files changed, 275 insertions, 46 deletions
diff --git a/test/tsan/CMakeLists.txt b/test/tsan/CMakeLists.txt index a689086129521..2b1d3004b1def 100644 --- a/test/tsan/CMakeLists.txt +++ b/test/tsan/CMakeLists.txt @@ -7,9 +7,9 @@ endif() if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND TSAN_TEST_DEPS tsan) endif() -if(COMPILER_RT_HAS_LIBCXX_SOURCES AND +if(COMPILER_RT_LIBCXX_PATH AND COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang" - AND NOT APPLE) + AND NOT APPLE AND NOT ANDROID) list(APPEND TSAN_TEST_DEPS libcxx_tsan) set(TSAN_HAS_LIBCXX True) else() diff --git a/test/tsan/Darwin/xpc-race.mm b/test/tsan/Darwin/xpc-race.mm index a1e214c12b7df..872c9ee73e138 100644 --- a/test/tsan/Darwin/xpc-race.mm +++ b/test/tsan/Darwin/xpc-race.mm @@ -1,22 +1,26 @@ -// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %clangxx_tsan %s -o %t -framework Foundation // RUN: %deflake %run %t 2>&1 | FileCheck %s // UNSUPPORTED: ios #import <Foundation/Foundation.h> #import <xpc/xpc.h> +#import <stdatomic.h> #import "../test.h" long global; -long received_msgs; +_Atomic(long) msg_counter; +_Atomic(long) processed_msgs; xpc_connection_t server_conn; xpc_connection_t client_conns[2]; int main(int argc, const char *argv[]) { @autoreleasepool { - NSLog(@"Hello world."); + fprintf(stderr, "Hello world.\n"); + // CHECK: Hello world. + barrier_init(&barrier, 2); dispatch_queue_t server_q = dispatch_queue_create("server.queue", DISPATCH_QUEUE_CONCURRENT); @@ -24,21 +28,34 @@ int main(int argc, const char *argv[]) { server_conn = xpc_connection_create(NULL, server_q); xpc_connection_set_event_handler(server_conn, ^(xpc_object_t client) { - NSLog(@"server event handler, client = %@", client); + fprintf(stderr, "server event handler, client = %p\n", client); if (client == XPC_ERROR_CONNECTION_INTERRUPTED || client == XPC_ERROR_CONNECTION_INVALID) { return; } xpc_connection_set_event_handler(client, ^(xpc_object_t object) { - NSLog(@"received message: %@", object); + fprintf(stderr, "received message: %p\n", object); - barrier_wait(&barrier); - global = 42; + long msg_number = atomic_fetch_add_explicit(&msg_counter, 1, memory_order_relaxed); - dispatch_sync(dispatch_get_main_queue(), ^{ - received_msgs++; + if (msg_number == 0) + barrier_wait(&barrier); + + global++; + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 8 + // CHECK: #0 {{.*}}xpc-race.mm:[[@LINE-3]] + // CHECK: Previous write of size 8 + // CHECK: #0 {{.*}}xpc-race.mm:[[@LINE-5]] + // CHECK: Location is global 'global' + + if (msg_number == 1) + barrier_wait(&barrier); - if (received_msgs >= 2) { + atomic_fetch_add(&processed_msgs, 1); + + dispatch_sync(dispatch_get_main_queue(), ^{ + if (processed_msgs >= 2) { xpc_connection_cancel(client_conns[0]); xpc_connection_cancel(client_conns[1]); xpc_connection_cancel(server_conn); @@ -55,12 +72,12 @@ int main(int argc, const char *argv[]) { for (int i = 0; i < 2; i++) { client_conns[i] = xpc_connection_create_from_endpoint(endpoint); xpc_connection_set_event_handler(client_conns[i], ^(xpc_object_t event) { - NSLog(@"client event handler, event = %@", event); + fprintf(stderr, "client event handler, event = %p\n", event); }); xpc_object_t msg = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(msg, "hello", "world"); - NSLog(@"sending message: %@", msg); + fprintf(stderr, "sending message: %p\n", msg); xpc_connection_send_message(client_conns[i], msg); xpc_connection_resume(client_conns[i]); @@ -68,16 +85,8 @@ int main(int argc, const char *argv[]) { CFRunLoopRun(); - NSLog(@"Done."); + fprintf(stderr, "Done.\n"); + // CHECK: Done. } return 0; } - -// CHECK: Hello world. -// CHECK: WARNING: ThreadSanitizer: data race -// CHECK: Write of size 8 -// CHECK: #0 {{.*}}xpc-race.mm:36 -// CHECK: Previous write of size 8 -// CHECK: #0 {{.*}}xpc-race.mm:36 -// CHECK: Location is global 'global' -// CHECK: Done. diff --git a/test/tsan/Linux/double_race.cc b/test/tsan/Linux/double_race.cc new file mode 100644 index 0000000000000..2b4af35a2676e --- /dev/null +++ b/test/tsan/Linux/double_race.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "../test.h" +#include <memory.h> + +// A reproducer for a known issue. +// See reference to double_race.cc in tsan_rtl_report.cc for an explanation. + +char buf[16]; +volatile int nreport; + +void __sanitizer_report_error_summary(const char *summary) { + nreport++; +} + +const int kEventPCBits = 61; + +extern "C" bool __tsan_symbolize_external(unsigned long pc, char *func_buf, + unsigned long func_siz, + char *file_buf, + unsigned long file_siz, int *line, + int *col) { + if (pc >> kEventPCBits) { + printf("bad PC passed to __tsan_symbolize_external: %lx\n", pc); + _exit(1); + } + return true; +} + +void *Thread(void *arg) { + barrier_wait(&barrier); + memset(buf, 2, sizeof(buf)); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + memset(buf, 1, sizeof(buf)); + barrier_wait(&barrier); + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 8 at {{.*}} by thread T1: +// CHECK: #0 memset +// CHECK: #1 Thread +// CHECK-NOT: bad PC passed to __tsan_symbolize_external +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 8 at {{.*}} by thread T1: +// CHECK: #0 Thread diff --git a/test/tsan/Linux/user_malloc.cc b/test/tsan/Linux/user_malloc.cc index 6d51a9dd7d9b2..b470e6c543f87 100644 --- a/test/tsan/Linux/user_malloc.cc +++ b/test/tsan/Linux/user_malloc.cc @@ -1,5 +1,12 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + // UNSUPPORTED: powerpc64le + +// FIXME: Remove the test or find how to fix this. +// On some distributions, probably with newer glibc, tsan initialization calls +// dlsym which then calls malloc and crashes because of tsan is not initialized. +// UNSUPPORTED: linux + #include <stdio.h> // Defined by tsan. diff --git a/test/tsan/allocator_returns_null.cc b/test/tsan/allocator_returns_null.cc index 852dda035f23a..5e2e2e9a53c73 100644 --- a/test/tsan/allocator_returns_null.cc +++ b/test/tsan/allocator_returns_null.cc @@ -37,9 +37,10 @@ // RUN: | FileCheck %s --check-prefix=CHECK-nnNULL #include <assert.h> -#include <string.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <limits> #include <new> @@ -51,6 +52,7 @@ int main(int argc, char **argv) { const char *action = argv[1]; fprintf(stderr, "%s:\n", action); + // The limit enforced in tsan_mman.cc, user_alloc_internal function. static const size_t kMaxAllowedMallocSizePlusOne = (1ULL << 40) + 1; void *x = 0; @@ -78,10 +80,13 @@ int main(int argc, char **argv) { assert(0); } + fprintf(stderr, "errno: %d\n", errno); + // The NULL pointer is printed differently on different systems, while (long)0 // is always the same. fprintf(stderr, "x: %lx\n", (long)x); free(x); + return x != 0; } @@ -101,14 +106,19 @@ int main(int argc, char **argv) { // CHECK-nnCRASH: ThreadSanitizer's allocator is terminating the process // CHECK-mNULL: malloc: +// CHECK-mNULL: errno: 12 // CHECK-mNULL: x: 0 // CHECK-cNULL: calloc: +// CHECK-cNULL: errno: 12 // CHECK-cNULL: x: 0 // CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: errno: 12 // CHECK-coNULL: x: 0 // CHECK-rNULL: realloc: +// CHECK-rNULL: errno: 12 // CHECK-rNULL: x: 0 // CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: errno: 12 // CHECK-mrNULL: x: 0 // CHECK-nnNULL: new-nothrow: // CHECK-nnNULL: x: 0 diff --git a/test/tsan/atexit3.cc b/test/tsan/atexit3.cc new file mode 100644 index 0000000000000..43ba5bbf6919f --- /dev/null +++ b/test/tsan/atexit3.cc @@ -0,0 +1,41 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +static void atexit5() { + fprintf(stderr, "5"); +} + +static void atexit4() { + fprintf(stderr, "4"); +} + +static void atexit3() { + fprintf(stderr, "3"); +} + +static void atexit2() { + fprintf(stderr, "2"); +} + +static void atexit1() { + fprintf(stderr, "1"); +} + +static void atexit0() { + fprintf(stderr, "\n"); +} + +int main() { + atexit(atexit0); + atexit(atexit1); + atexit(atexit2); + atexit(atexit3); + atexit(atexit4); + atexit(atexit5); +} + +// CHECK-NOT: FATAL: ThreadSanitizer +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: 54321 diff --git a/test/tsan/cond_cancel.c b/test/tsan/cond_cancel.c index fb6a66136b8af..5c497155cb69e 100644 --- a/test/tsan/cond_cancel.c +++ b/test/tsan/cond_cancel.c @@ -8,7 +8,7 @@ // (defined in sanitizer_thread_registry.cc). It might seem a bug on glibc, // however the same version GLIBC-2.17 will not make fail the test on // powerpc64 BE (VMA=46) -// XFAIL: powerpc64-unknown-linux-gnu +// UNSUPPORTED: powerpc64-unknown-linux-gnu #include "test.h" diff --git a/test/tsan/custom_mutex.h b/test/tsan/custom_mutex.h index e3ac7a9a52a7f..8e226a170ec4f 100644 --- a/test/tsan/custom_mutex.h +++ b/test/tsan/custom_mutex.h @@ -6,15 +6,16 @@ // A very primitive mutex annotated with tsan annotations. class Mutex { public: - Mutex(bool prof, unsigned flags) + Mutex(bool prof, unsigned create_flags, unsigned destroy_flags=0) : prof_(prof) , locked_(false) - , seq_(0) { - __tsan_mutex_create(this, flags); + , seq_(0) + , destroy_flags_(destroy_flags) { + __tsan_mutex_create(this, create_flags); } ~Mutex() { - __tsan_mutex_destroy(this, 0); + __tsan_mutex_destroy(this, destroy_flags_); } void Lock() { @@ -57,6 +58,7 @@ class Mutex { const bool prof_; std::atomic<bool> locked_; int seq_; + unsigned destroy_flags_; // This models mutex profiling subsystem. static Mutex prof_mu_; diff --git a/test/tsan/custom_mutex4.cc b/test/tsan/custom_mutex4.cc new file mode 100644 index 0000000000000..539a8be803c60 --- /dev/null +++ b/test/tsan/custom_mutex4.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 --std=c++11 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "custom_mutex.h" + +#include <type_traits> + +// Test that the destruction events of a mutex are ignored when the +// annotations request this. +// +// Use after destruction is UB, but __tsan_mutex_linker_init and +// __tsan_mutex_not_static exist to support global variables of mutex type, +// which might be accessed during program shutdown after the class's destructor +// has run. + +int main() { + std::aligned_storage<sizeof(Mutex), alignof(Mutex)>::type mu1_store; + Mutex* mu1 = reinterpret_cast<Mutex*>(&mu1_store); + new(&mu1_store) Mutex(false, __tsan_mutex_linker_init); + mu1->Lock(); + mu1->~Mutex(); + mu1->Unlock(); + + std::aligned_storage<sizeof(Mutex), alignof(Mutex)>::type mu2_store; + Mutex* mu2 = reinterpret_cast<Mutex*>(&mu2_store); + new(&mu2_store) Mutex(false, 0, __tsan_mutex_not_static); + mu2->Lock(); + mu2->~Mutex(); + mu2->Unlock(); + + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/custom_mutex5.cc b/test/tsan/custom_mutex5.cc new file mode 100644 index 0000000000000..ad906e38aeb19 --- /dev/null +++ b/test/tsan/custom_mutex5.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 --std=c++11 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "custom_mutex.h" + +#include <type_traits> + +// Test that we detect the destruction of an in-use mutex when the +// thread annotations don't otherwise disable the check. + +int main() { + std::aligned_storage<sizeof(Mutex), alignof(Mutex)>::type mu1_store; + Mutex* mu1 = reinterpret_cast<Mutex*>(&mu1_store); + new(&mu1_store) Mutex(false, 0); + mu1->Lock(); + mu1->~Mutex(); + mu1->Unlock(); + + std::aligned_storage<sizeof(Mutex), alignof(Mutex)>::type mu2_store; + Mutex* mu2 = reinterpret_cast<Mutex*>(&mu2_store); + new(&mu2_store) + Mutex(false, __tsan_mutex_not_static, __tsan_mutex_not_static); + mu2->Lock(); + mu2->~Mutex(); + mu2->Unlock(); + + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: main {{.*}}custom_mutex5.cc:14 +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: main {{.*}}custom_mutex5.cc:22 +// CHECK: DONE diff --git a/test/tsan/lit.cfg b/test/tsan/lit.cfg index 0ab62db0907fd..fdbafefbc66fd 100644 --- a/test/tsan/lit.cfg +++ b/test/tsan/lit.cfg @@ -66,7 +66,7 @@ if config.has_libcxx and config.host_os != 'Darwin': "-Wl,-rpath=%s" % libcxx_libdir] def build_invocation(compile_flags): - return " " + " ".join([config.compile_wrapper, config.clang] + compile_flags) + " " + return " " + " ".join([config.clang] + compile_flags) + " " config.substitutions.append( ("%clang_tsan ", build_invocation(clang_tsan_cflags)) ) config.substitutions.append( ("%clangxx_tsan ", build_invocation(clang_tsan_cxxflags)) ) @@ -79,14 +79,11 @@ config.substitutions.append( ("%deflake ", os.path.join(os.path.dirname(__file__ # Default test suffixes. config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm'] -# ThreadSanitizer tests are currently supported on FreeBSD, Linux and Darwin. -if config.host_os not in ['FreeBSD', 'Linux', 'Darwin']: +if config.host_os not in ['FreeBSD', 'Linux', 'Darwin', 'NetBSD']: config.unsupported = True -# Allow tests to use REQUIRES=stable-runtime. For use when you cannot use XFAIL -# because the test hangs. -if config.target_arch != 'aarch64': - config.available_features.add('stable-runtime') +if config.android: + config.unsupported = True if config.host_os == 'Darwin' and config.target_arch in ["x86_64", "x86_64h"]: config.parallelism_group = "darwin-64bit-sanitizer" diff --git a/test/tsan/map32bit.cc b/test/tsan/map32bit.cc index 3b4f8990016e8..8aef27bc1900d 100644 --- a/test/tsan/map32bit.cc +++ b/test/tsan/map32bit.cc @@ -12,8 +12,8 @@ // XFAIL: aarch64 // XFAIL: powerpc64 -// MAP_32BIT doesn't exist on OS X. -// UNSUPPORTED: darwin +// MAP_32BIT doesn't exist on OS X and NetBSD. +// UNSUPPORTED: darwin,netbsd void *Thread(void *ptr) { *(int*)ptr = 42; @@ -45,4 +45,3 @@ int main() { // CHECK: WARNING: ThreadSanitizer: data race // CHECK: DONE - diff --git a/test/tsan/signal_pause.cc b/test/tsan/signal_pause.cc new file mode 100644 index 0000000000000..cbcef9491e9e4 --- /dev/null +++ b/test/tsan/signal_pause.cc @@ -0,0 +1,35 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Test that pause loop handles signals. + +#include "test.h" +#include <signal.h> +#include <errno.h> + +void handler(int signum) { + write(2, "DONE\n", 5); + _exit(0); +} + +void *thread(void *arg) { + for (;;) + pause(); + return 0; +} + +int main(int argc, char** argv) { + struct sigaction act = {}; + act.sa_handler = &handler; + if (sigaction(SIGUSR1, &act, 0)) { + fprintf(stderr, "sigaction failed %d\n", errno); + return 1; + } + pthread_t th; + pthread_create(&th, 0, thread, 0); + sleep(1); // give it time to block in pause + pthread_kill(th, SIGUSR1); + sleep(10); // signal handler must exit the process while we are here + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/strerror_r.cc b/test/tsan/strerror_r.cc index 06c92d3bb641d..ad482013012c2 100644 --- a/test/tsan/strerror_r.cc +++ b/test/tsan/strerror_r.cc @@ -11,7 +11,8 @@ char buffer[1000]; void *Thread(void *p) { - return strerror_r(TEST_ERROR, buffer, sizeof(buffer)); + strerror_r(TEST_ERROR, buffer, sizeof(buffer)); + return buffer; } int main() { diff --git a/test/tsan/thread_name.cc b/test/tsan/thread_name.cc index 80d30b82d8b5f..17caa62ef4401 100644 --- a/test/tsan/thread_name.cc +++ b/test/tsan/thread_name.cc @@ -3,10 +3,14 @@ #if defined(__linux__) #define USE_PTHREAD_SETNAME_NP __GLIBC_PREREQ(2, 12) +#define tsan_pthread_setname_np pthread_setname_np #elif defined(__FreeBSD__) #include <pthread_np.h> #define USE_PTHREAD_SETNAME_NP 1 -#define pthread_setname_np pthread_set_name_np +#define tasn_pthread_setname_np pthread_set_name_np +#elif defined(__NetBSD__) +#define USE_PTHREAD_SETNAME_NP 1 +#define tsan_pthread_setname_np(a, b) pthread_setname_np((a), "%s", (void *)(b)) #else #define USE_PTHREAD_SETNAME_NP 0 #endif @@ -24,7 +28,7 @@ void *Thread1(void *x) { void *Thread2(void *x) { #if USE_PTHREAD_SETNAME_NP - pthread_setname_np(pthread_self(), "Thread2"); + tsan_pthread_setname_np(pthread_self(), "Thread2"); #else AnnotateThreadName(__FILE__, __LINE__, "Thread2"); #endif diff --git a/test/tsan/thread_name2.cc b/test/tsan/thread_name2.cc index d7ed0f0d1952f..9ebac29ddb5ed 100644 --- a/test/tsan/thread_name2.cc +++ b/test/tsan/thread_name2.cc @@ -6,7 +6,11 @@ #if defined(__FreeBSD__) #include <pthread_np.h> -#define pthread_setname_np pthread_set_name_np +#define tsan_pthread_setname_np pthread_set_name_np +#elif defined(__NetBSD__) +#define tsan_pthread_setname_np(a, b) pthread_setname_np((a), "%s", (void *)(b)) +#else +#define tsan_pthread_setname_np pthread_setname_np #endif long long Global; @@ -18,7 +22,7 @@ void *Thread1(void *x) { } void *Thread2(void *x) { - pthread_setname_np(pthread_self(), "foobar2"); + tsan_pthread_setname_np(pthread_self(), "foobar2"); Global--; barrier_wait(&barrier); return 0; @@ -29,7 +33,7 @@ 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"); + tsan_pthread_setname_np(t[0], "foobar1"); barrier_wait(&barrier); pthread_join(t[0], NULL); pthread_join(t[1], NULL); diff --git a/test/tsan/tls_race.cc b/test/tsan/tls_race.cc index b43a514cc8aa8..dd37ff01d9751 100644 --- a/test/tsan/tls_race.cc +++ b/test/tsan/tls_race.cc @@ -20,4 +20,5 @@ int main() { // CHECK: WARNING: ThreadSanitizer: data race // CHECK-Linux: Location is TLS of main thread. // CHECK-FreeBSD: Location is TLS of main thread. +// CHECK-NetBSD: Location is TLS of main thread. // CHECK-Darwin: Location is heap block of size 4 diff --git a/test/tsan/tls_race2.cc b/test/tsan/tls_race2.cc index b04ff67881009..f3139b69fc082 100644 --- a/test/tsan/tls_race2.cc +++ b/test/tsan/tls_race2.cc @@ -27,4 +27,5 @@ int main() { // CHECK: WARNING: ThreadSanitizer: data race // CHECK-Linux: Location is TLS of thread T1. // CHECK-FreeBSD: Location is TLS of thread T1. +// CHECK-NetBSD: Location is TLS of thread T1. // CHECK-Darwin: Location is heap block of size 4 |