tsan: add mutexsets to reports
authorDmitry Vyukov <dvyukov@google.com>
Thu, 6 Dec 2012 12:16:15 +0000 (12:16 +0000)
committerDmitry Vyukov <dvyukov@google.com>
Thu, 6 Dec 2012 12:16:15 +0000 (12:16 +0000)
With this change reports say what mutexes the threads hold around the racy memory accesses.

llvm-svn: 169493

40 files changed:
compiler-rt/lib/tsan/lit_tests/free_race.c
compiler-rt/lib/tsan/lit_tests/ignore_race.cc [new file with mode: 0644]
compiler-rt/lib/tsan/lit_tests/memcpy_race.cc
compiler-rt/lib/tsan/lit_tests/mop_with_offset.cc
compiler-rt/lib/tsan/lit_tests/mop_with_offset2.cc
compiler-rt/lib/tsan/lit_tests/mutexset1.cc [new file with mode: 0644]
compiler-rt/lib/tsan/lit_tests/mutexset2.cc [new file with mode: 0644]
compiler-rt/lib/tsan/lit_tests/mutexset3.cc [new file with mode: 0644]
compiler-rt/lib/tsan/lit_tests/mutexset4.cc [new file with mode: 0644]
compiler-rt/lib/tsan/lit_tests/mutexset5.cc [new file with mode: 0644]
compiler-rt/lib/tsan/lit_tests/mutexset6.cc [new file with mode: 0644]
compiler-rt/lib/tsan/lit_tests/mutexset7.cc [new file with mode: 0644]
compiler-rt/lib/tsan/lit_tests/race_on_heap.cc
compiler-rt/lib/tsan/lit_tests/race_on_mutex.c
compiler-rt/lib/tsan/lit_tests/race_with_finished_thread.cc
compiler-rt/lib/tsan/lit_tests/simple_stack.c
compiler-rt/lib/tsan/lit_tests/simple_stack2.cc
compiler-rt/lib/tsan/lit_tests/thread_name.cc
compiler-rt/lib/tsan/lit_tests/write_in_reader_lock.cc
compiler-rt/lib/tsan/rtl/tsan_defs.h
compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cc
compiler-rt/lib/tsan/rtl/tsan_mman.cc
compiler-rt/lib/tsan/rtl/tsan_mutex.cc
compiler-rt/lib/tsan/rtl/tsan_mutex.h
compiler-rt/lib/tsan/rtl/tsan_mutexset.cc [new file with mode: 0644]
compiler-rt/lib/tsan/rtl/tsan_mutexset.h [new file with mode: 0644]
compiler-rt/lib/tsan/rtl/tsan_report.cc
compiler-rt/lib/tsan/rtl/tsan_report.h
compiler-rt/lib/tsan/rtl/tsan_rtl.cc
compiler-rt/lib/tsan/rtl/tsan_rtl.h
compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc
compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc
compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc
compiler-rt/lib/tsan/rtl/tsan_stat.cc
compiler-rt/lib/tsan/rtl/tsan_stat.h
compiler-rt/lib/tsan/rtl/tsan_sync.cc
compiler-rt/lib/tsan/rtl/tsan_sync.h
compiler-rt/lib/tsan/rtl/tsan_trace.h
compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc [new file with mode: 0644]
compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc

index 9200c3b..1252471 100644 (file)
@@ -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 (file)
index 0000000..156f240
--- /dev/null
@@ -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
index c87bc9c..a091df5 100644 (file)
@@ -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
index 14ece1a..198afc6 100644 (file)
@@ -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:
index 2a6fde7..3cb3424 100644 (file)
@@ -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 (file)
index 0000000..1140e54
--- /dev/null
@@ -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
+
diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset2.cc b/compiler-rt/lib/tsan/lit_tests/mutexset2.cc
new file mode 100644 (file)
index 0000000..35f4d89
--- /dev/null
@@ -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
+
diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset3.cc b/compiler-rt/lib/tsan/lit_tests/mutexset3.cc
new file mode 100644 (file)
index 0000000..652dff0
--- /dev/null
@@ -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
+
diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset4.cc b/compiler-rt/lib/tsan/lit_tests/mutexset4.cc
new file mode 100644 (file)
index 0000000..06bf48f
--- /dev/null
@@ -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
+
diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset5.cc b/compiler-rt/lib/tsan/lit_tests/mutexset5.cc
new file mode 100644 (file)
index 0000000..c3c4cd2
--- /dev/null
@@ -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
+
diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset6.cc b/compiler-rt/lib/tsan/lit_tests/mutexset6.cc
new file mode 100644 (file)
index 0000000..e25088c
--- /dev/null
@@ -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
+
diff --git a/compiler-rt/lib/tsan/lit_tests/mutexset7.cc b/compiler-rt/lib/tsan/lit_tests/mutexset7.cc
new file mode 100644 (file)
index 0000000..2516928
--- /dev/null
@@ -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
+
index 855c309..62987bf 100644 (file)
@@ -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
index 6c6697d..95b4604 100644 (file)
@@ -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)?}} ({{.*}})
index 4008ecd..1d37b93 100644 (file)
@@ -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
index b130957..322e15e 100644 (file)
@@ -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)?}} ({{.*}})
index ed95c68..c1a82a3 100644 (file)
@@ -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)?}} ({{.*}})
index 76ca993..dcb1d63 100644 (file)
@@ -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'
 
index ce3152d..f22c8d7 100644 (file)
@@ -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
index 3a53004..e0c0473 100644 (file)
@@ -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;
index 26cf5b4..a9d75e5 100644 (file)
@@ -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);
index fcc3000..b7e3c76 100644 (file)
@@ -60,8 +60,9 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
   void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
   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);
 }
index dde97f4..ca9b108 100644 (file)
@@ -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++) {
index d74bfd8..68b33a7 100644 (file)
@@ -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 (file)
index 0000000..2158777
--- /dev/null
@@ -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 (file)
index 0000000..09223ff
--- /dev/null
@@ -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
index 3324320..af8235a 100644 (file)
@@ -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");
index 73f1c44..2c3667e 100644 (file)
@@ -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;
 };
 
index 17e7bd5..3732bdd 100644 (file)
@@ -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--;
 }
 
index 7754b74..5d74286 100644 (file)
@@ -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)) {
index 7c04ab0..e5c61d0 100644 (file)
@@ -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);
index 488019f..c953091 100644 (file)
@@ -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()))
index 7d80066..2277a08 100644 (file)
@@ -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);
index da7a02c..c16819f 100644 (file)
@@ -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++)
index 8e59d80..d10580e 100644 (file)
@@ -255,6 +255,7 @@ enum StatType {
   StatMtxSlab,
   StatMtxAnnotations,
   StatMtxAtExit,
+  StatMtxMBlock,
 
   // This must be the last.
   StatCnt
index 642d1b2..38ecc6e 100644 (file)
 
 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
index 89de81d..34ea55b 100644 (file)
@@ -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.
 };
index 921f7d0..7df7160 100644 (file)
@@ -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 (file)
index 0000000..da1ae2e
--- /dev/null
@@ -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
index b7605a5..dddf0b2 100644 (file)
@@ -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;