tsan: add mutexsets to reports

With this change reports say what mutexes the threads hold around the racy memory accesses.

llvm-svn: 169493
This commit is contained in:
Dmitry Vyukov 2012-12-06 12:16:15 +00:00
parent dbe7d7e965
commit fd5ebcd1b0
40 changed files with 852 additions and 78 deletions

View File

@ -35,9 +35,9 @@ int main() {
} }
// CHECK: WARNING: ThreadSanitizer: heap-use-after-free // CHECK: WARNING: ThreadSanitizer: heap-use-after-free
// CHECK: Write of size 4 at {{.*}} by main thread: // CHECK: Write of size 4 at {{.*}} by main thread{{.*}}:
// CHECK: #0 Thread2 // CHECK: #0 Thread2
// CHECK: #1 main // CHECK: #1 main
// CHECK: Previous write of size 8 at {{.*}} by thread 1: // CHECK: Previous write of size 8 at {{.*}} by thread T1{{.*}}:
// CHECK: #0 free // CHECK: #0 free
// CHECK: #1 Thread1 // CHECK: #1 Thread1

View File

@ -0,0 +1,31 @@
// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int Global;
extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l);
extern "C" void AnnotateIgnoreWritesEnd(const char *f, int l);
extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l);
extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l);
void *Thread(void *x) {
AnnotateIgnoreWritesBegin(__FILE__, __LINE__);
AnnotateIgnoreReadsBegin(__FILE__, __LINE__);
Global = 42;
AnnotateIgnoreReadsEnd(__FILE__, __LINE__);
AnnotateIgnoreWritesEnd(__FILE__, __LINE__);
return 0;
}
int main() {
pthread_t t;
pthread_create(&t, 0, Thread, 0);
usleep(100000);
Global = 43;
pthread_join(t, 0);
return 0;
}
// CHECK-NOT: ThreadSanitizer

View File

@ -32,9 +32,9 @@ int main() {
// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] // CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 1 at [[ADDR]] by thread 2: // CHECK: Write of size 1 at [[ADDR]] by thread T2:
// CHECK: #0 memcpy // CHECK: #0 memcpy
// CHECK: #1 Thread2 // CHECK: #1 Thread2
// CHECK: Previous write of size 1 at [[ADDR]] by thread 1: // CHECK: Previous write of size 1 at [[ADDR]] by thread T1:
// CHECK: #0 memcpy // CHECK: #0 memcpy
// CHECK: #1 Thread1 // CHECK: #1 Thread1

View File

@ -32,5 +32,5 @@ int main() {
// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] // CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]]
// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] // CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]]
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 1 at [[PTR2]] by thread 2: // CHECK: Write of size 1 at [[PTR2]] by thread T2:
// CHECK: Previous write of size 4 at [[PTR1]] by thread 1: // CHECK: Previous write of size 4 at [[PTR1]] by thread T1:

View File

@ -32,5 +32,5 @@ int main() {
// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] // CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]]
// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] // CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]]
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at [[PTR1]] by thread 1: // CHECK: Write of size 4 at [[PTR1]] by thread T1:
// CHECK: Previous write of size 1 at [[PTR2]] by thread 2: // CHECK: Previous write of size 1 at [[PTR2]] by thread T2:

View File

@ -0,0 +1,38 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int Global;
pthread_mutex_t mtx;
void *Thread1(void *x) {
usleep(100*1000);
pthread_mutex_lock(&mtx);
Global++;
pthread_mutex_unlock(&mtx);
return NULL;
}
void *Thread2(void *x) {
Global--;
return NULL;
}
int main() {
pthread_mutex_init(&mtx, 0);
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);
}
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread T1 (mutexes: write M1):
// CHECK: Previous write of size 4 at {{.*}} by thread T2:
// CHECK: Mutex M1 created at:
// CHECK: #0 pthread_mutex_init
// CHECK: #1 main {{.*}}/mutexset1.cc:23

View File

@ -0,0 +1,38 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int Global;
pthread_mutex_t mtx;
void *Thread1(void *x) {
pthread_mutex_lock(&mtx);
Global++;
pthread_mutex_unlock(&mtx);
return NULL;
}
void *Thread2(void *x) {
usleep(100*1000);
Global--;
return NULL;
}
int main() {
pthread_mutex_init(&mtx, 0);
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);
}
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread T2:
// CHECK: Previous write of size 4 at {{.*}} by thread T1 (mutexes: write M1):
// CHECK: Mutex M1 created at:
// CHECK: #0 pthread_mutex_init
// CHECK: #1 main {{.*}}/mutexset2.cc:23

View File

@ -0,0 +1,46 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int Global;
pthread_mutex_t mtx1;
pthread_mutex_t mtx2;
void *Thread1(void *x) {
usleep(100*1000);
pthread_mutex_lock(&mtx1);
pthread_mutex_lock(&mtx2);
Global++;
pthread_mutex_unlock(&mtx2);
pthread_mutex_unlock(&mtx1);
return NULL;
}
void *Thread2(void *x) {
Global--;
return NULL;
}
int main() {
pthread_mutex_init(&mtx1, 0);
pthread_mutex_init(&mtx2, 0);
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(&mtx1);
pthread_mutex_destroy(&mtx2);
}
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread T1 (mutexes: write M1, write M2):
// CHECK: Previous write of size 4 at {{.*}} by thread T2:
// CHECK: Mutex M1 created at:
// CHECK: #0 pthread_mutex_init
// CHECK: #1 main {{.*}}/mutexset3.cc:26
// CHECK: Mutex M2 created at:
// CHECK: #0 pthread_mutex_init
// CHECK: #1 main {{.*}}/mutexset3.cc:27

View File

@ -0,0 +1,47 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int Global;
pthread_mutex_t mtx1;
pthread_mutex_t mtx2;
void *Thread1(void *x) {
pthread_mutex_lock(&mtx1);
pthread_mutex_lock(&mtx2);
Global++;
pthread_mutex_unlock(&mtx2);
pthread_mutex_unlock(&mtx1);
return NULL;
}
void *Thread2(void *x) {
usleep(100*1000);
Global--;
return NULL;
}
int main() {
pthread_mutex_init(&mtx1, 0);
pthread_mutex_init(&mtx2, 0);
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(&mtx1);
pthread_mutex_destroy(&mtx2);
}
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread T2:
// CHECK: Previous write of size 4 at {{.*}} by thread T1
// CHECK: (mutexes: write M1, write M2):
// CHECK: Mutex M1 created at:
// CHECK: #0 pthread_mutex_init
// CHECK: #1 main {{.*}}/mutexset4.cc:26
// CHECK: Mutex M2 created at:
// CHECK: #0 pthread_mutex_init
// CHECK: #1 main {{.*}}/mutexset4.cc:27

View File

@ -0,0 +1,46 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int Global;
pthread_mutex_t mtx1;
pthread_mutex_t mtx2;
void *Thread1(void *x) {
usleep(100*1000);
pthread_mutex_lock(&mtx1);
Global++;
pthread_mutex_unlock(&mtx1);
return NULL;
}
void *Thread2(void *x) {
pthread_mutex_lock(&mtx2);
Global--;
pthread_mutex_unlock(&mtx2);
return NULL;
}
int main() {
pthread_mutex_init(&mtx1, 0);
pthread_mutex_init(&mtx2, 0);
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(&mtx1);
pthread_mutex_destroy(&mtx2);
}
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread T1 (mutexes: write M1):
// CHECK: Previous write of size 4 at {{.*}} by thread T2 (mutexes: write M2):
// CHECK: Mutex M1 created at:
// CHECK: #0 pthread_mutex_init
// CHECK: #1 main {{.*}}/mutexset5.cc:26
// CHECK: Mutex M2 created at:
// CHECK: #0 pthread_mutex_init
// CHECK: #1 main {{.*}}/mutexset5.cc:27

View File

@ -0,0 +1,54 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int Global;
pthread_mutex_t mtx1;
pthread_spinlock_t mtx2;
pthread_rwlock_t mtx3;
void *Thread1(void *x) {
usleep(100*1000);
pthread_mutex_lock(&mtx1);
Global++;
pthread_mutex_unlock(&mtx1);
return NULL;
}
void *Thread2(void *x) {
pthread_mutex_lock(&mtx1);
pthread_mutex_unlock(&mtx1);
pthread_spin_lock(&mtx2);
pthread_rwlock_rdlock(&mtx3);
Global--;
pthread_spin_unlock(&mtx2);
pthread_rwlock_unlock(&mtx3);
return NULL;
}
int main() {
pthread_mutex_init(&mtx1, 0);
pthread_spin_init(&mtx2, 0);
pthread_rwlock_init(&mtx3, 0);
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(&mtx1);
pthread_spin_destroy(&mtx2);
pthread_rwlock_destroy(&mtx3);
}
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread T1 (mutexes: write M1):
// CHECK: Previous write of size 4 at {{.*}} by thread T2
// CHECK: (mutexes: write M2, read M3):
// CHECK: Mutex M1 created at:
// CHECK: #1 main {{.*}}/mutexset6.cc:31
// CHECK: Mutex M2 created at:
// CHECK: #1 main {{.*}}/mutexset6.cc:32
// CHECK: Mutex M3 created at:
// CHECK: #1 main {{.*}}/mutexset6.cc:33

View File

@ -0,0 +1,38 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int Global;
void *Thread1(void *x) {
usleep(100*1000);
Global++;
return NULL;
}
void *Thread2(void *x) {
pthread_mutex_t mtx;
pthread_mutex_init(&mtx, 0);
pthread_mutex_lock(&mtx);
Global--;
pthread_mutex_unlock(&mtx);
pthread_mutex_destroy(&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);
}
// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread T1:
// CHECK: Previous write of size 4 at {{.*}} by thread T2
// CHECK: (mutexes: write M{{[0-9]+}}):
// CHECK: Mutex M{{[0-9]+}} is already destroyed
// CHECK-NOT: Mutex {{.*}} created at

View File

@ -37,11 +37,11 @@ int main() {
// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] // CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// ... // ...
// CHECK: Location is heap block of size 99 at [[ADDR]] allocated by thread 1: // CHECK: Location is heap block of size 99 at [[ADDR]] allocated by thread T1:
// CHCEKL #0 malloc // CHCEKL #0 malloc
// CHECK: #1 alloc // CHECK: #1 alloc
// CHECK: #2 AllocThread // CHECK: #2 AllocThread
// ... // ...
// CHECK: Thread 1 (tid={{.*}}, finished) created at: // CHECK: Thread T1 (tid={{.*}}, finished) created at:
// CHECK: #0 pthread_create // CHECK: #0 pthread_create
// CHECK: #1 main // CHECK: #1 main

View File

@ -34,9 +34,9 @@ int main() {
} }
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK-NEXT: Read of size 1 at {{.*}} by thread 2: // CHECK-NEXT: Read of size 1 at {{.*}} by thread T2:
// CHECK-NEXT: #0 pthread_mutex_lock // CHECK-NEXT: #0 pthread_mutex_lock
// CHECK-NEXT: #1 Thread2{{.*}} {{.*}}race_on_mutex.c:20{{(:3)?}} ({{.*}}) // CHECK-NEXT: #1 Thread2{{.*}} {{.*}}race_on_mutex.c:20{{(:3)?}} ({{.*}})
// CHECK: Previous write of size 1 at {{.*}} by thread 1: // CHECK: Previous write of size 1 at {{.*}} by thread T1:
// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}}) // CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}})
// CHECK-NEXT: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}}) // CHECK-NEXT: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}})

View File

@ -34,10 +34,10 @@ int main() {
} }
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread 2: // CHECK: Write of size 4 at {{.*}} by thread T2:
// CHECK: Previous write of size 4 at {{.*}} by thread 1: // CHECK: Previous write of size 4 at {{.*}} by thread T1:
// CHECK: #0 foobar // CHECK: #0 foobar
// CHECK: #1 Thread1 // CHECK: #1 Thread1
// CHECK: Thread 1 (tid={{.*}}, finished) created at: // CHECK: Thread T1 (tid={{.*}}, finished) created at:
// CHECK: #0 pthread_create // CHECK: #0 pthread_create
// CHECK: #1 main // CHECK: #1 main

