diff --git a/compiler-rt/lib/tsan/lit_tests/free_race.c b/compiler-rt/lib/tsan/lit_tests/free_race.c index 9200c3bb35f5..12524711ab6e 100644 --- a/compiler-rt/lib/tsan/lit_tests/free_race.c +++ b/compiler-rt/lib/tsan/lit_tests/free_race.c @@ -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 diff --git a/compiler-rt/lib/tsan/lit_tests/ignore_race.cc b/compiler-rt/lib/tsan/lit_tests/ignore_race.cc new file mode 100644 index 000000000000..156f240882af --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/ignore_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +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 diff --git a/compiler-rt/lib/tsan/lit_tests/memcpy_race.cc b/compiler-rt/lib/tsan/lit_tests/memcpy_race.cc index c87bc9c9a919..a091df56301d 100644 --- a/compiler-rt/lib/tsan/lit_tests/memcpy_race.cc +++ b/compiler-rt/lib/tsan/lit_tests/memcpy_race.cc @@ -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 diff --git a/compiler-rt/lib/tsan/lit_tests/mop_with_offset.cc b/compiler-rt/lib/tsan/lit_tests/mop_with_offset.cc index 14ece1a73d90..198afc685478 100644 --- a/compiler-rt/lib/tsan/lit_tests/mop_with_offset.cc +++ b/compiler-rt/lib/tsan/lit_tests/mop_with_offset.cc @@ -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: diff --git a/compiler-rt/lib/tsan/lit_tests/mop_with_offset2.cc b/compiler-rt/lib/tsan/lit_tests/mop_with_offset2.cc index 2a6fde730682..3cb3424555cf 100644 --- a/compiler-rt/lib/tsan/lit_tests/mop_with_offset2.cc +++ b/compiler-rt/lib/tsan/lit_tests/mop_with_offset2.cc @@ -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: diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset1.cc b/compiler-rt/lib/tsan/lit_tests/mutexset1.cc new file mode 100644 index 000000000000..1140e545db2a --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/mutexset1.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +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 + diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset2.cc b/compiler-rt/lib/tsan/lit_tests/mutexset2.cc new file mode 100644 index 000000000000..35f4d899a8ab --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/mutexset2.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +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 + diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset3.cc b/compiler-rt/lib/tsan/lit_tests/mutexset3.cc new file mode 100644 index 000000000000..652dff0d43e8 --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/mutexset3.cc @@ -0,0 +1,46 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +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 + diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset4.cc b/compiler-rt/lib/tsan/lit_tests/mutexset4.cc new file mode 100644 index 000000000000..06bf48f55548 --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/mutexset4.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +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 + diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset5.cc b/compiler-rt/lib/tsan/lit_tests/mutexset5.cc new file mode 100644 index 000000000000..c3c4cd22b691 --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/mutexset5.cc @@ -0,0 +1,46 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +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 + diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset6.cc b/compiler-rt/lib/tsan/lit_tests/mutexset6.cc new file mode 100644 index 000000000000..e25088c2ab5f --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/mutexset6.cc @@ -0,0 +1,54 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +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 + diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset7.cc b/compiler-rt/lib/tsan/lit_tests/mutexset7.cc new file mode 100644 index 000000000000..2516928bafa0 --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/mutexset7.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +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 + diff --git a/compiler-rt/lib/tsan/lit_tests/race_on_heap.cc b/compiler-rt/lib/tsan/lit_tests/race_on_heap.cc index 855c309a955b..62987bf95004 100644 --- a/compiler-rt/lib/tsan/lit_tests/race_on_heap.cc +++ b/compiler-rt/lib/tsan/lit_tests/race_on_heap.cc @@ -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 diff --git a/compiler-rt/lib/tsan/lit_tests/race_on_mutex.c b/compiler-rt/lib/tsan/lit_tests/race_on_mutex.c index 6c6697ddf728..95b4604abc04 100644 --- a/compiler-rt/lib/tsan/lit_tests/race_on_mutex.c +++ b/compiler-rt/lib/tsan/lit_tests/race_on_mutex.c @@ -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)?}} ({{.*}}) diff --git a/compiler-rt/lib/tsan/lit_tests/race_with_finished_thread.cc b/compiler-rt/lib/tsan/lit_tests/race_with_finished_thread.cc index 4008ecd66c03..1d37b934948d 100644 --- a/compiler-rt/lib/tsan/lit_tests/race_with_finished_thread.cc +++ b/compiler-rt/lib/tsan/lit_tests/race_with_finished_thread.cc @@ -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 diff --git a/compiler-rt/lib/tsan/lit_tests/simple_stack.c b/compiler-rt/lib/tsan/lit_tests/simple_stack.c index b1309573537a..322e15e40897 100644 --- a/compiler-rt/lib/tsan/lit_tests/simple_stack.c +++ b/compiler-rt/lib/tsan/lit_tests/simple_stack.c @@ -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)?}} ({{.*}}) diff --git a/compiler-rt/lib/tsan/lit_tests/simple_stack2.cc b/compiler-rt/lib/tsan/lit_tests/simple_stack2.cc index ed95c684b367..c1a82a3d2545 100644 --- a/compiler-rt/lib/tsan/lit_tests/simple_stack2.cc +++ b/compiler-rt/lib/tsan/lit_tests/simple_stack2.cc @@ -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)?}} ({{.*}}) diff --git a/compiler-rt/lib/tsan/lit_tests/thread_name.cc b/compiler-rt/lib/tsan/lit_tests/thread_name.cc index 76ca99390c71..dcb1d639487f 100644 --- a/compiler-rt/lib/tsan/lit_tests/thread_name.cc +++ b/compiler-rt/lib/tsan/lit_tests/thread_name.cc @@ -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' diff --git a/compiler-rt/lib/tsan/lit_tests/write_in_reader_lock.cc b/compiler-rt/lib/tsan/lit_tests/write_in_reader_lock.cc index ce3152dfb98d..f22c8d7233d8 100644 --- a/compiler-rt/lib/tsan/lit_tests/write_in_reader_lock.cc +++ b/compiler-rt/lib/tsan/lit_tests/write_in_reader_lock.cc @@ -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 diff --git a/compiler-rt/lib/tsan/rtl/tsan_defs.h b/compiler-rt/lib/tsan/rtl/tsan_defs.h index 3a530041d600..e0c04733f0a3 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_defs.h +++ b/compiler-rt/lib/tsan/rtl/tsan_defs.h @@ -139,6 +139,12 @@ T RoundDown(T p, u64 align) { return (T)((u64)p & ~(align - 1)); } +// Zeroizes high part, returns 'bits' lsb bits. +template +T GetLsb(T v, int bits) { + return (T)((u64)v & ((1ull << bits) - 1)); +} + struct MD5Hash { u64 hash[2]; bool operator==(const MD5Hash &other) const; diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cc b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cc index 26cf5b4f6860..a9d75e5bf76c 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cc @@ -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 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 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); diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cc b/compiler-rt/lib/tsan/rtl/tsan_mman.cc index fcc300071094..b7e3c76454a5 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_mman.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cc @@ -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); } diff --git a/compiler-rt/lib/tsan/rtl/tsan_mutex.cc b/compiler-rt/lib/tsan/rtl/tsan_mutex.cc index dde97f4f002e..ca9b1083826a 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_mutex.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_mutex.cc @@ -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++) { diff --git a/compiler-rt/lib/tsan/rtl/tsan_mutex.h b/compiler-rt/lib/tsan/rtl/tsan_mutex.h index d74bfd850e9c..68b33a7f837f 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_mutex.h +++ b/compiler-rt/lib/tsan/rtl/tsan_mutex.h @@ -29,6 +29,7 @@ enum MutexType { MutexTypeSlab, MutexTypeAnnotations, MutexTypeAtExit, + MutexTypeMBlock, // This must be the last. MutexTypeCount diff --git a/compiler-rt/lib/tsan/rtl/tsan_mutexset.cc b/compiler-rt/lib/tsan/rtl/tsan_mutexset.cc new file mode 100644 index 000000000000..21587770f687 --- /dev/null +++ b/compiler-rt/lib/tsan/rtl/tsan_mutexset.cc @@ -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 diff --git a/compiler-rt/lib/tsan/rtl/tsan_mutexset.h b/compiler-rt/lib/tsan/rtl/tsan_mutexset.h new file mode 100644 index 000000000000..09223ff6cc48 --- /dev/null +++ b/compiler-rt/lib/tsan/rtl/tsan_mutexset.h @@ -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 diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cc b/compiler-rt/lib/tsan/rtl/tsan_report.cc index 3324320aa243..af8235a1a02f 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_report.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_report.cc @@ -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 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"); diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.h b/compiler-rt/lib/tsan/rtl/tsan_report.h index 73f1c4432050..2c3667e41c39 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_report.h +++ b/compiler-rt/lib/tsan/rtl/tsan_report.h @@ -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 mset; ReportStack *stack; + + ReportMop(); }; enum ReportLocationType { @@ -76,7 +82,8 @@ struct ReportThread { }; struct ReportMutex { - int id; + u64 id; + bool destroyed; ReportStack *stack; }; diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl.cc index 17e7bd53c764..3732bddbe3e6 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cc @@ -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--; } diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h index 7754b744dacc..5d7428695c04 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h @@ -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)) { diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc index 7c04ab0fc182..e5c61d0070ef 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -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); diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc index 488019fa2c35..c953091ed34b 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc @@ -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 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())) diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc index 7d80066257af..2277a08e41e3 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc @@ -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); diff --git a/compiler-rt/lib/tsan/rtl/tsan_stat.cc b/compiler-rt/lib/tsan/rtl/tsan_stat.cc index da7a02caa4c3..c16819f8c34b 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_stat.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_stat.cc @@ -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++) diff --git a/compiler-rt/lib/tsan/rtl/tsan_stat.h b/compiler-rt/lib/tsan/rtl/tsan_stat.h index 8e59d803d270..d10580e7e928 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_stat.h +++ b/compiler-rt/lib/tsan/rtl/tsan_stat.h @@ -255,6 +255,7 @@ enum StatType { StatMtxSlab, StatMtxAnnotations, StatMtxAtExit, + StatMtxMBlock, // This must be the last. StatCnt diff --git a/compiler-rt/lib/tsan/rtl/tsan_sync.cc b/compiler-rt/lib/tsan/rtl/tsan_sync.cc index 642d1b2ea496..38ecc6e8c5f9 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_sync.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_sync.cc @@ -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 diff --git a/compiler-rt/lib/tsan/rtl/tsan_sync.h b/compiler-rt/lib/tsan/rtl/tsan_sync.h index 89de81d54d8a..34ea55bd8215 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_sync.h +++ b/compiler-rt/lib/tsan/rtl/tsan_sync.h @@ -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. }; diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h index 921f7d05923c..7df716046567 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_trace.h +++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h @@ -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 diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc new file mode 100644 index 000000000000..da1ae2e49e0c --- /dev/null +++ b/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc @@ -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 diff --git a/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc b/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc index b7605a57a331..dddf0b290883 100644 --- a/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc +++ b/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc @@ -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;