[tsan] ThreadSanitizer tests and micro benchmarks. No makefiles yet.

llvm-svn: 156545
This commit is contained in:
Kostya Serebryany 2012-05-10 14:18:22 +00:00
parent df68b67f06
commit ff15ef0c50
51 changed files with 3652 additions and 0 deletions

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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

View 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

View 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

View 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:

View 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:

View 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

View 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

View 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 ({{.*}})

View File

@ -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

View 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

View 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

View 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 ({{.*}})

View 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 ({{.*}})

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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);
}

View 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();
}

View 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

View 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);
}

View 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

View 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;
}

View 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

View 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);
}

View 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);
}
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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