View File

@ -48,19 +48,19 @@ int main() {
} }
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK-NEXT: Write of size 4 at {{.*}} by thread 1: // CHECK-NEXT: Write of size 4 at {{.*}} by thread T1:
// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:9{{(:3)?}} ({{.*}}) // CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:9{{(:3)?}} ({{.*}})
// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(:3)?}} ({{.*}}) // CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(:3)?}} ({{.*}})
// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:28{{(:3)?}} ({{.*}}) // CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:28{{(:3)?}} ({{.*}})
// CHECK: Previous read of size 4 at {{.*}} by thread 2: // CHECK: Previous read of size 4 at {{.*}} by thread T2:
// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:26)?}} ({{.*}}) // CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:26)?}} ({{.*}})
// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(:3)?}} ({{.*}}) // CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(:3)?}} ({{.*}})
// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:33{{(:3)?}} ({{.*}}) // CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:33{{(:3)?}} ({{.*}})
// CHECK: Thread 1 (tid={{.*}}, running) created at: // CHECK: Thread T1 (tid={{.*}}, running) created at:
// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) // CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}}) // CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}})
// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}}) // CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}})
// CHECK: Thread 2 ({{.*}}) created at: // CHECK: Thread T2 ({{.*}}) created at:
// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) // CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}}) // CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}})
// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}}) // CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}})

View File

@ -43,7 +43,7 @@ int main() {
} }
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK-NEXT: Write of size 4 at {{.*}} by thread 1: // CHECK-NEXT: Write of size 4 at {{.*}} by thread T1:
// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} ({{.*}}) // CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} ({{.*}})
// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} ({{.*}}) // CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} ({{.*}})
// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}}) // CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}})

View File

@ -29,6 +29,6 @@ int main() {
} }
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Thread 1 'Thread1' // CHECK: Thread T1 'Thread1'
// CHECK: Thread 2 'Thread2' // CHECK: Thread T2 'Thread2'

View File

@ -29,7 +29,7 @@ int main(int argc, char *argv[]) {
} }
// CHECK: WARNING: ThreadSanitizer: data race // CHECK: WARNING: ThreadSanitizer: data race
// CHECK: Write of size 4 at {{.*}} by thread 1: // CHECK: Write of size 4 at {{.*}} by thread T1{{.*}}:
// CHECK: #0 Thread1(void*) {{.*}}write_in_reader_lock.cc:13 // CHECK: #0 Thread1(void*) {{.*}}write_in_reader_lock.cc:13
// CHECK: Previous read of size 4 at {{.*}} by main thread: // CHECK: Previous read of size 4 at {{.*}} by main thread{{.*}}:
// CHECK: #0 main {{.*}}write_in_reader_lock.cc:23 // CHECK: #0 main {{.*}}write_in_reader_lock.cc:23

