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:
parent
dbe7d7e965
commit
fd5ebcd1b0
|
@ -35,9 +35,9 @@ int main() {
|
|||
}
|
||||
|
||||
// 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: #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: #1 Thread1
|
||||
|
|
|
@ -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
|
|
@ -32,9 +32,9 @@ int main() {
|
|||
|
||||
// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
|
||||
// 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: #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: #1 Thread1
|
||||
|
|
|
@ -32,5 +32,5 @@ int main() {
|
|||
// 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:
|
||||
// CHECK: Write of size 1 at [[PTR2]] by thread T2:
|
||||
// CHECK: Previous write of size 4 at [[PTR1]] by thread T1:
|
||||
|
|
|
@ -32,5 +32,5 @@ int main() {
|
|||
// 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:
|
||||
// CHECK: Write of size 4 at [[PTR1]] by thread T1:
|
||||
// CHECK: Previous write of size 1 at [[PTR2]] by thread T2:
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -37,11 +37,11 @@ int main() {
|
|||
// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
|
||||
// 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
|
||||
// CHECK: #1 alloc
|
||||
// CHECK: #2 AllocThread
|
||||
// ...
|
||||
// CHECK: Thread 1 (tid={{.*}}, finished) created at:
|
||||
// CHECK: Thread T1 (tid={{.*}}, finished) created at:
|
||||
// CHECK: #0 pthread_create
|
||||
// CHECK: #1 main
|
||||
|
|
|
@ -34,9 +34,9 @@ int main() {
|
|||
}
|
||||
|
||||
// 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: #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: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}})
|
||||
|
|
|
@ -34,10 +34,10 @@ int main() {
|
|||
}
|
||||
|
||||
// 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: Write of size 4 at {{.*}} by thread T2:
|
||||
// CHECK: Previous write of size 4 at {{.*}} by thread T1:
|
||||
// CHECK: #0 foobar
|
||||
// CHECK: #1 Thread1
|
||||
// CHECK: Thread 1 (tid={{.*}}, finished) created at:
|
||||
// CHECK: Thread T1 (tid={{.*}}, finished) created at:
|
||||
// CHECK: #0 pthread_create
|
||||
// CHECK: #1 main
|
||||
|
|
|
@ -48,19 +48,19 @@ int main() {
|
|||
}
|
||||
|
||||
// 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: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(: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: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(: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: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(: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: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}})
|
||||
// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}})
|
||||
|
|
|
@ -43,7 +43,7 @@ int main() {
|
|||
}
|
||||
|
||||
// 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: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} ({{.*}})
|
||||
// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}})
|
||||
|
|
|
@ -29,6 +29,6 @@ int main() {
|
|||
}
|
||||
|
||||
// CHECK: WARNING: ThreadSanitizer: data race
|
||||
// CHECK: Thread 1 'Thread1'
|
||||
// CHECK: Thread 2 'Thread2'
|
||||
// CHECK: Thread T1 'Thread1'
|
||||
// CHECK: Thread T2 'Thread2'
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
// 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: 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
|
||||
|
|
|
@ -139,6 +139,12 @@ T RoundDown(T p, u64 align) {
|
|||
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 {
|
||||
u64 hash[2];
|
||||
bool operator==(const MD5Hash &other) const;
|
||||
|
|
|
@ -231,7 +231,7 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
|
|||
// Assume the access is atomic.
|
||||
if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(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.acquire(&s->clock);
|
||||
T v = *a;
|
||||
|
@ -253,7 +253,7 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
|
|||
return;
|
||||
}
|
||||
__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.ReleaseStore(&s->clock);
|
||||
*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)>
|
||||
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());
|
||||
if (IsAcqRelOrder(mo))
|
||||
thr->clock.acq_rel(&s->clock);
|
||||
|
@ -324,7 +324,7 @@ template<typename T>
|
|||
static bool AtomicCAS(ThreadState *thr, uptr pc,
|
||||
volatile T *a, T *c, T v, morder mo, morder fmo) {
|
||||
(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());
|
||||
if (IsAcqRelOrder(mo))
|
||||
thr->clock.acq_rel(&s->clock);
|
||||
|
|
|
@ -60,8 +60,9 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
|
|||
void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
|
||||
if (p == 0)
|
||||
return 0;
|
||||
MBlock *b = (MBlock*)allocator()->GetMetaData(p);
|
||||
MBlock *b = new(allocator()->GetMetaData(p)) MBlock;
|
||||
b->size = sz;
|
||||
b->head = 0;
|
||||
b->alloc_tid = thr->unique_id;
|
||||
b->alloc_stack_id = CurrentStackId(thr, pc);
|
||||
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) {
|
||||
MemoryRangeFreed(thr, pc, (uptr)p, b->size);
|
||||
}
|
||||
b->~MBlock();
|
||||
allocator()->Deallocate(&thr->alloc_cache, p);
|
||||
SignalUnsafeCall(thr, pc);
|
||||
}
|
||||
|
|
|
@ -30,12 +30,13 @@ static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
|
|||
/*0 MutexTypeInvalid*/ {},
|
||||
/*1 MutexTypeTrace*/ {MutexTypeLeaf},
|
||||
/*2 MutexTypeThreads*/ {MutexTypeReport},
|
||||
/*3 MutexTypeReport*/ {},
|
||||
/*3 MutexTypeReport*/ {MutexTypeSyncTab, MutexTypeMBlock},
|
||||
/*4 MutexTypeSyncVar*/ {},
|
||||
/*5 MutexTypeSyncTab*/ {MutexTypeSyncVar},
|
||||
/*6 MutexTypeSlab*/ {MutexTypeLeaf},
|
||||
/*7 MutexTypeAnnotations*/ {},
|
||||
/*8 MutexTypeAtExit*/ {MutexTypeSyncTab},
|
||||
/*9 MutexTypeMBlock*/ {MutexTypeSyncVar},
|
||||
};
|
||||
|
||||
static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
|
||||
|
@ -122,6 +123,8 @@ DeadlockDetector::DeadlockDetector() {
|
|||
|
||||
void DeadlockDetector::Lock(MutexType t) {
|
||||
// Printf("LOCK %d @%zu\n", t, seq_ + 1);
|
||||
CHECK_GT(t, MutexTypeInvalid);
|
||||
CHECK_LT(t, MutexTypeCount);
|
||||
u64 max_seq = 0;
|
||||
u64 max_idx = MutexTypeInvalid;
|
||||
for (int i = 0; i != MutexTypeCount; i++) {
|
||||
|
|
|
@ -29,6 +29,7 @@ enum MutexType {
|
|||
MutexTypeSlab,
|
||||
MutexTypeAnnotations,
|
||||
MutexTypeAtExit,
|
||||
MutexTypeMBlock,
|
||||
|
||||
// This must be the last.
|
||||
MutexTypeCount
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -25,6 +25,10 @@ ReportDesc::ReportDesc()
|
|||
, sleep() {
|
||||
}
|
||||
|
||||
ReportMop::ReportMop()
|
||||
: mset(MBlockReportMutex) {
|
||||
}
|
||||
|
||||
ReportDesc::~ReportDesc() {
|
||||
// FIXME(dvyukov): it must be leaking a lot of memory.
|
||||
}
|
||||
|
@ -67,15 +71,27 @@ void PrintStack(const ReportStack *ent) {
|
|||
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) {
|
||||
Printf(" %s of size %d at %p",
|
||||
(first ? (mop->write ? "Write" : "Read")
|
||||
: (mop->write ? "Previous write" : "Previous read")),
|
||||
mop->size, (void*)mop->addr);
|
||||
if (mop->tid == 0)
|
||||
Printf(" by main thread:\n");
|
||||
Printf(" by main thread");
|
||||
else
|
||||
Printf(" by thread %d:\n", mop->tid);
|
||||
Printf(" by thread T%d", mop->tid);
|
||||
PrintMutexSet(mop->mset);
|
||||
Printf(":\n");
|
||||
PrintStack(mop->stack);
|
||||
}
|
||||
|
||||
|
@ -90,24 +106,26 @@ static void PrintLocation(const ReportLocation *loc) {
|
|||
if (loc->tid == 0)
|
||||
Printf(" by main thread:\n");
|
||||
else
|
||||
Printf(" by thread %d:\n", loc->tid);
|
||||
Printf(" by thread T%d:\n", loc->tid);
|
||||
PrintStack(loc->stack);
|
||||
} 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) {
|
||||
if (rm->stack == 0)
|
||||
return;
|
||||
Printf(" Mutex %d created at:\n", rm->id);
|
||||
PrintStack(rm->stack);
|
||||
if (rm->destroyed) {
|
||||
Printf(" Mutex M%llu is already destroyed.\n\n", rm->id);
|
||||
} else {
|
||||
Printf(" Mutex M%llu created at:\n", rm->id);
|
||||
PrintStack(rm->stack);
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintThread(const ReportThread *rt) {
|
||||
if (rt->id == 0) // Little sense in describing the main thread.
|
||||
return;
|
||||
Printf(" Thread %d", rt->id);
|
||||
Printf(" Thread T%d", rt->id);
|
||||
if (rt->name)
|
||||
Printf(" '%s'", rt->name);
|
||||
Printf(" (tid=%zu, %s)", rt->pid, rt->running ? "running" : "finished");
|
||||
|
|
|
@ -38,14 +38,20 @@ struct ReportStack {
|
|||
int col;
|
||||
};
|
||||
|
||||
struct ReportMopMutex {
|
||||
u64 id;
|
||||
bool write;
|
||||
};
|
||||
|
||||
struct ReportMop {
|
||||
int tid;
|
||||
uptr addr;
|
||||
int size;
|
||||
bool write;
|
||||
int nmutex;
|
||||
int *mutex;
|
||||
Vector<ReportMopMutex> mset;
|
||||
ReportStack *stack;
|
||||
|
||||
ReportMop();
|
||||
};
|
||||
|
||||
enum ReportLocationType {
|
||||
|
@ -76,7 +82,8 @@ struct ReportThread {
|
|||
};
|
||||
|
||||
struct ReportMutex {
|
||||
int id;
|
||||
u64 id;
|
||||
bool destroyed;
|
||||
ReportStack *stack;
|
||||
};
|
||||
|
||||
|
|
|
@ -291,6 +291,7 @@ void TraceSwitch(ThreadState *thr) {
|
|||
TraceHeader *hdr = &thr->trace.headers[trace];
|
||||
hdr->epoch0 = thr->fast_state.epoch();
|
||||
hdr->stack0.ObtainCurrent(thr, 0);
|
||||
hdr->mset0 = thr->mset;
|
||||
thr->nomalloc--;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "tsan_vector.h"
|
||||
#include "tsan_report.h"
|
||||
#include "tsan_platform.h"
|
||||
#include "tsan_mutexset.h"
|
||||
|
||||
#if SANITIZER_WORDSIZE != 64
|
||||
# error "ThreadSanitizer is supported only on 64-bit platforms"
|
||||
|
@ -50,6 +51,10 @@ struct MBlock {
|
|||
u32 alloc_tid;
|
||||
u32 alloc_stack_id;
|
||||
SyncVar *head;
|
||||
|
||||
MBlock()
|
||||
: mtx(MutexTypeMBlock, StatMtxMBlock) {
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef TSAN_GO
|
||||
|
@ -300,6 +305,7 @@ struct ThreadState {
|
|||
uptr *shadow_stack;
|
||||
uptr *shadow_stack_end;
|
||||
#endif
|
||||
MutexSet mset;
|
||||
ThreadClock clock;
|
||||
#ifndef TSAN_GO
|
||||
AllocatorCache alloc_cache;
|
||||
|
@ -447,7 +453,8 @@ class ScopedReport {
|
|||
~ScopedReport();
|
||||
|
||||
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 AddMutex(const SyncVar *s);
|
||||
void AddLocation(uptr addr, uptr size);
|
||||
|
@ -459,11 +466,13 @@ class ScopedReport {
|
|||
Context *ctx_;
|
||||
ReportDesc *rep_;
|
||||
|
||||
void AddMutex(u64 id);
|
||||
|
||||
ScopedReport(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 StatOutput(u64 *stat);
|
||||
|
@ -577,7 +586,10 @@ uptr TraceParts();
|
|||
|
||||
extern "C" void __tsan_trace_switch();
|
||||
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);
|
||||
u64 pos = fs.GetTracePos();
|
||||
if (UNLIKELY((pos % kTracePartSize) == 0)) {
|
||||
|
|
|
@ -28,7 +28,7 @@ void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
|
|||
StatInc(thr, StatMutexCreate);
|
||||
if (!linker_init && IsAppMem(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_recursive = recursive;
|
||||
s->is_linker_init = linker_init;
|
||||
|
@ -61,11 +61,12 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
|
|||
trace.ObtainCurrent(thr, pc);
|
||||
rep.AddStack(&trace);
|
||||
FastState last(s->last_lock);
|
||||
RestoreStack(last.tid(), last.epoch(), &trace);
|
||||
RestoreStack(last.tid(), last.epoch(), &trace, 0);
|
||||
rep.AddStack(&trace);
|
||||
rep.AddLocation(s->addr, 1);
|
||||
OutputReport(ctx, rep);
|
||||
}
|
||||
thr->mset.Remove(s->GetId());
|
||||
DestroyAndFree(s);
|
||||
}
|
||||
|
||||
|
@ -74,9 +75,9 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
|
|||
DPrintf("#%d: MutexLock %zx\n", thr->tid, addr);
|
||||
if (IsAppMem(addr))
|
||||
MemoryRead1Byte(thr, pc, addr);
|
||||
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
|
||||
thr->fast_state.IncrementEpoch();
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeLock, addr);
|
||||
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
|
||||
if (s->owner_tid == SyncVar::kInvalidTid) {
|
||||
CHECK_EQ(s->recursion, 0);
|
||||
s->owner_tid = thr->tid;
|
||||
|
@ -98,6 +99,7 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
|
|||
StatInc(thr, StatMutexRecLock);
|
||||
}
|
||||
s->recursion++;
|
||||
thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
|
||||
s->mtx.Unlock();
|
||||
}
|
||||
|
||||
|
@ -106,9 +108,9 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
|
|||
DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr);
|
||||
if (IsAppMem(addr))
|
||||
MemoryRead1Byte(thr, pc, addr);
|
||||
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
|
||||
thr->fast_state.IncrementEpoch();
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, addr);
|
||||
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
|
||||
if (s->recursion == 0) {
|
||||
if (!s->is_broken) {
|
||||
s->is_broken = true;
|
||||
|
@ -134,6 +136,7 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
|
|||
StatInc(thr, StatMutexRecUnlock);
|
||||
}
|
||||
}
|
||||
thr->mset.Del(s->GetId(), true);
|
||||
s->mtx.Unlock();
|
||||
}
|
||||
|
||||
|
@ -143,9 +146,9 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
|
|||
StatInc(thr, StatMutexReadLock);
|
||||
if (IsAppMem(addr))
|
||||
MemoryRead1Byte(thr, pc, addr);
|
||||
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
|
||||
thr->fast_state.IncrementEpoch();
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeRLock, addr);
|
||||
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
|
||||
if (s->owner_tid != SyncVar::kInvalidTid) {
|
||||
Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
|
||||
PrintCurrentStack(thr, pc);
|
||||
|
@ -154,6 +157,7 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
|
|||
thr->clock.acquire(&s->clock);
|
||||
s->last_lock = thr->fast_state.raw();
|
||||
StatInc(thr, StatSyncAcquire);
|
||||
thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
|
||||
s->mtx.ReadUnlock();
|
||||
}
|
||||
|
||||
|
@ -163,9 +167,9 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
|
|||
StatInc(thr, StatMutexReadUnlock);
|
||||
if (IsAppMem(addr))
|
||||
MemoryRead1Byte(thr, pc, addr);
|
||||
SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
|
||||
thr->fast_state.IncrementEpoch();
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, addr);
|
||||
SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
|
||||
if (s->owner_tid != SyncVar::kInvalidTid) {
|
||||
Printf("ThreadSanitizer WARNING: read unlock of a write "
|
||||
"locked mutex\n");
|
||||
|
@ -176,6 +180,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
|
|||
thr->clock.release(&s->read_clock);
|
||||
StatInc(thr, StatSyncRelease);
|
||||
s->mtx.Unlock();
|
||||
thr->mset.Del(s->GetId(), false);
|
||||
}
|
||||
|
||||
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);
|
||||
if (IsAppMem(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) {
|
||||
// Seems to be read unlock.
|
||||
write = false;
|
||||
StatInc(thr, StatMutexReadUnlock);
|
||||
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->fast_synch_epoch = thr->fast_state.epoch();
|
||||
thr->clock.release(&s->read_clock);
|
||||
StatInc(thr, StatSyncRelease);
|
||||
} else if (s->owner_tid == thr->tid) {
|
||||
// Seems to be write unlock.
|
||||
thr->fast_state.IncrementEpoch();
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
|
||||
CHECK_GT(s->recursion, 0);
|
||||
s->recursion--;
|
||||
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.
|
||||
// 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.
|
||||
thr->fast_state.IncrementEpoch();
|
||||
TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, addr);
|
||||
thr->clock.set(thr->tid, thr->fast_state.epoch());
|
||||
thr->fast_synch_epoch = thr->fast_state.epoch();
|
||||
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");
|
||||
PrintCurrentStack(thr, pc);
|
||||
}
|
||||
thr->mset.Del(s->GetId(), write);
|
||||
s->mtx.Unlock();
|
||||
}
|
||||
|
||||
void Acquire(ThreadState *thr, uptr pc, uptr addr) {
|
||||
CHECK_GT(thr->in_rtl, 0);
|
||||
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.acquire(&s->clock);
|
||||
StatInc(thr, StatSyncAcquire);
|
||||
|
@ -248,7 +256,7 @@ void AcquireGlobal(ThreadState *thr, uptr pc) {
|
|||
void Release(ThreadState *thr, uptr pc, uptr addr) {
|
||||
CHECK_GT(thr->in_rtl, 0);
|
||||
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.release(&s->clock);
|
||||
StatInc(thr, StatSyncRelease);
|
||||
|
@ -258,7 +266,7 @@ void Release(ThreadState *thr, uptr pc, uptr addr) {
|
|||
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
|
||||
CHECK_GT(thr->in_rtl, 0);
|
||||
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.ReleaseStore(&s->clock);
|
||||
StatInc(thr, StatSyncRelease);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "tsan_platform.h"
|
||||
#include "tsan_rtl.h"
|
||||
#include "tsan_suppressions.h"
|
||||
|
@ -25,6 +26,8 @@
|
|||
|
||||
namespace __tsan {
|
||||
|
||||
using namespace __sanitizer; // NOLINT
|
||||
|
||||
void TsanCheckFailed(const char *file, int line, const char *cond,
|
||||
u64 v1, u64 v2) {
|
||||
ScopedInRtl in_rtl;
|
||||
|
@ -134,7 +137,7 @@ void ScopedReport::AddStack(const StackTrace *stack) {
|
|||
}
|
||||
|
||||
void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
|
||||
const StackTrace *stack) {
|
||||
const StackTrace *stack, const MutexSet *mset) {
|
||||
void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
|
||||
ReportMop *mop = new(mem) ReportMop;
|
||||
rep_->mops.PushBack(mop);
|
||||
|
@ -142,8 +145,27 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
|
|||
mop->addr = addr + s.addr0();
|
||||
mop->size = s.size();
|
||||
mop->write = s.is_write();
|
||||
mop->nmutex = 0;
|
||||
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) {
|
||||
|
@ -175,13 +197,31 @@ static ThreadContext *FindThread(int unique_id) {
|
|||
#endif
|
||||
|
||||
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));
|
||||
ReportMutex *rm = new(mem) ReportMutex();
|
||||
rep_->mutexes.PushBack(rm);
|
||||
rm->id = 42;
|
||||
rm->id = s->uid;
|
||||
rm->destroyed = false;
|
||||
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) {
|
||||
if (addr == 0)
|
||||
return;
|
||||
|
@ -248,7 +288,10 @@ const ReportDesc *ScopedReport::GetReport() const {
|
|||
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];
|
||||
if (tctx == 0)
|
||||
return;
|
||||
|
@ -269,6 +312,7 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
|
|||
TraceHeader* hdr = &trace->headers[partidx];
|
||||
if (epoch < hdr->epoch0)
|
||||
return;
|
||||
const u64 epoch0 = RoundDown(epoch, TraceSize());
|
||||
const u64 eend = epoch % TraceSize();
|
||||
const u64 ebegin = RoundDown(eend, kTracePartSize);
|
||||
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);
|
||||
DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]);
|
||||
}
|
||||
if (mset)
|
||||
*mset = hdr->mset0;
|
||||
uptr pos = hdr->stack0.Size();
|
||||
Event *events = (Event*)GetThreadTrace(tid);
|
||||
for (uptr i = ebegin; i <= eend; i++) {
|
||||
Event ev = events[i];
|
||||
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);
|
||||
if (typ == EventTypeMop) {
|
||||
stack[pos] = pc;
|
||||
|
@ -293,6 +339,17 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
|
|||
if (pos > 0)
|
||||
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++)
|
||||
DPrintf2(" #%zu: %zx\n", j, stack[j]);
|
||||
}
|
||||
|
@ -456,15 +513,18 @@ void ReportRace(ThreadState *thr) {
|
|||
traces[0].ObtainCurrent(thr, toppc);
|
||||
if (IsFiredSuppression(ctx, rep, traces[0]))
|
||||
return;
|
||||
InternalScopedBuffer<MutexSet> mset2(1);
|
||||
new(mset2.data()) MutexSet();
|
||||
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))
|
||||
return;
|
||||
|
||||
for (uptr i = 0; i < kMop; 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()))
|
||||
|
|
|
@ -305,6 +305,7 @@ void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
|
|||
Printf("ThreadSanitizer: join of non-existent thread\n");
|
||||
return;
|
||||
}
|
||||
// FIXME(dvyukov): print message and continue (it's user error).
|
||||
CHECK_EQ(tctx->detached, false);
|
||||
CHECK_EQ(tctx->status, ThreadStatusFinished);
|
||||
thr->clock.acquire(&tctx->sync);
|
||||
|
|
|
@ -253,6 +253,7 @@ void StatOutput(u64 *stat) {
|
|||
name[StatMtxSlab] = " Slab ";
|
||||
name[StatMtxAtExit] = " Atexit ";
|
||||
name[StatMtxAnnotations] = " Annotations ";
|
||||
name[StatMtxMBlock] = " MBlock ";
|
||||
|
||||
Printf("Statistics:\n");
|
||||
for (int i = 0; i < StatCnt; i++)
|
||||
|
|
|
@ -255,6 +255,7 @@ enum StatType {
|
|||
StatMtxSlab,
|
||||
StatMtxAnnotations,
|
||||
StatMtxAtExit,
|
||||
StatMtxMBlock,
|
||||
|
||||
// This must be the last.
|
||||
StatCnt
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
|
||||
namespace __tsan {
|
||||
|
||||
SyncVar::SyncVar(uptr addr)
|
||||
SyncVar::SyncVar(uptr addr, u64 uid)
|
||||
: mtx(MutexTypeSyncVar, StatMtxSyncVar)
|
||||
, addr(addr)
|
||||
, uid(uid)
|
||||
, owner_tid(kInvalidTid)
|
||||
, last_lock()
|
||||
, recursion()
|
||||
|
@ -47,8 +48,17 @@ SyncTab::~SyncTab() {
|
|||
}
|
||||
}
|
||||
|
||||
SyncVar* SyncTab::GetOrCreateAndLock(ThreadState *thr, uptr pc,
|
||||
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) {
|
||||
uptr addr, bool write_lock, bool create) {
|
||||
#ifndef TSAN_GO
|
||||
if (PrimaryAllocator::PointerIsMine((void*)addr)) {
|
||||
MBlock *b = user_mblock(thr, (void*)addr);
|
||||
|
@ -59,9 +69,12 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
|
|||
break;
|
||||
}
|
||||
if (res == 0) {
|
||||
if (!create)
|
||||
return 0;
|
||||
StatInc(thr, StatSyncCreated);
|
||||
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->next = b->head;
|
||||
b->head = res;
|
||||
|
@ -87,6 +100,8 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!create)
|
||||
return 0;
|
||||
{
|
||||
Lock l(&p->mtx);
|
||||
SyncVar *res = p->val;
|
||||
|
@ -97,7 +112,8 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
|
|||
if (res == 0) {
|
||||
StatInc(thr, StatSyncCreated);
|
||||
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
|
||||
res->creation_stack.ObtainCurrent(thr, pc);
|
||||
#endif
|
||||
|
|
|
@ -50,12 +50,13 @@ class StackTrace {
|
|||
};
|
||||
|
||||
struct SyncVar {
|
||||
explicit SyncVar(uptr addr);
|
||||
explicit SyncVar(uptr addr, u64 uid);
|
||||
|
||||
static const int kInvalidTid = -1;
|
||||
|
||||
Mutex mtx;
|
||||
const uptr addr;
|
||||
const u64 uid; // Globally unique id.
|
||||
SyncClock clock;
|
||||
SyncClock read_clock; // Used for rw mutexes only.
|
||||
StackTrace creation_stack;
|
||||
|
@ -69,6 +70,18 @@ struct SyncVar {
|
|||
SyncVar *next; // In SyncTab hashtable.
|
||||
|
||||
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 {
|
||||
|
@ -76,9 +89,9 @@ class SyncTab {
|
|||
SyncTab();
|
||||
~SyncTab();
|
||||
|
||||
// If the SyncVar does not exist yet, it is created.
|
||||
SyncVar* GetAndLock(ThreadState *thr, uptr pc,
|
||||
uptr addr, bool write_lock);
|
||||
SyncVar* GetOrCreateAndLock(ThreadState *thr, uptr pc,
|
||||
uptr addr, bool write_lock);
|
||||
SyncVar* GetIfExistsAndLock(uptr addr, bool write_lock);
|
||||
|
||||
// If the SyncVar does not exist, returns 0.
|
||||
SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr);
|
||||
|
@ -96,9 +109,13 @@ class SyncTab {
|
|||
// FIXME: Implement something more sane.
|
||||
static const int kPartCount = 1009;
|
||||
Part tab_[kPartCount];
|
||||
atomic_uint64_t uid_gen_;
|
||||
|
||||
int PartIdx(uptr addr);
|
||||
|
||||
SyncVar* GetAndLock(ThreadState *thr, uptr pc,
|
||||
uptr addr, bool write_lock, bool create);
|
||||
|
||||
SyncTab(const SyncTab&); // Not implemented.
|
||||
void operator = (const SyncTab&); // Not implemented.
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "tsan_defs.h"
|
||||
#include "tsan_mutex.h"
|
||||
#include "tsan_sync.h"
|
||||
#include "tsan_mutexset.h"
|
||||
|
||||
namespace __tsan {
|
||||
|
||||
|
@ -43,6 +44,7 @@ typedef u64 Event;
|
|||
struct TraceHeader {
|
||||
StackTrace stack0; // Start stack for the trace.
|
||||
u64 epoch0; // Start epoch for the trace.
|
||||
MutexSet mset0;
|
||||
#ifndef TSAN_GO
|
||||
uptr stack0buf[kTraceStackSize];
|
||||
#endif
|
||||
|
|
|
@ -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
|
|
@ -36,7 +36,7 @@ TEST(Sync, Table) {
|
|||
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);
|
||||
SyncVar *v = tab.GetOrCreateAndLock(thr, pc, addr, true);
|
||||
EXPECT_TRUE(golden[addr] == 0 || golden[addr] == v);
|
||||
EXPECT_EQ(v->addr, addr);
|
||||
golden[addr] = v;
|
||||
|
|
Loading…
Reference in New Issue