diff options
| author | Kostya Serebryany <kcc@google.com> | 2012-05-10 14:18:22 +0000 |
|---|---|---|
| committer | Kostya Serebryany <kcc@google.com> | 2012-05-10 14:18:22 +0000 |
| commit | ff15ef0c506e65a7ecff95f52356320a96a89e4b (patch) | |
| tree | 0778e5bfd36a80473a7970eb42e692e51c002c79 /compiler-rt/lib/tsan | |
| parent | df68b67f067a1e762eb045a6ebef3cb1ef8b8cbe (diff) | |
| download | bcm5719-llvm-ff15ef0c506e65a7ecff95f52356320a96a89e4b.tar.gz bcm5719-llvm-ff15ef0c506e65a7ecff95f52356320a96a89e4b.zip | |
[tsan] ThreadSanitizer tests and micro benchmarks. No makefiles yet.
llvm-svn: 156545
Diffstat (limited to 'compiler-rt/lib/tsan')
51 files changed, 3652 insertions, 0 deletions
diff --git a/compiler-rt/lib/tsan/benchmarks/mini_bench_local.cc b/compiler-rt/lib/tsan/benchmarks/mini_bench_local.cc new file mode 100644 index 00000000000..accdcb63878 --- /dev/null +++ b/compiler-rt/lib/tsan/benchmarks/mini_bench_local.cc @@ -0,0 +1,49 @@ +// Mini-benchmark for tsan: non-shared memory writes. +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +int len; +int *a; +const int kNumIter = 1000; + +__attribute__((noinline)) +void Run(int idx) { + for (int i = 0, n = len; i < n; i++) + a[i + idx * n] = i; +} + +void *Thread(void *arg) { + long idx = (long)arg; + printf("Thread %ld started\n", idx); + for (int i = 0; i < kNumIter; i++) + Run(idx); + printf("Thread %ld done\n", idx); + return 0; +} + +int main(int argc, char **argv) { + int n_threads = 0; + if (argc != 3) { + n_threads = 4; + len = 1000000; + } else { + n_threads = atoi(argv[1]); + assert(n_threads > 0 && n_threads <= 32); + len = atoi(argv[2]); + } + printf("%s: n_threads=%d len=%d iter=%d\n", + __FILE__, n_threads, len, kNumIter); + a = new int[n_threads * len]; + pthread_t *t = new pthread_t[n_threads]; + for (int i = 0; i < n_threads; i++) { + pthread_create(&t[i], 0, Thread, (void*)i); + } + for (int i = 0; i < n_threads; i++) { + pthread_join(t[i], 0); + } + delete [] t; + delete [] a; + return 0; +} diff --git a/compiler-rt/lib/tsan/benchmarks/mini_bench_shared.cc b/compiler-rt/lib/tsan/benchmarks/mini_bench_shared.cc new file mode 100644 index 00000000000..f9b9f42f78a --- /dev/null +++ b/compiler-rt/lib/tsan/benchmarks/mini_bench_shared.cc @@ -0,0 +1,51 @@ +// Mini-benchmark for tsan: shared memory reads. +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +int len; +int *a; +const int kNumIter = 1000; + +__attribute__((noinline)) +void Run(int idx) { + for (int i = 0, n = len; i < n; i++) + if (a[i] != i) abort(); +} + +void *Thread(void *arg) { + long idx = (long)arg; + printf("Thread %ld started\n", idx); + for (int i = 0; i < kNumIter; i++) + Run(idx); + printf("Thread %ld done\n", idx); + return 0; +} + +int main(int argc, char **argv) { + int n_threads = 0; + if (argc != 3) { + n_threads = 4; + len = 1000000; + } else { + n_threads = atoi(argv[1]); + assert(n_threads > 0 && n_threads <= 32); + len = atoi(argv[2]); + } + printf("%s: n_threads=%d len=%d iter=%d\n", + __FILE__, n_threads, len, kNumIter); + a = new int[len]; + for (int i = 0, n = len; i < n; i++) + a[i] = i; + pthread_t *t = new pthread_t[n_threads]; + for (int i = 0; i < n_threads; i++) { + pthread_create(&t[i], 0, Thread, (void*)i); + } + for (int i = 0; i < n_threads; i++) { + pthread_join(t[i], 0); + } + delete [] t; + delete [] a; + return 0; +} diff --git a/compiler-rt/lib/tsan/benchmarks/start_many_threads.cc b/compiler-rt/lib/tsan/benchmarks/start_many_threads.cc new file mode 100644 index 00000000000..1e86fa6c502 --- /dev/null +++ b/compiler-rt/lib/tsan/benchmarks/start_many_threads.cc @@ -0,0 +1,52 @@ +// Mini-benchmark for creating a lot of threads. +// +// Some facts: +// a) clang -O1 takes <15ms to start N=500 threads, +// consuming ~4MB more RAM than N=1. +// b) clang -O1 -ftsan takes ~26s to start N=500 threads, +// eats 5GB more RAM than N=1 (which is somewhat expected but still a lot) +// but then it consumes ~4GB of extra memory when the threads shut down! +// (definitely not in the barrier_wait interceptor) +// Also, it takes 26s to run with N=500 vs just 1.1s to run with N=1. +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +pthread_barrier_t all_threads_ready; + +void* Thread(void *unused) { + pthread_barrier_wait(&all_threads_ready); + return 0; +} + +int main(int argc, char **argv) { + int n_threads; + if (argc == 1) { + n_threads = 100; + } else if (argc == 2) { + n_threads = atoi(argv[1]); + } else { + printf("Usage: %s n_threads\n", argv[0]); + return 1; + } + printf("%s: n_threads=%d\n", __FILE__, n_threads); + + pthread_barrier_init(&all_threads_ready, NULL, n_threads + 1); + + pthread_t *t = new pthread_t[n_threads]; + for (int i = 0; i < n_threads; i++) { + int status = pthread_create(&t[i], 0, Thread, (void*)i); + assert(status == 0); + } + // sleep(5); // FIXME: simplify measuring the memory usage. + pthread_barrier_wait(&all_threads_ready); + for (int i = 0; i < n_threads; i++) { + pthread_join(t[i], 0); + } + // sleep(5); // FIXME: simplify measuring the memory usage. + delete [] t; + + return 0; +} diff --git a/compiler-rt/lib/tsan/benchmarks/vts_many_threads_bench.cc b/compiler-rt/lib/tsan/benchmarks/vts_many_threads_bench.cc new file mode 100644 index 00000000000..f1056e20c87 --- /dev/null +++ b/compiler-rt/lib/tsan/benchmarks/vts_many_threads_bench.cc @@ -0,0 +1,120 @@ +// Mini-benchmark for tsan VTS worst case performance +// Idea: +// 1) Spawn M + N threads (M >> N) +// We'll call the 'M' threads as 'garbage threads'. +// 2) Make sure all threads have created thus no TIDs were reused +// 3) Join the garbage threads +// 4) Do many sync operations on the remaining N threads +// +// It turns out that due to O(M+N) VTS complexity the (4) is much slower with +// when N is large. +// +// Some numbers: +// a) clang++ native O1 with n_iterations=200kk takes +// 5s regardless of M +// clang++ tsanv2 O1 with n_iterations=20kk takes +// 23.5s with M=200 +// 11.5s with M=1 +// i.e. tsanv2 is ~23x to ~47x slower than native, depends on M. +// b) g++ native O1 with n_iterations=200kk takes +// 5.5s regardless of M +// g++ tsanv1 O1 with n_iterations=2kk takes +// 39.5s with M=200 +// 20.5s with M=1 +// i.e. tsanv1 is ~370x to ~720x slower than native, depends on M. + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +class __attribute__((aligned(64))) Mutex { + public: + Mutex() { pthread_mutex_init(&m_, NULL); } + ~Mutex() { pthread_mutex_destroy(&m_); } + void Lock() { pthread_mutex_lock(&m_); } + void Unlock() { pthread_mutex_unlock(&m_); } + + private: + pthread_mutex_t m_; +}; + +const int kNumMutexes = 1024; +Mutex mutexes[kNumMutexes]; + +int n_threads, n_iterations; + +pthread_barrier_t all_threads_ready, main_threads_ready; + +void* GarbageThread(void *unused) { + pthread_barrier_wait(&all_threads_ready); + return 0; +} + +void *Thread(void *arg) { + long idx = (long)arg; + pthread_barrier_wait(&all_threads_ready); + + // Wait for the main thread to join the garbage threads. + pthread_barrier_wait(&main_threads_ready); + + printf("Thread %ld go!\n", idx); + int offset = idx * kNumMutexes / n_threads; + for (int i = 0; i < n_iterations; i++) { + mutexes[(offset + i) % kNumMutexes].Lock(); + mutexes[(offset + i) % kNumMutexes].Unlock(); + } + printf("Thread %ld done\n", idx); + return 0; +} + +int main(int argc, char **argv) { + int n_garbage_threads; + if (argc == 1) { + n_threads = 2; + n_garbage_threads = 200; + n_iterations = 20000000; + } else if (argc == 4) { + n_threads = atoi(argv[1]); + assert(n_threads > 0 && n_threads <= 32); + n_garbage_threads = atoi(argv[2]); + assert(n_garbage_threads > 0 && n_garbage_threads <= 16000); + n_iterations = atoi(argv[3]); + } else { + printf("Usage: %s n_threads n_garbage_threads n_iterations\n", argv[0]); + return 1; + } + printf("%s: n_threads=%d n_garbage_threads=%d n_iterations=%d\n", + __FILE__, n_threads, n_garbage_threads, n_iterations); + + pthread_barrier_init(&all_threads_ready, NULL, n_garbage_threads + n_threads + 1); + pthread_barrier_init(&main_threads_ready, NULL, n_threads + 1); + + pthread_t *t = new pthread_t[n_threads]; + { + pthread_t *g_t = new pthread_t[n_garbage_threads]; + for (int i = 0; i < n_garbage_threads; i++) { + int status = pthread_create(&g_t[i], 0, GarbageThread, NULL); + assert(status == 0); + } + for (int i = 0; i < n_threads; i++) { + int status = pthread_create(&t[i], 0, Thread, (void*)i); + assert(status == 0); + } + pthread_barrier_wait(&all_threads_ready); + printf("All threads started! Killing the garbage threads.\n"); + for (int i = 0; i < n_garbage_threads; i++) { + pthread_join(g_t[i], 0); + } + delete [] g_t; + } + printf("Resuming the main threads.\n"); + pthread_barrier_wait(&main_threads_ready); + + + for (int i = 0; i < n_threads; i++) { + pthread_join(t[i], 0); + } + delete [] t; + return 0; +} diff --git a/compiler-rt/lib/tsan/output_tests/free_race.c b/compiler-rt/lib/tsan/output_tests/free_race.c new file mode 100644 index 00000000000..24f21206d4b --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/free_race.c @@ -0,0 +1,36 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +int *mem; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx); + free(mem); + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + usleep(1000000); + pthread_mutex_lock(&mtx); + mem[0] = 42; + pthread_mutex_unlock(&mtx); + return NULL; +} + +int main() { + mem = (int*)malloc(100); + pthread_mutex_init(&mtx, 0); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + pthread_mutex_destroy(&mtx); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/heap_race.cc b/compiler-rt/lib/tsan/output_tests/heap_race.cc new file mode 100644 index 00000000000..e92bb379737 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/heap_race.cc @@ -0,0 +1,19 @@ +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> + +void *Thread(void *a) { + ((int*)a)[0]++; + return NULL; +} + +int main() { + int *p = new int(42); + pthread_t t; + pthread_create(&t, NULL, Thread, p); + p[0]++; + pthread_join(t, NULL); + delete p; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/memcpy_race.cc b/compiler-rt/lib/tsan/output_tests/memcpy_race.cc new file mode 100644 index 00000000000..c6b79a709e4 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/memcpy_race.cc @@ -0,0 +1,40 @@ +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +char *data = new char[10]; +char *data1 = new char[10]; +char *data2 = new char[10]; + +void *Thread1(void *x) { + memcpy(data+5, data1, 1); + return NULL; +} + +void *Thread2(void *x) { + usleep(500*1000); + memcpy(data+3, data2, 4); + return NULL; +} + +int main() { + fprintf(stderr, "addr=%p\n", &data[5]); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 at [[ADDR]] by thread 2: +// CHECK: #0 memcpy +// CHECK: #1 Thread2 +// CHECK: Previous write of size 1 at [[ADDR]] by thread 1: +// CHECK: #0 memcpy +// CHECK: #1 Thread1 + diff --git a/compiler-rt/lib/tsan/output_tests/mop_with_offset.cc b/compiler-rt/lib/tsan/output_tests/mop_with_offset.cc new file mode 100644 index 00000000000..c785de3a799 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/mop_with_offset.cc @@ -0,0 +1,35 @@ +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> + +void *Thread1(void *x) { + int *p = (int*)x; + p[0] = 1; + return NULL; +} + +void *Thread2(void *x) { + usleep(500*1000); + char *p = (char*)x; + p[2] = 1; + return NULL; +} + +int main() { + int data = 42; + fprintf(stderr, "ptr1=%p\n", &data); + fprintf(stderr, "ptr2=%p\n", (char*)&data + 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, &data); + pthread_create(&t[1], NULL, Thread2, &data); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] +// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 at [[PTR2]] by thread 2: +// CHECK: Previous write of size 4 at [[PTR1]] by thread 1: + diff --git a/compiler-rt/lib/tsan/output_tests/mop_with_offset2.cc b/compiler-rt/lib/tsan/output_tests/mop_with_offset2.cc new file mode 100644 index 00000000000..8c0ec072557 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/mop_with_offset2.cc @@ -0,0 +1,35 @@ +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> + +void *Thread1(void *x) { + usleep(500*1000); + int *p = (int*)x; + p[0] = 1; + return NULL; +} + +void *Thread2(void *x) { + char *p = (char*)x; + p[2] = 1; + return NULL; +} + +int main() { + int data = 42; + fprintf(stderr, "ptr1=%p\n", &data); + fprintf(stderr, "ptr2=%p\n", (char*)&data + 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, &data); + pthread_create(&t[1], NULL, Thread2, &data); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] +// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at [[PTR1]] by thread 1: +// CHECK: Previous write of size 1 at [[PTR2]] by thread 2: + diff --git a/compiler-rt/lib/tsan/output_tests/race_on_barrier.c b/compiler-rt/lib/tsan/output_tests/race_on_barrier.c new file mode 100644 index 00000000000..98d7a1d847f --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/race_on_barrier.c @@ -0,0 +1,31 @@ +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_barrier_t B; +int Global; + +void *Thread1(void *x) { + pthread_barrier_init(&B, 0, 2); + pthread_barrier_wait(&B); + return NULL; +} + +void *Thread2(void *x) { + usleep(1000000); + pthread_barrier_wait(&B); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + pthread_barrier_destroy(&B); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/compiler-rt/lib/tsan/output_tests/race_on_barrier2.c b/compiler-rt/lib/tsan/output_tests/race_on_barrier2.c new file mode 100644 index 00000000000..dbdb6b55700 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/race_on_barrier2.c @@ -0,0 +1,30 @@ +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_barrier_t B; +int Global; + +void *Thread1(void *x) { + if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD) + pthread_barrier_destroy(&B); + return NULL; +} + +void *Thread2(void *x) { + if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD) + pthread_barrier_destroy(&B); + return NULL; +} + +int main() { + pthread_barrier_init(&B, 0, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/race_on_mutex.c b/compiler-rt/lib/tsan/output_tests/race_on_mutex.c new file mode 100644 index 00000000000..90c32ba5445 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/race_on_mutex.c @@ -0,0 +1,42 @@ +#include <pthread.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> + +pthread_mutex_t Mtx; +int Global; + +void *Thread1(void *x) { + pthread_mutex_init(&Mtx, 0); + pthread_mutex_lock(&Mtx); + Global = 42; + pthread_mutex_unlock(&Mtx); + return NULL; +} + +void *Thread2(void *x) { + usleep(1000000); + pthread_mutex_lock(&Mtx); + Global = 43; + pthread_mutex_unlock(&Mtx); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&Mtx); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Read of size 1 at {{.*}} by thread 2: +// CHECK-NEXT: #0 pthread_mutex_lock {{.*}} ({{.*}}) +// CHECK-NEXT: #1 Thread2 {{.*}}race_on_mutex.c:19 ({{.*}}) +// CHECK-NEXT: Previous write of size 1 at {{.*}} by thread 1: +// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}}) +// CHECK-NEXT: #1 Thread1 {{.*}}race_on_mutex.c:10 ({{.*}}) + diff --git a/compiler-rt/lib/tsan/output_tests/race_with_finished_thread.cc b/compiler-rt/lib/tsan/output_tests/race_with_finished_thread.cc new file mode 100644 index 00000000000..1f60f4ba349 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/race_with_finished_thread.cc @@ -0,0 +1,43 @@ +#include <pthread.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +// Ensure that we can restore a stack of a finished thread. + +int g_data; + +void __attribute__((noinline)) foobar(int *p) { + *p = 42; +} + +void *Thread1(void *x) { + foobar(&g_data); + return NULL; +} + +void *Thread2(void *x) { + usleep(1000*1000); + g_data = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread 2: +// CHECK: Previous write of size 4 at {{.*}} by thread 1: +// CHECK: #0 foobar +// CHECK: #1 Thread1 +// CHECK: Thread 1 (finished) created at: +// CHECK: #0 pthread_create +// CHECK: #1 main + diff --git a/compiler-rt/lib/tsan/output_tests/simple_race.c b/compiler-rt/lib/tsan/output_tests/simple_race.c new file mode 100644 index 00000000000..ed831fd8c5a --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/simple_race.c @@ -0,0 +1,25 @@ +#include <pthread.h> +#include <stdio.h> + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/simple_race.cc b/compiler-rt/lib/tsan/output_tests/simple_race.cc new file mode 100644 index 00000000000..8d2cabff772 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/simple_race.cc @@ -0,0 +1,24 @@ +#include <pthread.h> +#include <stdio.h> + +int Global; + +void *Thread1(void *x) { + Global++; + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/simple_stack.c b/compiler-rt/lib/tsan/output_tests/simple_stack.c new file mode 100644 index 00000000000..ade99da7a63 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/simple_stack.c @@ -0,0 +1,66 @@ +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void __attribute__((noinline)) foo1() { + Global = 42; +} + +void __attribute__((noinline)) bar1() { + volatile int tmp = 42; (void)tmp; + foo1(); +} + +void __attribute__((noinline)) foo2() { + volatile int v = Global; (void)v; +} + +void __attribute__((noinline)) bar2() { + volatile int tmp = 42; (void)tmp; + foo2(); +} + +void *Thread1(void *x) { + usleep(1000000); + bar1(); + return NULL; +} + +void *Thread2(void *x) { + bar2(); + return NULL; +} + +void StartThread(pthread_t *t, void *(*f)(void*)) { + pthread_create(t, NULL, f, NULL); +} + +int main() { + pthread_t t[2]; + StartThread(&t[0], Thread1); + StartThread(&t[1], Thread2); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 4 at {{.*}} by thread 1: +// CHECK-NEXT: #0 foo1 {{.*}}simple_stack.c:8 ({{.*}}) +// CHECK-NEXT: #1 bar1 {{.*}}simple_stack.c:13 ({{.*}}) +// CHECK-NEXT: #2 Thread1 {{.*}}simple_stack.c:27 ({{.*}}) +// CHECK-NEXT: Previous read of size 4 at {{.*}} by thread 2: +// CHECK-NEXT: #0 foo2 {{.*}}simple_stack.c:17 ({{.*}}) +// CHECK-NEXT: #1 bar2 {{.*}}simple_stack.c:22 ({{.*}}) +// CHECK-NEXT: #2 Thread2 {{.*}}simple_stack.c:32 ({{.*}}) +// CHECK-NEXT: Thread 1 (running) created at: +// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-NEXT: #1 StartThread {{.*}}simple_stack.c:37 ({{.*}}) +// CHECK-NEXT: #2 main {{.*}}simple_stack.c:42 ({{.*}}) +// CHECK-NEXT: Thread 2 ({{.*}}) created at: +// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-NEXT: #1 StartThread {{.*}}simple_stack.c:37 ({{.*}}) +// CHECK-NEXT: #2 main {{.*}}simple_stack.c:43 ({{.*}}) + diff --git a/compiler-rt/lib/tsan/output_tests/simple_stack2.cc b/compiler-rt/lib/tsan/output_tests/simple_stack2.cc new file mode 100644 index 00000000000..d3b03aef7cd --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/simple_stack2.cc @@ -0,0 +1,48 @@ +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +int Global; + +void __attribute__((noinline)) foo1() { + Global = 42; +} + +void __attribute__((noinline)) bar1() { + volatile int tmp = 42; (void)tmp; + foo1(); +} + +void __attribute__((noinline)) foo2() { + volatile int v = Global; (void)v; +} + +void __attribute__((noinline)) bar2() { + volatile int tmp = 42; (void)tmp; + foo2(); +} + +void *Thread1(void *x) { + usleep(1000000); + bar1(); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + bar2(); + pthread_join(t, NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 4 at {{.*}} by thread 1: +// CHECK-NEXT: #0 foo1() {{.*}}simple_stack2.cc:8 ({{.*}}) +// CHECK-NEXT: #1 bar1() {{.*}}simple_stack2.cc:13 ({{.*}}) +// CHECK-NEXT: #2 Thread1(void*) {{.*}}simple_stack2.cc:27 ({{.*}}) +// CHECK-NEXT: Previous read of size 4 at {{.*}} by main thread: +// CHECK-NEXT: #0 foo2() {{.*}}simple_stack2.cc:17 ({{.*}}) +// CHECK-NEXT: #1 bar2() {{.*}}simple_stack2.cc:22 ({{.*}}) +// CHECK-NEXT: #2 main {{.*}}simple_stack2.cc:34 ({{.*}}) + + diff --git a/compiler-rt/lib/tsan/output_tests/static_init1.cc b/compiler-rt/lib/tsan/output_tests/static_init1.cc new file mode 100644 index 00000000000..75d281954e1 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/static_init1.cc @@ -0,0 +1,25 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +struct P { + int x; + int y; +}; + +void *Thread(void *x) { + static P p = {rand(), rand()}; + if (p.x > RAND_MAX || p.y > RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread, 0); + pthread_create(&t[1], 0, Thread, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/static_init2.cc b/compiler-rt/lib/tsan/output_tests/static_init2.cc new file mode 100644 index 00000000000..f6e95965521 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/static_init2.cc @@ -0,0 +1,31 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +struct Cache { + int x; + Cache(int x) + : x(x) { + } +}; + +void foo(Cache *my) { + static Cache *c = my ? my : new Cache(rand()); + if (c->x >= RAND_MAX) + exit(1); +} + +void *Thread(void *x) { + foo(new Cache(rand())); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread, 0); + pthread_create(&t[1], 0, Thread, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/static_init3.cc b/compiler-rt/lib/tsan/output_tests/static_init3.cc new file mode 100644 index 00000000000..718f811d0df --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/static_init3.cc @@ -0,0 +1,46 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; +}; + +Cache g_cache; + +Cache *CreateCache() { + g_cache.x = rand(); + return &g_cache; +} + +_Atomic(Cache*) queue; + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + __c11_atomic_store(&queue, c, 0); + return 0; +} + +void *Thread2(void *x) { + Cache *c = 0; + for (;;) { + c = __c11_atomic_load(&queue, 0); + if (c) + break; + sched_yield(); + } + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/static_init4.cc b/compiler-rt/lib/tsan/output_tests/static_init4.cc new file mode 100644 index 00000000000..cdacbce8002 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/static_init4.cc @@ -0,0 +1,35 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; + Cache(int x) + : x(x) { + } +}; + +int g_other; + +Cache *CreateCache() { + g_other = rand(); + return new Cache(rand()); +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x == g_other) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/static_init5.cc b/compiler-rt/lib/tsan/output_tests/static_init5.cc new file mode 100644 index 00000000000..4b050c9fa69 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/static_init5.cc @@ -0,0 +1,40 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sched.h> + +struct Cache { + int x; + Cache(int x) + : x(x) { + } +}; + +void *AsyncInit(void *p) { + return new Cache((int)(long)p); +} + +Cache *CreateCache() { + pthread_t t; + pthread_create(&t, 0, AsyncInit, (void*)rand()); + void *res; + pthread_join(t, &res); + return (Cache*)res; +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/suppress_same_address.cc b/compiler-rt/lib/tsan/output_tests/suppress_same_address.cc new file mode 100644 index 00000000000..6e98970a16e --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/suppress_same_address.cc @@ -0,0 +1,27 @@ +#include <pthread.h> + +int X; + +void *Thread1(void *x) { + X = 42; + X = 66; + X = 78; + return 0; +} + +void *Thread2(void *x) { + X = 11; + X = 99; + X = 73; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Thread2(0); + pthread_join(t, 0); +} + +// CHECK: ThreadSanitizer: reported 1 warnings + diff --git a/compiler-rt/lib/tsan/output_tests/suppress_same_stacks.cc b/compiler-rt/lib/tsan/output_tests/suppress_same_stacks.cc new file mode 100644 index 00000000000..6046a4ea9f3 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/suppress_same_stacks.cc @@ -0,0 +1,27 @@ +#include <pthread.h> + +volatile int N; // Prevent loop unrolling. +int **data; + +void *Thread1(void *x) { + for (int i = 0; i < N; i++) + data[i][0] = 42; + return 0; +} + +int main() { + N = 4; + data = new int*[N]; + for (int i = 0; i < N; i++) + data[i] = new int; + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Thread1(0); + pthread_join(t, 0); + for (int i = 0; i < N; i++) + delete data[i]; + delete[] data; +} + +// CHECK: ThreadSanitizer: reported 1 warnings + diff --git a/compiler-rt/lib/tsan/output_tests/suppress_sequence.cc b/compiler-rt/lib/tsan/output_tests/suppress_sequence.cc new file mode 100644 index 00000000000..1ce02071814 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/suppress_sequence.cc @@ -0,0 +1,26 @@ +#include <pthread.h> +#include <unistd.h> + +volatile int g_data1; +volatile int g_data2; +volatile int g_data3; +volatile int g_data4; + +void *Thread1(void *x) { + if (x) + usleep(1000000); + g_data1 = 42; + g_data2 = 43; + g_data3 = 44; + g_data4 = 45; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, (void*)1); + Thread1(0); + pthread_join(t, 0); +} + +// CHECK: ThreadSanitizer: reported 1 warnings diff --git a/compiler-rt/lib/tsan/output_tests/test_output.sh b/compiler-rt/lib/tsan/output_tests/test_output.sh new file mode 100755 index 00000000000..f7391d8eb70 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/test_output.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +ulimit -s 8192; +set -e # fail on any error + +ROOTDIR=`dirname $0`/.. + +# Assuming clang is in path. +CC=clang +CXX=clang++ + +# TODO: add testing for all of -O0...-O3 +CFLAGS="-fthread-sanitizer -fPIE -O1 -g -fno-builtin -Wall -Werror=return-type" +LDFLAGS="-pie -lpthread -ldl $ROOTDIR/tsan/libtsan.a" +if [ "$LLDB" != "" ]; then + LDFLAGS+=" -L$LLDB -llldb" +fi + +strip() { + grep -v "$1" test.out > test.out2 + mv -f test.out2 test.out +} + +test_file() { + SRC=$1 + COMPILER=$2 + echo ----- TESTING $1 + OBJ=$SRC.o + EXE=$SRC.exe + $COMPILER $SRC $CFLAGS -c -o $OBJ + # Link with CXX, because lldb and suppressions require C++. + $CXX $OBJ $LDFLAGS -o $EXE + LD_LIBRARY_PATH=$LLDB TSAN_OPTIONS="atexit_sleep_ms=0" $EXE 2> test.out || echo -n + if [ "$3" != "" ]; then + cat test.out + fi + echo >>test.out # FileCheck fails on empty files + FileCheck < test.out $SRC + if [ "$3" == "" ]; then + rm -f $EXE $OBJ test.out *.tmp *.tmp2 + fi +} + +if [ "$1" == "" ]; then + for c in $ROOTDIR/output_tests/*.c; do + if [[ $c == */failing_* ]]; then + echo SKIPPING FAILING TEST $c + continue + fi + test_file $c $CC + done + for c in $ROOTDIR/output_tests/*.cc; do + if [[ $c == */failing_* ]]; then + echo SKIPPING FAILING TEST $c + continue + fi + test_file $c $CXX + done +else + test_file $ROOTDIR/output_tests/$1 $CXX "DUMP" +fi diff --git a/compiler-rt/lib/tsan/output_tests/thread_leak.c b/compiler-rt/lib/tsan/output_tests/thread_leak.c new file mode 100644 index 00000000000..88a11be4e9c --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/thread_leak.c @@ -0,0 +1,15 @@ +#include <pthread.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak + diff --git a/compiler-rt/lib/tsan/output_tests/thread_leak2.c b/compiler-rt/lib/tsan/output_tests/thread_leak2.c new file mode 100644 index 00000000000..71e9c50b8a2 --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/thread_leak2.c @@ -0,0 +1,15 @@ +#include <pthread.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_detach(t); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak + diff --git a/compiler-rt/lib/tsan/output_tests/thread_leak3.c b/compiler-rt/lib/tsan/output_tests/thread_leak3.c new file mode 100644 index 00000000000..058b6e54d9d --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/thread_leak3.c @@ -0,0 +1,14 @@ +#include <pthread.h> + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: thread leak + diff --git a/compiler-rt/lib/tsan/output_tests/vptr_benign_race.cc b/compiler-rt/lib/tsan/output_tests/vptr_benign_race.cc new file mode 100644 index 00000000000..fec4ffbb6bc --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/vptr_benign_race.cc @@ -0,0 +1,50 @@ +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { + sem_wait(&sem_); + sem_destroy(&sem_); + } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "PASS\n"); +} +// CHECK: PASS +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/output_tests/vptr_harmful_race.cc b/compiler-rt/lib/tsan/output_tests/vptr_harmful_race.cc new file mode 100644 index 00000000000..a19e6abc7bc --- /dev/null +++ b/compiler-rt/lib/tsan/output_tests/vptr_harmful_race.cc @@ -0,0 +1,48 @@ +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + sem_wait(&sem_); + sem_destroy(&sem_); + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_bench.cc b/compiler-rt/lib/tsan/rtl_tests/tsan_bench.cc new file mode 100644 index 00000000000..9bdbe61c529 --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_bench.cc @@ -0,0 +1,105 @@ +//===-- tsan_bench.cc -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "tsan_interface.h" +#include "tsan_defs.h" +#include "gtest/gtest.h" +#include <stdint.h> + +const int kSize = 128; +const int kRepeat = 2*1024*1024; + +void noinstr(void *p) {} + +template<typename T, void(*__tsan_mop)(void *p)> +static void Benchmark() { + volatile T data[kSize]; + for (int i = 0; i < kRepeat; i++) { + for (int j = 0; j < kSize; j++) { + __tsan_mop((void*)&data[j]); + data[j]++; + } + } +} + +TEST(DISABLED_BENCH, Mop1) { + Benchmark<uint8_t, noinstr>(); +} + +TEST(DISABLED_BENCH, Mop1Read) { + Benchmark<uint8_t, __tsan_read1>(); +} + +TEST(DISABLED_BENCH, Mop1Write) { + Benchmark<uint8_t, __tsan_write1>(); +} + +TEST(DISABLED_BENCH, Mop2) { + Benchmark<uint16_t, noinstr>(); +} + +TEST(DISABLED_BENCH, Mop2Read) { + Benchmark<uint16_t, __tsan_read2>(); +} + +TEST(DISABLED_BENCH, Mop2Write) { + Benchmark<uint16_t, __tsan_write2>(); +} + +TEST(DISABLED_BENCH, Mop4) { + Benchmark<uint32_t, noinstr>(); +} + +TEST(DISABLED_BENCH, Mop4Read) { + Benchmark<uint32_t, __tsan_read4>(); +} + +TEST(DISABLED_BENCH, Mop4Write) { + Benchmark<uint32_t, __tsan_write4>(); +} + +TEST(DISABLED_BENCH, Mop8) { + Benchmark<uint8_t, noinstr>(); +} + +TEST(DISABLED_BENCH, Mop8Read) { + Benchmark<uint64_t, __tsan_read8>(); +} + +TEST(DISABLED_BENCH, Mop8Write) { + Benchmark<uint64_t, __tsan_write8>(); +} + +TEST(DISABLED_BENCH, FuncCall) { + for (int i = 0; i < kRepeat; i++) { + for (int j = 0; j < kSize; j++) + __tsan_func_entry((void*)(uintptr_t)j); + for (int j = 0; j < kSize; j++) + __tsan_func_exit(); + } +} + +TEST(DISABLED_BENCH, MutexLocal) { + Mutex m; + ScopedThread().Create(m); + for (int i = 0; i < 50; i++) { + ScopedThread t; + t.Lock(m); + t.Unlock(m); + } + for (int i = 0; i < 16*1024*1024; i++) { + m.Lock(); + m.Unlock(); + } + ScopedThread().Destroy(m); +} diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_mop.cc b/compiler-rt/lib/tsan/rtl_tests/tsan_mop.cc new file mode 100644 index 00000000000..79bfc28d7e0 --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_mop.cc @@ -0,0 +1,231 @@ +//===-- tsan_mop.cc ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include <stddef.h> +#include <stdint.h> + +TEST(ThreadSanitizer, SimpleWrite) { + ScopedThread t; + MemLoc l; + t.Write1(l); +} + +TEST(ThreadSanitizer, SimpleWriteWrite) { + ScopedThread t1, t2; + MemLoc l1, l2; + t1.Write1(l1); + t2.Write1(l2); +} + +TEST(ThreadSanitizer, WriteWriteRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Write1(l, true); +} + +TEST(ThreadSanitizer, ReadWriteRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Read1(l); + t2.Write1(l, true); +} + +TEST(ThreadSanitizer, WriteReadRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Read1(l, true); +} + +TEST(ThreadSanitizer, ReadReadNoRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Read1(l); + t2.Read1(l); +} + +TEST(ThreadSanitizer, WriteThenRead) { + MemLoc l; + ScopedThread t1, t2; + t1.Write1(l); + t1.Read1(l); + t2.Read1(l, true); +} + +TEST(ThreadSanitizer, WriteThenLockedRead) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + MemLoc l; + { + ScopedThread t1, t2; + + t1.Write8(l); + + t1.Lock(m); + t1.Read8(l); + t1.Unlock(m); + + t2.Read8(l, true); + } + t0.Destroy(m); +} + +TEST(ThreadSanitizer, LockedWriteThenRead) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + MemLoc l; + { + ScopedThread t1, t2; + + t1.Lock(m); + t1.Write8(l); + t1.Unlock(m); + + t1.Read8(l); + + t2.Read8(l, true); + } + t0.Destroy(m); +} + + +TEST(ThreadSanitizer, RaceWithOffset) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access(l.loc(), true, 8, false); + t2.Access((char*)l.loc() + 4, true, 4, true); + } + { + MemLoc l; + t1.Access(l.loc(), true, 8, false); + t2.Access((char*)l.loc() + 7, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 4, true, 4, false); + t2.Access((char*)l.loc() + 4, true, 2, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 4, true, 4, false); + t2.Access((char*)l.loc() + 6, true, 2, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 3, true, 2, false); + t2.Access((char*)l.loc() + 4, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 1, true, 8, false); + t2.Access((char*)l.loc() + 3, true, 1, true); + } +} + +TEST(ThreadSanitizer, RaceWithOffset2) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access((char*)l.loc(), true, 4, false); + t2.Access((char*)l.loc() + 2, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 2, true, 1, false); + t2.Access((char*)l.loc(), true, 4, true); + } +} + +TEST(ThreadSanitizer, NoRaceWithOffset) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access(l.loc(), true, 4, false); + t2.Access((char*)l.loc() + 4, true, 4, false); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 3, true, 2, false); + t2.Access((char*)l.loc() + 1, true, 2, false); + t2.Access((char*)l.loc() + 5, true, 2, false); + } +} + +TEST(ThreadSanitizer, RaceWithDeadThread) { + MemLoc l; + ScopedThread t; + ScopedThread().Write1(l); + t.Write1(l, true); +} + +TEST(ThreadSanitizer, BenignRaceOnVptr) { + void *vptr_storage; + MemLoc vptr(&vptr_storage), val; + vptr_storage = val.loc(); + ScopedThread t1, t2; + t1.VptrUpdate(vptr, val); + t2.Read8(vptr); +} + +TEST(ThreadSanitizer, HarmfulRaceOnVptr) { + void *vptr_storage; + MemLoc vptr(&vptr_storage), val1, val2; + vptr_storage = val1.loc(); + ScopedThread t1, t2; + t1.VptrUpdate(vptr, val2); + t2.Read8(vptr, true); +} + +static void foo() { + volatile int x = 42; + (void)x; +} + +static void bar() { + volatile int x = 43; + (void)x; +} + +TEST(ThreadSanitizer, ReportDeadThread) { + MemLoc l; + ScopedThread t1; + { + ScopedThread t2; + t2.Call(&foo); + t2.Call(&bar); + t2.Write1(l); + } + t1.Write1(l, true); +} + +struct ClassWithStatic { + static int Data[4]; +}; + +int ClassWithStatic::Data[4]; + +static void foobarbaz() {} + +TEST(ThreadSanitizer, ReportRace) { + ScopedThread t1; + MainThread().Access(&ClassWithStatic::Data, true, 4, false); + t1.Call(&foobarbaz); + t1.Access(&ClassWithStatic::Data, true, 2, true); + t1.Return(); +} diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_mutex.cc b/compiler-rt/lib/tsan/rtl_tests/tsan_mutex.cc new file mode 100644 index 00000000000..47495f4fb55 --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_mutex.cc @@ -0,0 +1,221 @@ +//===-- tsan_mutex.cc -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_atomic.h" +#include "tsan_interface.h" +#include "tsan_interface_ann.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include <stdint.h> + +namespace __tsan { + +TEST(ThreadSanitizer, BasicMutex) { + ScopedThread t; + Mutex m; + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, BasicSpinMutex) { + ScopedThread t; + Mutex m(Mutex::Spin); + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, BasicRwMutex) { + ScopedThread t; + Mutex m(Mutex::RW); + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.ReadLock(m); + t.ReadUnlock(m); + + CHECK(t.TryReadLock(m)); + t.ReadUnlock(m); + + t.Lock(m); + CHECK(!t.TryReadLock(m)); + t.Unlock(m); + + t.ReadLock(m); + CHECK(!t.TryLock(m)); + t.ReadUnlock(m); + + t.ReadLock(m); + CHECK(t.TryReadLock(m)); + t.ReadUnlock(m); + t.ReadUnlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, Mutex) { + Mutex m; + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, SpinMutex) { + Mutex m(Mutex::Spin); + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, RwMutex) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2, t3; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t1.ReadLock(m); + t3.ReadLock(m); + t1.Read1(l); + t3.Read1(l); + t1.ReadUnlock(m); + t3.ReadUnlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, StaticMutex) { + // Emulates statically initialized mutex. + Mutex m; + m.StaticInit(); + { + ScopedThread t1, t2; + t1.Lock(m); + t1.Unlock(m); + t2.Lock(m); + t2.Unlock(m); + } + MainThread().Destroy(m); +} + +static void *singleton_thread(void *param) { + atomic_uintptr_t *singleton = (atomic_uintptr_t *)param; + for (int i = 0; i < 4*1024*1024; i++) { + int *val = (int *)atomic_load(singleton, memory_order_acquire); + __tsan_acquire(singleton); + __tsan_read4(val); + CHECK_EQ(*val, 42); + } + return 0; +} + +TEST(DISABLED_BENCH_ThreadSanitizer, Singleton) { + const int kClockSize = 100; + const int kThreadCount = 8; + + // Puff off thread's clock. + for (int i = 0; i < kClockSize; i++) { + ScopedThread t1; + (void)t1; + } + // Create the singleton. + int val = 42; + __tsan_write4(&val); + atomic_uintptr_t singleton; + __tsan_release(&singleton); + atomic_store(&singleton, (uintptr_t)&val, memory_order_release); + // Create reader threads. + pthread_t threads[kThreadCount]; + for (int t = 0; t < kThreadCount; t++) + pthread_create(&threads[t], 0, singleton_thread, &singleton); + for (int t = 0; t < kThreadCount; t++) + pthread_join(threads[t], 0); +} + +TEST(DISABLED_BENCH_ThreadSanitizer, StopFlag) { + const int kClockSize = 100; + const int kIters = 16*1024*1024; + + // Puff off thread's clock. + for (int i = 0; i < kClockSize; i++) { + ScopedThread t1; + (void)t1; + } + // Create the stop flag. + atomic_uintptr_t flag; + __tsan_release(&flag); + atomic_store(&flag, 0, memory_order_release); + // Read it a lot. + for (int i = 0; i < kIters; i++) { + uptr v = atomic_load(&flag, memory_order_acquire); + __tsan_acquire(&flag); + CHECK_EQ(v, 0); + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_posix.cc b/compiler-rt/lib/tsan/rtl_tests/tsan_posix.cc new file mode 100644 index 00000000000..4f98d50dd30 --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_posix.cc @@ -0,0 +1,146 @@ +//===-- tsan_posix.cc -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include <pthread.h> + +struct thread_key { + pthread_key_t key; + pthread_mutex_t *mtx; + int val; + int *cnt; + thread_key(pthread_key_t key, pthread_mutex_t *mtx, int val, int *cnt) + : key(key) + , mtx(mtx) + , val(val) + , cnt(cnt) { + } +}; + +static void thread_secific_dtor(void *v) { + thread_key *k = (thread_key *)v; + EXPECT_EQ(pthread_mutex_lock(k->mtx), 0); + (*k->cnt)++; + __tsan_write4(&k->cnt); + EXPECT_EQ(pthread_mutex_unlock(k->mtx), 0); + if (k->val == 42) { + delete k; + } else if (k->val == 43 || k->val == 44) { + k->val--; + EXPECT_EQ(pthread_setspecific(k->key, k), 0); + } else { + ASSERT_TRUE(false); + } +} + +static void *dtors_thread(void *p) { + thread_key *k = (thread_key *)p; + EXPECT_EQ(pthread_setspecific(k->key, k), 0); + return 0; +} + +TEST(Posix, ThreadSpecificDtors) { + int cnt = 0; + pthread_key_t key; + EXPECT_EQ(pthread_key_create(&key, thread_secific_dtor), 0); + pthread_mutex_t mtx; + EXPECT_EQ(pthread_mutex_init(&mtx, 0), 0); + pthread_t th[3]; + thread_key *k[3]; + k[0] = new thread_key(key, &mtx, 42, &cnt); + k[1] = new thread_key(key, &mtx, 43, &cnt); + k[2] = new thread_key(key, &mtx, 44, &cnt); + EXPECT_EQ(pthread_create(&th[0], 0, dtors_thread, k[0]), 0); + EXPECT_EQ(pthread_create(&th[1], 0, dtors_thread, k[1]), 0); + EXPECT_EQ(pthread_join(th[0], 0), 0); + EXPECT_EQ(pthread_create(&th[2], 0, dtors_thread, k[2]), 0); + EXPECT_EQ(pthread_join(th[1], 0), 0); + EXPECT_EQ(pthread_join(th[2], 0), 0); + EXPECT_EQ(pthread_key_delete(key), 0); + EXPECT_EQ(6, cnt); +} + +static __thread int local_var; + +static void *local_thread(void *p) { + __tsan_write1(&local_var); + __tsan_write1(&p); + if (p == 0) + return 0; + const int kThreads = 4; + pthread_t th[kThreads]; + for (int i = 0; i < kThreads; i++) + EXPECT_EQ(pthread_create(&th[i], 0, local_thread, + (void*)((long)p - 1)), 0); // NOLINT + for (int i = 0; i < kThreads; i++) + EXPECT_EQ(pthread_join(th[i], 0), 0); + return 0; +} + +TEST(Posix, ThreadLocalAccesses) { + local_thread((void*)2); +} + +struct CondContext { + pthread_mutex_t m; + pthread_cond_t c; + int data; +}; + +static void *cond_thread(void *p) { + CondContext &ctx = *static_cast<CondContext*>(p); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + EXPECT_EQ(ctx.data, 0); + ctx.data = 1; + EXPECT_EQ(pthread_cond_signal(&ctx.c), 0); + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 2) + EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + ctx.data = 3; + EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + + return 0; +} + +TEST(Posix, CondBasic) { + CondContext ctx; + EXPECT_EQ(pthread_mutex_init(&ctx.m, 0), 0); + EXPECT_EQ(pthread_cond_init(&ctx.c, 0), 0); + ctx.data = 0; + pthread_t th; + EXPECT_EQ(pthread_create(&th, 0, cond_thread, &ctx), 0); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 1) + EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); + ctx.data = 2; + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 3) + EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(pthread_join(th, 0), 0); + EXPECT_EQ(pthread_cond_destroy(&ctx.c), 0); + EXPECT_EQ(pthread_mutex_destroy(&ctx.m), 0); +} diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_string.cc b/compiler-rt/lib/tsan/rtl_tests/tsan_string.cc new file mode 100644 index 00000000000..13b0553d846 --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_string.cc @@ -0,0 +1,82 @@ +//===-- tsan_string.cc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include <string.h> + +namespace __tsan { + +TEST(ThreadSanitizer, Memcpy) { + char data0[7] = {1, 2, 3, 4, 5, 6, 7}; + char data[7] = {42, 42, 42, 42, 42, 42, 42}; + MainThread().Memcpy(data+1, data0+1, 5); + EXPECT_EQ(data[0], 42); + EXPECT_EQ(data[1], 2); + EXPECT_EQ(data[2], 3); + EXPECT_EQ(data[3], 4); + EXPECT_EQ(data[4], 5); + EXPECT_EQ(data[5], 6); + EXPECT_EQ(data[6], 42); + MainThread().Memset(data+1, 13, 5); + EXPECT_EQ(data[0], 42); + EXPECT_EQ(data[1], 13); + EXPECT_EQ(data[2], 13); + EXPECT_EQ(data[3], 13); + EXPECT_EQ(data[4], 13); + EXPECT_EQ(data[5], 13); + EXPECT_EQ(data[6], 42); +} + +TEST(ThreadSanitizer, MemcpyRace1) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data, data2, 10, true); +} + +TEST(ThreadSanitizer, MemcpyRace2) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data+5, data1, 1); + t2.Memcpy(data+3, data2, 4, true); +} + +TEST(ThreadSanitizer, MemcpyRace3) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data1, data2, 10, true); +} + +TEST(ThreadSanitizer, MemcpyStack) { + char *data = new char[10]; + char *data1 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data, data1, 10, true); +} + +TEST(ThreadSanitizer, MemsetRace1) { + char *data = new char[10]; + ScopedThread t1, t2; + t1.Memset(data, 1, 10); + t2.Memset(data, 2, 10, true); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_test.cc b/compiler-rt/lib/tsan/rtl_tests/tsan_test.cc new file mode 100644 index 00000000000..839f7da401a --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_test.cc @@ -0,0 +1,43 @@ +//===-- tsan_test.cc --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" + +static void foo() {} +static void bar() {} + +TEST(ThreadSanitizer, FuncCall) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Call(foo); + t2.Call(bar); + t2.Write1(l, true); + t2.Return(); + t2.Return(); +} + +int main(int argc, char **argv) { + TestMutexBeforeInit(); // Mutexes must be usable before __tsan_init(); + __tsan_init(); + __tsan_func_entry(__builtin_return_address(0)); + __tsan_func_entry((char*)&main + 1); + + testing::InitGoogleTest(&argc, argv); + int res = RUN_ALL_TESTS(); + + __tsan_func_exit(); + __tsan_func_exit(); + return res; +} diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_test_util.h b/compiler-rt/lib/tsan/rtl_tests/tsan_test_util.h new file mode 100644 index 00000000000..483a564c847 --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_test_util.h @@ -0,0 +1,122 @@ +//===-- tsan_test_util.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Test utils. +//===----------------------------------------------------------------------===// +#ifndef TSAN_TEST_UTIL_H +#define TSAN_TEST_UTIL_H + +void TestMutexBeforeInit(); + +// A location of memory on which a race may be detected. +class MemLoc { + public: + explicit MemLoc(int offset_from_aligned = 0); + explicit MemLoc(void *const real_addr) : loc_(real_addr) { } + ~MemLoc(); + void *loc() const { return loc_; } + private: + void *const loc_; + MemLoc(const MemLoc&); + void operator = (const MemLoc&); +}; + +class Mutex { + public: + enum Type { Normal, Spin, RW }; + + explicit Mutex(Type type = Normal); + ~Mutex(); + + void Init(); + void StaticInit(); // Emulates static initalization (tsan invisible). + void Destroy(); + void Lock(); + bool TryLock(); + void Unlock(); + void ReadLock(); + bool TryReadLock(); + void ReadUnlock(); + + private: + // Placeholder for pthread_mutex_t, CRITICAL_SECTION or whatever. + void *mtx_[128]; + bool alive_; + const Type type_; + + Mutex(const Mutex&); + void operator = (const Mutex&); +}; + +// A thread is started in CTOR and joined in DTOR. +class ScopedThread { + public: + explicit ScopedThread(bool detached = false, bool main = false); + ~ScopedThread(); + void Detach(); + + void Access(void *addr, bool is_write, int size, bool expect_race); + void Read(const MemLoc &ml, int size, bool expect_race = false) { + Access(ml.loc(), false, size, expect_race); + } + void Write(const MemLoc &ml, int size, bool expect_race = false) { + Access(ml.loc(), true, size, expect_race); + } + void Read1(const MemLoc &ml, bool expect_race = false) { + Read(ml, 1, expect_race); } + void Read2(const MemLoc &ml, bool expect_race = false) { + Read(ml, 2, expect_race); } + void Read4(const MemLoc &ml, bool expect_race = false) { + Read(ml, 4, expect_race); } + void Read8(const MemLoc &ml, bool expect_race = false) { + Read(ml, 8, expect_race); } + void Write1(const MemLoc &ml, bool expect_race = false) { + Write(ml, 1, expect_race); } + void Write2(const MemLoc &ml, bool expect_race = false) { + Write(ml, 2, expect_race); } + void Write4(const MemLoc &ml, bool expect_race = false) { + Write(ml, 4, expect_race); } + void Write8(const MemLoc &ml, bool expect_race = false) { + Write(ml, 8, expect_race); } + + void VptrUpdate(const MemLoc &vptr, const MemLoc &new_val, + bool expect_race = false); + + void Call(void(*pc)()); + void Return(); + + void Create(const Mutex &m); + void Destroy(const Mutex &m); + void Lock(const Mutex &m); + bool TryLock(const Mutex &m); + void Unlock(const Mutex &m); + void ReadLock(const Mutex &m); + bool TryReadLock(const Mutex &m); + void ReadUnlock(const Mutex &m); + + void Memcpy(void *dst, const void *src, int size, bool expect_race = false); + void Memset(void *dst, int val, int size, bool expect_race = false); + + private: + struct Impl; + Impl *impl_; + ScopedThread(const ScopedThread&); // Not implemented. + void operator = (const ScopedThread&); // Not implemented. +}; + +class MainThread : public ScopedThread { + public: + MainThread() + : ScopedThread(false, true) { + } +}; + +#endif // #ifndef TSAN_TEST_UTIL_H diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_test_util_linux.cc b/compiler-rt/lib/tsan/rtl_tests/tsan_test_util_linux.cc new file mode 100644 index 00000000000..10367accb1f --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_test_util_linux.cc @@ -0,0 +1,460 @@ +//===-- tsan_test_util_linux.cc ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Test utils, linux implementation. +//===----------------------------------------------------------------------===// + +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "tsan_atomic.h" +#include "tsan_report.h" + +#include "gtest/gtest.h" + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +using namespace __tsan; // NOLINT + +static __thread bool expect_report; +static __thread bool expect_report_reported; +static __thread ReportType expect_report_type; + +static void *BeforeInitThread(void *param) { + (void)param; + return 0; +} + +static void AtExit() { +} + +void TestMutexBeforeInit() { + // Mutexes must be usable before __tsan_init(); + pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&mtx); + pthread_mutex_unlock(&mtx); + pthread_mutex_destroy(&mtx); + pthread_t thr; + pthread_create(&thr, 0, BeforeInitThread, 0); + pthread_join(thr, 0); + atexit(AtExit); +} + +namespace __tsan { +bool OnReport(const ReportDesc *rep, bool suppressed) { + if (expect_report) { + if (rep->typ != expect_report_type) { + printf("Expected report of type %d, got type %d\n", + (int)expect_report_type, (int)rep->typ); + EXPECT_FALSE("Wrong report type"); + return false; + } + } else { + EXPECT_FALSE("Unexpected report"); + return false; + } + expect_report_reported = true; + return true; +} +} + +static void* allocate_addr(int size, int offset_from_aligned = 0) { + static uintptr_t foo; + static atomic_uintptr_t uniq = {(uintptr_t)&foo}; // Some real address. + const int kAlign = 16; + CHECK(offset_from_aligned < kAlign); + size = (size + 2 * kAlign) & ~(kAlign - 1); + uintptr_t addr = atomic_fetch_add(&uniq, size, memory_order_relaxed); + return (void*)(addr + offset_from_aligned); +} + +MemLoc::MemLoc(int offset_from_aligned) + : loc_(allocate_addr(16, offset_from_aligned)) { +} + +MemLoc::~MemLoc() { +} + +Mutex::Mutex(Type type) + : alive_() + , type_(type) { +} + +Mutex::~Mutex() { + CHECK(!alive_); +} + +void Mutex::Init() { + CHECK(!alive_); + alive_ = true; + if (type_ == Normal) + CHECK_EQ(pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0); + else if (type_ == Spin) + CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0); + else if (type_ == RW) + CHECK_EQ(pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0); + else + CHECK(0); +} + +void Mutex::StaticInit() { + CHECK(!alive_); + CHECK(type_ == Normal); + alive_ = true; + pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER; + memcpy(mtx_, &tmp, sizeof(tmp)); +} + +void Mutex::Destroy() { + CHECK(alive_); + alive_ = false; + if (type_ == Normal) + CHECK_EQ(pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0); + else if (type_ == Spin) + CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0); + else if (type_ == RW) + CHECK_EQ(pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0); +} + +void Mutex::Lock() { + CHECK(alive_); + if (type_ == Normal) + CHECK_EQ(pthread_mutex_lock((pthread_mutex_t*)mtx_), 0); + else if (type_ == Spin) + CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0); + else if (type_ == RW) + CHECK_EQ(pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0); +} + +bool Mutex::TryLock() { + CHECK(alive_); + if (type_ == Normal) + return pthread_mutex_trylock((pthread_mutex_t*)mtx_) == 0; + else if (type_ == Spin) + return pthread_spin_trylock((pthread_spinlock_t*)mtx_) == 0; + else if (type_ == RW) + return pthread_rwlock_trywrlock((pthread_rwlock_t*)mtx_) == 0; + return false; +} + +void Mutex::Unlock() { + CHECK(alive_); + if (type_ == Normal) + CHECK_EQ(pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0); + else if (type_ == Spin) + CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0); + else if (type_ == RW) + CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); +} + +void Mutex::ReadLock() { + CHECK(alive_); + CHECK(type_ == RW); + CHECK_EQ(pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0); +} + +bool Mutex::TryReadLock() { + CHECK(alive_); + CHECK(type_ == RW); + return pthread_rwlock_tryrdlock((pthread_rwlock_t*)mtx_) == 0; +} + +void Mutex::ReadUnlock() { + CHECK(alive_); + CHECK(type_ == RW); + CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); +} + +struct Event { + enum Type { + SHUTDOWN, + READ, + WRITE, + VPTR_UPDATE, + CALL, + RETURN, + MUTEX_CREATE, + MUTEX_DESTROY, + MUTEX_LOCK, + MUTEX_TRYLOCK, + MUTEX_UNLOCK, + MUTEX_READLOCK, + MUTEX_TRYREADLOCK, + MUTEX_READUNLOCK, + MEMCPY, + MEMSET + }; + Type type; + void *ptr; + uptr arg; + uptr arg2; + bool res; + bool expect_report; + ReportType report_type; + + Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0) + : type(type) + , ptr(const_cast<void*>(ptr)) + , arg(arg) + , arg2(arg2) + , res() + , expect_report() + , report_type() { + } + + void ExpectReport(ReportType type) { + expect_report = true; + report_type = type; + } +}; + +struct ScopedThread::Impl { + pthread_t thread; + bool main; + bool detached; + atomic_uintptr_t event; // Event* + + static void *ScopedThreadCallback(void *arg); + void send(Event *ev); + void HandleEvent(Event *ev); +}; + +void ScopedThread::Impl::HandleEvent(Event *ev) { + CHECK_EQ(expect_report, false); + expect_report = ev->expect_report; + expect_report_reported = false; + expect_report_type = ev->report_type; + switch (ev->type) { + case Event::READ: + case Event::WRITE: { + void (*tsan_mop)(void *addr) = 0; + if (ev->type == Event::READ) { + switch (ev->arg /*size*/) { + case 1: tsan_mop = __tsan_read1; break; + case 2: tsan_mop = __tsan_read2; break; + case 4: tsan_mop = __tsan_read4; break; + case 8: tsan_mop = __tsan_read8; break; + case 16: tsan_mop = __tsan_read16; break; + } + } else { + switch (ev->arg /*size*/) { + case 1: tsan_mop = __tsan_write1; break; + case 2: tsan_mop = __tsan_write2; break; + case 4: tsan_mop = __tsan_write4; break; + case 8: tsan_mop = __tsan_write8; break; + case 16: tsan_mop = __tsan_write16; break; + } + } + CHECK_NE(tsan_mop, 0); + errno = ECHRNG; + tsan_mop(ev->ptr); + CHECK_EQ(errno, ECHRNG); // In no case must errno be changed. + break; + } + case Event::VPTR_UPDATE: + __tsan_vptr_update((void**)ev->ptr, (void*)ev->arg); + break; + case Event::CALL: + __tsan_func_entry((void*)((uptr)ev->ptr)); + break; + case Event::RETURN: + __tsan_func_exit(); + break; + case Event::MUTEX_CREATE: + static_cast<Mutex*>(ev->ptr)->Init(); + break; + case Event::MUTEX_DESTROY: + static_cast<Mutex*>(ev->ptr)->Destroy(); + break; + case Event::MUTEX_LOCK: + static_cast<Mutex*>(ev->ptr)->Lock(); + break; + case Event::MUTEX_TRYLOCK: + ev->res = static_cast<Mutex*>(ev->ptr)->TryLock(); + break; + case Event::MUTEX_UNLOCK: + static_cast<Mutex*>(ev->ptr)->Unlock(); + break; + case Event::MUTEX_READLOCK: + static_cast<Mutex*>(ev->ptr)->ReadLock(); + break; + case Event::MUTEX_TRYREADLOCK: + ev->res = static_cast<Mutex*>(ev->ptr)->TryReadLock(); + break; + case Event::MUTEX_READUNLOCK: + static_cast<Mutex*>(ev->ptr)->ReadUnlock(); + break; + case Event::MEMCPY: + memcpy(ev->ptr, (void*)ev->arg, ev->arg2); + break; + case Event::MEMSET: + memset(ev->ptr, ev->arg, ev->arg2); + break; + default: CHECK(0); + } + if (expect_report && !expect_report_reported) { + printf("Missed expected report of type %d\n", (int)ev->report_type); + EXPECT_FALSE("Missed expected race"); + } + expect_report = false; +} + +void *ScopedThread::Impl::ScopedThreadCallback(void *arg) { + __tsan_func_entry(__builtin_return_address(0)); + Impl *impl = (Impl*)arg; + for (;;) { + Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire); + if (ev == 0) { + pthread_yield(); + continue; + } + if (ev->type == Event::SHUTDOWN) { + atomic_store(&impl->event, 0, memory_order_release); + break; + } + impl->HandleEvent(ev); + atomic_store(&impl->event, 0, memory_order_release); + } + __tsan_func_exit(); + return 0; +} + +void ScopedThread::Impl::send(Event *e) { + if (main) { + HandleEvent(e); + } else { + CHECK_EQ(atomic_load(&event, memory_order_relaxed), 0); + atomic_store(&event, (uintptr_t)e, memory_order_release); + while (atomic_load(&event, memory_order_acquire) != 0) + pthread_yield(); + } +} + +ScopedThread::ScopedThread(bool detached, bool main) { + impl_ = new Impl; + impl_->main = main; + impl_->detached = detached; + atomic_store(&impl_->event, 0, memory_order_relaxed); + if (!main) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, detached); + pthread_create(&impl_->thread, &attr, + ScopedThread::Impl::ScopedThreadCallback, impl_); + } +} + +ScopedThread::~ScopedThread() { + if (!impl_->main) { + Event event(Event::SHUTDOWN); + impl_->send(&event); + if (!impl_->detached) + pthread_join(impl_->thread, 0); + } + delete impl_; +} + +void ScopedThread::Detach() { + CHECK(!impl_->main); + CHECK(!impl_->detached); + impl_->detached = true; + pthread_detach(impl_->thread); +} + +void ScopedThread::Access(void *addr, bool is_write, + int size, bool expect_race) { + Event event(is_write ? Event::WRITE : Event::READ, addr, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::VptrUpdate(const MemLoc &vptr, + const MemLoc &new_val, + bool expect_race) { + Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc()); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::Call(void(*pc)()) { + Event event(Event::CALL, (void*)pc); + impl_->send(&event); +} + +void ScopedThread::Return() { + Event event(Event::RETURN); + impl_->send(&event); +} + +void ScopedThread::Create(const Mutex &m) { + Event event(Event::MUTEX_CREATE, &m); + impl_->send(&event); +} + +void ScopedThread::Destroy(const Mutex &m) { + Event event(Event::MUTEX_DESTROY, &m); + impl_->send(&event); +} + +void ScopedThread::Lock(const Mutex &m) { + Event event(Event::MUTEX_LOCK, &m); + impl_->send(&event); +} + +bool ScopedThread::TryLock(const Mutex &m) { + Event event(Event::MUTEX_TRYLOCK, &m); + impl_->send(&event); + return event.res; +} + +void ScopedThread::Unlock(const Mutex &m) { + Event event(Event::MUTEX_UNLOCK, &m); + impl_->send(&event); +} + +void ScopedThread::ReadLock(const Mutex &m) { + Event event(Event::MUTEX_READLOCK, &m); + impl_->send(&event); +} + +bool ScopedThread::TryReadLock(const Mutex &m) { + Event event(Event::MUTEX_TRYREADLOCK, &m); + impl_->send(&event); + return event.res; +} + +void ScopedThread::ReadUnlock(const Mutex &m) { + Event event(Event::MUTEX_READUNLOCK, &m); + impl_->send(&event); +} + +void ScopedThread::Memcpy(void *dst, const void *src, int size, + bool expect_race) { + Event event(Event::MEMCPY, dst, (uptr)src, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::Memset(void *dst, int val, int size, + bool expect_race) { + Event event(Event::MEMSET, dst, val, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} diff --git a/compiler-rt/lib/tsan/rtl_tests/tsan_thread.cc b/compiler-rt/lib/tsan/rtl_tests/tsan_thread.cc new file mode 100644 index 00000000000..4ee7c5fa71a --- /dev/null +++ b/compiler-rt/lib/tsan/rtl_tests/tsan_thread.cc @@ -0,0 +1,59 @@ +//===-- tsan_thread.cc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "gtest/gtest.h" + +TEST(ThreadSanitizer, ThreadSync) { + MainThread t0; + MemLoc l; + t0.Write1(l); + { + ScopedThread t1; + t1.Write1(l); + } + t0.Write1(l); +} + +TEST(ThreadSanitizer, ThreadDetach1) { + ScopedThread t1(true); + MemLoc l; + t1.Write1(l); +} + +TEST(ThreadSanitizer, ThreadDetach2) { + ScopedThread t1; + MemLoc l; + t1.Write1(l); + t1.Detach(); +} + +static void *thread_alot_func(void *arg) { + (void)arg; + int usleep(unsigned); + usleep(50); + return 0; +} + +TEST(DISABLED_SLOW_ThreadSanitizer, ThreadALot) { + const int kThreads = 70000; + const int kAlive = 1000; + pthread_t threads[kAlive] = {}; + for (int i = 0; i < kThreads; i++) { + if (threads[i % kAlive]) + pthread_join(threads[i % kAlive], 0); + pthread_create(&threads[i % kAlive], 0, thread_alot_func, 0); + } + for (int i = 0; i < kAlive; i++) { + pthread_join(threads[i], 0); + } +} diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_allocator_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_allocator_test.cc new file mode 100644 index 00000000000..e9d096398e1 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_allocator_test.cc @@ -0,0 +1,56 @@ +//===-- tsan_allocator_test.c------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_allocator.h" +#include "gtest/gtest.h" +#include <stdlib.h> + +namespace __tsan { + +TEST(Allocator, Basic) { + char *p = (char*)Alloc(10); + EXPECT_NE(p, (char*)0); + char *p2 = (char*)Alloc(20); + EXPECT_NE(p2, (char*)0); + EXPECT_NE(p2, p); + for (int i = 0; i < 10; i++) { + p[i] = 42; + EXPECT_EQ(p, AllocBlock(p + i)); + } + for (int i = 0; i < 20; i++) { + ((char*)p2)[i] = 42; + EXPECT_EQ(p2, AllocBlock(p2 + i)); + } + Free(p); + Free(p2); +} + +TEST(Allocator, Stress) { + const int kCount = 1000; + char *ptrs[kCount]; + unsigned rnd = 42; + for (int i = 0; i < kCount; i++) { + uptr sz = rand_r(&rnd) % 1000; + char *p = (char*)Alloc(sz); + EXPECT_NE(p, (char*)0); + for (uptr j = 0; j < sz; j++) { + p[j] = 42; + EXPECT_EQ(p, AllocBlock(p + j)); + } + ptrs[i] = p; + } + for (int i = 0; i < kCount; i++) { + Free(ptrs[i]); + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_clock_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_clock_test.cc new file mode 100644 index 00000000000..9c35fb52f5b --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_clock_test.cc @@ -0,0 +1,123 @@ +//===-- tsan_clock_test.cc --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_clock.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Clock, VectorBasic) { + ScopedInRtl in_rtl; + ThreadClock clk; + CHECK_EQ(clk.size(), 0); + clk.tick(0); + CHECK_EQ(clk.size(), 1); + CHECK_EQ(clk.get(0), 1); + clk.tick(3); + CHECK_EQ(clk.size(), 4); + CHECK_EQ(clk.get(0), 1); + CHECK_EQ(clk.get(1), 0); + CHECK_EQ(clk.get(2), 0); + CHECK_EQ(clk.get(3), 1); + clk.tick(3); + CHECK_EQ(clk.get(3), 2); +} + +TEST(Clock, ChunkedBasic) { + ScopedInRtl in_rtl; + ThreadClock vector; + SyncClock chunked; + CHECK_EQ(vector.size(), 0); + CHECK_EQ(chunked.size(), 0); + vector.acquire(&chunked); + CHECK_EQ(vector.size(), 0); + CHECK_EQ(chunked.size(), 0); + vector.release(&chunked); + CHECK_EQ(vector.size(), 0); + CHECK_EQ(chunked.size(), 0); + vector.acq_rel(&chunked); + CHECK_EQ(vector.size(), 0); + CHECK_EQ(chunked.size(), 0); +} + +TEST(Clock, AcquireRelease) { + ScopedInRtl in_rtl; + ThreadClock vector1; + vector1.tick(100); + SyncClock chunked; + vector1.release(&chunked); + CHECK_EQ(chunked.size(), 101); + ThreadClock vector2; + vector2.acquire(&chunked); + CHECK_EQ(vector2.size(), 101); + CHECK_EQ(vector2.get(0), 0); + CHECK_EQ(vector2.get(1), 0); + CHECK_EQ(vector2.get(99), 0); + CHECK_EQ(vector2.get(100), 1); +} + +TEST(Clock, ManyThreads) { + ScopedInRtl in_rtl; + SyncClock chunked; + for (int i = 0; i < 100; i++) { + ThreadClock vector; + vector.tick(i); + vector.release(&chunked); + CHECK_EQ(chunked.size(), i + 1); + vector.acquire(&chunked); + CHECK_EQ(vector.size(), i + 1); + } + ThreadClock vector; + vector.acquire(&chunked); + CHECK_EQ(vector.size(), 100); + for (int i = 0; i < 100; i++) + CHECK_EQ(vector.get(i), 1); +} + +TEST(Clock, DifferentSizes) { + ScopedInRtl in_rtl; + { + ThreadClock vector1; + vector1.tick(10); + ThreadClock vector2; + vector2.tick(20); + { + SyncClock chunked; + vector1.release(&chunked); + CHECK_EQ(chunked.size(), 11); + vector2.release(&chunked); + CHECK_EQ(chunked.size(), 21); + } + { + SyncClock chunked; + vector2.release(&chunked); + CHECK_EQ(chunked.size(), 21); + vector1.release(&chunked); + CHECK_EQ(chunked.size(), 21); + } + { + SyncClock chunked; + vector1.release(&chunked); + vector2.acquire(&chunked); + CHECK_EQ(vector2.size(), 21); + } + { + SyncClock chunked; + vector2.release(&chunked); + vector1.acquire(&chunked); + CHECK_EQ(vector1.size(), 21); + } + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_flags_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_flags_test.cc new file mode 100644 index 00000000000..2e63011c494 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_flags_test.cc @@ -0,0 +1,107 @@ +//===-- tsan_flags_test.cc --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_flags.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Flags, Basic) { + ScopedInRtl in_rtl; + // At least should not crash. + Flags f = {}; + InitializeFlags(&f, 0); + InitializeFlags(&f, ""); +} + +TEST(Flags, ParseBool) { + ScopedInRtl in_rtl; + Flags f = {}; + + f.enable_annotations = false; + InitializeFlags(&f, "enable_annotations"); + EXPECT_EQ(f.enable_annotations, true); + + f.enable_annotations = false; + InitializeFlags(&f, "--enable_annotations"); + EXPECT_EQ(f.enable_annotations, true); + + f.enable_annotations = false; + InitializeFlags(&f, "--enable_annotations=1"); + EXPECT_EQ(f.enable_annotations, true); + + // This flag is false by default. + f.force_seq_cst_atomics = false; + InitializeFlags(&f, "--force_seq_cst_atomics=1"); + EXPECT_EQ(f.force_seq_cst_atomics, true); + + f.enable_annotations = true; + InitializeFlags(&f, "asdas enable_annotations=0 asdasd"); + EXPECT_EQ(f.enable_annotations, false); + + f.enable_annotations = true; + InitializeFlags(&f, " --enable_annotations=0 "); + EXPECT_EQ(f.enable_annotations, false); +} + +TEST(Flags, ParseInt) { + ScopedInRtl in_rtl; + Flags f = {}; + + f.exitcode = -11; + InitializeFlags(&f, "exitcode"); + EXPECT_EQ(f.exitcode, 0); + + f.exitcode = -11; + InitializeFlags(&f, "--exitcode="); + EXPECT_EQ(f.exitcode, 0); + + f.exitcode = -11; + InitializeFlags(&f, "--exitcode=42"); + EXPECT_EQ(f.exitcode, 42); + + f.exitcode = -11; + InitializeFlags(&f, "--exitcode=-42"); + EXPECT_EQ(f.exitcode, -42); +} + +TEST(Flags, ParseStr) { + ScopedInRtl in_rtl; + Flags f = {}; + + InitializeFlags(&f, 0); + EXPECT_EQ(0, strcmp(f.strip_path_prefix, "")); + FinalizeFlags(&f); + + InitializeFlags(&f, "strip_path_prefix"); + EXPECT_EQ(0, strcmp(f.strip_path_prefix, "")); + FinalizeFlags(&f); + + InitializeFlags(&f, "--strip_path_prefix="); + EXPECT_EQ(0, strcmp(f.strip_path_prefix, "")); + FinalizeFlags(&f); + + InitializeFlags(&f, "--strip_path_prefix=abc"); + EXPECT_EQ(0, strcmp(f.strip_path_prefix, "abc")); + FinalizeFlags(&f); + + InitializeFlags(&f, "--strip_path_prefix='abc zxc'"); + EXPECT_EQ(0, strcmp(f.strip_path_prefix, "abc zxc")); + FinalizeFlags(&f); + + InitializeFlags(&f, "--strip_path_prefix=\"abc zxc\""); + EXPECT_EQ(0, strcmp(f.strip_path_prefix, "abc zxc")); + FinalizeFlags(&f); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_mman_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_mman_test.cc new file mode 100644 index 00000000000..5af803b8f19 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_mman_test.cc @@ -0,0 +1,109 @@ +//===-- tsan_mman_test.cc ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_mman.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Mman, Internal) { + ScopedInRtl in_rtl; + char *p = (char*)internal_alloc(MBlockScopedBuf, 10); + EXPECT_NE(p, (char*)0); + char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20); + EXPECT_NE(p2, (char*)0); + EXPECT_NE(p2, p); + for (int i = 0; i < 10; i++) { + p[i] = 42; + } + for (int i = 0; i < 20; i++) { + ((char*)p2)[i] = 42; + } + internal_free(p); + internal_free(p2); +} + +TEST(Mman, User) { + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + uptr pc = 0; + char *p = (char*)user_alloc(thr, pc, 10); + EXPECT_NE(p, (char*)0); + char *p2 = (char*)user_alloc(thr, pc, 20); + EXPECT_NE(p2, (char*)0); + EXPECT_NE(p2, p); + MBlock *b = user_mblock(thr, p); + EXPECT_NE(b, (MBlock*)0); + EXPECT_EQ(b->size, (uptr)10); + MBlock *b2 = user_mblock(thr, p2); + EXPECT_NE(b2, (MBlock*)0); + EXPECT_EQ(b2->size, (uptr)20); + for (int i = 0; i < 10; i++) { + p[i] = 42; + EXPECT_EQ(b, user_mblock(thr, p + i)); + } + for (int i = 0; i < 20; i++) { + ((char*)p2)[i] = 42; + EXPECT_EQ(b2, user_mblock(thr, p2 + i)); + } + user_free(thr, pc, p); + user_free(thr, pc, p2); +} + +TEST(Mman, UserRealloc) { + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + uptr pc = 0; + { + void *p = user_realloc(thr, pc, 0, 0); + // Strictly saying this is incorrect, realloc(NULL, N) is equivalent to + // malloc(N), thus must return non-NULL pointer. + EXPECT_EQ(p, (void*)0); + } + { + void *p = user_realloc(thr, pc, 0, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + user_free(thr, pc, p); + } + { + void *p = user_alloc(thr, pc, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + void *p2 = user_realloc(thr, pc, p, 0); + EXPECT_EQ(p2, (void*)0); + } + { + void *p = user_realloc(thr, pc, 0, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + void *p2 = user_realloc(thr, pc, p, 10000); + EXPECT_NE(p2, (void*)0); + for (int i = 0; i < 100; i++) + EXPECT_EQ(((char*)p2)[i], (char)0xde); + memset(p2, 0xde, 10000); + user_free(thr, pc, p2); + } + { + void *p = user_realloc(thr, pc, 0, 10000); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 10000); + void *p2 = user_realloc(thr, pc, p, 10); + EXPECT_NE(p2, (void*)0); + for (int i = 0; i < 10; i++) + EXPECT_EQ(((char*)p2)[i], (char)0xde); + user_free(thr, pc, p2); + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_mutex_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_mutex_test.cc new file mode 100644 index 00000000000..77c0cda7474 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_mutex_test.cc @@ -0,0 +1,101 @@ +//===-- tsan_mutex_test.cc --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_atomic.h" +#include "tsan_mutex.h" +#include "gtest/gtest.h" + +namespace __tsan { + +class TestData { + public: + TestData() + : mtx_(MutexTypeAnnotations, StatMtxAnnotations) { + for (int i = 0; i < kSize; i++) + data_[i] = 0; + } + + void Write() { + Lock l(&mtx_); + T v0 = data_[0]; + for (int i = 0; i < kSize; i++) { + CHECK_EQ(data_[i], v0); + data_[i]++; + } + } + + void Read() { + ReadLock l(&mtx_); + T v0 = data_[0]; + for (int i = 0; i < kSize; i++) { + CHECK_EQ(data_[i], v0); + } + } + + private: + static const int kSize = 64; + typedef u64 T; + Mutex mtx_; + char pad_[kCacheLineSize]; + T data_[kSize]; +}; + +const int kThreads = 8; +const int kWriteRate = 1024; +#if TSAN_DEBUG +const int kIters = 16*1024; +#else +const int kIters = 64*1024; +#endif + +static void *write_mutex_thread(void *param) { + TestData *data = (TestData *)param; + TestData local; + for (int i = 0; i < kIters; i++) { + data->Write(); + local.Write(); + } + return 0; +} + +static void *read_mutex_thread(void *param) { + TestData *data = (TestData *)param; + TestData local; + for (int i = 0; i < kIters; i++) { + if ((i % kWriteRate) == 0) + data->Write(); + else + data->Read(); + local.Write(); + } + return 0; +} + +TEST(Mutex, Write) { + TestData data; + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, write_mutex_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +TEST(Mutex, ReadWrite) { + TestData data; + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, read_mutex_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_platform_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_platform_test.cc new file mode 100644 index 00000000000..16ee6bd53b2 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_platform_test.cc @@ -0,0 +1,83 @@ +//===-- tsan_platform_test.cc -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_platform.h" +#include "gtest/gtest.h" + +namespace __tsan { + +static void *TestThreadInfo(void *arg) { + ScopedInRtl in_rtl; + uptr stk_addr = 0; + uptr stk_size = 0; + uptr tls_addr = 0; + uptr tls_size = 0; + GetThreadStackAndTls(&stk_addr, &stk_size, &tls_addr, &tls_size); + // Printf("stk=%lx-%lx(%lu)\n", stk_addr, stk_addr + stk_size, stk_size); + // Printf("tls=%lx-%lx(%lu)\n", tls_addr, tls_addr + tls_size, tls_size); + + int stack_var; + EXPECT_NE(stk_addr, (uptr)0); + EXPECT_NE(stk_size, (uptr)0); + EXPECT_GT((uptr)&stack_var, stk_addr); + EXPECT_LT((uptr)&stack_var, stk_addr + stk_size); + + static __thread int thread_var; + EXPECT_NE(tls_addr, (uptr)0); + EXPECT_NE(tls_size, (uptr)0); + EXPECT_GT((uptr)&thread_var, tls_addr); + EXPECT_LT((uptr)&thread_var, tls_addr + tls_size); + + // Ensure that tls and stack do not intersect. + uptr tls_end = tls_addr + tls_size; + EXPECT_TRUE(tls_addr < stk_addr || tls_addr >= stk_addr + stk_size); + EXPECT_TRUE(tls_end < stk_addr || tls_end >= stk_addr + stk_size); + EXPECT_TRUE((tls_addr < stk_addr) == (tls_end < stk_addr)); + return 0; +} + +TEST(Platform, ThreadInfoMain) { + TestThreadInfo(0); +} + +TEST(Platform, ThreadInfoWorker) { + pthread_t t; + pthread_create(&t, 0, TestThreadInfo, 0); + pthread_join(t, 0); +} + +TEST(Platform, FileOps) { + const char *str1 = "qwerty"; + uptr len1 = internal_strlen(str1); + const char *str2 = "zxcv"; + uptr len2 = internal_strlen(str2); + + fd_t fd = internal_open("./tsan_test.tmp", true); + EXPECT_NE(fd, kInvalidFd); + EXPECT_EQ(len1, internal_write(fd, str1, len1)); + EXPECT_EQ(len2, internal_write(fd, str2, len2)); + internal_close(fd); + + fd = internal_open("./tsan_test.tmp", false); + EXPECT_NE(fd, kInvalidFd); + EXPECT_EQ(len1 + len2, internal_filesize(fd)); + char buf[64] = {}; + EXPECT_EQ(len1, internal_read(fd, buf, len1)); + EXPECT_EQ(0, internal_memcmp(buf, str1, len1)); + EXPECT_EQ((char)0, buf[len1 + 1]); + internal_memset(buf, 0, len1); + EXPECT_EQ(len2, internal_read(fd, buf, len2)); + EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); + internal_close(fd); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_printf_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_printf_test.cc new file mode 100644 index 00000000000..1aed1414264 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_printf_test.cc @@ -0,0 +1,109 @@ +//===-- tsan_printf_test.cc -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +#include <string.h> +#include <limits.h> + +namespace __tsan { + +TEST(Printf, Basic) { + char buf[1024]; + uptr len = Snprintf(buf, sizeof(buf), + "a%db%ldc%lldd%ue%luf%llug%xh%lxq%llxw%pe%sr", + (int)-1, (long)-2, (long long)-3, // NOLINT + (unsigned)-4, (unsigned long)5, (unsigned long long)6, // NOLINT + (unsigned)10, (unsigned long)11, (unsigned long long)12, // NOLINT + (void*)0x123, "_string_"); + EXPECT_EQ(len, strlen(buf)); + EXPECT_EQ(0, strcmp(buf, "a-1b-2c-3d4294967292e5f6gahbqcw" + "0x000000000123e_string_r")); +} + +TEST(Printf, OverflowStr) { + char buf[] = "123456789"; + uptr len = Snprintf(buf, 4, "%s", "abcdef"); + EXPECT_EQ(len, (uptr)6); + EXPECT_EQ(0, strcmp(buf, "abc")); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(buf[4], '5'); + EXPECT_EQ(buf[5], '6'); + EXPECT_EQ(buf[6], '7'); + EXPECT_EQ(buf[7], '8'); + EXPECT_EQ(buf[8], '9'); + EXPECT_EQ(buf[9], 0); +} + +TEST(Printf, OverflowInt) { + char buf[] = "123456789"; + Snprintf(buf, 4, "%d", -123456789); + EXPECT_EQ(0, strcmp(buf, "-12")); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(buf[4], '5'); + EXPECT_EQ(buf[5], '6'); + EXPECT_EQ(buf[6], '7'); + EXPECT_EQ(buf[7], '8'); + EXPECT_EQ(buf[8], '9'); + EXPECT_EQ(buf[9], 0); +} + +TEST(Printf, OverflowUint) { + char buf[] = "123456789"; + Snprintf(buf, 4, "a%llx", (long long)0x123456789); // NOLINT + EXPECT_EQ(0, strcmp(buf, "a12")); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(buf[4], '5'); + EXPECT_EQ(buf[5], '6'); + EXPECT_EQ(buf[6], '7'); + EXPECT_EQ(buf[7], '8'); + EXPECT_EQ(buf[8], '9'); + EXPECT_EQ(buf[9], 0); +} + +TEST(Printf, OverflowPtr) { + char buf[] = "123456789"; + Snprintf(buf, 4, "%p", (void*)0x123456789); + EXPECT_EQ(0, strcmp(buf, "0x0")); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(buf[4], '5'); + EXPECT_EQ(buf[5], '6'); + EXPECT_EQ(buf[6], '7'); + EXPECT_EQ(buf[7], '8'); + EXPECT_EQ(buf[8], '9'); + EXPECT_EQ(buf[9], 0); +} + +template<typename T> +static void TestMinMax(const char *fmt, T min, T max) { + char buf[1024]; + uptr len = Snprintf(buf, sizeof(buf), fmt, min, max); + char buf2[1024]; + snprintf(buf2, sizeof(buf2), fmt, min, max); + EXPECT_EQ(len, strlen(buf)); + EXPECT_EQ(0, strcmp(buf, buf2)); +} + +TEST(Printf, MinMax) { + TestMinMax<int>("%d-%d", INT_MIN, INT_MAX); // NOLINT + TestMinMax<long>("%ld-%ld", LONG_MIN, LONG_MAX); // NOLINT + TestMinMax<long long>("%lld-%lld", LLONG_MIN, LLONG_MAX); // NOLINT + TestMinMax<unsigned>("%u-%u", 0, UINT_MAX); // NOLINT + TestMinMax<unsigned long>("%lu-%lu", 0, ULONG_MAX); // NOLINT + TestMinMax<unsigned long long>("%llu-%llu", 0, ULLONG_MAX); // NOLINT + TestMinMax<unsigned>("%x-%x", 0, UINT_MAX); // NOLINT + TestMinMax<unsigned long>("%lx-%lx", 0, ULONG_MAX); // NOLINT + TestMinMax<unsigned long long>("%llx-%llx", 0, ULLONG_MAX); // NOLINT +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_shadow_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_shadow_test.cc new file mode 100644 index 00000000000..81c076e89bc --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_shadow_test.cc @@ -0,0 +1,47 @@ +//===-- tsan_shadow_test.cc -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_platform.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Shadow, Mapping) { + static int global; + int stack; + void *heap = malloc(0); + free(heap); + + CHECK(IsAppMem((uptr)&global)); + CHECK(IsAppMem((uptr)&stack)); + CHECK(IsAppMem((uptr)heap)); + + CHECK(IsShadowMem(MemToShadow((uptr)&global))); + CHECK(IsShadowMem(MemToShadow((uptr)&stack))); + CHECK(IsShadowMem(MemToShadow((uptr)heap))); +} + +TEST(Shadow, Celling) { + u64 aligned_data[4]; + char *data = (char*)aligned_data; + CHECK_EQ((uptr)data % kShadowSize, 0); + uptr s0 = MemToShadow((uptr)&data[0]); + CHECK_EQ(s0 % kShadowSize, 0); + for (unsigned i = 1; i < kShadowCell; i++) + CHECK_EQ(s0, MemToShadow((uptr)&data[i])); + for (unsigned i = kShadowCell; i < 2*kShadowCell; i++) + CHECK_EQ(s0 + kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); + for (unsigned i = 2*kShadowCell; i < 3*kShadowCell; i++) + CHECK_EQ(s0 + 2*kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_suppressions_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_suppressions_test.cc new file mode 100644 index 00000000000..dbedeb25bc2 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_suppressions_test.cc @@ -0,0 +1,132 @@ +//===-- tsan_suppressions_test.cc -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_suppressions.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +#include <string.h> + +namespace __tsan { + +TEST(Suppressions, Parse) { + ScopedInRtl in_rtl; + Suppression *supp0 = SuppressionParse( + "race:foo\n" + " race:bar\n" // NOLINT + "race:baz \n" // NOLINT + "# a comment\n" + "race:quz\n" + ); // NOLINT + Suppression *supp = supp0; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "quz")); + supp = supp->next; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "baz")); + supp = supp->next; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "bar")); + supp = supp->next; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "foo")); + supp = supp->next; + EXPECT_EQ((Suppression*)0, supp); + SuppressionFree(supp0); +} + +TEST(Suppressions, Parse2) { + ScopedInRtl in_rtl; + Suppression *supp0 = SuppressionParse( + " # first line comment\n" // NOLINT + " race:bar \n" // NOLINT + "race:baz* *baz\n" + "# a comment\n" + "# last line comment\n" + ); // NOLINT + Suppression *supp = supp0; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "baz* *baz")); + supp = supp->next; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "bar")); + supp = supp->next; + EXPECT_EQ((Suppression*)0, supp); + SuppressionFree(supp0); +} + +TEST(Suppressions, Parse3) { + ScopedInRtl in_rtl; + Suppression *supp0 = SuppressionParse( + "# last suppression w/o line-feed\n" + "race:foo\n" + "race:bar" + ); // NOLINT + Suppression *supp = supp0; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "bar")); + supp = supp->next; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "foo")); + supp = supp->next; + EXPECT_EQ((Suppression*)0, supp); + SuppressionFree(supp0); +} + +TEST(Suppressions, ParseType) { + ScopedInRtl in_rtl; + Suppression *supp0 = SuppressionParse( + "race:foo\n" + "thread:bar\n" + "mutex:baz\n" + "signal:quz\n" + ); // NOLINT + Suppression *supp = supp0; + EXPECT_EQ(supp->type, SuppressionSignal); + EXPECT_EQ(0, strcmp(supp->func, "quz")); + supp = supp->next; + EXPECT_EQ(supp->type, SuppressionMutex); + EXPECT_EQ(0, strcmp(supp->func, "baz")); + supp = supp->next; + EXPECT_EQ(supp->type, SuppressionThread); + EXPECT_EQ(0, strcmp(supp->func, "bar")); + supp = supp->next; + EXPECT_EQ(supp->type, SuppressionRace); + EXPECT_EQ(0, strcmp(supp->func, "foo")); + supp = supp->next; + EXPECT_EQ((Suppression*)0, supp); + SuppressionFree(supp0); +} + +static bool MyMatch(const char *templ, const char *func) { + char tmp[1024]; + strcpy(tmp, templ); // NOLINT + return SuppressionMatch(tmp, func); +} + +TEST(Suppressions, Match) { + EXPECT_TRUE(MyMatch("foobar", "foobar")); + EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix")); + EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix")); + EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar")); + EXPECT_TRUE(MyMatch("foo*bar", "foobar")); + EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz")); + EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz")); + + EXPECT_FALSE(MyMatch("foo", "baz")); + EXPECT_FALSE(MyMatch("foobarbaz", "foobar")); + EXPECT_FALSE(MyMatch("foobarbaz", "barbaz")); + EXPECT_FALSE(MyMatch("foo*bar", "foobaz")); + EXPECT_FALSE(MyMatch("foo*bar", "foo_baz")); +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_sync_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_sync_test.cc new file mode 100644 index 00000000000..58726bf3579 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_sync_test.cc @@ -0,0 +1,65 @@ +//===-- tsan_sync_test.cc ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_sync.h" +#include "tsan_rtl.h" +#include "tsan_mman.h" +#include "gtest/gtest.h" + +#include <stdlib.h> +#include <stdint.h> +#include <map> + +namespace __tsan { + +TEST(Sync, Table) { + const uintptr_t kIters = 512*1024; + const uintptr_t kRange = 10000; + + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + uptr pc = 0; + + SyncTab tab; + SyncVar *golden[kRange] = {}; + unsigned seed = 0; + for (uintptr_t i = 0; i < kIters; i++) { + uintptr_t addr = rand_r(&seed) % (kRange - 1) + 1; + if (rand_r(&seed) % 2) { + // Get or add. + SyncVar *v = tab.GetAndLock(thr, pc, addr, true); + EXPECT_TRUE(golden[addr] == 0 || golden[addr] == v); + EXPECT_EQ(v->addr, addr); + golden[addr] = v; + v->mtx.Unlock(); + } else { + // Remove. + SyncVar *v = tab.GetAndRemove(thr, pc, addr); + EXPECT_EQ(golden[addr], v); + if (v) { + EXPECT_EQ(v->addr, addr); + golden[addr] = 0; + DestroyAndFree(v); + } + } + } + for (uintptr_t addr = 0; addr < kRange; addr++) { + if (golden[addr] == 0) + continue; + SyncVar *v = tab.GetAndRemove(thr, pc, addr); + EXPECT_EQ(v, golden[addr]); + EXPECT_EQ(v->addr, addr); + DestroyAndFree(v); + } +} + +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/unit_tests/tsan_vector_test.cc b/compiler-rt/lib/tsan/unit_tests/tsan_vector_test.cc new file mode 100644 index 00000000000..96909eb19d0 --- /dev/null +++ b/compiler-rt/lib/tsan/unit_tests/tsan_vector_test.cc @@ -0,0 +1,45 @@ +//===-- tsan_vector_test.cc -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_vector.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Vector, Basic) { + ScopedInRtl in_rtl; + Vector<int> v(MBlockScopedBuf); + EXPECT_EQ(v.Size(), (uptr)0); + v.PushBack(42); + EXPECT_EQ(v.Size(), (uptr)1); + EXPECT_EQ(v[0], 42); + v.PushBack(43); + EXPECT_EQ(v.Size(), (uptr)2); + EXPECT_EQ(v[0], 42); + EXPECT_EQ(v[1], 43); +} + +TEST(Vector, Stride) { + ScopedInRtl in_rtl; + Vector<int> v(MBlockScopedBuf); + for (int i = 0; i < 1000; i++) { + v.PushBack(i); + EXPECT_EQ(v.Size(), (uptr)(i + 1)); + EXPECT_EQ(v[i], i); + } + for (int i = 0; i < 1000; i++) { + EXPECT_EQ(v[i], i); + } +} + +} // namespace __tsan |