View File

@ -139,6 +139,12 @@ T RoundDown(T p, u64 align) {
return (T)((u64)p & ~(align - 1)); return (T)((u64)p & ~(align - 1));
} }
// Zeroizes high part, returns 'bits' lsb bits.
template<typename T>
T GetLsb(T v, int bits) {
return (T)((u64)v & ((1ull << bits) - 1));
}
struct MD5Hash { struct MD5Hash {
u64 hash[2]; u64 hash[2];
bool operator==(const MD5Hash &other) const; bool operator==(const MD5Hash &other) const;

View File

@ -231,7 +231,7 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
// Assume the access is atomic. // Assume the access is atomic.
if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a))
return *a; return *a;
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, (uptr)a, false); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false);
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.acquire(&s->clock); thr->clock.acquire(&s->clock);
T v = *a; T v = *a;
@ -253,7 +253,7 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
return; return;
} }
__sync_synchronize(); __sync_synchronize();
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, (uptr)a, true); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.ReleaseStore(&s->clock); thr->clock.ReleaseStore(&s->clock);
*a = v; *a = v;
@ -265,7 +265,7 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
template<typename T, T (*F)(volatile T *v, T op)> template<typename T, T (*F)(volatile T *v, T op)>
static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, (uptr)a, true); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
if (IsAcqRelOrder(mo)) if (IsAcqRelOrder(mo))
thr->clock.acq_rel(&s->clock); thr->clock.acq_rel(&s->clock);
@ -324,7 +324,7 @@ template<typename T>
static bool AtomicCAS(ThreadState *thr, uptr pc, static bool AtomicCAS(ThreadState *thr, uptr pc,
volatile T *a, T *c, T v, morder mo, morder fmo) { volatile T *a, T *c, T v, morder mo, morder fmo) {
(void)fmo; // Unused because llvm does not pass it yet. (void)fmo; // Unused because llvm does not pass it yet.
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, (uptr)a, true); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
if (IsAcqRelOrder(mo)) if (IsAcqRelOrder(mo))
thr->clock.acq_rel(&s->clock); thr->clock.acq_rel(&s->clock);

View File

@ -60,8 +60,9 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
if (p == 0) if (p == 0)
return 0; return 0;
MBlock *b = (MBlock*)allocator()->GetMetaData(p); MBlock *b = new(allocator()->GetMetaData(p)) MBlock;
b->size = sz; b->size = sz;
b->head = 0;
b->alloc_tid = thr->unique_id; b->alloc_tid = thr->unique_id;
b->alloc_stack_id = CurrentStackId(thr, pc); b->alloc_stack_id = CurrentStackId(thr, pc);
if (CTX() && CTX()->initialized) { if (CTX() && CTX()->initialized) {
@ -92,6 +93,7 @@ void user_free(ThreadState *thr, uptr pc, void *p) {
if (CTX() && CTX()->initialized && thr->in_rtl == 1) { if (CTX() && CTX()->initialized && thr->in_rtl == 1) {
MemoryRangeFreed(thr, pc, (uptr)p, b->size); MemoryRangeFreed(thr, pc, (uptr)p, b->size);
} }
b->~MBlock();
allocator()->Deallocate(&thr->alloc_cache, p); allocator()->Deallocate(&thr->alloc_cache, p);
SignalUnsafeCall(thr, pc); SignalUnsafeCall(thr, pc);
} }

View File

@ -30,12 +30,13 @@ static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
/*0 MutexTypeInvalid*/ {}, /*0 MutexTypeInvalid*/ {},
/*1 MutexTypeTrace*/ {MutexTypeLeaf}, /*1 MutexTypeTrace*/ {MutexTypeLeaf},
/*2 MutexTypeThreads*/ {MutexTypeReport}, /*2 MutexTypeThreads*/ {MutexTypeReport},
/*3 MutexTypeReport*/ {}, /*3 MutexTypeReport*/ {MutexTypeSyncTab, MutexTypeMBlock},
/*4 MutexTypeSyncVar*/ {}, /*4 MutexTypeSyncVar*/ {},
/*5 MutexTypeSyncTab*/ {MutexTypeSyncVar}, /*5 MutexTypeSyncTab*/ {MutexTypeSyncVar},
/*6 MutexTypeSlab*/ {MutexTypeLeaf}, /*6 MutexTypeSlab*/ {MutexTypeLeaf},
/*7 MutexTypeAnnotations*/ {}, /*7 MutexTypeAnnotations*/ {},
/*8 MutexTypeAtExit*/ {MutexTypeSyncTab}, /*8 MutexTypeAtExit*/ {MutexTypeSyncTab},
/*9 MutexTypeMBlock*/ {MutexTypeSyncVar},
}; };
static bool CanLockAdj[MutexTypeCount][MutexTypeCount]; static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
@ -122,6 +123,8 @@ DeadlockDetector::DeadlockDetector() {
void DeadlockDetector::Lock(MutexType t) { void DeadlockDetector::Lock(MutexType t) {
// Printf("LOCK %d @%zu\n", t, seq_ + 1); // Printf("LOCK %d @%zu\n", t, seq_ + 1);
CHECK_GT(t, MutexTypeInvalid);
CHECK_LT(t, MutexTypeCount);
u64 max_seq = 0; u64 max_seq = 0;
u64 max_idx = MutexTypeInvalid; u64 max_idx = MutexTypeInvalid;
for (int i = 0; i != MutexTypeCount; i++) { for (int i = 0; i != MutexTypeCount; i++) {

View File

@ -29,6 +29,7 @@ enum MutexType {
MutexTypeSlab, MutexTypeSlab,
MutexTypeAnnotations, MutexTypeAnnotations,
MutexTypeAtExit, MutexTypeAtExit,
MutexTypeMBlock,
// This must be the last. // This must be the last.
MutexTypeCount MutexTypeCount

View File

@ -0,0 +1,89 @@
//===-- tsan_mutexset.cc --------------------------------------------------===//
//
// 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_mutexset.h"
#include "tsan_rtl.h"
namespace __tsan {
const uptr MutexSet::kMaxSize;
MutexSet::MutexSet() {
size_ = 0;
internal_memset(&descs_, 0, sizeof(descs_));
}
void MutexSet::Add(u64 id, bool write, u64 epoch) {
// Look up existing mutex with the same id.
for (uptr i = 0; i < size_; i++) {
if (descs_[i].id == id) {
descs_[i].count++;
descs_[i].epoch = epoch;
return;
}
}
// On overflow, find the oldest mutex and drop it.
if (size_ == kMaxSize) {
u64 minepoch = (u64)-1;
u64 mini = (u64)-1;
for (uptr i = 0; i < size_; i++) {
if (descs_[i].epoch < minepoch) {
minepoch = descs_[i].epoch;
mini = i;
}
}
RemovePos(mini);
CHECK_EQ(size_, kMaxSize - 1);
}
// Add new mutex descriptor.
descs_[size_].id = id;
descs_[size_].write = write;
descs_[size_].epoch = epoch;
descs_[size_].count = 1;
size_++;
}
void MutexSet::Del(u64 id, bool write) {
for (uptr i = 0; i < size_; i++) {
if (descs_[i].id == id) {
if (--descs_[i].count == 0)
RemovePos(i);
return;
}
}
}
void MutexSet::Remove(u64 id) {
for (uptr i = 0; i < size_; i++) {
if (descs_[i].id == id) {
RemovePos(i);
return;
}
}
}
void MutexSet::RemovePos(uptr i) {
CHECK_LT(i, size_);
descs_[i] = descs_[size_ - 1];
size_--;
}
uptr MutexSet::Size() const {
return size_;
}
MutexSet::Desc MutexSet::Get(uptr i) const {
CHECK_LT(i, size_);
return descs_[i];
}
} // namespace __tsan

View File

@ -0,0 +1,65 @@
//===-- tsan_mutexset.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.
//
// MutexSet holds the set of mutexes currently held by a thread.
//===----------------------------------------------------------------------===//
#ifndef TSAN_MUTEXSET_H
#define TSAN_MUTEXSET_H
#include "tsan_defs.h"
namespace __tsan {
class MutexSet {
public:
// Holds limited number of mutexes.
// The oldest mutexes are discarded on overflow.
static const uptr kMaxSize = 64;
struct Desc {
u64 id;
u64 epoch;
int count;
bool write;
};
MutexSet();
// The 'id' is obtained from SyncVar::GetId().
void Add(u64 id, bool write, u64 epoch);
void Del(u64 id, bool write);
void Remove(u64 id); // Removes the mutex completely (if it's destroyed).
uptr Size() const;
Desc Get(uptr i) const;
private:
#ifndef TSAN_GO
uptr size_;
Desc descs_[kMaxSize];
#endif
void RemovePos(uptr i);
};
// Go does not have mutexes, so do not spend memory and time.
// (Go sync.Mutex is actually a semaphore -- can be unlocked
// in different goroutine).
#ifdef TSAN_GO
MutexSet::MutexSet() {}
void MutexSet::Add(u64 id, bool write, u64 epoch) {}
void MutexSet::Del(u64 id, bool write) {}
void MutexSet::Remove(u64 id) {}
void MutexSet::RemovePos(uptr i) {}
uptr MutexSet::Size() const { return 0; }
MutexSet::Desc MutexSet::Get(uptr i) const { return Desc(); }
#endif
} // namespace __tsan
#endif // TSAN_REPORT_H

View File

@ -25,6 +25,10 @@ ReportDesc::ReportDesc()
, sleep() { , sleep() {
} }
ReportMop::ReportMop()
: mset(MBlockReportMutex) {
}
ReportDesc::~ReportDesc() { ReportDesc::~ReportDesc() {
// FIXME(dvyukov): it must be leaking a lot of memory. // FIXME(dvyukov): it must be leaking a lot of memory.
} }
@ -67,15 +71,27 @@ void PrintStack(const ReportStack *ent) {
Printf("\n"); Printf("\n");
} }
static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
for (uptr i = 0; i < mset.Size(); i++) {
if (i == 0)
Printf(" (mutexes:");
const ReportMopMutex m = mset[i];
Printf(" %s M%llu", m.write ? "write" : "read", m.id);
Printf(i == mset.Size() - 1 ? ")" : ",");
}
}
static void PrintMop(const ReportMop *mop, bool first) { static void PrintMop(const ReportMop *mop, bool first) {
Printf(" %s of size %d at %p", Printf(" %s of size %d at %p",
(first ? (mop->write ? "Write" : "Read") (first ? (mop->write ? "Write" : "Read")
: (mop->write ? "Previous write" : "Previous read")), : (mop->write ? "Previous write" : "Previous read")),
mop->size, (void*)mop->addr); mop->size, (void*)mop->addr);
if (mop->tid == 0) if (mop->tid == 0)
Printf(" by main thread:\n"); Printf(" by main thread");
else else
Printf(" by thread %d:\n", mop->tid); Printf(" by thread T%d", mop->tid);
PrintMutexSet(mop->mset);
Printf(":\n");
PrintStack(mop->stack); PrintStack(mop->stack);
} }
@ -90,24 +106,26 @@ static void PrintLocation(const ReportLocation *loc) {
if (loc->tid == 0) if (loc->tid == 0)
Printf(" by main thread:\n"); Printf(" by main thread:\n");
else else
Printf(" by thread %d:\n", loc->tid); Printf(" by thread T%d:\n", loc->tid);
PrintStack(loc->stack); PrintStack(loc->stack);
} else if (loc->type == ReportLocationStack) { } else if (loc->type == ReportLocationStack) {
Printf(" Location is stack of thread %d:\n\n", loc->tid); Printf(" Location is stack of thread T%d:\n\n", loc->tid);
} }
} }
static void PrintMutex(const ReportMutex *rm) { static void PrintMutex(const ReportMutex *rm) {
if (rm->stack == 0) if (rm->destroyed) {
return; Printf(" Mutex M%llu is already destroyed.\n\n", rm->id);
Printf(" Mutex %d created at:\n", rm->id); } else {
Printf(" Mutex M%llu created at:\n", rm->id);
PrintStack(rm->stack); PrintStack(rm->stack);
}
} }
static void PrintThread(const ReportThread *rt) { static void PrintThread(const ReportThread *rt) {
if (rt->id == 0) // Little sense in describing the main thread. if (rt->id == 0) // Little sense in describing the main thread.
return; return;
Printf(" Thread %d", rt->id); Printf(" Thread T%d", rt->id);
if (rt->name) if (rt->name)
Printf(" '%s'", rt->name); Printf(" '%s'", rt->name);
Printf(" (tid=%zu, %s)", rt->pid, rt->running ? "running" : "finished"); Printf(" (tid=%zu, %s)", rt->pid, rt->running ? "running" : "finished");

View File

@ -38,14 +38,20 @@ struct ReportStack {
int col; int col;
}; };
struct ReportMopMutex {
u64 id;
bool write;
};
struct ReportMop { struct ReportMop {
int tid; int tid;
uptr addr; uptr addr;
int size; int size;
bool write; bool write;
int nmutex; Vector<ReportMopMutex> mset;
int *mutex;
ReportStack *stack; ReportStack *stack;
ReportMop();
}; };
enum ReportLocationType { enum ReportLocationType {
@ -76,7 +82,8 @@ struct ReportThread {
}; };
struct ReportMutex { struct ReportMutex {
int id; u64 id;
bool destroyed;
ReportStack *stack; ReportStack *stack;
}; };

View File

@ -291,6 +291,7 @@ void TraceSwitch(ThreadState *thr) {
TraceHeader *hdr = &thr->trace.headers[trace]; TraceHeader *hdr = &thr->trace.headers[trace];
hdr->epoch0 = thr->fast_state.epoch(); hdr->epoch0 = thr->fast_state.epoch();
hdr->stack0.ObtainCurrent(thr, 0); hdr->stack0.ObtainCurrent(thr, 0);
hdr->mset0 = thr->mset;
thr->nomalloc--; thr->nomalloc--;
} }

View File

@ -36,6 +36,7 @@
#include "tsan_vector.h" #include "tsan_vector.h"
#include "tsan_report.h" #include "tsan_report.h"
#include "tsan_platform.h" #include "tsan_platform.h"
#include "tsan_mutexset.h"
#if SANITIZER_WORDSIZE != 64 #if SANITIZER_WORDSIZE != 64
# error "ThreadSanitizer is supported only on 64-bit platforms" # error "ThreadSanitizer is supported only on 64-bit platforms"
@ -50,6 +51,10 @@ struct MBlock {
u32 alloc_tid; u32 alloc_tid;
u32 alloc_stack_id; u32 alloc_stack_id;
SyncVar *head; SyncVar *head;
MBlock()
: mtx(MutexTypeMBlock, StatMtxMBlock) {
}
}; };
#ifndef TSAN_GO #ifndef TSAN_GO
@ -300,6 +305,7 @@ struct ThreadState {
uptr *shadow_stack; uptr *shadow_stack;
uptr *shadow_stack_end; uptr *shadow_stack_end;
#endif #endif
MutexSet mset;
ThreadClock clock; ThreadClock clock;
#ifndef TSAN_GO #ifndef TSAN_GO
AllocatorCache alloc_cache; AllocatorCache alloc_cache;
@ -447,7 +453,8 @@ class ScopedReport {
~ScopedReport(); ~ScopedReport();
void AddStack(const StackTrace *stack); void AddStack(const StackTrace *stack);
void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack); void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack,
const MutexSet *mset);
void AddThread(const ThreadContext *tctx); void AddThread(const ThreadContext *tctx);
void AddMutex(const SyncVar *s); void AddMutex(const SyncVar *s);
void AddLocation(uptr addr, uptr size); void AddLocation(uptr addr, uptr size);
@ -459,11 +466,13 @@ class ScopedReport {
Context *ctx_; Context *ctx_;
ReportDesc *rep_; ReportDesc *rep_;
void AddMutex(u64 id);
ScopedReport(const ScopedReport&); ScopedReport(const ScopedReport&);
void operator = (const ScopedReport&); void operator = (const ScopedReport&);
}; };
void RestoreStack(int tid, const u64 epoch, StackTrace *stk); void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset);
void StatAggregate(u64 *dst, u64 *src); void StatAggregate(u64 *dst, u64 *src);
void StatOutput(u64 *stat); void StatOutput(u64 *stat);
@ -577,7 +586,10 @@ uptr TraceParts();
extern "C" void __tsan_trace_switch(); extern "C" void __tsan_trace_switch();
void ALWAYS_INLINE INLINE TraceAddEvent(ThreadState *thr, FastState fs, void ALWAYS_INLINE INLINE TraceAddEvent(ThreadState *thr, FastState fs,
EventType typ, uptr addr) { EventType typ, u64 addr) {
DCHECK_GE((int)typ, 0);
DCHECK_LE((int)typ, 7);
DCHECK_EQ(GetLsb(addr, 61), addr);
StatInc(thr, StatEvents); StatInc(thr, StatEvents);
u64 pos = fs.GetTracePos(); u64 pos = fs.GetTracePos();
if (UNLIKELY((pos % kTracePartSize) == 0)) { if (UNLIKELY((pos % kTracePartSize) == 0)) {

View File

@ -28,7 +28,7 @@ void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
StatInc(thr, StatMutexCreate); StatInc(thr, StatMutexCreate);
if (!linker_init && IsAppMem(addr)) if (!linker_init && IsAppMem(addr))
MemoryWrite1Byte(thr, pc, addr); MemoryWrite1Byte(thr, pc, addr);
SyncVar *s = ctx->synctab.GetAndLock(thr, pc, addr, true); SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
s->is_rw = rw; s->is_rw = rw;
s->is_recursive = recursive; s->is_recursive = recursive;
s->is_linker_init = linker_init; s->is_linker_init = linker_init;
@ -61,11 +61,12 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
trace.ObtainCurrent(thr, pc); trace.ObtainCurrent(thr, pc);
rep.AddStack(&trace); rep.AddStack(&trace);
FastState last(s->last_lock); FastState last(s->last_lock);
RestoreStack(last.tid(), last.epoch(), &trace); RestoreStack(last.tid(), last.epoch(), &trace, 0);
rep.AddStack(&trace); rep.AddStack(&trace);
rep.AddLocation(s->addr, 1); rep.AddLocation(s->addr, 1);
OutputReport(ctx, rep); OutputReport(ctx, rep);
} }
thr->mset.Remove(s->GetId());
DestroyAndFree(s); DestroyAndFree(s);
} }
@ -74,9 +75,9 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
DPrintf("#%d: MutexLock %zx\n", thr->tid, addr); DPrintf("#%d: MutexLock %zx\n", thr->tid, addr);
if (IsAppMem(addr)) if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr); MemoryRead1Byte(thr, pc, addr);
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch(); thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeLock, addr); TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
if (s->owner_tid == SyncVar::kInvalidTid) { if (s->owner_tid == SyncVar::kInvalidTid) {
CHECK_EQ(s->recursion, 0); CHECK_EQ(s->recursion, 0);
s->owner_tid = thr->tid; s->owner_tid = thr->tid;
@ -98,6 +99,7 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
StatInc(thr, StatMutexRecLock); StatInc(thr, StatMutexRecLock);
} }
s->recursion++; s->recursion++;
thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
s->mtx.Unlock(); s->mtx.Unlock();
} }
@ -106,9 +108,9 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr); DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr);
if (IsAppMem(addr)) if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr); MemoryRead1Byte(thr, pc, addr);
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch(); thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, addr); TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
if (s->recursion == 0) { if (s->recursion == 0) {
if (!s->is_broken) { if (!s->is_broken) {
s->is_broken = true; s->is_broken = true;
@ -134,6 +136,7 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
StatInc(thr, StatMutexRecUnlock); StatInc(thr, StatMutexRecUnlock);
} }
} }
thr->mset.Del(s->GetId(), true);
s->mtx.Unlock(); s->mtx.Unlock();
} }
@ -143,9 +146,9 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
StatInc(thr, StatMutexReadLock); StatInc(thr, StatMutexReadLock);
if (IsAppMem(addr)) if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr); MemoryRead1Byte(thr, pc, addr);
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
thr->fast_state.IncrementEpoch(); thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeRLock, addr); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
if (s->owner_tid != SyncVar::kInvalidTid) { if (s->owner_tid != SyncVar::kInvalidTid) {
Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n"); Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
PrintCurrentStack(thr, pc); PrintCurrentStack(thr, pc);
@ -154,6 +157,7 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
thr->clock.acquire(&s->clock); thr->clock.acquire(&s->clock);
s->last_lock = thr->fast_state.raw(); s->last_lock = thr->fast_state.raw();
StatInc(thr, StatSyncAcquire); StatInc(thr, StatSyncAcquire);
thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
s->mtx.ReadUnlock(); s->mtx.ReadUnlock();
} }
@ -163,9 +167,9 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
StatInc(thr, StatMutexReadUnlock); StatInc(thr, StatMutexReadUnlock);
if (IsAppMem(addr)) if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr); MemoryRead1Byte(thr, pc, addr);
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch(); thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, addr); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
if (s->owner_tid != SyncVar::kInvalidTid) { if (s->owner_tid != SyncVar::kInvalidTid) {
Printf("ThreadSanitizer WARNING: read unlock of a write " Printf("ThreadSanitizer WARNING: read unlock of a write "
"locked mutex\n"); "locked mutex\n");
@ -176,6 +180,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
thr->clock.release(&s->read_clock); thr->clock.release(&s->read_clock);
StatInc(thr, StatSyncRelease); StatInc(thr, StatSyncRelease);
s->mtx.Unlock(); s->mtx.Unlock();
thr->mset.Del(s->GetId(), false);
} }
void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
@ -183,18 +188,22 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr); DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
if (IsAppMem(addr)) if (IsAppMem(addr))
MemoryRead1Byte(thr, pc, addr); MemoryRead1Byte(thr, pc, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
bool write = true;
if (s->owner_tid == SyncVar::kInvalidTid) { if (s->owner_tid == SyncVar::kInvalidTid) {
// Seems to be read unlock. // Seems to be read unlock.
write = false;
StatInc(thr, StatMutexReadUnlock); StatInc(thr, StatMutexReadUnlock);
thr->fast_state.IncrementEpoch(); thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, addr); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch(); thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.release(&s->read_clock); thr->clock.release(&s->read_clock);
StatInc(thr, StatSyncRelease); StatInc(thr, StatSyncRelease);
} else if (s->owner_tid == thr->tid) { } else if (s->owner_tid == thr->tid) {
// Seems to be write unlock. // Seems to be write unlock.
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
CHECK_GT(s->recursion, 0); CHECK_GT(s->recursion, 0);
s->recursion--; s->recursion--;
if (s->recursion == 0) { if (s->recursion == 0) {
@ -204,8 +213,6 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
// The sequence of events is quite tricky and doubled in several places. // The sequence of events is quite tricky and doubled in several places.
// First, it's a bug to increment the epoch w/o writing to the trace. // First, it's a bug to increment the epoch w/o writing to the trace.
// Then, the acquire/release logic can be factored out as well. // Then, the acquire/release logic can be factored out as well.
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, addr);
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch(); thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.ReleaseStore(&s->clock); thr->clock.ReleaseStore(&s->clock);
@ -218,13 +225,14 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
PrintCurrentStack(thr, pc); PrintCurrentStack(thr, pc);
} }
thr->mset.Del(s->GetId(), write);
s->mtx.Unlock(); s->mtx.Unlock();
} }
void Acquire(ThreadState *thr, uptr pc, uptr addr) { void Acquire(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0); CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Acquire %zx\n", thr->tid, addr); DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.acquire(&s->clock); thr->clock.acquire(&s->clock);
StatInc(thr, StatSyncAcquire); StatInc(thr, StatSyncAcquire);
@ -248,7 +256,7 @@ void AcquireGlobal(ThreadState *thr, uptr pc) {
void Release(ThreadState *thr, uptr pc, uptr addr) { void Release(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0); CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Release %zx\n", thr->tid, addr); DPrintf("#%d: Release %zx\n", thr->tid, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.release(&s->clock); thr->clock.release(&s->clock);
StatInc(thr, StatSyncRelease); StatInc(thr, StatSyncRelease);
@ -258,7 +266,7 @@ void Release(ThreadState *thr, uptr pc, uptr addr) {
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0); CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr); DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.ReleaseStore(&s->clock); thr->clock.ReleaseStore(&s->clock);
StatInc(thr, StatSyncRelease); StatInc(thr, StatSyncRelease);

View File

@ -14,6 +14,7 @@
#include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_common.h"
#include "tsan_platform.h" #include "tsan_platform.h"
#include "tsan_rtl.h" #include "tsan_rtl.h"
#include "tsan_suppressions.h" #include "tsan_suppressions.h"
@ -25,6 +26,8 @@
namespace __tsan { namespace __tsan {
using namespace __sanitizer; // NOLINT
void TsanCheckFailed(const char *file, int line, const char *cond, void TsanCheckFailed(const char *file, int line, const char *cond,
u64 v1, u64 v2) { u64 v1, u64 v2) {
ScopedInRtl in_rtl; ScopedInRtl in_rtl;
@ -134,7 +137,7 @@ void ScopedReport::AddStack(const StackTrace *stack) {
} }
void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
const StackTrace *stack) { const StackTrace *stack, const MutexSet *mset) {
void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop)); void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
ReportMop *mop = new(mem) ReportMop; ReportMop *mop = new(mem) ReportMop;
rep_->mops.PushBack(mop); rep_->mops.PushBack(mop);
@ -142,8 +145,27 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
mop->addr = addr + s.addr0(); mop->addr = addr + s.addr0();
mop->size = s.size(); mop->size = s.size();
mop->write = s.is_write(); mop->write = s.is_write();
mop->nmutex = 0;
mop->stack = SymbolizeStack(*stack); mop->stack = SymbolizeStack(*stack);
for (uptr i = 0; i < mset->Size(); i++) {
MutexSet::Desc d = mset->Get(i);
u64 uid = 0;
uptr addr = SyncVar::SplitId(d.id, &uid);
SyncVar *s = ctx_->synctab.GetIfExistsAndLock(addr, false);
// Check that the mutex is still alive.
// Another mutex can be created at the same address,
// so check uid as well.
if (s && s->CheckId(uid)) {
ReportMopMutex mtx = {s->uid, d.write};
mop->mset.PushBack(mtx);
AddMutex(s);
} else {
ReportMopMutex mtx = {d.id, d.write};
mop->mset.PushBack(mtx);
AddMutex(d.id);
}
if (s)
s->mtx.ReadUnlock();
}
} }
void ScopedReport::AddThread(const ThreadContext *tctx) { void ScopedReport::AddThread(const ThreadContext *tctx) {
@ -175,13 +197,31 @@ static ThreadContext *FindThread(int unique_id) {
#endif #endif
void ScopedReport::AddMutex(const SyncVar *s) { void ScopedReport::AddMutex(const SyncVar *s) {
for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
if (rep_->mutexes[i]->id == s->uid)
return;
}
void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
ReportMutex *rm = new(mem) ReportMutex(); ReportMutex *rm = new(mem) ReportMutex();
rep_->mutexes.PushBack(rm); rep_->mutexes.PushBack(rm);
rm->id = 42; rm->id = s->uid;
rm->destroyed = false;
rm->stack = SymbolizeStack(s->creation_stack); rm->stack = SymbolizeStack(s->creation_stack);
} }
void ScopedReport::AddMutex(u64 id) {
for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
if (rep_->mutexes[i]->id == id)
return;
}
void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
ReportMutex *rm = new(mem) ReportMutex();
rep_->mutexes.PushBack(rm);
rm->id = id;
rm->destroyed = true;
rm->stack = 0;
}
void ScopedReport::AddLocation(uptr addr, uptr size) { void ScopedReport::AddLocation(uptr addr, uptr size) {
if (addr == 0) if (addr == 0)
return; return;
@ -248,7 +288,10 @@ const ReportDesc *ScopedReport::GetReport() const {
return rep_; return rep_;
} }
void RestoreStack(int tid, const u64 epoch, StackTrace *stk) { void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
// This function restores stack trace and mutex set for the thread/epoch.
// It does so by getting stack trace and mutex set at the beginning of
// trace part, and then replaying the trace till the given epoch.
ThreadContext *tctx = CTX()->threads[tid]; ThreadContext *tctx = CTX()->threads[tid];
if (tctx == 0) if (tctx == 0)
return; return;
@ -269,6 +312,7 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
TraceHeader* hdr = &trace->headers[partidx]; TraceHeader* hdr = &trace->headers[partidx];
if (epoch < hdr->epoch0) if (epoch < hdr->epoch0)
return; return;
const u64 epoch0 = RoundDown(epoch, TraceSize());
const u64 eend = epoch % TraceSize(); const u64 eend = epoch % TraceSize();
const u64 ebegin = RoundDown(eend, kTracePartSize); const u64 ebegin = RoundDown(eend, kTracePartSize);
DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
@ -278,12 +322,14 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
stack[i] = hdr->stack0.Get(i); stack[i] = hdr->stack0.Get(i);
DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]);
} }
if (mset)
*mset = hdr->mset0;
uptr pos = hdr->stack0.Size(); uptr pos = hdr->stack0.Size();
Event *events = (Event*)GetThreadTrace(tid); Event *events = (Event*)GetThreadTrace(tid);
for (uptr i = ebegin; i <= eend; i++) { for (uptr i = ebegin; i <= eend; i++) {
Event ev = events[i]; Event ev = events[i];
EventType typ = (EventType)(ev >> 61); EventType typ = (EventType)(ev >> 61);
uptr pc = (uptr)(ev & 0xffffffffffffull); uptr pc = (uptr)(ev & ((1ull << 61) - 1));
DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc); DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc);
if (typ == EventTypeMop) { if (typ == EventTypeMop) {
stack[pos] = pc; stack[pos] = pc;
@ -293,6 +339,17 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
if (pos > 0) if (pos > 0)
pos--; pos--;
} }
if (mset) {
if (typ == EventTypeLock) {
mset->Add(pc, true, epoch0 + i);
} else if (typ == EventTypeUnlock) {
mset->Del(pc, true);
} else if (typ == EventTypeRLock) {
mset->Add(pc, false, epoch0 + i);
} else if (typ == EventTypeRUnlock) {
mset->Del(pc, false);
}
}
for (uptr j = 0; j <= pos; j++) for (uptr j = 0; j <= pos; j++)
DPrintf2(" #%zu: %zx\n", j, stack[j]); DPrintf2(" #%zu: %zx\n", j, stack[j]);
} }
@ -456,15 +513,18 @@ void ReportRace(ThreadState *thr) {
traces[0].ObtainCurrent(thr, toppc); traces[0].ObtainCurrent(thr, toppc);
if (IsFiredSuppression(ctx, rep, traces[0])) if (IsFiredSuppression(ctx, rep, traces[0]))
return; return;
InternalScopedBuffer<MutexSet> mset2(1);
new(mset2.data()) MutexSet();
Shadow s2(thr->racy_state[1]); Shadow s2(thr->racy_state[1]);
RestoreStack(s2.tid(), s2.epoch(), &traces[1]); RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data());
if (HandleRacyStacks(thr, traces, addr_min, addr_max)) if (HandleRacyStacks(thr, traces, addr_min, addr_max))
return; return;
for (uptr i = 0; i < kMop; i++) { for (uptr i = 0; i < kMop; i++) {
Shadow s(thr->racy_state[i]); Shadow s(thr->racy_state[i]);
rep.AddMemoryAccess(addr, s, &traces[i]); rep.AddMemoryAccess(addr, s, &traces[i],
i == 0 ? &thr->mset : mset2.data());
} }
if (flags()->suppress_java && IsJavaNonsense(rep.GetReport())) if (flags()->suppress_java && IsJavaNonsense(rep.GetReport()))

