diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:18:27 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:18:27 +0000 |
commit | 316d58822dada9440bd06ecfc758dcc2364d617c (patch) | |
tree | fe72ec2e6ce9a360dda74d9d57f7acdb0e3c39d6 /test/tsan | |
parent | 0230fcf22fe7d19f03d981c9c2c59a3db0b72ea5 (diff) | |
download | src-316d58822dada9440bd06ecfc758dcc2364d617c.tar.gz src-316d58822dada9440bd06ecfc758dcc2364d617c.zip |
Vendor import of compiler-rt trunk r290819:vendor/compiler-rt/compiler-rt-trunk-r290819
Notes
Notes:
svn path=/vendor/compiler-rt/dist/; revision=311120
svn path=/vendor/compiler-rt/compiler-rt-trunk-r290819/; revision=311121; tag=vendor/compiler-rt/compiler-rt-trunk-r290819
Diffstat (limited to 'test/tsan')
35 files changed, 914 insertions, 70 deletions
diff --git a/test/tsan/CMakeLists.txt b/test/tsan/CMakeLists.txt index e05b100f3699..2db6ce0a8c1a 100644 --- a/test/tsan/CMakeLists.txt +++ b/test/tsan/CMakeLists.txt @@ -24,15 +24,7 @@ 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. - # Build all tests with host compiler and use host tools. - set(TSAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER}) - set(TSAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS}) - else() - get_target_flags_for_arch(${arch} TSAN_TEST_TARGET_CFLAGS) - string(REPLACE ";" " " TSAN_TEST_TARGET_CFLAGS "${TSAN_TEST_TARGET_CFLAGS}") - endif() + get_test_cc_for_arch(${arch} TSAN_TEST_TARGET_CC TSAN_TEST_TARGET_CFLAGS) string(TOUPPER ${arch} ARCH_UPPER_CASE) set(CONFIG_NAME ${ARCH_UPPER_CASE}Config) diff --git a/test/tsan/Darwin/gcd-apply-race.mm b/test/tsan/Darwin/gcd-apply-race.mm index 13b24e0fdb90..028be1ac5b1c 100644 --- a/test/tsan/Darwin/gcd-apply-race.mm +++ b/test/tsan/Darwin/gcd-apply-race.mm @@ -10,6 +10,10 @@ long global; int main(int argc, const char *argv[]) { barrier_init(&barrier, 2); fprintf(stderr, "start\n"); + + // Warm up GCD (workaround for macOS Sierra where dispatch_apply might run single-threaded). + dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ }); + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_apply(2, q, ^(size_t i) { global = i; diff --git a/test/tsan/Darwin/gcd-apply.mm b/test/tsan/Darwin/gcd-apply.mm index e68a4b18205d..a7dc3740dc7b 100644 --- a/test/tsan/Darwin/gcd-apply.mm +++ b/test/tsan/Darwin/gcd-apply.mm @@ -17,6 +17,10 @@ void callback(void *context, size_t i) { int main(int argc, const char *argv[]) { barrier_init(&barrier, 2); fprintf(stderr, "start\n"); + + // Warm up GCD (workaround for macOS Sierra where dispatch_apply might run single-threaded). + dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ }); + dispatch_queue_t q = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT); global = 42; diff --git a/test/tsan/Darwin/gcd-suspend.mm b/test/tsan/Darwin/gcd-suspend.mm new file mode 100644 index 000000000000..3e8818a2d56e --- /dev/null +++ b/test/tsan/Darwin/gcd-suspend.mm @@ -0,0 +1,45 @@ +// 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 q1 = dispatch_queue_create("queue1", NULL); + dispatch_queue_t q2 = dispatch_queue_create("queue2", NULL); + dispatch_group_t g = dispatch_group_create(); + + dispatch_sync(q1, ^{ + dispatch_suspend(q1); + dispatch_async(q2, ^{ + my_global++; + dispatch_resume(q1); + }); + }); + + dispatch_sync(q1, ^{ + my_global++; + }); + + dispatch_sync(q1, ^{ + dispatch_suspend(q1); + dispatch_group_enter(g); + dispatch_async(q1,^{ my_global++; }); + dispatch_async(q1,^{ my_global++; }); + dispatch_async(q1,^{ my_global++; dispatch_group_leave(g); }); + my_global++; + dispatch_resume(q1); + }); + + dispatch_group_wait(g, 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-target-queue-norace.mm b/test/tsan/Darwin/gcd-target-queue-norace.mm new file mode 100644 index 000000000000..36cb1b9298de --- /dev/null +++ b/test/tsan/Darwin/gcd-target-queue-norace.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 global; + +int main(int argc, const char *argv[]) { + dispatch_queue_t target_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); + dispatch_queue_t q1 = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT); + dispatch_queue_t q2 = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT); + dispatch_set_target_queue(q1, target_queue); + dispatch_set_target_queue(q2, target_queue); + + for (int i = 0; i < 100000; i++) { + dispatch_async(q1, ^{ + global++; + + if (global == 200000) { + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + } + }); + dispatch_async(q2, ^{ + global++; + + if (global == 200000) { + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + } + }); + } + + CFRunLoopRun(); + NSLog(@"Done."); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer diff --git a/test/tsan/Darwin/libcxx-call-once.mm b/test/tsan/Darwin/libcxx-call-once.mm new file mode 100644 index 000000000000..5388e495c9c2 --- /dev/null +++ b/test/tsan/Darwin/libcxx-call-once.mm @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan %s -o %t -framework Foundation -std=c++11 +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +#import <iostream> +#import <thread> + +long my_global; +std::once_flag once_token; + +void thread_func() { + std::call_once(once_token, [] { + my_global = 17; + }); + + long val = my_global; + fprintf(stderr, "my_global = %ld\n", val); +} + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + std::thread t1(thread_func); + std::thread t2(thread_func); + t1.join(); + t2.join(); + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/libcxx-future.mm b/test/tsan/Darwin/libcxx-future.mm new file mode 100644 index 000000000000..902f267ecb89 --- /dev/null +++ b/test/tsan/Darwin/libcxx-future.mm @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#include <iostream> +#include <future> +#include <vector> + +int main(int argc, const char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + auto my_task = [] { return 42; }; + + std::vector<std::thread> threads; + + for (int i = 0; i < 100; i++) { + std::packaged_task<int(void)> task(my_task); + std::future<int> future = task.get_future(); + threads.push_back(std::thread(std::move(task))); + } + + for (auto &t : threads) { + t.join(); + } + + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: Done. diff --git a/test/tsan/Darwin/norace-objcxx-run-time.mm b/test/tsan/Darwin/norace-objcxx-run-time.mm new file mode 100644 index 000000000000..0cf729e7f2d8 --- /dev/null +++ b/test/tsan/Darwin/norace-objcxx-run-time.mm @@ -0,0 +1,113 @@ +// RUN: %clang_tsan %s -lc++ -fobjc-arc -lobjc -o %t -framework Foundation +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +// Check that we do not report races between: +// - Object retain and initialize +// - Object release and dealloc +// - Object release and .cxx_destruct + +#import <Foundation/Foundation.h> +#include "../test.h" +invisible_barrier_t barrier2; + +class NeedCleanup { + public: + int x; + NeedCleanup() { + x = 1; + } + ~NeedCleanup() { + x = 0; + } +}; + +@interface TestDeallocObject : NSObject { + @public + int v; + } + - (id)init; + - (void)accessMember; + - (void)dealloc; +@end + +@implementation TestDeallocObject + - (id)init { + if ([super self]) { + v = 1; + return self; + } + return nil; + } + - (void)accessMember { + int local = v; + local++; + } + - (void)dealloc { + v = 0; + } +@end + +@interface TestCXXDestructObject : NSObject { + @public + NeedCleanup cxxMemberWithCleanup; + } + - (void)accessMember; +@end + +@implementation TestCXXDestructObject + - (void)accessMember { + int local = cxxMemberWithCleanup.x; + local++; + } +@end + +@interface TestInitializeObject : NSObject +@end + +@implementation TestInitializeObject + static long InitializerAccessedGlobal = 0; + + (void)initialize { + InitializerAccessedGlobal = 42; + } +@end + +int main(int argc, const char *argv[]) { + // Ensure that there is no race when calling initialize on TestInitializeObject; + // otherwise, the locking from ObjC runtime becomes observable. Also ensures that + // blocks are dispatched to 2 different threads. + barrier_init(&barrier, 2); + // Ensure that objects are destructed during block object release. + barrier_init(&barrier2, 3); + + TestDeallocObject *tdo = [[TestDeallocObject alloc] init]; + TestCXXDestructObject *tcxxdo = [[TestCXXDestructObject alloc] init]; + [tdo accessMember]; + [tcxxdo accessMember]; + { + dispatch_queue_t q = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT); + dispatch_async(q, ^{ + [TestInitializeObject new]; + barrier_wait(&barrier); + long local = InitializerAccessedGlobal; + local++; + [tdo accessMember]; + [tcxxdo accessMember]; + barrier_wait(&barrier2); + }); + dispatch_async(q, ^{ + barrier_wait(&barrier); + [TestInitializeObject new]; + long local = InitializerAccessedGlobal; + local++; + [tdo accessMember]; + [tcxxdo accessMember]; + barrier_wait(&barrier2); + }); + } + barrier_wait(&barrier2); + NSLog(@"Done."); + return 0; +} + +// CHECK: Done. +// CHECK-NOT: ThreadSanitizer: data race diff --git a/test/tsan/Darwin/objc-double-property.mm b/test/tsan/Darwin/objc-double-property.mm new file mode 100644 index 000000000000..51b10f21c9ca --- /dev/null +++ b/test/tsan/Darwin/objc-double-property.mm @@ -0,0 +1,21 @@ +// RUN: %clangxx_tsan -O0 %s -o %t -framework Foundation && %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t -framework Foundation && %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O2 %s -o %t -framework Foundation && %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O3 %s -o %t -framework Foundation && %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s + +#import <Foundation/Foundation.h> + +@interface MyClass : NSObject +@property float a; +@property double b; +@property long double c; +@end + +@implementation MyClass +@end + +int main() { + NSLog(@"Hello world"); +} + +// CHECK: Hello world diff --git a/test/tsan/Darwin/osatomics-bitops.mm b/test/tsan/Darwin/osatomics-bitops.mm new file mode 100644 index 000000000000..68badb792fd6 --- /dev/null +++ b/test/tsan/Darwin/osatomics-bitops.mm @@ -0,0 +1,34 @@ +// 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> + +int main(int argc, const char *argv[]) { + int value = 1; + bool ret = OSAtomicTestAndClear(7, &value); + fprintf(stderr, "value = %d, ret = %d\n", value, ret); + // CHECK: value = 0, ret = 1 + + ret = OSAtomicTestAndSet(4, &value); + fprintf(stderr, "value = %d, ret = %d\n", value, ret); + // CHECK: value = 8, ret = 0 + + ret = OSAtomicTestAndClear(4, &value); + fprintf(stderr, "value = %d, ret = %d\n", value, ret); + // CHECK: value = 0, ret = 1 + + ret = OSAtomicTestAndSet(12, &value); + fprintf(stderr, "value = %d, ret = %d\n", value, ret); + // CHECK: value = 2048, ret = 0 + + ret = OSAtomicTestAndSet(13, &value); + fprintf(stderr, "value = %d, ret = %d\n", value, ret); + // CHECK: value = 3072, ret = 0 + + ret = OSAtomicTestAndClear(12, &value); + fprintf(stderr, "value = %d, ret = %d\n", value, ret); + // CHECK: value = 1024, ret = 1 + + return 0; +} diff --git a/test/tsan/Darwin/realloc-zero.cc b/test/tsan/Darwin/realloc-zero.cc new file mode 100644 index 000000000000..98262463cb2d --- /dev/null +++ b/test/tsan/Darwin/realloc-zero.cc @@ -0,0 +1,20 @@ +// Test that realloc(nullptr, 0) return a non-NULL pointer. + +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <malloc/malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> + +int main() { + void *p = realloc(NULL, 0); + if (!p) { + abort(); + } + fprintf(stderr, "Okay.\n"); + return 0; +} + +// CHECK: Okay. diff --git a/test/tsan/atomic_free.cc b/test/tsan/atomic_free.cc index a0d8e426b49f..446949ddb986 100644 --- a/test/tsan/atomic_free.cc +++ b/test/tsan/atomic_free.cc @@ -1,4 +1,13 @@ -// 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 + +// Also check that atomics instrumentation can be configured by either driver or +// legacy flags: + +// RUN: %clangxx_tsan -O1 %s -o %t -fno-sanitize-thread-atomics && not %deflake %run %t 2>&1 \ +// RUN: | FileCheck --allow-empty --check-prefix=CHECK-NO-ATOMICS %s +// RUN: %clangxx_tsan -O1 %s -o %t -mllvm -tsan-instrument-atomics=0 && not %deflake %run %t 2>&1 \ +// RUN: | FileCheck --allow-empty --check-prefix=CHECK-NO-ATOMICS %s <%t + #include "test.h" void *Thread(void *a) { @@ -18,3 +27,5 @@ int main() { } // CHECK: WARNING: ThreadSanitizer: data race + +// CHECK-NO-ATOMICS-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/atomic_store.cc b/test/tsan/atomic_store.cc new file mode 100644 index 000000000000..7ff4879d3cae --- /dev/null +++ b/test/tsan/atomic_store.cc @@ -0,0 +1,49 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "test.h" + +long long Data; +long long Sync; + +void *Thread1(void *x) { + Data++; + __atomic_store_n(&Sync, 1, __ATOMIC_RELEASE); + barrier_wait(&barrier); + barrier_wait(&barrier); + return NULL; +} + +void *Thread2(void *x) { + barrier_wait(&barrier); + if (__atomic_load_n(&Sync, __ATOMIC_RELAXED) != 1) + exit(0); + // This store must terminate release sequence of the store in Thread1, + // thus tsan must detect race between Thread1 and main on Data. + __atomic_store_n(&Sync, 2, __ATOMIC_RELEASE); + barrier_wait(&barrier); + return NULL; +} + +int main() { + barrier_init(&barrier, 3); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + barrier_wait(&barrier); + barrier_wait(&barrier); + if (__atomic_load_n(&Sync, __ATOMIC_ACQUIRE) != 2) + exit(0); + if (Data != 1) + exit(0); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Read +// CHECK: #0 main +// CHECK: Previous write +// CHECK: #0 Thread1 +// CHECK: Location is global 'Data' +// CHECK: DONE diff --git a/test/tsan/debug_alloc_stack.cc b/test/tsan/debug_alloc_stack.cc new file mode 100644 index 000000000000..303c103206f8 --- /dev/null +++ b/test/tsan/debug_alloc_stack.cc @@ -0,0 +1,84 @@ +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: env %env_tsan_opts=stack_trace_format=DEFAULT %deflake %run %t 2>&1 | FileCheck %s + +// Until I figure out how to make this test work on Linux +// REQUIRES: system-darwin + +#include "test.h" +#include <pthread.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef __APPLE__ +#include <sys/types.h> +#endif + +extern "C" int __tsan_get_alloc_stack(void *addr, void **trace, size_t size, + int *thread_id, void *os_id); + +char *mem; +void alloc_func() { mem = (char *)malloc(10); } + +void *AllocThread(void *context) { + uint64_t tid; +#ifdef __APPLE__ + pthread_threadid_np(NULL, &tid); +#else + tid = gettid(); +#endif + fprintf(stderr, "alloc stack thread os id = 0x%llx\n", tid); + // CHECK: alloc stack thread os id = [[THREAD_OS_ID:0x[0-9a-f]+]] + alloc_func(); + return NULL; +} + +void *RaceThread(void *context) { + *mem = 'a'; + barrier_wait(&barrier); + return NULL; +} + +int main() { + pthread_t t; + barrier_init(&barrier, 2); + + pthread_create(&t, NULL, AllocThread, NULL); + pthread_join(t, NULL); + + void *trace[100]; + size_t num_frames = 100; + int thread_id; + void *thread_os_id; + num_frames = + __tsan_get_alloc_stack(mem, trace, num_frames, &thread_id, &thread_os_id); + + fprintf(stderr, "alloc stack retval %s\n", + (num_frames > 0 && num_frames < 10) ? "ok" : ""); + // CHECK: alloc stack retval ok + fprintf(stderr, "thread id = %d\n", thread_id); + // CHECK: thread id = 1 + fprintf(stderr, "thread os id = 0x%llx\n", (uint64_t)thread_os_id); + // CHECK: thread os id = [[THREAD_OS_ID]] + fprintf(stderr, "%p\n", trace[0]); + // CHECK: [[ALLOC_FRAME_0:0x[0-9a-f]+]] + fprintf(stderr, "%p\n", trace[1]); + // CHECK: [[ALLOC_FRAME_1:0x[0-9a-f]+]] + fprintf(stderr, "%p\n", trace[2]); + // CHECK: [[ALLOC_FRAME_2:0x[0-9a-f]+]] + + pthread_create(&t, NULL, RaceThread, NULL); + barrier_wait(&barrier); + mem[0] = 'b'; + pthread_join(t, NULL); + + free(mem); + + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is heap block of size 10 at {{.*}} allocated by thread T1 +// CHECK: #0 [[ALLOC_FRAME_0]] +// CHECK: #1 [[ALLOC_FRAME_1]] in alloc_func +// CHECK: #2 [[ALLOC_FRAME_2]] in AllocThread diff --git a/test/tsan/debug_locate.cc b/test/tsan/debug_locate.cc new file mode 100644 index 000000000000..01b0960d85b7 --- /dev/null +++ b/test/tsan/debug_locate.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +extern "C" const char * +__tsan_locate_address(void *addr, char *name, size_t name_size, + void **region_address_ptr, size_t *region_size_ptr); + +long global_var; + +int main() { + long stack_var; + void *heap_var = malloc(10); + + fprintf(stderr, "stack_var = %p\n", &stack_var); + fprintf(stderr, "global_var = %p\n", &global_var); + fprintf(stderr, "heap_var = %p\n", heap_var); + // CHECK: stack_var = [[STACK_VAR:0x[0-9a-f]+]] + // CHECK: global_var = [[GLOBAL_VAR:0x[0-9a-f]+]] + // CHECK: heap_var = [[HEAP_VAR:0x[0-9a-f]+]] + + const char *type; + char name[128]; + void *start; + size_t size; + type = __tsan_locate_address(&stack_var, name, 128, &start, &size); + fprintf(stderr, "type: %s\n", type); + // CHECK: type: stack + + type = __tsan_locate_address(&global_var, name, 128, &start, &size); + fprintf(stderr, "type: %s, name = %s, start = %p, size = %zu\n", type, name, + start, size); + // CHECK: type: global, name = global_var, start = [[GLOBAL_VAR]], size = {{8|0}} + + type = __tsan_locate_address(heap_var, name, 128, &start, &size); + fprintf(stderr, "type: %s, start = %p, size = %zu\n", type, start, size); + // CHECK: type: heap, start = [[HEAP_VAR]], size = 10 + + free(heap_var); + return 0; +} diff --git a/test/tsan/exceptions.cc b/test/tsan/exceptions.cc new file mode 100644 index 000000000000..193e115f7802 --- /dev/null +++ b/test/tsan/exceptions.cc @@ -0,0 +1,185 @@ +// RUN: %clangxx_tsan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include "test.h" +#include <setjmp.h> + +__attribute__((noinline)) void throws_int() { + throw 42; +} + +__attribute__((noinline)) void callee_throws() { + try { + throws_int(); + } catch (int) { // NOLINT + fprintf(stderr, "callee_throws caught exception\n"); + } +} + +__attribute__((noinline)) void throws_catches_rethrows() { + try { + throws_int(); + } catch (int) { // NOLINT + fprintf(stderr, "throws_catches_rethrows caught exception\n"); + throw; + } +} + +__attribute__((noinline)) void callee_rethrows() { + try { + throws_catches_rethrows(); + } catch (int) { // NOLINT + fprintf(stderr, "callee_rethrows caught exception\n"); + } +} + +__attribute__((noinline)) void throws_and_catches() { + try { + throws_int(); + } catch (int) { // NOLINT + fprintf(stderr, "throws_and_catches caught exception\n"); + } +} + +__attribute__((noinline)) void nested_try() { + try { + try { + throws_int(); + } catch (double) { // NOLINT + fprintf(stderr, "nested_try inner block caught exception\n"); + } + } catch (int) { // NOLINT + fprintf(stderr, "nested_try outer block caught exception\n"); + } +} + +__attribute__((noinline)) void nested_try2() { + try { + try { + throws_int(); + } catch (int) { // NOLINT + fprintf(stderr, "nested_try inner block caught exception\n"); + } + } catch (double) { // NOLINT + fprintf(stderr, "nested_try outer block caught exception\n"); + } +} + +class ClassWithDestructor { + public: + ClassWithDestructor() { + fprintf(stderr, "ClassWithDestructor\n"); + } + ~ClassWithDestructor() { + fprintf(stderr, "~ClassWithDestructor\n"); + } +}; + +__attribute__((noinline)) void local_object_then_throw() { + ClassWithDestructor obj; + throws_int(); +} + +__attribute__((noinline)) void cpp_object_with_destructor() { + try { + local_object_then_throw(); + } catch (int) { // NOLINT + fprintf(stderr, "cpp_object_with_destructor caught exception\n"); + } +} + +__attribute__((noinline)) void recursive_call(long n) { + if (n > 0) { + recursive_call(n - 1); + } else { + throws_int(); + } +} + +__attribute__((noinline)) void multiframe_unwind() { + try { + recursive_call(5); + } catch (int) { // NOLINT + fprintf(stderr, "multiframe_unwind caught exception\n"); + } +} + +__attribute__((noinline)) void longjmp_unwind() { + jmp_buf env; + int i = setjmp(env); + if (i != 0) { + fprintf(stderr, "longjmp_unwind jumped\n"); + return; + } + + try { + longjmp(env, 42); + } catch (int) { // NOLINT + fprintf(stderr, "longjmp_unwind caught exception\n"); + } +} + +__attribute__((noinline)) void recursive_call_longjmp(jmp_buf env, long n) { + if (n > 0) { + recursive_call_longjmp(env, n - 1); + } else { + longjmp(env, 42); + } +} + +__attribute__((noinline)) void longjmp_unwind_multiple_frames() { + jmp_buf env; + int i = setjmp(env); + if (i != 0) { + fprintf(stderr, "longjmp_unwind_multiple_frames jumped\n"); + return; + } + + try { + recursive_call_longjmp(env, 5); + } catch (int) { // NOLINT + fprintf(stderr, "longjmp_unwind_multiple_frames caught exception\n"); + } +} + +#define CHECK_SHADOW_STACK(val) \ + fprintf(stderr, (val == __tsan_testonly_shadow_stack_current_size() \ + ? "OK.\n" \ + : "Shadow stack leak!\n")); + +int main(int argc, const char * argv[]) { + fprintf(stderr, "Hello, World!\n"); + unsigned long shadow_stack_size = __tsan_testonly_shadow_stack_current_size(); + + throws_and_catches(); + CHECK_SHADOW_STACK(shadow_stack_size); + + callee_throws(); + CHECK_SHADOW_STACK(shadow_stack_size); + + callee_rethrows(); + CHECK_SHADOW_STACK(shadow_stack_size); + + nested_try(); + CHECK_SHADOW_STACK(shadow_stack_size); + + nested_try2(); + CHECK_SHADOW_STACK(shadow_stack_size); + + cpp_object_with_destructor(); + CHECK_SHADOW_STACK(shadow_stack_size); + + multiframe_unwind(); + CHECK_SHADOW_STACK(shadow_stack_size); + + longjmp_unwind(); + CHECK_SHADOW_STACK(shadow_stack_size); + + longjmp_unwind_multiple_frames(); + CHECK_SHADOW_STACK(shadow_stack_size); + + return 0; +} + +// CHECK: Hello, World! +// CHECK-NOT: Shadow stack leak diff --git a/test/tsan/fork_atexit.cc b/test/tsan/fork_atexit.cc index 15cf0a2485ca..6e3a2f5c4ba7 100644 --- a/test/tsan/fork_atexit.cc +++ b/test/tsan/fork_atexit.cc @@ -7,7 +7,7 @@ #include <sys/wait.h> void foo() { - printf("CHILD ATEXIT\n"); + fprintf(stderr, "CHILD ATEXIT\n"); } void *worker(void *unused) { diff --git a/test/tsan/global_race.cc b/test/tsan/global_race.cc index a35299619e9d..ec26b06f5c1c 100644 --- a/test/tsan/global_race.cc +++ b/test/tsan/global_race.cc @@ -1,4 +1,14 @@ -// RUN: %clangxx_tsan -O1 %s -o %T/global_race.cc.exe && %deflake %run %T/global_race.cc.exe | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %T/global_race.cc.exe && %deflake %run %T/global_race.cc.exe 2>&1 \ +// RUN: | FileCheck %s + +// Also check that memory access instrumentation can be configured by either +// driver or legacy flags: + +// RUN: %clangxx_tsan -O1 %s -o %T/global_race.cc.exe -fno-sanitize-thread-memory-access && not %deflake %run %T/global_race.cc.exe 2>&1 \ +// RUN: | FileCheck --allow-empty --check-prefix=CHECK-MEMORY-ACCESS-OFF %s +// RUN: %clangxx_tsan -O1 %s -o %T/global_race.cc.exe -mllvm -tsan-instrument-memory-accesses=0 && not %deflake %run %T/global_race.cc.exe 2>&1 \ +// RUN: | FileCheck --allow-empty --check-prefix=CHECK-MEMORY-ACCESS-OFF %s + #include "test.h" int GlobalData[10]; @@ -23,3 +33,4 @@ int main() { // CHECK: WARNING: ThreadSanitizer: data race // CHECK: Location is global 'GlobalData' {{(of size 40 )?}}at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}}) +// CHECK-MEMORY-ACCESS-OFF-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/ignore_lib4.cc b/test/tsan/ignore_lib4.cc index 193df11d2b2a..84d8b2768a94 100644 --- a/test/tsan/ignore_lib4.cc +++ b/test/tsan/ignore_lib4.cc @@ -3,8 +3,6 @@ // 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 diff --git a/test/tsan/ignore_lib5.cc b/test/tsan/ignore_lib5.cc new file mode 100644 index 000000000000..d7cd28500be9 --- /dev/null +++ b/test/tsan/ignore_lib5.cc @@ -0,0 +1,75 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib1.so +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: echo running w/o suppressions: +// RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOSUPP +// RUN: echo running with suppressions: +// RUN: %env_tsan_opts=suppressions='%s.supp' %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP + +// REQUIRES: stable-runtime + +// Previously the test episodically failed with: +// ThreadSanitizer: called_from_lib suppression '/libignore_lib1.so$' is +// matched against 2 libraries: '/libignore_lib1.so' and '/libignore_lib1.so' +// This was caused by non-atomicity of reading of /proc/self/maps. + +#ifndef LIB + +#include <dlfcn.h> +#include <sys/mman.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <libgen.h> +#include <string> +#include "test.h" + +#ifndef MAP_32BIT +# define MAP_32BIT 0 +#endif + +#ifdef __APPLE__ +# define TSAN_MAP_ANON MAP_ANON +#else +# define TSAN_MAP_ANON MAP_ANONYMOUS +#endif + +void *thr(void *arg) { + // This thread creates lots of separate mappings in /proc/self/maps before + // the ignored library. + for (int i = 0; i < 10000; i++) { + if (i == 5000) + barrier_wait(&barrier); + mmap(0, 4096, PROT_READ, TSAN_MAP_ANON | MAP_PRIVATE | MAP_32BIT, -1 , 0); + mmap(0, 4096, PROT_WRITE, TSAN_MAP_ANON | MAP_PRIVATE | MAP_32BIT, -1 , 0); + } + return 0; +} + +int main(int argc, char **argv) { + barrier_init(&barrier, 2); + pthread_t th; + pthread_create(&th, 0, thr, 0); + barrier_wait(&barrier); + std::string lib = std::string(dirname(argv[0])) + "/libignore_lib1.so"; + void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW); + if (h == 0) + exit(printf("failed to load the library (%d)\n", errno)); + void (*f)() = (void(*)())dlsym(h, "libfunc"); + if (f == 0) + exit(printf("failed to find the func (%d)\n", errno)); + pthread_join(th, 0); + f(); +} + +#else // #ifdef LIB + +#include "ignore_lib_lib.h" + +#endif // #ifdef LIB + +// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race +// CHECK-NOSUPP: OK + +// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race +// CHECK-WITHSUPP: OK + diff --git a/test/tsan/ignore_lib5.cc.supp b/test/tsan/ignore_lib5.cc.supp new file mode 100644 index 000000000000..9f4119ec0bc4 --- /dev/null +++ b/test/tsan/ignore_lib5.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib1.so$ + diff --git a/test/tsan/java.h b/test/tsan/java.h index 565a7a7fdabf..e9aa4ee2440b 100644 --- a/test/tsan/java.h +++ b/test/tsan/java.h @@ -7,6 +7,7 @@ void __tsan_java_init(jptr heap_begin, jptr heap_size); int __tsan_java_fini(); void __tsan_java_alloc(jptr ptr, jptr size); void __tsan_java_free(jptr ptr, jptr size); +jptr __tsan_java_find(jptr *from_ptr, jptr to); void __tsan_java_move(jptr src, jptr dst, jptr size); void __tsan_java_finalize(); void __tsan_java_mutex_lock(jptr addr); diff --git a/test/tsan/libcxx/lit.local.cfg b/test/tsan/libcxx/lit.local.cfg index 202b44ee116d..3ee705736e6f 100644 --- a/test/tsan/libcxx/lit.local.cfg +++ b/test/tsan/libcxx/lit.local.cfg @@ -5,6 +5,8 @@ def getRoot(config): root = getRoot(config) -if not root.has_libcxx: +# Only run if we have an instrumented libcxx. On Darwin, run always (we have +# interceptors to support the system-provided libcxx). +if not root.has_libcxx and root.host_os != 'Darwin': config.unsupported = True diff --git a/test/tsan/lit.cfg b/test/tsan/lit.cfg index 1fc1eccd15ea..5d82cc9d4921 100644 --- a/test/tsan/lit.cfg +++ b/test/tsan/lit.cfg @@ -38,13 +38,15 @@ if config.compiler_id == 'GNU': else: extra_cflags = [] +tsan_incdir = config.test_source_root + "/../" # Setup default compiler flags used with -fsanitize=thread option. clang_tsan_cflags = (["-fsanitize=thread", "-Wall"] + [config.target_cflags] + config.debug_info_flags + - extra_cflags) -clang_tsan_cxxflags = config.cxx_mode_flags + clang_tsan_cflags + extra_cflags + + ["-I%s" % tsan_incdir]) +clang_tsan_cxxflags = config.cxx_mode_flags + clang_tsan_cflags + ["-std=c++11"] + ["-I%s" % tsan_incdir] # Add additional flags if we're using instrumented libc++. # Instrumented libcxx currently not supported on Darwin. if config.has_libcxx and config.host_os != 'Darwin': @@ -54,8 +56,7 @@ if config.has_libcxx and config.host_os != 'Darwin': 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++", + clang_tsan_cxxflags += ["-nostdinc++", "-I%s" % libcxx_incdir, libcxx_so, "-Wl,-rpath=%s" % libcxx_libdir] @@ -69,7 +70,7 @@ config.substitutions.append( ("%clangxx_tsan ", build_invocation(clang_tsan_cxxf # Define CHECK-%os to check for OS-dependent output. config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) -config.substitutions.append( ("%deflake ", os.path.join(os.path.dirname(__file__), "deflake.bash")) ) +config.substitutions.append( ("%deflake ", os.path.join(os.path.dirname(__file__), "deflake.bash") + " ")) # Default test suffixes. config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm'] diff --git a/test/tsan/longjmp.cc b/test/tsan/longjmp.cc index 61d285c11bf2..a8abca601758 100644 --- a/test/tsan/longjmp.cc +++ b/test/tsan/longjmp.cc @@ -1,8 +1,5 @@ // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s -// Longjmp assembly has not been implemented for mips64 yet -// XFAIL: mips64 - #include <stdio.h> #include <stdlib.h> #include <setjmp.h> diff --git a/test/tsan/longjmp2.cc b/test/tsan/longjmp2.cc index 2b2775a8ca1e..d396f3fec66e 100644 --- a/test/tsan/longjmp2.cc +++ b/test/tsan/longjmp2.cc @@ -1,8 +1,5 @@ // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s -// Longjmp assembly has not been implemented for mips64 yet -// XFAIL: mips64 - #include <stdio.h> #include <stdlib.h> #include <setjmp.h> diff --git a/test/tsan/longjmp3.cc b/test/tsan/longjmp3.cc index 197b91e1cdc3..842cf264c977 100644 --- a/test/tsan/longjmp3.cc +++ b/test/tsan/longjmp3.cc @@ -1,8 +1,5 @@ // 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 - #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/tsan/longjmp4.cc b/test/tsan/longjmp4.cc index 3785a0f07261..4c2fbf0c4028 100644 --- a/test/tsan/longjmp4.cc +++ b/test/tsan/longjmp4.cc @@ -1,8 +1,5 @@ // 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 - #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/tsan/map32bit.cc b/test/tsan/map32bit.cc index 0411f29a9504..3b4f8990016e 100644 --- a/test/tsan/map32bit.cc +++ b/test/tsan/map32bit.cc @@ -8,7 +8,7 @@ // https://github.com/google/sanitizers/issues/412 // MAP_32BIT flag for mmap is supported only for x86_64. -// XFAIL: mips64 +// XFAIL: mips // XFAIL: aarch64 // XFAIL: powerpc64 diff --git a/test/tsan/pie_test.cc b/test/tsan/pie_test.cc index 8635f9cd403f..93d31daf5fd6 100644 --- a/test/tsan/pie_test.cc +++ b/test/tsan/pie_test.cc @@ -1,12 +1,6 @@ // Check if tsan work with PIE binaries. // RUN: %clang_tsan %s -pie -fpic -o %t && %run %t -// Some kernels might map PIE segments outside the current segment -// mapping defined for x86 [1]. -// [1] https://git.kernel.org/linus/d1fd836dcf00d2028c700c7e44d2c23404062c90 - -// UNSUPPORTED: x86 - int main(void) { return 0; } diff --git a/test/tsan/signal_block.cc b/test/tsan/signal_block.cc new file mode 100644 index 000000000000..dfd4259c43c9 --- /dev/null +++ b/test/tsan/signal_block.cc @@ -0,0 +1,60 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Test that a signal is not delivered when it is blocked. + +#include "test.h" +#include <semaphore.h> +#include <signal.h> +#include <errno.h> + +int stop; +sig_atomic_t signal_blocked; + +void handler(int signum) { + if (signal_blocked) { + fprintf(stderr, "signal arrived when blocked\n"); + exit(1); + } +} + +void *thread(void *arg) { + sigset_t myset; + sigemptyset(&myset); + sigaddset(&myset, SIGUSR1); + while (!__atomic_load_n(&stop, __ATOMIC_RELAXED)) { + usleep(1); + if (pthread_sigmask(SIG_BLOCK, &myset, 0)) { + fprintf(stderr, "pthread_sigmask failed %d\n", errno); + exit(1); + } + signal_blocked = 1; + usleep(1); + signal_blocked = 0; + if (pthread_sigmask(SIG_UNBLOCK, &myset, 0)) { + fprintf(stderr, "pthread_sigmask failed %d\n", errno); + exit(1); + } + } + return 0; +} + +int main(int argc, char** argv) { + struct sigaction act = {}; + act.sa_handler = &handler; + if (sigaction(SIGUSR1, &act, 0)) { + fprintf(stderr, "sigaction failed %d\n", errno); + return 1; + } + pthread_t th; + pthread_create(&th, 0, thread, 0); + for (int i = 0; i < 100000; i++) + pthread_kill(th, SIGUSR1); + __atomic_store_n(&stop, 1, __ATOMIC_RELAXED); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: ThreadSanitizer CHECK +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: DONE diff --git a/test/tsan/signal_cond.cc b/test/tsan/signal_cond.cc index beb2e0266e50..6c20dd8f2e39 100644 --- a/test/tsan/signal_cond.cc +++ b/test/tsan/signal_cond.cc @@ -14,7 +14,7 @@ pthread_cond_t cond; void sig_handler(int sig) { (void)sig; - write(1, "SIGNAL\n", sizeof("SIGNAL\n") - 1); + write(2, "SIGNAL\n", sizeof("SIGNAL\n") - 1); barrier_wait(&barrier); } diff --git a/test/tsan/signal_longjmp.cc b/test/tsan/signal_longjmp.cc index 45e24626cbfa..f9fa4f54f507 100644 --- a/test/tsan/signal_longjmp.cc +++ b/test/tsan/signal_longjmp.cc @@ -3,8 +3,6 @@ // Test case for longjumping out of signal handler: // https://github.com/google/sanitizers/issues/482 -// Longjmp assembly has not been implemented for mips64 yet -// XFAIL: mips64 // This test fails on powerpc64 BE (VMA=44), a segmentation fault // error happens at the second assignment // "((volatile int *volatile)mem)[1] = 1". diff --git a/test/tsan/simple_stack.c b/test/tsan/simple_stack.c index 6ef92fb46c68..71a3911b39be 100644 --- a/test/tsan/simple_stack.c +++ b/test/tsan/simple_stack.c @@ -1,4 +1,3 @@ -// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s #include "test.h" int Global; @@ -47,20 +46,40 @@ int main() { return 0; } +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s + +// Also check that functions instrumentation can be configured by either driver +// or legacy flags: + +// RUN: %clangxx_tsan -O1 %s -o %t -fno-sanitize-thread-func-entry-exit && %deflake %run %t 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-FUNC-ENTRY-EXIT-OFF %s +// RUN: %clangxx_tsan -O1 %s -o %t -mllvm -tsan-instrument-func-entry-exit=0 && %deflake %run %t 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-FUNC-ENTRY-EXIT-OFF %s + // CHECK: WARNING: ThreadSanitizer: data race // CHECK-NEXT: Write of size 4 at {{.*}} by thread T1: -// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:7{{(:10)?}} ({{.*}}) -// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:12{{(:3)?}} ({{.*}}) -// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:26{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:6{{(:10)?}} ({{.*}}) +// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:11{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:25{{(:3)?}} ({{.*}}) // CHECK: Previous read of size 4 at {{.*}} by thread T2: -// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:16{{(:20)?}} ({{.*}}) -// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:21{{(:3)?}} ({{.*}}) -// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:31{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:15{{(:20)?}} ({{.*}}) +// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:20{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:30{{(:3)?}} ({{.*}}) // CHECK: Thread T1 (tid={{.*}}, running) created by main thread at: // CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) -// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:37{{(:3)?}} ({{.*}}) -// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:36{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:42{{(:3)?}} ({{.*}}) // CHECK: Thread T2 ({{.*}}) created by main thread at: // CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) -// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:37{{(:3)?}} ({{.*}}) -// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:36{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}}) + +// CHECK-FUNC-ENTRY-EXIT-OFF: WARNING: ThreadSanitizer: data race +// CHECK-FUNC-ENTRY-EXIT-OFF-NEXT: Write of size 4 at {{.*}} by thread T1: +// CHECK-FUNC-ENTRY-EXIT-OFF-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:6{{(:10)?}} ({{.*}}) +// CHECK-FUNC-ENTRY-EXIT-OFF: Previous read of size 4 at {{.*}} by thread T2: +// CHECK-FUNC-ENTRY-EXIT-OFF-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:15{{(:20)?}} ({{.*}}) +// CHECK-FUNC-ENTRY-EXIT-OFF: Thread T1 (tid={{.*}}, running) created by main thread at: +// CHECK-FUNC-ENTRY-EXIT-OFF-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-FUNC-ENTRY-EXIT-OFF: Thread T2 ({{.*}}) created by main thread at: +// CHECK-FUNC-ENTRY-EXIT-OFF-NEXT: #0 pthread_create {{.*}} ({{.*}}) diff --git a/test/tsan/test.h b/test/tsan/test.h index e3affdc0837d..6b981c09f53d 100644 --- a/test/tsan/test.h +++ b/test/tsan/test.h @@ -6,6 +6,7 @@ #include <stddef.h> #include <sched.h> #include <stdarg.h> +#include "sanitizer_common/print_address.h" #ifdef __APPLE__ #include <mach/mach_time.h> @@ -22,6 +23,7 @@ extern "C" { void __tsan_testonly_barrier_init(invisible_barrier_t *barrier, unsigned count); void __tsan_testonly_barrier_wait(invisible_barrier_t *barrier); +unsigned long __tsan_testonly_shadow_stack_current_size(); #ifdef __cplusplus } #endif @@ -37,23 +39,6 @@ static inline void barrier_wait(invisible_barrier_t *barrier) { // Default instance of the barrier, but a test can declare more manually. invisible_barrier_t barrier; -void print_address(const char *str, int n, ...) { - fprintf(stderr, "%s", str); - va_list ap; - va_start(ap, n); - while (n--) { - void *p = va_arg(ap, void *); -#if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) - // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not - // match to the format used in the diagnotic message. - fprintf(stderr, "0x%012lx ", (unsigned long) p); -#elif defined(__mips64) - fprintf(stderr, "0x%010lx ", (unsigned long) p); -#endif - } - fprintf(stderr, "\n"); -} - #ifdef __APPLE__ unsigned long long monotonic_clock_ns() { static mach_timebase_info_data_t timebase_info; |