mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-19 13:16:46 +00:00
[tsan] ThreadSanitizer tests and micro benchmarks. No makefiles yet.
llvm-svn: 156545
This commit is contained in:
parent
df68b67f06
commit
ff15ef0c50
49
compiler-rt/lib/tsan/benchmarks/mini_bench_local.cc
Normal file
49
compiler-rt/lib/tsan/benchmarks/mini_bench_local.cc
Normal file
@ -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;
|
||||
}
|
51
compiler-rt/lib/tsan/benchmarks/mini_bench_shared.cc
Normal file
51
compiler-rt/lib/tsan/benchmarks/mini_bench_shared.cc
Normal file
@ -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;
|
||||
}
|
52
compiler-rt/lib/tsan/benchmarks/start_many_threads.cc
Normal file
52
compiler-rt/lib/tsan/benchmarks/start_many_threads.cc
Normal file
@ -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;
|
||||
}
|
120
compiler-rt/lib/tsan/benchmarks/vts_many_threads_bench.cc
Normal file
120
compiler-rt/lib/tsan/benchmarks/vts_many_threads_bench.cc
Normal file
@ -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;
|
||||
}
|
36
compiler-rt/lib/tsan/output_tests/free_race.c
Normal file
36
compiler-rt/lib/tsan/output_tests/free_race.c
Normal file
@ -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
|
19
compiler-rt/lib/tsan/output_tests/heap_race.cc
Normal file
19
compiler-rt/lib/tsan/output_tests/heap_race.cc
Normal file
@ -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
|
40
compiler-rt/lib/tsan/output_tests/memcpy_race.cc
Normal file
40
compiler-rt/lib/tsan/output_tests/memcpy_race.cc
Normal file
@ -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
|
||||
|
35
compiler-rt/lib/tsan/output_tests/mop_with_offset.cc
Normal file
35
compiler-rt/lib/tsan/output_tests/mop_with_offset.cc
Normal file
@ -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:
|
||||
|
35
compiler-rt/lib/tsan/output_tests/mop_with_offset2.cc
Normal file
35
compiler-rt/lib/tsan/output_tests/mop_with_offset2.cc
Normal file
@ -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:
|
||||
|
31
compiler-rt/lib/tsan/output_tests/race_on_barrier.c
Normal file
31
compiler-rt/lib/tsan/output_tests/race_on_barrier.c
Normal file
@ -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
|
||||
|
30
compiler-rt/lib/tsan/output_tests/race_on_barrier2.c
Normal file
30
compiler-rt/lib/tsan/output_tests/race_on_barrier2.c
Normal file
@ -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
|
42
compiler-rt/lib/tsan/output_tests/race_on_mutex.c
Normal file
42
compiler-rt/lib/tsan/output_tests/race_on_mutex.c
Normal file
@ -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 ({{.*}})
|
||||
|
@ -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
|
||||
|
25
compiler-rt/lib/tsan/output_tests/simple_race.c
Normal file
25
compiler-rt/lib/tsan/output_tests/simple_race.c
Normal file
@ -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
|
24
compiler-rt/lib/tsan/output_tests/simple_race.cc
Normal file
24
compiler-rt/lib/tsan/output_tests/simple_race.cc
Normal file
@ -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
|
66
compiler-rt/lib/tsan/output_tests/simple_stack.c
Normal file
66
compiler-rt/lib/tsan/output_tests/simple_stack.c
Normal file
@ -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 ({{.*}})
|
||||
|
48
compiler-rt/lib/tsan/output_tests/simple_stack2.cc
Normal file
48
compiler-rt/lib/tsan/output_tests/simple_stack2.cc
Normal file
@ -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 ({{.*}})
|
||||
|
||||
|
25
compiler-rt/lib/tsan/output_tests/static_init1.cc
Normal file
25
compiler-rt/lib/tsan/output_tests/static_init1.cc
Normal file
@ -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
|
31
compiler-rt/lib/tsan/output_tests/static_init2.cc
Normal file
31
compiler-rt/lib/tsan/output_tests/static_init2.cc
Normal file
@ -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
|
46
compiler-rt/lib/tsan/output_tests/static_init3.cc
Normal file
46
compiler-rt/lib/tsan/output_tests/static_init3.cc
Normal file
@ -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
|
35
compiler-rt/lib/tsan/output_tests/static_init4.cc
Normal file
35
compiler-rt/lib/tsan/output_tests/static_init4.cc
Normal file
@ -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
|
40
compiler-rt/lib/tsan/output_tests/static_init5.cc
Normal file
40
compiler-rt/lib/tsan/output_tests/static_init5.cc
Normal file
@ -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
|
27
compiler-rt/lib/tsan/output_tests/suppress_same_address.cc
Normal file
27
compiler-rt/lib/tsan/output_tests/suppress_same_address.cc
Normal file
@ -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
|
||||
|
27
compiler-rt/lib/tsan/output_tests/suppress_same_stacks.cc
Normal file
27
compiler-rt/lib/tsan/output_tests/suppress_same_stacks.cc
Normal file
@ -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
|
||||
|
26
compiler-rt/lib/tsan/output_tests/suppress_sequence.cc
Normal file
26
compiler-rt/lib/tsan/output_tests/suppress_sequence.cc
Normal file
@ -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
|
61
compiler-rt/lib/tsan/output_tests/test_output.sh
Executable file
61
compiler-rt/lib/tsan/output_tests/test_output.sh
Executable file
@ -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
|
15
compiler-rt/lib/tsan/output_tests/thread_leak.c
Normal file
15
compiler-rt/lib/tsan/output_tests/thread_leak.c
Normal file
@ -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
|
||||
|
15
compiler-rt/lib/tsan/output_tests/thread_leak2.c
Normal file
15
compiler-rt/lib/tsan/output_tests/thread_leak2.c
Normal file
@ -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
|
||||
|
14
compiler-rt/lib/tsan/output_tests/thread_leak3.c
Normal file
14
compiler-rt/lib/tsan/output_tests/thread_leak3.c
Normal file
@ -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
|
||||
|
50
compiler-rt/lib/tsan/output_tests/vptr_benign_race.cc
Normal file
50
compiler-rt/lib/tsan/output_tests/vptr_benign_race.cc
Normal file
@ -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
|
48
compiler-rt/lib/tsan/output_tests/vptr_harmful_race.cc
Normal file
48
compiler-rt/lib/tsan/output_tests/vptr_harmful_race.cc
Normal file
@ -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
|
105
compiler-rt/lib/tsan/rtl_tests/tsan_bench.cc
Normal file
105
compiler-rt/lib/tsan/rtl_tests/tsan_bench.cc
Normal file
@ -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);
|
||||
}
|
231
compiler-rt/lib/tsan/rtl_tests/tsan_mop.cc
Normal file
231
compiler-rt/lib/tsan/rtl_tests/tsan_mop.cc
Normal file
@ -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();
|
||||
}
|
221
compiler-rt/lib/tsan/rtl_tests/tsan_mutex.cc
Normal file
221
compiler-rt/lib/tsan/rtl_tests/tsan_mutex.cc
Normal file
@ -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
|
146
compiler-rt/lib/tsan/rtl_tests/tsan_posix.cc
Normal file
146
compiler-rt/lib/tsan/rtl_tests/tsan_posix.cc
Normal file
@ -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);
|
||||
}
|
82
compiler-rt/lib/tsan/rtl_tests/tsan_string.cc
Normal file
82
compiler-rt/lib/tsan/rtl_tests/tsan_string.cc
Normal file
@ -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
|
43
compiler-rt/lib/tsan/rtl_tests/tsan_test.cc
Normal file
43
compiler-rt/lib/tsan/rtl_tests/tsan_test.cc
Normal file
@ -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;
|
||||
}
|
122
compiler-rt/lib/tsan/rtl_tests/tsan_test_util.h
Normal file
122
compiler-rt/lib/tsan/rtl_tests/tsan_test_util.h
Normal file
@ -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
|
460
compiler-rt/lib/tsan/rtl_tests/tsan_test_util_linux.cc
Normal file
460
compiler-rt/lib/tsan/rtl_tests/tsan_test_util_linux.cc
Normal file
@ -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);
|
||||
}
|
59
compiler-rt/lib/tsan/rtl_tests/tsan_thread.cc
Normal file
59
compiler-rt/lib/tsan/rtl_tests/tsan_thread.cc
Normal file
@ -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);
|
||||
}
|
||||
}
|
56
compiler-rt/lib/tsan/unit_tests/tsan_allocator_test.cc
Normal file
56
compiler-rt/lib/tsan/unit_tests/tsan_allocator_test.cc
Normal file
@ -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
|
123
compiler-rt/lib/tsan/unit_tests/tsan_clock_test.cc
Normal file
123
compiler-rt/lib/tsan/unit_tests/tsan_clock_test.cc
Normal file
@ -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
|
107
compiler-rt/lib/tsan/unit_tests/tsan_flags_test.cc
Normal file
107
compiler-rt/lib/tsan/unit_tests/tsan_flags_test.cc
Normal file
@ -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
|
109
compiler-rt/lib/tsan/unit_tests/tsan_mman_test.cc
Normal file
109
compiler-rt/lib/tsan/unit_tests/tsan_mman_test.cc
Normal file
@ -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
|
101
compiler-rt/lib/tsan/unit_tests/tsan_mutex_test.cc
Normal file
101
compiler-rt/lib/tsan/unit_tests/tsan_mutex_test.cc
Normal file
@ -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
|
83
compiler-rt/lib/tsan/unit_tests/tsan_platform_test.cc
Normal file
83
compiler-rt/lib/tsan/unit_tests/tsan_platform_test.cc
Normal file
@ -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
|
109
compiler-rt/lib/tsan/unit_tests/tsan_printf_test.cc
Normal file
109
compiler-rt/lib/tsan/unit_tests/tsan_printf_test.cc
Normal file
@ -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
|
47
compiler-rt/lib/tsan/unit_tests/tsan_shadow_test.cc
Normal file
47
compiler-rt/lib/tsan/unit_tests/tsan_shadow_test.cc
Normal file
@ -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
|
132
compiler-rt/lib/tsan/unit_tests/tsan_suppressions_test.cc
Normal file
132
compiler-rt/lib/tsan/unit_tests/tsan_suppressions_test.cc
Normal file
@ -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
|
65
compiler-rt/lib/tsan/unit_tests/tsan_sync_test.cc
Normal file
65
compiler-rt/lib/tsan/unit_tests/tsan_sync_test.cc
Normal file
@ -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
|
45
compiler-rt/lib/tsan/unit_tests/tsan_vector_test.cc
Normal file
45
compiler-rt/lib/tsan/unit_tests/tsan_vector_test.cc
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user