View File

@ -305,6 +305,7 @@ void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
Printf("ThreadSanitizer: join of non-existent thread\n"); Printf("ThreadSanitizer: join of non-existent thread\n");
return; return;
} }
// FIXME(dvyukov): print message and continue (it's user error).
CHECK_EQ(tctx->detached, false); CHECK_EQ(tctx->detached, false);
CHECK_EQ(tctx->status, ThreadStatusFinished); CHECK_EQ(tctx->status, ThreadStatusFinished);
thr->clock.acquire(&tctx->sync); thr->clock.acquire(&tctx->sync);

View File

@ -253,6 +253,7 @@ void StatOutput(u64 *stat) {
name[StatMtxSlab] = " Slab "; name[StatMtxSlab] = " Slab ";
name[StatMtxAtExit] = " Atexit "; name[StatMtxAtExit] = " Atexit ";
name[StatMtxAnnotations] = " Annotations "; name[StatMtxAnnotations] = " Annotations ";
name[StatMtxMBlock] = " MBlock ";
Printf("Statistics:\n"); Printf("Statistics:\n");
for (int i = 0; i < StatCnt; i++) for (int i = 0; i < StatCnt; i++)

View File

@ -255,6 +255,7 @@ enum StatType {
StatMtxSlab, StatMtxSlab,
StatMtxAnnotations, StatMtxAnnotations,
StatMtxAtExit, StatMtxAtExit,
StatMtxMBlock,
// This must be the last. // This must be the last.
StatCnt StatCnt

View File

@ -17,9 +17,10 @@
namespace __tsan { namespace __tsan {
SyncVar::SyncVar(uptr addr) SyncVar::SyncVar(uptr addr, u64 uid)
: mtx(MutexTypeSyncVar, StatMtxSyncVar) : mtx(MutexTypeSyncVar, StatMtxSyncVar)
, addr(addr) , addr(addr)
, uid(uid)
, owner_tid(kInvalidTid) , owner_tid(kInvalidTid)
, last_lock() , last_lock()
, recursion() , recursion()
@ -47,8 +48,17 @@ SyncTab::~SyncTab() {
} }
} }
SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc, SyncVar* SyncTab::GetOrCreateAndLock(ThreadState *thr, uptr pc,
uptr addr, bool write_lock) { uptr addr, bool write_lock) {
return GetAndLock(thr, pc, addr, write_lock, true);
}
SyncVar* SyncTab::GetIfExistsAndLock(uptr addr, bool write_lock) {
return GetAndLock(0, 0, addr, write_lock, false);
}
SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
uptr addr, bool write_lock, bool create) {
#ifndef TSAN_GO #ifndef TSAN_GO
if (PrimaryAllocator::PointerIsMine((void*)addr)) { if (PrimaryAllocator::PointerIsMine((void*)addr)) {
MBlock *b = user_mblock(thr, (void*)addr); MBlock *b = user_mblock(thr, (void*)addr);
@ -59,9 +69,12 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
break; break;
} }
if (res == 0) { if (res == 0) {
if (!create)
return 0;
StatInc(thr, StatSyncCreated); StatInc(thr, StatSyncCreated);
void *mem = internal_alloc(MBlockSync, sizeof(SyncVar)); void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
res = new(mem) SyncVar(addr); const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
res = new(mem) SyncVar(addr, uid);
res->creation_stack.ObtainCurrent(thr, pc); res->creation_stack.ObtainCurrent(thr, pc);
res->next = b->head; res->next = b->head;
b->head = res; b->head = res;
@ -87,6 +100,8 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
} }
} }
} }
if (!create)
return 0;
{ {
Lock l(&p->mtx); Lock l(&p->mtx);
SyncVar *res = p->val; SyncVar *res = p->val;
@ -97,7 +112,8 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
if (res == 0) { if (res == 0) {
StatInc(thr, StatSyncCreated); StatInc(thr, StatSyncCreated);
void *mem = internal_alloc(MBlockSync, sizeof(SyncVar)); void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
res = new(mem) SyncVar(addr); const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
res = new(mem) SyncVar(addr, uid);
#ifndef TSAN_GO #ifndef TSAN_GO
res->creation_stack.ObtainCurrent(thr, pc); res->creation_stack.ObtainCurrent(thr, pc);
#endif #endif

View File

@ -50,12 +50,13 @@ class StackTrace {
}; };
struct SyncVar { struct SyncVar {
explicit SyncVar(uptr addr); explicit SyncVar(uptr addr, u64 uid);
static const int kInvalidTid = -1; static const int kInvalidTid = -1;
Mutex mtx; Mutex mtx;
const uptr addr; const uptr addr;
const u64 uid; // Globally unique id.
SyncClock clock; SyncClock clock;
SyncClock read_clock; // Used for rw mutexes only. SyncClock read_clock; // Used for rw mutexes only.
StackTrace creation_stack; StackTrace creation_stack;
@ -69,6 +70,18 @@ struct SyncVar {
SyncVar *next; // In SyncTab hashtable. SyncVar *next; // In SyncTab hashtable.
uptr GetMemoryConsumption(); uptr GetMemoryConsumption();
u64 GetId() const {
// 47 lsb is addr, then 14 bits is low part of uid, then 3 zero bits.
return GetLsb((u64)addr | (uid << 47), 61);
}
bool CheckId(u64 uid) const {
CHECK_EQ(uid, GetLsb(uid, 14));
return GetLsb(this->uid, 14) == uid;
}
static uptr SplitId(u64 id, u64 *uid) {
*uid = id >> 47;
return (uptr)GetLsb(id, 47);
}
}; };
class SyncTab { class SyncTab {
@ -76,9 +89,9 @@ class SyncTab {
SyncTab(); SyncTab();
~SyncTab(); ~SyncTab();
// If the SyncVar does not exist yet, it is created. SyncVar* GetOrCreateAndLock(ThreadState *thr, uptr pc,
SyncVar* GetAndLock(ThreadState *thr, uptr pc,
uptr addr, bool write_lock); uptr addr, bool write_lock);
SyncVar* GetIfExistsAndLock(uptr addr, bool write_lock);
// If the SyncVar does not exist, returns 0. // If the SyncVar does not exist, returns 0.
SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr); SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr);
@ -96,9 +109,13 @@ class SyncTab {
// FIXME: Implement something more sane. // FIXME: Implement something more sane.
static const int kPartCount = 1009; static const int kPartCount = 1009;
Part tab_[kPartCount]; Part tab_[kPartCount];
atomic_uint64_t uid_gen_;
int PartIdx(uptr addr); int PartIdx(uptr addr);
SyncVar* GetAndLock(ThreadState *thr, uptr pc,
uptr addr, bool write_lock, bool create);
SyncTab(const SyncTab&); // Not implemented. SyncTab(const SyncTab&); // Not implemented.
void operator = (const SyncTab&); // Not implemented. void operator = (const SyncTab&); // Not implemented.
}; };

View File

@ -16,6 +16,7 @@
#include "tsan_defs.h" #include "tsan_defs.h"
#include "tsan_mutex.h" #include "tsan_mutex.h"
#include "tsan_sync.h" #include "tsan_sync.h"
#include "tsan_mutexset.h"
namespace __tsan { namespace __tsan {
@ -43,6 +44,7 @@ typedef u64 Event;
struct TraceHeader { struct TraceHeader {
StackTrace stack0; // Start stack for the trace. StackTrace stack0; // Start stack for the trace.
u64 epoch0; // Start epoch for the trace. u64 epoch0; // Start epoch for the trace.
MutexSet mset0;
#ifndef TSAN_GO #ifndef TSAN_GO
uptr stack0buf[kTraceStackSize]; uptr stack0buf[kTraceStackSize];
#endif #endif

View File

@ -0,0 +1,126 @@
//===-- tsan_mutexset_test.cc ---------------------------------------------===//
//
// 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_mutexset.h"
#include "gtest/gtest.h"
namespace __tsan {
static void Expect(const MutexSet &mset, uptr i, u64 id, bool write, u64 epoch,
int count) {
MutexSet::Desc d = mset.Get(i);
EXPECT_EQ(id, d.id);
EXPECT_EQ(write, d.write);
EXPECT_EQ(epoch, d.epoch);
EXPECT_EQ(count, d.count);
}
TEST(MutexSet, Basic) {
MutexSet mset;
EXPECT_EQ(mset.Size(), (uptr)0);
mset.Add(1, true, 2);
EXPECT_EQ(mset.Size(), (uptr)1);
Expect(mset, 0, 1, true, 2, 1);
mset.Del(1, true);
EXPECT_EQ(mset.Size(), (uptr)0);
mset.Add(3, true, 4);
mset.Add(5, false, 6);
EXPECT_EQ(mset.Size(), (uptr)2);
Expect(mset, 0, 3, true, 4, 1);
Expect(mset, 1, 5, false, 6, 1);
mset.Del(3, true);
EXPECT_EQ(mset.Size(), (uptr)1);
mset.Del(5, false);
EXPECT_EQ(mset.Size(), (uptr)0);
}
TEST(MutexSet, DoubleAdd) {
MutexSet mset;
mset.Add(1, true, 2);
EXPECT_EQ(mset.Size(), (uptr)1);
Expect(mset, 0, 1, true, 2, 1);
mset.Add(1, true, 2);
EXPECT_EQ(mset.Size(), (uptr)1);
Expect(mset, 0, 1, true, 2, 2);
mset.Del(1, true);
EXPECT_EQ(mset.Size(), (uptr)1);
Expect(mset, 0, 1, true, 2, 1);
mset.Del(1, true);
EXPECT_EQ(mset.Size(), (uptr)0);
}
TEST(MutexSet, DoubleDel) {
MutexSet mset;
mset.Add(1, true, 2);
EXPECT_EQ(mset.Size(), (uptr)1);
mset.Del(1, true);
EXPECT_EQ(mset.Size(), (uptr)0);
mset.Del(1, true);
EXPECT_EQ(mset.Size(), (uptr)0);
}
TEST(MutexSet, Remove) {
MutexSet mset;
mset.Add(1, true, 2);
mset.Add(1, true, 2);
mset.Add(3, true, 4);
mset.Add(3, true, 4);
EXPECT_EQ(mset.Size(), (uptr)2);
mset.Remove(1);
EXPECT_EQ(mset.Size(), (uptr)1);
Expect(mset, 0, 3, true, 4, 2);
}
TEST(MutexSet, Full) {
MutexSet mset;
for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
mset.Add(i, true, i + 1);
}
EXPECT_EQ(mset.Size(), MutexSet::kMaxSize);
for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
Expect(mset, i, i, true, i + 1, 1);
}
for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
mset.Add(i, true, i + 1);
}
EXPECT_EQ(mset.Size(), MutexSet::kMaxSize);
for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
Expect(mset, i, i, true, i + 1, 2);
}
}
TEST(MutexSet, Overflow) {
MutexSet mset;
for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
mset.Add(i, true, i + 1);
mset.Add(i, true, i + 1);
}
mset.Add(100, true, 200);
EXPECT_EQ(mset.Size(), MutexSet::kMaxSize);
for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
if (i == 0)
Expect(mset, i, 63, true, 64, 2);
else if (i == MutexSet::kMaxSize - 1)
Expect(mset, i, 100, true, 200, 1);
else
Expect(mset, i, i, true, i + 1, 2);
}
}
} // namespace __tsan

View File

@ -36,7 +36,7 @@ TEST(Sync, Table) {
uintptr_t addr = rand_r(&seed) % (kRange - 1) + 1; uintptr_t addr = rand_r(&seed) % (kRange - 1) + 1;
if (rand_r(&seed) % 2) { if (rand_r(&seed) % 2) {
// Get or add. // Get or add.
SyncVar *v = tab.GetAndLock(thr, pc, addr, true); SyncVar *v = tab.GetOrCreateAndLock(thr, pc, addr, true);
EXPECT_TRUE(golden[addr] == 0 || golden[addr] == v); EXPECT_TRUE(golden[addr] == 0 || golden[addr] == v);
EXPECT_EQ(v->addr, addr); EXPECT_EQ(v->addr, addr);
golden[addr] = v; golden[addr] = v;