diff options
Diffstat (limited to 'test/tsan')
122 files changed, 2451 insertions, 136 deletions
diff --git a/test/tsan/CMakeLists.txt b/test/tsan/CMakeLists.txt index a058602659c1..e05b100f3699 100644 --- a/test/tsan/CMakeLists.txt +++ b/test/tsan/CMakeLists.txt @@ -22,6 +22,7 @@ if(APPLE) endif() foreach(arch ${TSAN_TEST_ARCH}) + set(TSAN_TEST_TARGET_ARCH ${arch}) string(TOLOWER "-${arch}" TSAN_TEST_CONFIG_SUFFIX) if(ANDROID OR ${arch} MATCHES "arm|aarch64") # This is only true if we are cross-compiling. @@ -53,4 +54,4 @@ endif() add_lit_testsuite(check-tsan "Running ThreadSanitizer tests" ${TSAN_TESTSUITES} DEPENDS ${TSAN_TEST_DEPS}) -set_target_properties(check-tsan PROPERTIES FOLDER "TSan tests") +set_target_properties(check-tsan PROPERTIES FOLDER "Compiler-RT Tests") diff --git a/test/tsan/Darwin/dispatch_main.mm b/test/tsan/Darwin/dispatch_main.mm new file mode 100644 index 000000000000..75887547c606 --- /dev/null +++ b/test/tsan/Darwin/dispatch_main.mm @@ -0,0 +1,38 @@ +// Check that we don't crash when dispatch_main calls pthread_exit which +// quits the main thread. + +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +int main() { + fprintf(stderr,"Hello world"); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL); + + dispatch_async(q, ^{ + fprintf(stderr,"1"); + }); + + dispatch_async(q, ^{ + fprintf(stderr,"2"); + }); + + dispatch_async(q, ^{ + fprintf(stderr,"3"); + + dispatch_async(dispatch_get_main_queue(), ^{ + fprintf(stderr,"Done."); + sleep(1); + exit(0); + }); + }); + + dispatch_main(); +} + +// CHECK: Hello world +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: CHECK failed diff --git a/test/tsan/Darwin/dispatch_once_deadlock.mm b/test/tsan/Darwin/dispatch_once_deadlock.mm new file mode 100644 index 000000000000..e88cdc0d0da5 --- /dev/null +++ b/test/tsan/Darwin/dispatch_once_deadlock.mm @@ -0,0 +1,41 @@ +// Check that calling dispatch_once from a report callback works. + +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 not %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> +#import <pthread.h> + +long g = 0; +long h = 0; +void f() { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + g++; + }); + h++; +} + +extern "C" void __tsan_on_report() { + fprintf(stderr, "Report.\n"); + f(); +} + +int main() { + fprintf(stderr, "Hello world.\n"); + + f(); + + pthread_mutex_t mutex = {0}; + pthread_mutex_lock(&mutex); + + fprintf(stderr, "g = %ld.\n", g); + fprintf(stderr, "h = %ld.\n", h); + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: Report. +// CHECK: g = 1 +// CHECK: h = 2 +// CHECK: Done. diff --git a/test/tsan/Darwin/dlopen.cc b/test/tsan/Darwin/dlopen.cc new file mode 100644 index 000000000000..7382a6de28c5 --- /dev/null +++ b/test/tsan/Darwin/dlopen.cc @@ -0,0 +1,41 @@ +// Checks that on OS X 10.11+ (where we do not re-exec anymore, because +// interceptors work automatically), dlopen'ing a TSanified library from a +// non-instrumented program exits with a user-friendly message. + +// REQUIRES: osx-autointerception + +// RUN: %clangxx_tsan %s -o %t.so -shared -DSHARED_LIB +// RUN: %clangxx_tsan -fno-sanitize=thread %s -o %t + +// RUN: TSAN_DYLIB_PATH=`%clangxx_tsan %s -### 2>&1 \ +// RUN: | grep "libclang_rt.tsan_osx_dynamic.dylib" \ +// RUN: | sed -e 's/.*"\(.*libclang_rt.tsan_osx_dynamic.dylib\)".*/\1/'` + +// Launching a non-instrumented binary that dlopen's an instrumented library should fail. +// RUN: not %run %t %t.so 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// Launching a non-instrumented binary with an explicit DYLD_INSERT_LIBRARIES should work. +// RUN: DYLD_INSERT_LIBRARIES=$TSAN_DYLIB_PATH %run %t %t.so 2>&1 | FileCheck %s + +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> + +#if defined(SHARED_LIB) +extern "C" void foo() { + fprintf(stderr, "Hello world.\n"); +} +#else // defined(SHARED_LIB) +int main(int argc, char *argv[]) { + void *handle = dlopen(argv[1], RTLD_NOW); + fprintf(stderr, "handle = %p\n", handle); + void (*foo)() = (void (*)())dlsym(handle, "foo"); + fprintf(stderr, "foo = %p\n", foo); + foo(); +} +#endif // defined(SHARED_LIB) + +// CHECK: Hello world. +// CHECK-NOT: ERROR: Interceptors are not working. + +// CHECK-FAIL-NOT: Hello world. +// CHECK-FAIL: ERROR: Interceptors are not working. diff --git a/test/tsan/Darwin/gcd-after.mm b/test/tsan/Darwin/gcd-after.mm new file mode 100644 index 000000000000..49b6bc6f71e9 --- /dev/null +++ b/test/tsan/Darwin/gcd-after.mm @@ -0,0 +1,41 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long my_global; +long my_global2; + +void callback(void *context) { + my_global2 = 42; + + dispatch_async(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetMain()); + }); +} + +int main(int argc, const char *argv[]) { + fprintf(stderr, "start\n"); + + my_global = 10; + dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_MSEC)), q, ^{ + my_global = 42; + + dispatch_async(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetMain()); + }); + }); + CFRunLoopRun(); + + my_global2 = 10; + dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_MSEC)), q, NULL, &callback); + CFRunLoopRun(); + + fprintf(stderr, "done\n"); + return 0; +} + +// CHECK: start +// CHECK: done +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-apply-race.mm b/test/tsan/Darwin/gcd-apply-race.mm new file mode 100644 index 000000000000..13b24e0fdb90 --- /dev/null +++ b/test/tsan/Darwin/gcd-apply-race.mm @@ -0,0 +1,26 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import "../test.h" + +long global; + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "start\n"); + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_apply(2, q, ^(size_t i) { + global = i; + barrier_wait(&barrier); + }); + + fprintf(stderr, "done\n"); + return 0; +} + +// CHECK: start +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'global' +// CHECK: done diff --git a/test/tsan/Darwin/gcd-apply.mm b/test/tsan/Darwin/gcd-apply.mm new file mode 100644 index 000000000000..e68a4b18205d --- /dev/null +++ b/test/tsan/Darwin/gcd-apply.mm @@ -0,0 +1,44 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import "../test.h" + +long global; +long array[2]; + +void callback(void *context, size_t i) { + long n = global; + array[i] = n + i; + barrier_wait(&barrier); +} + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "start\n"); + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + global = 42; + + dispatch_apply(100, q, ^(size_t i) { + long n = global; + array[i] = n + i; + barrier_wait(&barrier); + }); + + for (int i = 0; i < 100; i++) { + fprintf(stderr, "array[%d] = %ld\n", i, array[i]); + } + + global = 43; + + dispatch_apply_f(100, q, NULL, &callback); + + fprintf(stderr, "done\n"); + return 0; +} + +// CHECK: start +// CHECK: done +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-async-norace.mm b/test/tsan/Darwin/gcd-async-norace.mm index b987e00656fb..c7e28b4ce791 100644 --- a/test/tsan/Darwin/gcd-async-norace.mm +++ b/test/tsan/Darwin/gcd-async-norace.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> diff --git a/test/tsan/Darwin/gcd-async-race.mm b/test/tsan/Darwin/gcd-async-race.mm index 31163f972896..1002a56b0a16 100644 --- a/test/tsan/Darwin/gcd-async-race.mm +++ b/test/tsan/Darwin/gcd-async-race.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %deflake %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> @@ -9,7 +9,7 @@ long global; int main() { NSLog(@"Hello world."); - NSLog(@"addr=%p\n", &global); + print_address("addr=", 1, &global); barrier_init(&barrier, 2); global = 42; @@ -34,5 +34,5 @@ int main() { // CHECK: Hello world. // CHECK: addr=[[ADDR:0x[0-9,a-f]+]] // CHECK: WARNING: ThreadSanitizer: data race -// CHECK: Location is global 'global' at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}}) +// CHECK: Location is global 'global' {{(of size 8 )?}}at [[ADDR]] (gcd-async-race.mm.tmp+0x{{[0-9,a-f]+}}) // CHECK: Done. diff --git a/test/tsan/Darwin/gcd-barrier-race.mm b/test/tsan/Darwin/gcd-barrier-race.mm new file mode 100644 index 000000000000..c42eaebded2b --- /dev/null +++ b/test/tsan/Darwin/gcd-barrier-race.mm @@ -0,0 +1,48 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import "../test.h" + +long global; + +int main() { + fprintf(stderr, "Hello world.\n"); + print_address("addr=", 1, &global); + barrier_init(&barrier, 2); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_queue_t bgq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + dispatch_barrier_sync(q, ^{ + global = 42; + }); + + dispatch_async(bgq, ^{ + dispatch_sync(q, ^{ + global = 43; + barrier_wait(&barrier); + }); + }); + + dispatch_async(bgq, ^{ + dispatch_sync(q, ^{ + barrier_wait(&barrier); + global = 44; + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + }); + + CFRunLoopRun(); + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'global' {{(of size 8 )?}}at [[ADDR]] (gcd-barrier-race.mm.tmp+0x{{[0-9,a-f]+}}) +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-barrier.mm b/test/tsan/Darwin/gcd-barrier.mm new file mode 100644 index 000000000000..6f58cae7d8f5 --- /dev/null +++ b/test/tsan/Darwin/gcd-barrier.mm @@ -0,0 +1,49 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import "../test.h" + +long global; + +int main() { + fprintf(stderr, "Hello world.\n"); + print_address("addr=", 1, &global); + barrier_init(&barrier, 2); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_queue_t bgq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + dispatch_async(bgq, ^{ + dispatch_sync(q, ^{ + global = 42; + }); + barrier_wait(&barrier); + }); + + dispatch_async(bgq, ^{ + barrier_wait(&barrier); + dispatch_barrier_sync(q, ^{ + global = 43; + }); + + dispatch_async(bgq, ^{ + barrier_wait(&barrier); + global = 44; + }); + + barrier_wait(&barrier); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + CFRunLoopRun(); + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-blocks.mm b/test/tsan/Darwin/gcd-blocks.mm new file mode 100644 index 000000000000..e0082605f24e --- /dev/null +++ b/test/tsan/Darwin/gcd-blocks.mm @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +int main() { + fprintf(stderr, "start\n"); + + dispatch_queue_t background_q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_queue_t main_q = dispatch_get_main_queue(); + + dispatch_async(background_q, ^{ + __block long block_var = 0; + + dispatch_sync(main_q, ^{ + block_var = 42; + }); + + fprintf(stderr, "block_var = %ld\n", block_var); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + CFRunLoopRun(); + fprintf(stderr, "done\n"); +} + +// CHECK: start +// CHECK: block_var = 42 +// CHECK: done +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: CHECK failed diff --git a/test/tsan/Darwin/gcd-data.mm b/test/tsan/Darwin/gcd-data.mm new file mode 100644 index 000000000000..a5154dc3598d --- /dev/null +++ b/test/tsan/Darwin/gcd-data.mm @@ -0,0 +1,36 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long global; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL); + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + + global = 44; + dispatch_data_t data = dispatch_data_create("buffer", 6, q, ^{ + fprintf(stderr, "Data destructor.\n"); + global++; + + dispatch_semaphore_signal(sem); + }); + dispatch_release(data); + data = nil; + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + data = dispatch_data_create("buffer", 6, q, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + dispatch_release(data); + data = nil; + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: Data destructor. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-fd.mm b/test/tsan/Darwin/gcd-fd.mm new file mode 100644 index 000000000000..75da9cd4252d --- /dev/null +++ b/test/tsan/Darwin/gcd-fd.mm @@ -0,0 +1,60 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL); + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + + NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path.fileSystemRepresentation, O_CREAT | O_WRONLY, + 0666, queue, ^(int error) { }); + dispatch_io_set_high_water(channel, 1); + + NSData *ns_data = [NSMutableData dataWithLength:1000]; + dispatch_data_t data = dispatch_data_create(ns_data.bytes, ns_data.length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + my_global++; + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + my_global++; + dispatch_async(queue, ^{ + my_global++; + if (done) { + dispatch_semaphore_signal(sem); + } + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + dispatch_io_close(channel, 0); + channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path.fileSystemRepresentation, O_RDONLY, + 0, queue, ^(int error) { }); + dispatch_io_set_high_water(channel, 1); + + my_global++; + dispatch_io_read(channel, 0, SIZE_MAX, queue, ^(bool done, dispatch_data_t remainingData, int error) { + my_global++; + dispatch_async(queue, ^{ + my_global++; + if (done) { + dispatch_semaphore_signal(sem); + } + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-groups-destructor.mm b/test/tsan/Darwin/gcd-groups-destructor.mm new file mode 100644 index 000000000000..19c2c9bd147f --- /dev/null +++ b/test/tsan/Darwin/gcd-groups-destructor.mm @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import <memory> +#import <stdatomic.h> + +_Atomic(long) destructor_counter = 0; + +struct MyStruct { + virtual ~MyStruct() { + usleep(10000); + atomic_fetch_add_explicit(&destructor_counter, 1, memory_order_relaxed); + } +}; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_group_t g = dispatch_group_create(); + + for (int i = 0; i < 100; i++) { + std::shared_ptr<MyStruct> shared(new MyStruct()); + + dispatch_group_async(g, q, ^{ + shared.get(); // just to make sure the object is captured by the block + }); + } + + dispatch_group_wait(g, DISPATCH_TIME_FOREVER); + + if (destructor_counter != 100) { + abort(); + } + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-groups-leave.mm b/test/tsan/Darwin/gcd-groups-leave.mm new file mode 100644 index 000000000000..6ecf85f5ff0d --- /dev/null +++ b/test/tsan/Darwin/gcd-groups-leave.mm @@ -0,0 +1,56 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import "../test.h" + +dispatch_semaphore_t sem; + +long global; +long global2; + +void callback(void *context) { + global2 = 48; + barrier_wait(&barrier); + + dispatch_semaphore_signal(sem); +} + +int main() { + fprintf(stderr, "Hello world.\n"); + barrier_init(&barrier, 2); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_group_t g = dispatch_group_create(); + sem = dispatch_semaphore_create(0); + + dispatch_group_enter(g); + dispatch_async(q, ^{ + global = 47; + dispatch_group_leave(g); + barrier_wait(&barrier); + }); + dispatch_group_notify(g, q, ^{ + global = 48; + barrier_wait(&barrier); + + dispatch_semaphore_signal(sem); + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + dispatch_group_enter(g); + dispatch_async(q, ^{ + global2 = 47; + dispatch_group_leave(g); + barrier_wait(&barrier); + }); + dispatch_group_notify_f(g, q, NULL, &callback); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-groups-norace.mm b/test/tsan/Darwin/gcd-groups-norace.mm index fb4d804ed8c7..64ec386ca40f 100644 --- a/test/tsan/Darwin/gcd-groups-norace.mm +++ b/test/tsan/Darwin/gcd-groups-norace.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> diff --git a/test/tsan/Darwin/gcd-groups-stress.mm b/test/tsan/Darwin/gcd-groups-stress.mm index 62a80085ed8d..457d9afd9c93 100644 --- a/test/tsan/Darwin/gcd-groups-stress.mm +++ b/test/tsan/Darwin/gcd-groups-stress.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> @@ -30,7 +30,7 @@ int main() { dispatch_async(q, ^{ dispatch_group_leave(g); }); - dispatch_group_notify_f(g, q, nullptr, ¬ify_callback); + dispatch_group_notify_f(g, q, NULL, ¬ify_callback); dispatch_release(g); } diff --git a/test/tsan/Darwin/gcd-io-barrier-race.mm b/test/tsan/Darwin/gcd-io-barrier-race.mm new file mode 100644 index 000000000000..fffc19bd16de --- /dev/null +++ b/test/tsan/Darwin/gcd-io-barrier-race.mm @@ -0,0 +1,55 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import "../test.h" + +dispatch_queue_t queue; +dispatch_data_t data; +dispatch_semaphore_t sem; +const char *path; + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + print_address("addr=", 1, &my_global); + barrier_init(&barrier, 2); + + queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + sem = dispatch_semaphore_create(0); + NSString *ns_path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + path = ns_path.fileSystemRepresentation; + NSData *ns_data = [NSMutableData dataWithLength:1000]; + data = dispatch_data_create(ns_data.bytes, ns_data.length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { }); + if (! channel) abort(); + dispatch_io_set_high_water(channel, 1); + + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + if (error) abort(); + my_global = 42; + barrier_wait(&barrier); + }); + + dispatch_io_barrier(channel, ^{ + barrier_wait(&barrier); + my_global = 43; + + dispatch_semaphore_signal(sem); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_io_close(channel, 0); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'my_global' {{(of size 8 )?}}at [[ADDR]] (gcd-io-barrier-race.mm.tmp+0x{{[0-9,a-f]+}}) +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-io-barrier.mm b/test/tsan/Darwin/gcd-io-barrier.mm new file mode 100644 index 000000000000..fe30138f6036 --- /dev/null +++ b/test/tsan/Darwin/gcd-io-barrier.mm @@ -0,0 +1,48 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +dispatch_queue_t queue; +dispatch_data_t data; +dispatch_semaphore_t sem; +const char *path; + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + sem = dispatch_semaphore_create(0); + NSString *ns_path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + path = ns_path.fileSystemRepresentation; + NSData *ns_data = [NSMutableData dataWithLength:1000]; + data = dispatch_data_create(ns_data.bytes, ns_data.length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { }); + if (! channel) abort(); + dispatch_io_set_high_water(channel, 1); + + for (int i = 0; i < 1000; i++) { + dispatch_io_barrier(channel, ^{ + my_global = 42; + }); + } + + dispatch_io_barrier(channel, ^{ + my_global = 43; + + dispatch_semaphore_signal(sem); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_io_close(channel, 0); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-io-cleanup.mm b/test/tsan/Darwin/gcd-io-cleanup.mm new file mode 100644 index 000000000000..b15fa0dc2532 --- /dev/null +++ b/test/tsan/Darwin/gcd-io-cleanup.mm @@ -0,0 +1,56 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + NSString *ns_path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + const char *path = ns_path.fileSystemRepresentation; + dispatch_io_t channel; + + dispatch_fd_t fd = open(path, O_CREAT | O_WRONLY, 0666); + my_global++; + channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) { + my_global++; + dispatch_semaphore_signal(sem); + }); + if (! channel) abort(); + my_global++; + dispatch_io_close(channel, 0); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + my_global++; + channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { + my_global++; + dispatch_semaphore_signal(sem); + }); + if (! channel) abort(); + my_global++; + dispatch_io_close(channel, 0); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + my_global++; + dispatch_io_t other_channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { }); + channel = dispatch_io_create_with_io(DISPATCH_IO_STREAM, other_channel, queue, ^(int error) { + my_global++; + dispatch_semaphore_signal(sem); + }); + if (! channel) abort(); + my_global++; + dispatch_io_close(channel, 0); + dispatch_io_close(other_channel, 0); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-io-race.mm b/test/tsan/Darwin/gcd-io-race.mm new file mode 100644 index 000000000000..0bec28fdb758 --- /dev/null +++ b/test/tsan/Darwin/gcd-io-race.mm @@ -0,0 +1,56 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s + +// REQUIRES: disabled + +#import <Foundation/Foundation.h> + +#import "../test.h" + +dispatch_queue_t queue; +dispatch_data_t data; +dispatch_semaphore_t sem; +const char *path; + +long my_global = 0; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + print_address("addr=", 1, &my_global); + barrier_init(&barrier, 2); + + queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + sem = dispatch_semaphore_create(0); + NSString *ns_path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + path = ns_path.fileSystemRepresentation; + NSData *ns_data = [NSMutableData dataWithLength:1000]; + data = dispatch_data_create(ns_data.bytes, ns_data.length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { }); + if (! channel) abort(); + dispatch_io_set_high_water(channel, 1); + + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + my_global = 42; + barrier_wait(&barrier); + }); + + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + barrier_wait(&barrier); + my_global = 42; + + dispatch_semaphore_signal(sem); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + dispatch_io_close(channel, 0); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'my_global' {{(of size 8 )?}}at [[ADDR]] (gcd-io-race.mm.tmp+0x{{[0-9,a-f]+}}) +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-io.mm b/test/tsan/Darwin/gcd-io.mm new file mode 100644 index 000000000000..4a1726dac8c7 --- /dev/null +++ b/test/tsan/Darwin/gcd-io.mm @@ -0,0 +1,117 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +dispatch_queue_t queue; +dispatch_data_t data; +dispatch_semaphore_t sem; +const char *path; + +long my_global = 0; + +void test_dispatch_io_write() { + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_CREAT | O_WRONLY, 0666, queue, ^(int error) { }); + if (! channel) abort(); + dispatch_io_set_high_water(channel, 1); + + my_global++; + dispatch_io_write(channel, 0, data, queue, ^(bool done, dispatch_data_t remainingData, int error) { + if (error) abort(); + my_global++; + dispatch_async(queue, ^{ + my_global++; + if (done) { + dispatch_semaphore_signal(sem); + } + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + dispatch_io_close(channel, 0); +} + +void test_dispatch_write() { + dispatch_fd_t fd = open(path, O_CREAT | O_WRONLY, 0666); + if (fd == -1) abort(); + + my_global++; + dispatch_write(fd, data, queue, ^(dispatch_data_t data, int error) { + if (error) abort(); + my_global++; + dispatch_async(queue, ^{ + my_global++; + + dispatch_semaphore_signal(sem); + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + close(fd); +} + +void test_dispatch_io_read() { + dispatch_io_t channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, path, O_RDONLY, + 0, queue, ^(int error) { }); + dispatch_io_set_high_water(channel, 1); + + my_global++; + dispatch_io_read(channel, 0, SIZE_MAX, queue, ^(bool done, dispatch_data_t remainingData, int error) { + if (error) abort(); + my_global++; + dispatch_async(queue, ^{ + my_global++; + if (done) { + dispatch_semaphore_signal(sem); + } + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + dispatch_io_close(channel, 0); +} + +void test_dispatch_read() { + dispatch_fd_t fd = open(path, O_RDONLY, 0); + if (fd == -1) abort(); + + my_global++; + dispatch_read(fd, SIZE_MAX, queue, ^(dispatch_data_t data, int error) { + if (error) abort(); + my_global++; + dispatch_async(queue, ^{ + my_global++; + dispatch_semaphore_signal(sem); + }); + }); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + my_global++; + close(fd); +} + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL); + sem = dispatch_semaphore_create(0); + NSString *ns_path = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"temp-gcd-io.%d", getpid()]]; + path = ns_path.fileSystemRepresentation; + NSData *ns_data = [NSMutableData dataWithLength:1000]; + data = dispatch_data_create(ns_data.bytes, ns_data.length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + test_dispatch_io_write(); + test_dispatch_write(); + test_dispatch_io_read(); + test_dispatch_read(); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-once.mm b/test/tsan/Darwin/gcd-once.mm index 17757d203751..3e4a5335607c 100644 --- a/test/tsan/Darwin/gcd-once.mm +++ b/test/tsan/Darwin/gcd-once.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> diff --git a/test/tsan/Darwin/gcd-semaphore-norace.mm b/test/tsan/Darwin/gcd-semaphore-norace.mm index cd52a79ca65a..20bc5724d165 100644 --- a/test/tsan/Darwin/gcd-semaphore-norace.mm +++ b/test/tsan/Darwin/gcd-semaphore-norace.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> diff --git a/test/tsan/Darwin/gcd-serial-queue-norace.mm b/test/tsan/Darwin/gcd-serial-queue-norace.mm index 8f6de27695a5..95efbb764c53 100644 --- a/test/tsan/Darwin/gcd-serial-queue-norace.mm +++ b/test/tsan/Darwin/gcd-serial-queue-norace.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> diff --git a/test/tsan/Darwin/gcd-source-cancel.mm b/test/tsan/Darwin/gcd-source-cancel.mm new file mode 100644 index 000000000000..86e1b28a61c4 --- /dev/null +++ b/test/tsan/Darwin/gcd-source-cancel.mm @@ -0,0 +1,36 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long global; + +int main(int argc, const char *argv[]) { + dispatch_queue_t queue = + dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + dispatch_source_t source = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + + dispatch_source_set_timer(source, dispatch_walltime(NULL, 0), 1e9, 5); + + global = 42; + + dispatch_source_set_cancel_handler(source, ^{ + fprintf(stderr, "global = %ld\n", global); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + dispatch_resume(source); + dispatch_cancel(source); + + CFRunLoopRun(); + + return 0; +} + +// CHECK: global = 42 +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-source-cancel2.mm b/test/tsan/Darwin/gcd-source-cancel2.mm new file mode 100644 index 000000000000..956fe87298bb --- /dev/null +++ b/test/tsan/Darwin/gcd-source-cancel2.mm @@ -0,0 +1,38 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long global; + +void handler(void *arg) { + fprintf(stderr, "global = %ld\n", global); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); +} + +int main(int argc, const char *argv[]) { + dispatch_queue_t queue = + dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + dispatch_source_t source = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + + dispatch_source_set_timer(source, dispatch_walltime(NULL, 0), 1e9, 5); + + global = 42; + + dispatch_source_set_cancel_handler_f(source, &handler); + + dispatch_resume(source); + dispatch_cancel(source); + + CFRunLoopRun(); + + return 0; +} + +// CHECK: global = 42 +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-source-event.mm b/test/tsan/Darwin/gcd-source-event.mm new file mode 100644 index 000000000000..e50cb568de1e --- /dev/null +++ b/test/tsan/Darwin/gcd-source-event.mm @@ -0,0 +1,35 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long global; + +int main(int argc, const char *argv[]) { + dispatch_queue_t queue = + dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + dispatch_source_t source = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + + dispatch_source_set_timer(source, dispatch_walltime(NULL, 0), 1e9, 5); + + global = 42; + + dispatch_source_set_event_handler(source, ^{ + fprintf(stderr, "global = %ld\n", global); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + dispatch_resume(source); + + CFRunLoopRun(); + + return 0; +} + +// CHECK: global = 42 +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-source-event2.mm b/test/tsan/Darwin/gcd-source-event2.mm new file mode 100644 index 000000000000..c45d481a0028 --- /dev/null +++ b/test/tsan/Darwin/gcd-source-event2.mm @@ -0,0 +1,37 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long global; + +void handler(void *arg) { + fprintf(stderr, "global = %ld\n", global); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); +} + +int main(int argc, const char *argv[]) { + dispatch_queue_t queue = + dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + dispatch_source_t source = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + + dispatch_source_set_timer(source, dispatch_walltime(NULL, 0), 1e9, 5); + + global = 42; + + dispatch_source_set_event_handler_f(source, &handler); + + dispatch_resume(source); + + CFRunLoopRun(); + + return 0; +} + +// CHECK: global = 42 +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-source-registration.mm b/test/tsan/Darwin/gcd-source-registration.mm new file mode 100644 index 000000000000..db22613eddae --- /dev/null +++ b/test/tsan/Darwin/gcd-source-registration.mm @@ -0,0 +1,33 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long global; + +int main(int argc, const char *argv[]) { + dispatch_queue_t queue = + dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + dispatch_source_t source = + dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, queue); + + global = 42; + + dispatch_source_set_registration_handler(source, ^{ + fprintf(stderr, "global = %ld\n", global); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + dispatch_resume(source); + + CFRunLoopRun(); + + return 0; +} + +// CHECK: global = 42 +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-source-registration2.mm b/test/tsan/Darwin/gcd-source-registration2.mm new file mode 100644 index 000000000000..4431bc9d6898 --- /dev/null +++ b/test/tsan/Darwin/gcd-source-registration2.mm @@ -0,0 +1,35 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long global; + +void handler(void *arg) { + fprintf(stderr, "global = %ld\n", global); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); +} + +int main(int argc, const char *argv[]) { + dispatch_queue_t queue = + dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); + + dispatch_source_t source = + dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, queue); + + global = 42; + + dispatch_source_set_registration_handler_f(source, handler); + + dispatch_resume(source); + + CFRunLoopRun(); + + return 0; +} + +// CHECK: global = 42 +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/gcd-source-serial.mm b/test/tsan/Darwin/gcd-source-serial.mm new file mode 100644 index 000000000000..c0989fcc732a --- /dev/null +++ b/test/tsan/Darwin/gcd-source-serial.mm @@ -0,0 +1,33 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +long global; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL); + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, q); + long long interval_ms = 10; + dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval_ms * NSEC_PER_MSEC, 0); + dispatch_source_set_event_handler(timer, ^{ + fprintf(stderr, "timer\n"); + global++; + + if (global > 50) { + dispatch_semaphore_signal(sem); + } + }); + dispatch_resume(timer); + + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/gcd-sync-norace.mm b/test/tsan/Darwin/gcd-sync-norace.mm index f21cfdedbce1..c683524f73b6 100644 --- a/test/tsan/Darwin/gcd-sync-norace.mm +++ b/test/tsan/Darwin/gcd-sync-norace.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> diff --git a/test/tsan/Darwin/gcd-sync-race.mm b/test/tsan/Darwin/gcd-sync-race.mm index 62901d9b2612..650faa4e082c 100644 --- a/test/tsan/Darwin/gcd-sync-race.mm +++ b/test/tsan/Darwin/gcd-sync-race.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %deflake %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> @@ -9,7 +9,7 @@ long global; int main() { NSLog(@"Hello world."); - NSLog(@"addr=%p\n", &global); + print_address("addr=", 1, &global); barrier_init(&barrier, 2); dispatch_queue_t q1 = dispatch_queue_create("my.queue1", DISPATCH_QUEUE_CONCURRENT); @@ -40,5 +40,5 @@ int main() { // CHECK: Hello world. // CHECK: addr=[[ADDR:0x[0-9,a-f]+]] // CHECK: WARNING: ThreadSanitizer: data race -// CHECK: Location is global 'global' at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}}) +// CHECK: Location is global 'global' {{(of size 8 )?}}at [[ADDR]] (gcd-sync-race.mm.tmp+0x{{[0-9,a-f]+}}) // CHECK: Done. diff --git a/test/tsan/Darwin/ignored-interceptors.mm b/test/tsan/Darwin/ignored-interceptors.mm new file mode 100644 index 000000000000..d51314281844 --- /dev/null +++ b/test/tsan/Darwin/ignored-interceptors.mm @@ -0,0 +1,55 @@ +// Check that ignore_interceptors_accesses=1 supresses reporting races from +// system libraries on OS X. There are currently false positives coming from +// libxpc, libdispatch, CoreFoundation and others, because these libraries use +// TSan-invisible atomics as synchronization. + +// RUN: %clang_tsan %s -o %t -framework Foundation + +// Check that without the flag, there are false positives. +// RUN: %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE + +// With ignore_interceptors_accesses=1, no races are reported. +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +// With ignore_interceptors_accesses=1, races in user's code are still reported. +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t race 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RACE + +#import <Foundation/Foundation.h> + +#import "../test.h" + +long global; + +void *Thread1(void *x) { + barrier_wait(&barrier); + global = 42; + return NULL; +} + +void *Thread2(void *x) { + global = 43; + barrier_wait(&barrier); + return NULL; +} + +int main(int argc, char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + // NSUserDefaults uses XPC which triggers the false positive. + NSDictionary *d = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]; + + if (argc > 1 && strcmp(argv[1], "race") == 0) { + barrier_init(&barrier, 2); + 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"); +} + +// CHECK: Hello world. +// CHECK-RACE: SUMMARY: ThreadSanitizer: data race +// CHECK: Done. diff --git a/test/tsan/Darwin/libcxx-shared-ptr-recursive.mm b/test/tsan/Darwin/libcxx-shared-ptr-recursive.mm new file mode 100644 index 000000000000..eea02dc561e1 --- /dev/null +++ b/test/tsan/Darwin/libcxx-shared-ptr-recursive.mm @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import <memory> + +struct InnerStruct { + ~InnerStruct() { + fprintf(stderr, "~InnerStruct\n"); + } +}; + +struct MyStruct { + std::shared_ptr<InnerStruct> inner_object; + ~MyStruct() { + fprintf(stderr, "~MyStruct\n"); + } +}; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + { + std::shared_ptr<MyStruct> shared(new MyStruct()); + shared->inner_object = std::shared_ptr<InnerStruct>(new InnerStruct()); + } + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: ~MyStruct +// CHECK: ~InnerStruct +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/libcxx-shared-ptr-stress.mm b/test/tsan/Darwin/libcxx-shared-ptr-stress.mm new file mode 100644 index 000000000000..7c36729f010f --- /dev/null +++ b/test/tsan/Darwin/libcxx-shared-ptr-stress.mm @@ -0,0 +1,75 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import <assert.h> +#import <memory> +#import <stdatomic.h> + +_Atomic(long) shared_call_counter = 0; +_Atomic(long) weak_call_counter = 0; +_Atomic(long) destructor_counter = 0; +_Atomic(long) weak_destroyed_counter = 0; + +struct MyStruct { + _Atomic(long) self_counter = 0; + virtual void shared_call() { + atomic_fetch_add_explicit(&self_counter, 1, memory_order_relaxed); + atomic_fetch_add_explicit(&shared_call_counter, 1, memory_order_relaxed); + } + virtual void weak_call() { + atomic_fetch_add_explicit(&weak_call_counter, 1, memory_order_relaxed); + } + virtual ~MyStruct() { + long n = self_counter; + assert(n == 1000); + atomic_fetch_add_explicit(&destructor_counter, 1, memory_order_relaxed); + } +}; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + dispatch_group_t g = dispatch_group_create(); + + for (int i = 0; i < 1000; i++) { + std::shared_ptr<MyStruct> shared(new MyStruct()); + std::weak_ptr<MyStruct> weak(shared); + + dispatch_group_async(g, q, ^{ + for (int j = 0; j < 1000; j++) { + std::shared_ptr<MyStruct> shared_copy(shared); + shared_copy->shared_call(); + } + }); + dispatch_group_async(g, q, ^{ + for (int j = 0; j < 1000; j++) { + std::shared_ptr<MyStruct> weak_copy = weak.lock(); + if (weak_copy) { + weak_copy->weak_call(); + } else { + atomic_fetch_add_explicit(&weak_destroyed_counter, 1, memory_order_relaxed); + break; + } + } + }); + } + + dispatch_group_wait(g, DISPATCH_TIME_FOREVER); + + fprintf(stderr, "shared_call_counter = %ld\n", shared_call_counter); + fprintf(stderr, "weak_call_counter = %ld\n", weak_call_counter); + fprintf(stderr, "destructor_counter = %ld\n", destructor_counter); + fprintf(stderr, "weak_destroyed_counter = %ld\n", weak_destroyed_counter); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: shared_call_counter = 1000000 +// CHECK: destructor_counter = 1000 +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/libcxx-shared-ptr.mm b/test/tsan/Darwin/libcxx-shared-ptr.mm new file mode 100644 index 000000000000..6187c438fec0 --- /dev/null +++ b/test/tsan/Darwin/libcxx-shared-ptr.mm @@ -0,0 +1,50 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import <memory> + +#import "../test.h" + +long my_global; + +struct MyStruct { + void setGlobal() { + my_global = 42; + } + ~MyStruct() { + my_global = 43; + } +}; + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + print_address("addr=", 1, &my_global); + barrier_init(&barrier, 2); + + std::shared_ptr<MyStruct> shared(new MyStruct()); + + dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + std::weak_ptr<MyStruct> weak(shared); + + dispatch_async(q, ^{ + { + std::shared_ptr<MyStruct> strong = weak.lock(); + if (!strong) exit(1); + + strong->setGlobal(); + } + barrier_wait(&barrier); + }); + + barrier_wait(&barrier); + shared.reset(); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/malloc-stack-logging.cc b/test/tsan/Darwin/malloc-stack-logging.cc new file mode 100644 index 000000000000..8d9c2122d0e6 --- /dev/null +++ b/test/tsan/Darwin/malloc-stack-logging.cc @@ -0,0 +1,24 @@ +// Test that MallocStackLogging=1 doesn't crash. MallocStackLogging turns on +// callbacks from mmap/munmap libc function into libmalloc. Darwin-specific +// ThreadState initialization needs to avoid calling the library functions (and +// use syscalls directly) to make sure other interceptors aren't called. + +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: MallocStackLogging=1 %run %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +void *foo(void *p) { + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, foo, NULL); + pthread_join(t, NULL); + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Done. diff --git a/test/tsan/Darwin/malloc_size.mm b/test/tsan/Darwin/malloc_size.mm new file mode 100644 index 000000000000..485d85bba4f8 --- /dev/null +++ b/test/tsan/Darwin/malloc_size.mm @@ -0,0 +1,57 @@ +// Test that malloc_zone_from_ptr returns a valid zone for a 0-sized allocation. + +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %run %t 2>&1 | FileCheck %s + +#include <malloc/malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> + +int some_global; + +void describe_zone(void *p) { + malloc_zone_t *z = malloc_zone_from_ptr(p); + if (z) { + fprintf(stderr, "zone = %p\n", z); + } else { + fprintf(stderr, "zone = no zone\n"); + } +} + +int main() { + void *p; + size_t s; + + p = malloc(0x40); + s = malloc_size(p); + fprintf(stderr, "size = 0x%zx\n", s); + // CHECK: size = 0x40 + describe_zone(p); + // CHECK: zone = 0x{{[0-9a-f]+}} + + p = malloc(0); + s = malloc_size(p); + fprintf(stderr, "size = 0x%zx\n", s); + // CHECK: size = 0x1 + describe_zone(p); + // CHECK: zone = 0x{{[0-9a-f]+}} + + p = &some_global; + s = malloc_size(p); + fprintf(stderr, "size = 0x%zx\n", s); + // CHECK: size = 0x0 + describe_zone(p); + // CHECK: zone = no zone + + p = mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); + if (!p) { + fprintf(stderr, "mmap failed\n"); + exit(1); + } + s = malloc_size(p); + fprintf(stderr, "size = 0x%zx\n", s); + // CHECK: size = 0x0 + describe_zone(p); + // CHECK: zone = no zone +} diff --git a/test/tsan/Darwin/objc-race.mm b/test/tsan/Darwin/objc-race.mm index bd93d2f1c2ea..82fcc4ef1785 100644 --- a/test/tsan/Darwin/objc-race.mm +++ b/test/tsan/Darwin/objc-race.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %deflake %run %t 2>&1 +// RUN: %deflake %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> @@ -49,7 +49,7 @@ int main() { // CHECK: WARNING: ThreadSanitizer: data race // CHECK: Write of size 8 // CHECK: #0 -[MyClass method:] -// CHECK: Write of size 8 +// CHECK: Previous write of size 8 // CHECK: #0 -[MyClass method:] // CHECK: Location is heap block // CHECK: Done. diff --git a/test/tsan/Darwin/objc-simple.mm b/test/tsan/Darwin/objc-simple.mm index a4bf1f1beaa0..a8fc35592962 100644 --- a/test/tsan/Darwin/objc-simple.mm +++ b/test/tsan/Darwin/objc-simple.mm @@ -1,7 +1,7 @@ // Test that a simple Obj-C program runs and exits without any warnings. // RUN: %clang_tsan %s -o %t -framework Foundation -// RUN: %run %t 2>&1 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s #import <Foundation/Foundation.h> diff --git a/test/tsan/Darwin/osatomics-add.mm b/test/tsan/Darwin/osatomics-add.mm new file mode 100644 index 000000000000..087958eff0f8 --- /dev/null +++ b/test/tsan/Darwin/osatomics-add.mm @@ -0,0 +1,48 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation -std=c++11 +// RUN: %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> +#import <libkern/OSAtomic.h> + +#include <thread> + +volatile int64_t retainCount = 0; + +long g = 0; + +void dealloc() { + g = 42; +} + +void release() { + if (OSAtomicAdd64Barrier(-1, &retainCount) == 0) { + dealloc(); + } +} + +void retain() { + OSAtomicAdd64Barrier(1, &retainCount); +} + +int main(int argc, const char * argv[]) { + fprintf(stderr, "start\n"); + retain(); + retain(); + + std::thread t([]{ + release(); + }); + + g = 47; + + release(); + t.join(); + + fprintf(stderr, "end, g = %ld\n", g); + + return 0; +} + +// CHECK: start +// CHECK: end, g = 42 +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/osatomics-list.mm b/test/tsan/Darwin/osatomics-list.mm new file mode 100644 index 000000000000..6c2fbe7e5c5a --- /dev/null +++ b/test/tsan/Darwin/osatomics-list.mm @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation -std=c++11 +// RUN: %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> +#import <libkern/OSAtomic.h> + +#include <thread> + +#include "../test.h" + +typedef struct { + void *next; + long data; +} ListItem; + +OSQueueHead q; + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + + std::thread t1([] { + ListItem *li = new ListItem{nullptr, 42}; + OSAtomicEnqueue(&q, li, 0); + barrier_wait(&barrier); + }); + + std::thread t2([] { + barrier_wait(&barrier); + ListItem *li = (ListItem *)OSAtomicDequeue(&q, 0); + fprintf(stderr, "data = %ld\n", li->data); + }); + + t1.join(); + t2.join(); + + fprintf(stderr, "done\n"); + + return 0; +} + +// CHECK: data = 42 +// CHECK: done +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/xpc-race.mm b/test/tsan/Darwin/xpc-race.mm new file mode 100644 index 000000000000..9141da42e3a0 --- /dev/null +++ b/test/tsan/Darwin/xpc-race.mm @@ -0,0 +1,81 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %deflake %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> +#import <xpc/xpc.h> + +#import "../test.h" + +long global; + +long received_msgs; +xpc_connection_t server_conn; +xpc_connection_t client_conns[2]; + +int main(int argc, const char *argv[]) { + @autoreleasepool { + NSLog(@"Hello world."); + barrier_init(&barrier, 2); + + dispatch_queue_t server_q = dispatch_queue_create("server.queue", DISPATCH_QUEUE_CONCURRENT); + + 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); + + 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); + + barrier_wait(&barrier); + global = 42; + + dispatch_sync(dispatch_get_main_queue(), ^{ + received_msgs++; + + if (received_msgs >= 2) { + xpc_connection_cancel(client_conns[0]); + xpc_connection_cancel(client_conns[1]); + xpc_connection_cancel(server_conn); + CFRunLoopStop(CFRunLoopGetCurrent()); + } + }); + }); + + xpc_connection_resume(client); + }); + xpc_connection_resume(server_conn); + xpc_endpoint_t endpoint = xpc_endpoint_create(server_conn); + + 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); + }); + + xpc_object_t msg = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_string(msg, "hello", "world"); + NSLog(@"sending message: %@", msg); + + xpc_connection_send_message(client_conns[i], msg); + xpc_connection_resume(client_conns[i]); + } + + CFRunLoopRun(); + + NSLog(@"Done."); + } + return 0; +} + +// CHECK: Hello world. +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 8 +// CHECK: #0 {{.*}}xpc-race.mm:34 +// CHECK: Previous write of size 8 +// CHECK: #0 {{.*}}xpc-race.mm:34 +// CHECK: Location is global 'global' +// CHECK: Done. diff --git a/test/tsan/Darwin/xpc.mm b/test/tsan/Darwin/xpc.mm new file mode 100644 index 000000000000..a939b02ef21a --- /dev/null +++ b/test/tsan/Darwin/xpc.mm @@ -0,0 +1,74 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> +#import <xpc/xpc.h> + +long global; + +int main(int argc, const char *argv[]) { + @autoreleasepool { + NSLog(@"Hello world."); + + dispatch_queue_t server_q = dispatch_queue_create("server.queue", DISPATCH_QUEUE_CONCURRENT); + dispatch_queue_t client_q = dispatch_queue_create("client.queue", DISPATCH_QUEUE_CONCURRENT); + + xpc_connection_t server_conn = xpc_connection_create(NULL, server_q); + + global = 42; + + xpc_connection_set_event_handler(server_conn, ^(xpc_object_t client) { + NSLog(@"global = %ld", global); + NSLog(@"server event handler, client = %@", 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); + + xpc_object_t reply = xpc_dictionary_create_reply(object); + if (!reply) + return; + xpc_dictionary_set_string(reply, "reply", "value"); + + xpc_connection_t remote = xpc_dictionary_get_remote_connection(object); + xpc_connection_send_message(remote, reply); + }); + + xpc_connection_resume(client); + }); + xpc_connection_resume(server_conn); + xpc_endpoint_t endpoint = xpc_endpoint_create(server_conn); + + xpc_connection_t client_conn = xpc_connection_create_from_endpoint(endpoint); + xpc_connection_set_event_handler(client_conn, ^(xpc_object_t event) { + NSLog(@"client event handler, event = %@", event); + }); + + xpc_object_t msg = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_string(msg, "hello", "world"); + NSLog(@"sending message: %@", msg); + + xpc_connection_send_message_with_reply( + client_conn, msg, client_q, ^(xpc_object_t object) { + NSLog(@"received reply: %@", object); + + xpc_connection_cancel(client_conn); + xpc_connection_cancel(server_conn); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + xpc_connection_resume(client_conn); + + CFRunLoopRun(); + + NSLog(@"Done."); + } + return 0; +} + +// CHECK: Done. +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Linux/check_preinit.cc b/test/tsan/Linux/check_preinit.cc new file mode 100644 index 000000000000..8f5bf4033760 --- /dev/null +++ b/test/tsan/Linux/check_preinit.cc @@ -0,0 +1,60 @@ +// RUN: %clang_tsan -fno-sanitize=thread -shared -fPIC -O1 -DBUILD_SO=1 %s -o \ +// RUN: %t.so && \ +// RUN: %clang_tsan -O1 %s %t.so -o %t && %run %t 2>&1 | FileCheck %s +// RUN: llvm-objdump -t %t | FileCheck %s --check-prefix=CHECK-DUMP +// CHECK-DUMP: {{[.]preinit_array.*__local_tsan_preinit}} + +// SANITIZER_CAN_USE_PREINIT_ARRAY is undefined on android. +// UNSUPPORTED: android + +// Test checks if __tsan_init is called from .preinit_array. +// Without initialization from .preinit_array, __tsan_init will be called from +// constructors of the binary which are called after constructors of shared +// library. + +#include <stdio.h> + +#if BUILD_SO + +// "volatile" is needed to avoid compiler optimize-out constructors. +volatile int counter = 0; +volatile int lib_constructor_call = 0; +volatile int tsan_init_call = 0; + +__attribute__ ((constructor)) +void LibConstructor() { + lib_constructor_call = ++counter; +}; + +#else // BUILD_SO + +extern int counter; +extern int lib_constructor_call; +extern int tsan_init_call; + +volatile int bin_constructor_call = 0; + +__attribute__ ((constructor)) +void BinConstructor() { + bin_constructor_call = ++counter; +}; + +namespace __tsan { + +void OnInitialize() { + tsan_init_call = ++counter; +} + +} + +int main() { + // CHECK: TSAN_INIT 1 + // CHECK: LIB_CONSTRUCTOR 2 + // CHECK: BIN_CONSTRUCTOR 3 + printf("TSAN_INIT %d\n", tsan_init_call); + printf("LIB_CONSTRUCTOR %d\n", lib_constructor_call); + printf("BIN_CONSTRUCTOR %d\n", bin_constructor_call); + return 0; +} + +#endif // BUILD_SO diff --git a/test/tsan/Linux/user_malloc.cc b/test/tsan/Linux/user_malloc.cc index c671bfcdd17a..9c3ce681d748 100644 --- a/test/tsan/Linux/user_malloc.cc +++ b/test/tsan/Linux/user_malloc.cc @@ -8,7 +8,7 @@ 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"); + fprintf(stderr, "user malloc\n"); return __interceptor_malloc(size); } diff --git a/test/tsan/Unit/lit.site.cfg.in b/test/tsan/Unit/lit.site.cfg.in index 9498105653a1..23894a839856 100644 --- a/test/tsan/Unit/lit.site.cfg.in +++ b/test/tsan/Unit/lit.site.cfg.in @@ -1,5 +1,4 @@ -## Autogenerated by LLVM/Clang configuration. -# Do not edit! +@LIT_SITE_CFG_IN_HEADER@ # Load common config for all compiler-rt unit tests. lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") diff --git a/test/tsan/aligned_vs_unaligned_race.cc b/test/tsan/aligned_vs_unaligned_race.cc index 5c1189f34a4b..fb299da8e72e 100644 --- a/test/tsan/aligned_vs_unaligned_race.cc +++ b/test/tsan/aligned_vs_unaligned_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s // Race between an aligned access and an unaligned access, which // touches the same memory region. #include "test.h" @@ -28,7 +28,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("Pass\n"); + fprintf(stderr, "Pass\n"); // CHECK: ThreadSanitizer: data race // CHECK: Pass return 0; diff --git a/test/tsan/benign_race.cc b/test/tsan/benign_race.cc index 2f72fe1860d0..90722aa93157 100644 --- a/test/tsan/benign_race.cc +++ b/test/tsan/benign_race.cc @@ -33,7 +33,7 @@ int main() { Global = 43; WTFGlobal = 143; pthread_join(t, 0); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/blacklist.cc b/test/tsan/blacklist.cc index d6ca383cb758..c1bcca60d505 100644 --- a/test/tsan/blacklist.cc +++ b/test/tsan/blacklist.cc @@ -23,7 +23,7 @@ int main() { pthread_create(&t[1], NULL, Blacklisted_Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } diff --git a/test/tsan/blacklist2.cc b/test/tsan/blacklist2.cc index 629b58821bfe..bf6c4eb75b65 100644 --- a/test/tsan/blacklist2.cc +++ b/test/tsan/blacklist2.cc @@ -44,6 +44,6 @@ int main() { pthread_create(&t[1], NULL, Blacklisted_Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } diff --git a/test/tsan/debugging.cc b/test/tsan/debugging.cc new file mode 100644 index 000000000000..653364404eb0 --- /dev/null +++ b/test/tsan/debugging.cc @@ -0,0 +1,108 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: %deflake %run %t 2>&1 | FileCheck %s + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "test.h" + +extern "C" { +void __tsan_on_report(void *report); +void *__tsan_get_current_report(); +int __tsan_get_report_data(void *report, const char **description, int *count, + int *stack_count, int *mop_count, int *loc_count, + int *mutex_count, int *thread_count, + int *unique_tid_count, void **sleep_trace, + unsigned long trace_size); +int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, + void **addr, int *size, int *write, int *atomic, + void **trace, unsigned long trace_size); +int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, + unsigned long *os_id, int *running, + const char **name, int *parent_tid, void **trace, + unsigned long trace_size); +} + +long my_global; + +void *Thread(void *a) { + barrier_wait(&barrier); + my_global = 42; + return NULL; +} + +int main() { + barrier_init(&barrier, 2); + fprintf(stderr, "&my_global = %p\n", &my_global); + // CHECK: &my_global = [[GLOBAL:0x[0-9a-f]+]] + pthread_t t; + pthread_create(&t, 0, Thread, 0); + my_global = 41; + barrier_wait(&barrier); + pthread_join(t, 0); + fprintf(stderr, "Done.\n"); +} + +void __tsan_on_report(void *report) { + fprintf(stderr, "__tsan_on_report(%p)\n", report); + fprintf(stderr, "__tsan_get_current_report() = %p\n", + __tsan_get_current_report()); + // CHECK: __tsan_on_report([[REPORT:0x[0-9a-f]+]]) + // CHECK: __tsan_get_current_report() = [[REPORT]] + + const char *description; + int count; + int stack_count, mop_count, loc_count, mutex_count, thread_count, + unique_tid_count; + void *sleep_trace[16] = {0}; + __tsan_get_report_data(report, &description, &count, &stack_count, &mop_count, + &loc_count, &mutex_count, &thread_count, + &unique_tid_count, sleep_trace, 16); + fprintf(stderr, "report type = '%s', count = %d\n", description, count); + // CHECK: report type = 'data-race', count = 0 + + fprintf(stderr, "mop_count = %d\n", mop_count); + // CHECK: mop_count = 2 + + int tid; + void *addr; + int size, write, atomic; + void *trace[16] = {0}; + + __tsan_get_report_mop(report, 0, &tid, &addr, &size, &write, &atomic, trace, + 16); + fprintf(stderr, "tid = %d, addr = %p, size = %d, write = %d, atomic = %d\n", + tid, addr, size, write, atomic); + // CHECK: tid = 1, addr = [[GLOBAL]], size = 8, write = 1, atomic = 0 + fprintf(stderr, "trace[0] = %p, trace[1] = %p\n", trace[0], trace[1]); + // CHECK: trace[0] = 0x{{[0-9a-f]+}}, trace[1] = {{0x0|\(nil\)|\(null\)}} + + __tsan_get_report_mop(report, 1, &tid, &addr, &size, &write, &atomic, trace, + 16); + fprintf(stderr, "tid = %d, addr = %p, size = %d, write = %d, atomic = %d\n", + tid, addr, size, write, atomic); + // CHECK: tid = 0, addr = [[GLOBAL]], size = 8, write = 1, atomic = 0 + fprintf(stderr, "trace[0] = %p, trace[1] = %p\n", trace[0], trace[1]); + // CHECK: trace[0] = 0x{{[0-9a-f]+}}, trace[1] = {{0x0|\(nil\)|\(null\)}} + + fprintf(stderr, "thread_count = %d\n", thread_count); + // CHECK: thread_count = 2 + + unsigned long os_id; + int running; + const char *name; + int parent_tid; + + __tsan_get_report_thread(report, 0, &tid, &os_id, &running, &name, &parent_tid, trace, 16); + fprintf(stderr, "tid = %d\n", tid); + // CHECK: tid = 1 + + __tsan_get_report_thread(report, 1, &tid, &os_id, &running, &name, &parent_tid, trace, 16); + fprintf(stderr, "tid = %d\n", tid); + // CHECK: tid = 0 +} + +// CHECK: Done. +// CHECK: ThreadSanitizer: reported 1 warnings diff --git a/test/tsan/dl_iterate_phdr.cc b/test/tsan/dl_iterate_phdr.cc index b9ce615f82fe..3c9821bf458a 100644 --- a/test/tsan/dl_iterate_phdr.cc +++ b/test/tsan/dl_iterate_phdr.cc @@ -47,7 +47,7 @@ int main(int argc, char *argv[]) { dlclose(lib); } pthread_join(th, 0); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return 0; } diff --git a/test/tsan/dtls.c b/test/tsan/dtls.c new file mode 100644 index 000000000000..51697565f1f1 --- /dev/null +++ b/test/tsan/dtls.c @@ -0,0 +1,62 @@ +// RUN: %clang_tsan %s -o %t +// RUN: %clang_tsan %s -DBUILD_SO -fPIC -o %t-so.so -shared +// RUN: %run %t 2>&1 | FileCheck %s + +// Test that tsan cleans up dynamic TLS memory between reuse. + +#include "test.h" + +#ifndef BUILD_SO +#include <assert.h> +#include <dlfcn.h> + +typedef volatile long *(* get_t)(); +get_t GetTls; + +void *Thread1(void *arg) { + pthread_detach(pthread_self()); + volatile long *x = GetTls(); + *x = 42; + fprintf(stderr, "stack: %p dtls: %p\n", &x, x); + barrier_wait(&barrier); + return 0; +} + +void *Thread2(void *arg) { + volatile long *x = GetTls(); + *x = 42; + fprintf(stderr, "stack: %p dtls: %p\n", &x, x); + return 0; +} + +int main(int argc, char *argv[]) { + char path[4096]; + snprintf(path, sizeof(path), "%s-so.so", argv[0]); + + void *handle = dlopen(path, RTLD_LAZY); + if (!handle) fprintf(stderr, "%s\n", dlerror()); + assert(handle != 0); + GetTls = (get_t)dlsym(handle, "GetTls"); + assert(dlerror() == 0); + + barrier_init(&barrier, 2); + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + barrier_wait(&barrier); + // Wait for actual thread termination without using pthread_join, + // which would synchronize threads. + sleep(1); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[1], 0); + fprintf(stderr, "DONE\n"); + return 0; +} +#else // BUILD_SO +__thread long huge_thread_local_array[1 << 17]; +long *GetTls() { + return &huge_thread_local_array[0]; +} +#endif + +// CHECK-NOT: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/fd_close_norace.cc b/test/tsan/fd_close_norace.cc index 1b52c20f990c..7d9d491f1dcc 100644 --- a/test/tsan/fd_close_norace.cc +++ b/test/tsan/fd_close_norace.cc @@ -25,7 +25,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_close_norace2.cc b/test/tsan/fd_close_norace2.cc index bf94fd5512b3..382ae5f34a83 100644 --- a/test/tsan/fd_close_norace2.cc +++ b/test/tsan/fd_close_norace2.cc @@ -23,7 +23,7 @@ int main() { while (write(pipes[1], &t, 1) != 1) { } pthread_join(t, 0); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_dup_norace.cc b/test/tsan/fd_dup_norace.cc index 5045325b22b5..e5995175bc0f 100644 --- a/test/tsan/fd_dup_norace.cc +++ b/test/tsan/fd_dup_norace.cc @@ -28,7 +28,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_dup_norace2.cc b/test/tsan/fd_dup_norace2.cc index 662c686f33a8..31aaed9d356c 100644 --- a/test/tsan/fd_dup_norace2.cc +++ b/test/tsan/fd_dup_norace2.cc @@ -53,7 +53,7 @@ int main() { exit(printf("close failed\n")); if (close(fd2) == -1) exit(printf("close failed\n")); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_dup_race.cc b/test/tsan/fd_dup_race.cc index a1aee5500753..d665eebff987 100644 --- a/test/tsan/fd_dup_race.cc +++ b/test/tsan/fd_dup_race.cc @@ -27,7 +27,7 @@ int main() { exit(printf("dup2 failed\n")); barrier_wait(&barrier); pthread_join(th, 0); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); } // CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_pipe_norace.cc b/test/tsan/fd_pipe_norace.cc index b434703d782a..01c4490c6c89 100644 --- a/test/tsan/fd_pipe_norace.cc +++ b/test/tsan/fd_pipe_norace.cc @@ -27,7 +27,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_socket_connect_norace.cc b/test/tsan/fd_socket_connect_norace.cc index ab2a950f17d6..b9fb4340ad78 100644 --- a/test/tsan/fd_socket_connect_norace.cc +++ b/test/tsan/fd_socket_connect_norace.cc @@ -38,7 +38,7 @@ int main() { pthread_join(t, 0); close(c); close(s); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_socket_norace.cc b/test/tsan/fd_socket_norace.cc index 0f41c4357354..07b0cb356b8c 100644 --- a/test/tsan/fd_socket_norace.cc +++ b/test/tsan/fd_socket_norace.cc @@ -45,7 +45,7 @@ int main() { close(c); close(s); pthread_join(t, 0); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fd_socketpair_norace.cc b/test/tsan/fd_socketpair_norace.cc index a455d44a3965..bee030dd3249 100644 --- a/test/tsan/fd_socketpair_norace.cc +++ b/test/tsan/fd_socketpair_norace.cc @@ -31,7 +31,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/fork_atexit.cc b/test/tsan/fork_atexit.cc index 51a64fc264d1..15cf0a2485ca 100644 --- a/test/tsan/fork_atexit.cc +++ b/test/tsan/fork_atexit.cc @@ -1,5 +1,4 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=atexit_sleep_ms=50 %run %t 2>&1 | FileCheck %s -// UNSUPPORTED: darwin #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/tsan/fork_deadlock.cc b/test/tsan/fork_deadlock.cc index 22bed086f7d0..32006e2ae530 100644 --- a/test/tsan/fork_deadlock.cc +++ b/test/tsan/fork_deadlock.cc @@ -1,5 +1,4 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=atexit_sleep_ms=50 %run %t 2>&1 | FileCheck %s -// UNSUPPORTED: darwin #include "test.h" #include <errno.h> #include <sys/types.h> @@ -13,18 +12,10 @@ static void *incrementer(void *p) { return 0; } -static void *watchdog(void *p) { - sleep(100); // is not intended to exit - fprintf(stderr, "timed out after 100 seconds\n"); - exit(1); - return 0; -} - int main() { barrier_init(&barrier, 2); - pthread_t th1, th2; + pthread_t th1; pthread_create(&th1, 0, incrementer, 0); - pthread_create(&th2, 0, watchdog, 0); for (int i = 0; i < 10; i++) { switch (fork()) { default: // parent diff --git a/test/tsan/fork_multithreaded.cc b/test/tsan/fork_multithreaded.cc index b345f58ad0c3..faf407b95656 100644 --- a/test/tsan/fork_multithreaded.cc +++ b/test/tsan/fork_multithreaded.cc @@ -1,13 +1,12 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-DIE // RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=die_after_fork=0 %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-NODIE -// UNSUPPORTED: darwin #include "test.h" #include <errno.h> #include <sys/types.h> #include <sys/wait.h> static void *sleeper(void *p) { - sleep(10); // not intended to exit during test + sleep(1000); // not intended to exit during test return 0; } diff --git a/test/tsan/fork_multithreaded3.cc b/test/tsan/fork_multithreaded3.cc index 5b8c13eb8b85..a651b3c18b4e 100644 --- a/test/tsan/fork_multithreaded3.cc +++ b/test/tsan/fork_multithreaded3.cc @@ -1,5 +1,4 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s -// UNSUPPORTED: darwin #include <stdlib.h> #include <stdio.h> #include <errno.h> diff --git a/test/tsan/ignore_lib4.cc b/test/tsan/ignore_lib4.cc new file mode 100644 index 000000000000..193df11d2b2a --- /dev/null +++ b/test/tsan/ignore_lib4.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -shared -o %T/libignore_lib4.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: echo "called_from_lib:libignore_lib4.so" > %t.supp +// RUN: %env_tsan_opts=suppressions='%t.supp' %run %t 2>&1 | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 +// powerpc64 big endian bots failed with "FileCheck error: '-' is empty" due +// to a segmentation fault. +// UNSUPPORTED: powerpc64-unknown-linux-gnu +// aarch64 bots failed with "called_from_lib suppression 'libignore_lib4.so' +// is matched against 2 libraries". +// UNSUPPORTED: aarch64 + +// Test longjmp in ignored lib. +// It used to crash since we jumped out of ScopedInterceptor scope. + +#include "test.h" +#include <setjmp.h> +#include <string.h> +#include <errno.h> +#include <libgen.h> +#include <string> + +#ifdef LIB + +extern "C" void myfunc() { + for (int i = 0; i < (1 << 20); i++) { + jmp_buf env; + if (!setjmp(env)) + longjmp(env, 1); + } +} + +#else + +int main(int argc, char **argv) { + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib4.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + void (*func)() = (void(*)())dlsym(h, "myfunc"); + func(); + fprintf(stderr, "DONE\n"); + return 0; +} + +#endif + +// CHECK: DONE diff --git a/test/tsan/ignore_race.cc b/test/tsan/ignore_race.cc index cc33b66b27d4..e410006ddc72 100644 --- a/test/tsan/ignore_race.cc +++ b/test/tsan/ignore_race.cc @@ -25,7 +25,7 @@ int main() { barrier_wait(&barrier); Global = 43; pthread_join(t, 0); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/ignored-interceptors-mmap.cc b/test/tsan/ignored-interceptors-mmap.cc new file mode 100644 index 000000000000..8715883238e2 --- /dev/null +++ b/test/tsan/ignored-interceptors-mmap.cc @@ -0,0 +1,61 @@ +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NORMAL +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-IGNORE + +#include <errno.h> +#include <sys/mman.h> + +#include "test.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 *global_p; + +int mmap_and_ignore_reads_and_writes() { + const size_t kSize = sysconf(_SC_PAGESIZE); + void *p = mmap(0, kSize, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON, -1, 0); + if (p == MAP_FAILED) + return printf("mmap failed with %d\n", errno); + munmap(p, kSize); + + void *new_p = mmap(p, kSize, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON, -1, 0); + if (p == MAP_FAILED || p != new_p) + return printf("second mmap failed with %d\n", errno); + + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + global_p = p; + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + barrier_wait(&barrier); + return 0; +} + +void *Thread(void *a) { + barrier_wait(&barrier); + + ((int*)global_p)[1] = 10; + printf("Read the zero value from mmapped memory %d\n", ((int*)global_p)[1]); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + if (mmap_and_ignore_reads_and_writes()) + return 1; + pthread_join(t, 0); + printf("OK\n"); + return 0; +} + +// CHECK-NORMAL: WARNING: ThreadSanitizer: data race +// CHECK-NORMAL: OK +// CHECK-IGNORE_NOT: WARNING: ThreadSanitizer: data race +// CHECK-IGNORE: OK diff --git a/test/tsan/inlined_memcpy_race.cc b/test/tsan/inlined_memcpy_race.cc index 720f2bfcac8c..4d085893aae0 100644 --- a/test/tsan/inlined_memcpy_race.cc +++ b/test/tsan/inlined_memcpy_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s #include "test.h" #include <string.h> @@ -24,7 +24,7 @@ int main() { pthread_create(&t[1], NULL, MemSetThread, x); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } diff --git a/test/tsan/inlined_memcpy_race2.cc b/test/tsan/inlined_memcpy_race2.cc index 37414ba5d3db..906a52bd32e4 100644 --- a/test/tsan/inlined_memcpy_race2.cc +++ b/test/tsan/inlined_memcpy_race2.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s #include "test.h" #include <string.h> @@ -25,7 +25,7 @@ int main() { pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } @@ -33,5 +33,5 @@ int main() { // CHECK: #0 memset // CHECK: #1 MemSetThread // CHECK: Previous write -// CHECK: #0 memmove +// CHECK: #0 {{(memcpy|memmove)}} // CHECK: #1 MemMoveThread diff --git a/test/tsan/interface_atomic_test.c b/test/tsan/interface_atomic_test.c index 18d860ea02e2..b7dfc86afa39 100644 --- a/test/tsan/interface_atomic_test.c +++ b/test/tsan/interface_atomic_test.c @@ -1,5 +1,5 @@ // Test that we can include header with TSan atomic interface. -// RUN: %clang_tsan %s -o %t && %run %t | FileCheck %s +// RUN: %clang_tsan %s -o %t && %run %t 2>&1 | FileCheck %s #include <sanitizer/tsan_interface_atomic.h> #include <stdio.h> @@ -9,7 +9,7 @@ int main() { int res = __tsan_atomic32_load(&a, __tsan_memory_order_acquire); if (res == 100) { // CHECK: PASS - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } return 1; diff --git a/test/tsan/java_alloc.cc b/test/tsan/java_alloc.cc index 4a606f7940d3..94919a4373a4 100644 --- a/test/tsan/java_alloc.cc +++ b/test/tsan/java_alloc.cc @@ -26,10 +26,10 @@ int main() { stress(jheap); pthread_join(th, 0); if (__tsan_java_fini() != 0) { - printf("FAILED\n"); + fprintf(stderr, "FAILED\n"); return 1; } - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return 0; } diff --git a/test/tsan/java_heap_init.cc b/test/tsan/java_heap_init.cc index bb7357c25b42..47ec5dbad28e 100644 --- a/test/tsan/java_heap_init.cc +++ b/test/tsan/java_heap_init.cc @@ -20,7 +20,7 @@ int main() { return printf("second mmap failed with %d\n", errno); __tsan_java_init(jheap, kHeapSize); __tsan_java_move(jheap + 16, jheap, 16); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return __tsan_java_fini(); } diff --git a/test/tsan/java_lock_move.cc b/test/tsan/java_lock_move.cc index fe5491dc2aa0..66599f8b7081 100644 --- a/test/tsan/java_lock_move.cc +++ b/test/tsan/java_lock_move.cc @@ -35,7 +35,7 @@ int main() { barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(varaddr2, kBlockSize); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return __tsan_java_fini(); } diff --git a/test/tsan/java_lock_rec.cc b/test/tsan/java_lock_rec.cc index f0bf40196e9f..aa8de97a148b 100644 --- a/test/tsan/java_lock_rec.cc +++ b/test/tsan/java_lock_rec.cc @@ -10,14 +10,14 @@ void *Thread(void *p) { *(int*)varaddr = 42; int rec = __tsan_java_mutex_unlock_rec(lockaddr); if (rec != 2) { - printf("FAILED 0 rec=%d\n", rec); + fprintf(stderr, "FAILED 0 rec=%d\n", rec); exit(1); } barrier_wait(&barrier); barrier_wait(&barrier); __tsan_java_mutex_lock_rec(lockaddr, rec); if (*(int*)varaddr != 43) { - printf("FAILED 3 var=%d\n", *(int*)varaddr); + fprintf(stderr, "FAILED 3 var=%d\n", *(int*)varaddr); exit(1); } __tsan_java_mutex_unlock(lockaddr); @@ -40,7 +40,7 @@ int main() { barrier_wait(&barrier); __tsan_java_mutex_lock(lockaddr); if (*(int*)varaddr != 42) { - printf("FAILED 1 var=%d\n", *(int*)varaddr); + fprintf(stderr, "FAILED 1 var=%d\n", *(int*)varaddr); exit(1); } *(int*)varaddr = 43; @@ -48,7 +48,7 @@ int main() { barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(jheap, kBlockSize); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return __tsan_java_fini(); } diff --git a/test/tsan/java_lock_rec_race.cc b/test/tsan/java_lock_rec_race.cc index 3da8ad076990..bf56eef2022c 100644 --- a/test/tsan/java_lock_rec_race.cc +++ b/test/tsan/java_lock_rec_race.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s #include "java.h" jptr varaddr; @@ -10,7 +10,7 @@ void *Thread(void *p) { __tsan_java_mutex_lock(lockaddr); int rec = __tsan_java_mutex_unlock_rec(lockaddr); if (rec != 3) { - printf("FAILED 0 rec=%d\n", rec); + fprintf(stderr, "FAILED 0 rec=%d\n", rec); exit(1); } *(int*)varaddr = 42; @@ -42,7 +42,7 @@ int main() { barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(jheap, kBlockSize); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return __tsan_java_fini(); } diff --git a/test/tsan/java_move_overlap.cc b/test/tsan/java_move_overlap.cc index 7ed98ef1a210..bf8d1e1550fc 100644 --- a/test/tsan/java_move_overlap.cc +++ b/test/tsan/java_move_overlap.cc @@ -66,7 +66,7 @@ int main(int argc, char **argv) { barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(varaddr1_new, kBlockSize); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return __tsan_java_fini(); } diff --git a/test/tsan/java_move_overlap_race.cc b/test/tsan/java_move_overlap_race.cc index 874b90b26341..fbbcf2c8a33f 100644 --- a/test/tsan/java_move_overlap_race.cc +++ b/test/tsan/java_move_overlap_race.cc @@ -1,6 +1,6 @@ // RUN: %clangxx_tsan -O1 %s -o %t -// RUN: %deflake %run %t | FileCheck %s -// RUN: %deflake %run %t arg | FileCheck %s +// RUN: %deflake %run %t 2>&1 | FileCheck %s +// RUN: %deflake %run %t arg 2>&1 | FileCheck %s #include "java.h" jptr varaddr1_old; @@ -46,7 +46,7 @@ int main(int argc, char **argv) { barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(varaddr1_new, kBlockSize); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return __tsan_java_fini(); } diff --git a/test/tsan/java_race_pc.cc b/test/tsan/java_race_pc.cc index 0745ade6c479..be1c5f26a3b1 100644 --- a/test/tsan/java_race_pc.cc +++ b/test/tsan/java_race_pc.cc @@ -1,8 +1,8 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s -// This test fails on powerpc64 on both VMA (44 and 46). +// This test fails on powerpc64 big endian. // The Tsan report is returning wrong information about // the location of the race. -// XFAIL: powerpc64 +// XFAIL: powerpc64-unknown-linux-gnu #include "java.h" void foobar() { @@ -13,7 +13,7 @@ void barbaz() { void *Thread(void *p) { barrier_wait(&barrier); - __tsan_read1_pc((jptr)p, (jptr)foobar + 1); + __tsan_read1_pc((jptr)p, (jptr)foobar + kPCInc); return 0; } @@ -26,7 +26,7 @@ int main() { __tsan_java_alloc(jheap, kBlockSize); pthread_t th; pthread_create(&th, 0, Thread, (void*)jheap); - __tsan_write1_pc((jptr)jheap, (jptr)barbaz + 1); + __tsan_write1_pc((jptr)jheap, (jptr)barbaz + kPCInc); barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(jheap, kBlockSize); diff --git a/test/tsan/java_rwlock.cc b/test/tsan/java_rwlock.cc index a4cc92a13635..aa77273a41b5 100644 --- a/test/tsan/java_rwlock.cc +++ b/test/tsan/java_rwlock.cc @@ -29,7 +29,7 @@ int main() { barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(jheap, kBlockSize); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return __tsan_java_fini(); } diff --git a/test/tsan/lit.cfg b/test/tsan/lit.cfg index d141fc228512..1fc1eccd15ea 100644 --- a/test/tsan/lit.cfg +++ b/test/tsan/lit.cfg @@ -50,11 +50,12 @@ clang_tsan_cxxflags = config.cxx_mode_flags + clang_tsan_cflags if config.has_libcxx and config.host_os != 'Darwin': # FIXME: Dehardcode this path somehow. libcxx_path = os.path.join(config.compiler_rt_obj_root, "lib", - "tsan", "libcxx_tsan_" + config.arch) + "tsan", "libcxx_tsan_" + config.target_arch) libcxx_incdir = os.path.join(libcxx_path, "include", "c++", "v1") libcxx_libdir = os.path.join(libcxx_path, "lib") libcxx_so = os.path.join(libcxx_libdir, "libc++.so") clang_tsan_cxxflags += ["-std=c++11", + "-nostdinc++", "-I%s" % libcxx_incdir, libcxx_so, "-Wl,-rpath=%s" % libcxx_libdir] diff --git a/test/tsan/lit.site.cfg.in b/test/tsan/lit.site.cfg.in index 08d4c1e00c7b..a87e8d25d6b2 100644 --- a/test/tsan/lit.site.cfg.in +++ b/test/tsan/lit.site.cfg.in @@ -1,10 +1,9 @@ -## Autogenerated by LLVM/Clang configuration. -# Do not edit! +@LIT_SITE_CFG_IN_HEADER@ config.name_suffix = "@TSAN_TEST_CONFIG_SUFFIX@" -config.arch = "@arch@" config.has_libcxx = @TSAN_HAS_LIBCXX@ config.target_cflags = "@TSAN_TEST_TARGET_CFLAGS@" +config.target_arch = "@TSAN_TEST_TARGET_ARCH@" # Load common config for all compiler-rt lit tests. lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") diff --git a/test/tsan/longjmp.cc b/test/tsan/longjmp.cc index d642067391fd..61d285c11bf2 100644 --- a/test/tsan/longjmp.cc +++ b/test/tsan/longjmp.cc @@ -14,11 +14,11 @@ int foo(jmp_buf env) { int main() { jmp_buf env; if (setjmp(env) == 42) { - printf("JUMPED\n"); + fprintf(stderr, "JUMPED\n"); return 0; } foo(env); - printf("FAILED\n"); + fprintf(stderr, "FAILED\n"); return 0; } diff --git a/test/tsan/longjmp2.cc b/test/tsan/longjmp2.cc index eee423dc5fbe..2b2775a8ca1e 100644 --- a/test/tsan/longjmp2.cc +++ b/test/tsan/longjmp2.cc @@ -16,11 +16,11 @@ int main() { sigjmp_buf env; printf("env=%p\n", env); if (sigsetjmp(env, 1) == 42) { - printf("JUMPED\n"); + fprintf(stderr, "JUMPED\n"); return 0; } foo(env); - printf("FAILED\n"); + fprintf(stderr, "FAILED\n"); return 0; } diff --git a/test/tsan/longjmp3.cc b/test/tsan/longjmp3.cc index 79965c4193d3..197b91e1cdc3 100644 --- a/test/tsan/longjmp3.cc +++ b/test/tsan/longjmp3.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s // Longjmp assembly has not been implemented for mips64 yet // XFAIL: mips64 @@ -34,7 +34,7 @@ void mymain() { return; } foo(env); - printf("FAILED\n"); + fprintf(stderr, "FAILED\n"); } int main() { diff --git a/test/tsan/longjmp4.cc b/test/tsan/longjmp4.cc index c8583997331e..3785a0f07261 100644 --- a/test/tsan/longjmp4.cc +++ b/test/tsan/longjmp4.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s // Longjmp assembly has not been implemented for mips64 yet // XFAIL: mips64 @@ -37,7 +37,7 @@ void mymain() { return; } foo(env); - printf("FAILED\n"); + fprintf(stderr, "FAILED\n"); } int main() { diff --git a/test/tsan/lots_of_threads.c b/test/tsan/lots_of_threads.c new file mode 100644 index 000000000000..eef9b1cb036c --- /dev/null +++ b/test/tsan/lots_of_threads.c @@ -0,0 +1,30 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" + +void *thr(void *arg) { + // Create a sync object on stack, so there is something to free on thread end. + volatile int x; + __atomic_fetch_add(&x, 1, __ATOMIC_SEQ_CST); + barrier_wait(&barrier); + return 0; +} + +int main() { + const int kThreads = 10; + barrier_init(&barrier, kThreads + 1); + pthread_t t[kThreads]; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 16 << 20); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + for (int i = 0; i < kThreads; i++) + pthread_create(&t[i], &attr, thr, 0); + pthread_attr_destroy(&attr); + barrier_wait(&barrier); + sleep(1); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE + diff --git a/test/tsan/malloc_overflow.cc b/test/tsan/malloc_overflow.cc index b2f9b0f57798..3db412978d04 100644 --- a/test/tsan/malloc_overflow.cc +++ b/test/tsan/malloc_overflow.cc @@ -6,17 +6,17 @@ int main() { void *p = malloc((size_t)-1); if (p != 0) - printf("FAIL malloc(-1) = %p\n", p); + fprintf(stderr, "FAIL malloc(-1) = %p\n", p); p = malloc((size_t)-1 / 2); if (p != 0) - printf("FAIL malloc(-1/2) = %p\n", p); + fprintf(stderr, "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); + fprintf(stderr, "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"); + fprintf(stderr, "FAIL calloc(-1/2, -1/2) = %p\n", p); + fprintf(stderr, "OK\n"); } // CHECK-NOT: FAIL diff --git a/test/tsan/mmap_stress.cc b/test/tsan/mmap_stress.cc index e01e7e92b8f9..f272779a49b9 100644 --- a/test/tsan/mmap_stress.cc +++ b/test/tsan/mmap_stress.cc @@ -47,6 +47,10 @@ void *Worker(void *arg) { } int main() { + // This test is flaky on several builders: + // https://groups.google.com/d/msg/llvm-dev/KUFPdLhBN3Q/L75rwW9xBgAJ + // The cause is unknown (lit hides test output on failures). +#if 0 pthread_t th[4]; for (int i = 0; i < 4; i++) { if (pthread_create(&th[i], 0, Worker, 0)) @@ -56,6 +60,7 @@ int main() { if (pthread_join(th[i], 0)) exit(printf("pthread_join failed: %d\n", errno)); } +#endif fprintf(stderr, "DONE\n"); } diff --git a/test/tsan/mutex_annotations.cc b/test/tsan/mutex_annotations.cc new file mode 100644 index 000000000000..59fa452c0a76 --- /dev/null +++ b/test/tsan/mutex_annotations.cc @@ -0,0 +1,49 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" + +// Test that a linker-initialized mutex can be created/destroyed while in use. + +// Stub for testing, just invokes annotations. +// Meant to be synchronized externally with test barrier. +class Mutex { + public: + void Create(bool linker_initialized = false) { + if (linker_initialized) + ANNOTATE_RWLOCK_CREATE_STATIC(&state_); + else + ANNOTATE_RWLOCK_CREATE(&state_); + } + + void Destroy() { + ANNOTATE_RWLOCK_DESTROY(&state_); + } + + void Lock() { + ANNOTATE_RWLOCK_ACQUIRED(&state_, true); + } + + void Unlock() { + ANNOTATE_RWLOCK_RELEASED(&state_, true); + } + + private: + long long state_; +}; + +int main() { + Mutex m; + + m.Lock(); + m.Create(true); + m.Unlock(); + + m.Lock(); + m.Destroy(); + m.Unlock(); + + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: DONE diff --git a/test/tsan/mutex_cycle_long.c b/test/tsan/mutex_cycle_long.c new file mode 100644 index 000000000000..b5d67c16c321 --- /dev/null +++ b/test/tsan/mutex_cycle_long.c @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: not %run %t 5 2>&1 | FileCheck %s +// RUN: not %run %t 10 2>&1 | FileCheck %s +// RUN: not %run %t 15 2>&1 | FileCheck %s +// RUN: not %run %t 20 2>&1 | FileCheck %s +// RUN: %run %t 30 2>&1 | FileCheck %s --check-prefix=CHECK-TOO-LONG-CYCLE + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + int num_mutexes = 5; + if (argc > 1) num_mutexes = atoi(argv[1]); + + pthread_mutex_t m[num_mutexes]; + for (int i = 0; i < num_mutexes; ++i) + pthread_mutex_init(&m[i], NULL); + + for (int i = 0; i < num_mutexes - 1; ++i) { + pthread_mutex_lock(&m[i]); + pthread_mutex_lock(&m[i + 1]); + + pthread_mutex_unlock(&m[i]); + pthread_mutex_unlock(&m[i + 1]); + } + + pthread_mutex_lock(&m[num_mutexes - 1]); + pthread_mutex_lock(&m[0]); + + pthread_mutex_unlock(&m[num_mutexes - 1]); + pthread_mutex_unlock(&m[0]); + + for (int i = 0; i < num_mutexes; ++i) + pthread_mutex_destroy(&m[i]); + + fprintf(stderr, "PASS\n"); +} + +// CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock) +// CHECK-TOO-LONG-CYCLE: WARNING: too long mutex cycle found +// CHECK: PASS diff --git a/test/tsan/mutex_lock_destroyed.cc b/test/tsan/mutex_lock_destroyed.cc new file mode 100644 index 000000000000..52d6be6210a6 --- /dev/null +++ b/test/tsan/mutex_lock_destroyed.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %deflake %run %t | FileCheck %s +// RUN: %deflake %run %t 1 | FileCheck %s + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + pthread_mutex_t *m = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(m, 0); + pthread_mutex_lock(m); + pthread_mutex_unlock(m); + pthread_mutex_destroy(m); + + if (argc > 1 && argv[1][0] == '1') + free(m); + + pthread_mutex_lock(m); + // CHECK: WARNING: ThreadSanitizer: use of an invalid mutex (e.g. uninitialized or destroyed) + // CHECK: #0 pthread_mutex_lock + // CHECK: #1 main {{.*}}mutex_lock_destroyed.cc:[[@LINE-3]] + + return 0; +} diff --git a/test/tsan/pthread_key.cc b/test/tsan/pthread_key.cc new file mode 100644 index 000000000000..798caa4aba86 --- /dev/null +++ b/test/tsan/pthread_key.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Extracted from: +// https://bugs.chromium.org/p/v8/issues/detail?id=4995 + +#include "test.h" + +void* thr(void* arg) { + const int N = 32; + pthread_key_t keys_[N]; + for (size_t i = 0; i < N; ++i) { + int err = pthread_key_create(&keys_[i], 0); + if (err) { + fprintf(stderr, "pthread_key_create failed with %d\n", err); + exit(1); + } + } + for (size_t i = 0; i < N; i++) + pthread_setspecific(keys_[i], (void*)(long)i); + for (size_t i = 0; i < N; i++) + pthread_key_delete(keys_[i]); + return 0; +} + +int main() { + for (int i = 0; i < 10; i++) { + pthread_t th; + pthread_create(&th, 0, thr, 0); + pthread_join(th, 0); + } + pthread_t th[2]; + pthread_create(&th[0], 0, thr, 0); + pthread_create(&th[1], 0, thr, 0); + pthread_join(th[0], 0); + pthread_join(th[1], 0); + fprintf(stderr, "DONE\n"); + // CHECK: DONE +} diff --git a/test/tsan/race_on_mutex.c b/test/tsan/race_on_mutex.c index d998fdca2df3..c7f5e06392c5 100644 --- a/test/tsan/race_on_mutex.c +++ b/test/tsan/race_on_mutex.c @@ -1,26 +1,30 @@ // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s -// This test fails on powerpc64 (VMA=46). -// The size of the write reported by Tsan for T1 is 8 instead of 1. -// XFAIL: powerpc64-unknown-linux-gnu #include "test.h" pthread_mutex_t Mtx; int Global; -void *Thread1(void *x) { - pthread_mutex_init(&Mtx, 0); +void *Thread2(void *x) { + barrier_wait(&barrier); +// 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:[[@LINE+1]]{{(:3)?}} ({{.*}}) pthread_mutex_lock(&Mtx); - Global = 42; + Global = 43; pthread_mutex_unlock(&Mtx); - barrier_wait(&barrier); return NULL; } -void *Thread2(void *x) { - barrier_wait(&barrier); +void *Thread1(void *x) { +// CHECK: Previous write of size {{[0-9]+}} at {{.*}} by thread T1: +// CHECK: #{{[0-9]+}} {{.*}}pthread_mutex_init {{.*}} ({{.*}}) +// CHECK-NEXT: #{{[0-9]+}} Thread1{{.*}} {{.*}}race_on_mutex.c:[[@LINE+1]]{{(:3)?}} ({{.*}}) + pthread_mutex_init(&Mtx, 0); pthread_mutex_lock(&Mtx); - Global = 43; + Global = 42; pthread_mutex_unlock(&Mtx); + barrier_wait(&barrier); return NULL; } @@ -34,11 +38,3 @@ int main() { 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:21{{(:3)?}} ({{.*}}) -// CHECK: Previous write of size 1 at {{.*}} by thread T1: -// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}}) -// CHECK-NEXT: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}}) diff --git a/test/tsan/race_on_speculative_load.cc b/test/tsan/race_on_speculative_load.cc index dd40daeb5c19..5a9d698ca9a6 100644 --- a/test/tsan/race_on_speculative_load.cc +++ b/test/tsan/race_on_speculative_load.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -O1 %s -o %t && %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s // Regtest for https://github.com/google/sanitizers/issues/447 // This is a correct program and tsan should not report a race. #include "test.h" @@ -24,7 +24,7 @@ int main() { g = 1; barrier_wait(&barrier); pthread_join(t, 0); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); // CHECK-NOT: ThreadSanitizer: data race // CHECK: PASS } diff --git a/test/tsan/setuid.c b/test/tsan/setuid.c index bc9c8ca3abaa..2d6b7c866df8 100644 --- a/test/tsan/setuid.c +++ b/test/tsan/setuid.c @@ -1,4 +1,10 @@ // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// +// setuid(0) hangs on powerpc64 big endian. When this is fixed remove +// the unsupported flag. +// https://llvm.org/bugs/show_bug.cgi?id=25799 +// +// UNSUPPORTED: powerpc64-unknown-linux-gnu #include "test.h" #include <sys/types.h> #include <unistd.h> diff --git a/test/tsan/setuid2.c b/test/tsan/setuid2.c index 9dbb6577e1c6..3ea897802cc5 100644 --- a/test/tsan/setuid2.c +++ b/test/tsan/setuid2.c @@ -1,4 +1,10 @@ // RUN: %clang_tsan -O1 %s -o %t && %env_tsan_opts=flush_memory_ms=1:memory_limit_mb=1 %run %t 2>&1 | FileCheck %s +// +// setuid(0) hangs on powerpc64 big endian. When this is fixed remove +// the unsupported flag. +// https://llvm.org/bugs/show_bug.cgi?id=25799 +// +// UNSUPPORTED: powerpc64-unknown-linux-gnu #include "test.h" #include <sys/types.h> #include <unistd.h> diff --git a/test/tsan/signal_sync2.cc b/test/tsan/signal_sync2.cc new file mode 100644 index 000000000000..163f20650756 --- /dev/null +++ b/test/tsan/signal_sync2.cc @@ -0,0 +1,77 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: darwin +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#include <errno.h> + +// Test synchronization in signal handled within IgnoreSync region. + +extern "C" void AnnotateIgnoreSyncBegin(const char *f, int l); +extern "C" void AnnotateIgnoreSyncEnd(const char *f, int l); + +const int kSignalCount = 500; + +__thread int process_signals; +int signals_handled; +int done; +int ready[kSignalCount]; +long long data[kSignalCount]; + +static void handler(int sig) { + if (!__atomic_load_n(&process_signals, __ATOMIC_RELAXED)) + return; + int pos = signals_handled++; + if (pos >= kSignalCount) + return; + data[pos] = pos; + __atomic_store_n(&ready[pos], 1, __ATOMIC_RELEASE); +} + +static void* thr(void *p) { + AnnotateIgnoreSyncBegin(__FILE__, __LINE__); + __atomic_store_n(&process_signals, 1, __ATOMIC_RELAXED); + while (!__atomic_load_n(&done, __ATOMIC_RELAXED)) { + } + AnnotateIgnoreSyncEnd(__FILE__, __LINE__); + return 0; +} + +int main() { + struct sigaction act = {}; + act.sa_handler = handler; + if (sigaction(SIGPROF, &act, 0)) { + perror("sigaction"); + exit(1); + } + itimerval t; + t.it_value.tv_sec = 0; + t.it_value.tv_usec = 10; + t.it_interval = t.it_value; + if (setitimer(ITIMER_PROF, &t, 0)) { + perror("setitimer"); + exit(1); + } + + pthread_t th; + pthread_create(&th, 0, thr, 0); + for (int pos = 0; pos < kSignalCount; pos++) { + while (__atomic_load_n(&ready[pos], __ATOMIC_ACQUIRE) == 0) { + } + if (data[pos] != pos) { + printf("at pos %d, expect %d, got %lld\n", pos, pos, data[pos]); + exit(1); + } + } + __atomic_store_n(&done, 1, __ATOMIC_RELAXED); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: DONE diff --git a/test/tsan/static_init1.cc b/test/tsan/static_init1.cc index 3e5fb14ba44b..3e6e4f9dfec1 100644 --- a/test/tsan/static_init1.cc +++ b/test/tsan/static_init1.cc @@ -21,7 +21,7 @@ int main() { pthread_create(&t[1], 0, Thread, 0); pthread_join(t[0], 0); pthread_join(t[1], 0); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init2.cc b/test/tsan/static_init2.cc index 667aed1343dc..ca2300ae6801 100644 --- a/test/tsan/static_init2.cc +++ b/test/tsan/static_init2.cc @@ -27,7 +27,7 @@ int main() { pthread_create(&t[1], 0, Thread, 0); pthread_join(t[0], 0); pthread_join(t[1], 0); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init4.cc b/test/tsan/static_init4.cc index 85835a2520f7..c8da78364c5c 100644 --- a/test/tsan/static_init4.cc +++ b/test/tsan/static_init4.cc @@ -31,7 +31,7 @@ int main() { pthread_create(&t[1], 0, Thread1, 0); pthread_join(t[0], 0); pthread_join(t[1], 0); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init5.cc b/test/tsan/static_init5.cc index 961e3a3b6329..b334981e85b0 100644 --- a/test/tsan/static_init5.cc +++ b/test/tsan/static_init5.cc @@ -36,7 +36,7 @@ int main() { pthread_create(&t[1], 0, Thread1, 0); pthread_join(t[0], 0); pthread_join(t[1], 0); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/static_init6.cc b/test/tsan/static_init6.cc index 77253eac173f..fd22e0a02e6a 100644 --- a/test/tsan/static_init6.cc +++ b/test/tsan/static_init6.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_tsan -static-libstdc++ -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -stdlib=libstdc++ -static-libstdc++ -O1 %s -o %t && %run %t 2>&1 | FileCheck %s #include <pthread.h> #include <stdlib.h> #include <stdio.h> @@ -36,7 +36,7 @@ int main() { pthread_create(&t[1], 0, Thread1, 0); pthread_join(t[0], 0); pthread_join(t[1], 0); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/sunrpc.cc b/test/tsan/sunrpc.cc index 579816d64098..5cfb5344ec10 100644 --- a/test/tsan/sunrpc.cc +++ b/test/tsan/sunrpc.cc @@ -19,7 +19,7 @@ int main(int argc, char *argv[]) { pthread_create(&th[1], 0, thr, 0); pthread_join(th[0], 0); pthread_join(th[1], 0); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); // CHECK: DONE return 0; } diff --git a/test/tsan/suppressions_global.cc b/test/tsan/suppressions_global.cc index 8928162cfb8a..282d261c19ef 100644 --- a/test/tsan/suppressions_global.cc +++ b/test/tsan/suppressions_global.cc @@ -20,7 +20,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("OK\n"); + fprintf(stderr, "OK\n"); return 0; } diff --git a/test/tsan/suppressions_race.cc b/test/tsan/suppressions_race.cc index 7a88434db820..d0aeeda95e36 100644 --- a/test/tsan/suppressions_race.cc +++ b/test/tsan/suppressions_race.cc @@ -22,7 +22,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("OK\n"); + fprintf(stderr, "OK\n"); return 0; } diff --git a/test/tsan/suppressions_race2.cc b/test/tsan/suppressions_race2.cc index b6566a80178d..6f8ca736d8ae 100644 --- a/test/tsan/suppressions_race2.cc +++ b/test/tsan/suppressions_race2.cc @@ -22,7 +22,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("OK\n"); + fprintf(stderr, "OK\n"); return 0; } diff --git a/test/tsan/test.h b/test/tsan/test.h index a681daa32906..e3affdc0837d 100644 --- a/test/tsan/test.h +++ b/test/tsan/test.h @@ -67,3 +67,38 @@ unsigned long long monotonic_clock_ns() { return (unsigned long long)t.tv_sec * 1000000000ull + t.tv_nsec; } #endif + +//The const kPCInc must be in sync with StackTrace::GetPreviousInstructionPc +#if defined(__powerpc64__) +// PCs are always 4 byte aligned. +const int kPCInc = 4; +#elif defined(__sparc__) || defined(__mips__) +const int kPCInc = 8; +#else +const int kPCInc = 1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void AnnotateRWLockCreate(const char *f, int l, void *m); +void AnnotateRWLockCreateStatic(const char *f, int l, void *m); +void AnnotateRWLockDestroy(const char *f, int l, void *m); +void AnnotateRWLockAcquired(const char *f, int l, void *m, long is_w); +void AnnotateRWLockReleased(const char *f, int l, void *m, long is_w); + +#ifdef __cplusplus +} +#endif + +#define ANNOTATE_RWLOCK_CREATE(m) \ + AnnotateRWLockCreate(__FILE__, __LINE__, m) +#define ANNOTATE_RWLOCK_CREATE_STATIC(m) \ + AnnotateRWLockCreateStatic(__FILE__, __LINE__, m) +#define ANNOTATE_RWLOCK_DESTROY(m) \ + AnnotateRWLockDestroy(__FILE__, __LINE__, m) +#define ANNOTATE_RWLOCK_ACQUIRED(m, is_w) \ + AnnotateRWLockAcquired(__FILE__, __LINE__, m, is_w) +#define ANNOTATE_RWLOCK_RELEASED(m, is_w) \ + AnnotateRWLockReleased(__FILE__, __LINE__, m, is_w) diff --git a/test/tsan/thread_detach.c b/test/tsan/thread_detach.c index 802d8ded0fad..2a95742b7cd8 100644 --- a/test/tsan/thread_detach.c +++ b/test/tsan/thread_detach.c @@ -12,7 +12,7 @@ int main() { pthread_create(&t, 0, Thread, 0); barrier_wait(&barrier); pthread_detach(t); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } diff --git a/test/tsan/thread_detach2.c b/test/tsan/thread_detach2.c index 8133980ba5a1..5ee94e9a967b 100644 --- a/test/tsan/thread_detach2.c +++ b/test/tsan/thread_detach2.c @@ -20,7 +20,7 @@ int main() { pthread_create(&t, 0, Thread, 0); pthread_detach(t); barrier_wait(&barrier); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } diff --git a/test/tsan/thread_leak.c b/test/tsan/thread_leak.c index 9b850dd4b567..9b42b16b72a4 100644 --- a/test/tsan/thread_leak.c +++ b/test/tsan/thread_leak.c @@ -10,7 +10,7 @@ int main() { pthread_t t; pthread_create(&t, 0, Thread, 0); pthread_join(t, 0); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } diff --git a/test/tsan/thread_leak2.c b/test/tsan/thread_leak2.c index fc2942b2a05d..c3cac7a49284 100644 --- a/test/tsan/thread_leak2.c +++ b/test/tsan/thread_leak2.c @@ -10,7 +10,7 @@ int main() { pthread_t t; pthread_create(&t, 0, Thread, 0); pthread_detach(t); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); return 0; } diff --git a/test/tsan/thread_leak4.c b/test/tsan/thread_leak4.c index 1ebca58871ac..1d0636f1a4c0 100644 --- a/test/tsan/thread_leak4.c +++ b/test/tsan/thread_leak4.c @@ -9,7 +9,7 @@ void *Thread(void *x) { int main() { pthread_t t; pthread_create(&t, 0, Thread, 0); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); return 0; } diff --git a/test/tsan/tsan-vs-gvn.cc b/test/tsan/tsan-vs-gvn.cc index 950f5d30d4da..efd81ef502fd 100644 --- a/test/tsan/tsan-vs-gvn.cc +++ b/test/tsan/tsan-vs-gvn.cc @@ -31,7 +31,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("PASS\n"); + fprintf(stderr, "PASS\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/unaligned_norace.cc b/test/tsan/unaligned_norace.cc index 94df1cf74cc1..7e360cf87042 100644 --- a/test/tsan/unaligned_norace.cc +++ b/test/tsan/unaligned_norace.cc @@ -77,7 +77,7 @@ int main() { pthread_create(&th, 0, Thread, 0); Test(true); pthread_join(th, 0); - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: diff --git a/test/tsan/vfork.cc b/test/tsan/vfork.cc index 98a82623ee65..2d669b305a9d 100644 --- a/test/tsan/vfork.cc +++ b/test/tsan/vfork.cc @@ -1,5 +1,4 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s -// UNSUPPORTED: darwin #include <pthread.h> #include <stdio.h> #include <stdlib.h> @@ -27,7 +26,7 @@ int main() { pipe(fds); int pid = vfork(); if (pid < 0) { - printf("FAIL to vfork\n"); + fprintf(stderr, "FAIL to vfork\n"); exit(1); } if (pid == 0) { // child @@ -44,7 +43,7 @@ int main() { pthread_create(&t[1], NULL, Thread2, NULL); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - printf("DONE\n"); + fprintf(stderr, "DONE\n"); } // CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/virtual_inheritance_compile_bug.cc b/test/tsan/virtual_inheritance_compile_bug.cc index 7da581d80601..3b1e08b1689b 100644 --- a/test/tsan/virtual_inheritance_compile_bug.cc +++ b/test/tsan/virtual_inheritance_compile_bug.cc @@ -10,6 +10,6 @@ struct DDD: CCC, BBB { DDD(); }; // NOLINT DDD::DDD() { } int main() { DDD d; - printf("OK\n"); + fprintf(stderr, "OK\n"); } // CHECK: OK |