diff options
author | Dmitry Vyukov <dvyukov@google.com> | 2017-03-26 15:27:04 +0000 |
---|---|---|
committer | Dmitry Vyukov <dvyukov@google.com> | 2017-03-26 15:27:04 +0000 |
commit | 96ccf181c8c1bf446bae8fc738b3dc8da5a65cfe (patch) | |
tree | f748f9f972ba30415914b0bd3863526a3578b590 /test/tsan | |
parent | 7218fa55e35c76ef0947cb0e9261696dc11e168d (diff) |
tsan: add new mutex annotations
There are several problems with the current annotations (AnnotateRWLockCreate and friends):
- they don't fully support deadlock detection (we need a hook _before_ mutex lock)
- they don't support insertion of random artificial delays to perturb execution (again we need a hook _before_ mutex lock)
- they don't support setting extended mutex attributes like read/write reentrancy (only "linker init" was bolted on)
- they don't support setting mutex attributes if a mutex don't have a "constructor" (e.g. static, Java, Go mutexes)
- they don't ignore synchronization inside of lock/unlock operations which leads to slowdown and false negatives
The new annotations solve of the above problems. See tsan_interface.h for the interface specification and comments.
Reviewed in https://reviews.llvm.org/D31093
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@298809 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/tsan')
-rw-r--r-- | test/tsan/custom_mutex.h | 91 | ||||
-rw-r--r-- | test/tsan/custom_mutex0.cc | 31 | ||||
-rw-r--r-- | test/tsan/custom_mutex1.cc | 39 | ||||
-rw-r--r-- | test/tsan/custom_mutex2.cc | 34 |
4 files changed, 195 insertions, 0 deletions
diff --git a/test/tsan/custom_mutex.h b/test/tsan/custom_mutex.h new file mode 100644 index 000000000..675ad59b3 --- /dev/null +++ b/test/tsan/custom_mutex.h @@ -0,0 +1,91 @@ +#include "test.h" +#include <atomic> +#include <vector> +#include <sanitizer/tsan_interface.h> + +// A very primitive mutex annotated with tsan annotations. +class Mutex { + public: + Mutex(bool prof = true) + : prof_(prof) + , locked_(false) + , seq_(0) { + __tsan_mutex_create(this, 0); + } + + ~Mutex() { + __tsan_mutex_destroy(this, 0); + } + + void Lock() { + __tsan_mutex_pre_lock(this, 0); + LockImpl(); + __tsan_mutex_post_lock(this, 0, 0); + } + + bool TryLock() { + __tsan_mutex_pre_lock(this, __tsan_mutex_try_lock); + bool ok = TryLockImpl(); + __tsan_mutex_post_lock(this, __tsan_mutex_try_lock | + (ok ? 0 : __tsan_mutex_try_lock_failed), 0); + return ok; + } + + void Unlock() { + __tsan_mutex_pre_unlock(this, 0); + UnlockImpl(); + __tsan_mutex_post_unlock(this, 0); + } + + void Wait() { + for (int seq = seq_; seq == seq_;) { + Unlock(); + usleep(100); + Lock(); + } + } + + void Broadcast() { + __tsan_mutex_pre_signal(this, 0); + LockImpl(false); + seq_++; + UnlockImpl(); + __tsan_mutex_post_signal(this, 0); + } + + private: + const bool prof_; + std::atomic<bool> locked_; + int seq_; + + // This models mutex profiling subsystem. + static Mutex prof_mu_; + static int prof_data_; + + void LockImpl(bool prof = true) { + while (!TryLockImpl()) + usleep(100); + if (prof && prof_) + Prof(); + } + + bool TryLockImpl() { + return !locked_.exchange(true); + } + + void UnlockImpl() { + locked_.store(false); + } + + void Prof() { + // This happens inside of mutex lock annotations. + __tsan_mutex_pre_divert(this, 0); + prof_mu_.Lock(); + prof_data_++; + prof_mu_.Unlock(); + __tsan_mutex_post_divert(this, 0); + } +}; + +Mutex Mutex::prof_mu_(false); +int Mutex::prof_data_; diff --git a/test/tsan/custom_mutex0.cc b/test/tsan/custom_mutex0.cc new file mode 100644 index 000000000..4079c72a1 --- /dev/null +++ b/test/tsan/custom_mutex0.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "custom_mutex.h" + +// Test that custom annoations provide normal mutex synchronization +// (no race reports for properly protected critical sections). + +Mutex mu; +long data; + +void *thr(void *arg) { + barrier_wait(&barrier); + mu.Lock(); + data++; + mu.Unlock(); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t th; + pthread_create(&th, 0, thr, 0); + barrier_wait(&barrier); + mu.Lock(); + data++; + mu.Unlock(); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/custom_mutex1.cc b/test/tsan/custom_mutex1.cc new file mode 100644 index 000000000..ec7294ccd --- /dev/null +++ b/test/tsan/custom_mutex1.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "custom_mutex.h" + +// Test that failed TryLock does not induce parasitic synchronization. + +Mutex mu; +long data; + +void *thr(void *arg) { + mu.Lock(); + data++; + mu.Unlock(); + mu.Lock(); + barrier_wait(&barrier); + barrier_wait(&barrier); + mu.Unlock(); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t th; + pthread_create(&th, 0, thr, 0); + barrier_wait(&barrier); + if (mu.TryLock()) { + fprintf(stderr, "TryLock succeeded, should not\n"); + exit(0); + } + data++; + barrier_wait(&barrier); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 8 at {{.*}} by main thread: +// CHECK-NEXT: #0 main {{.*}}custom_mutex1.cc:29 +// CHECK: DONE diff --git a/test/tsan/custom_mutex2.cc b/test/tsan/custom_mutex2.cc new file mode 100644 index 000000000..217b44466 --- /dev/null +++ b/test/tsan/custom_mutex2.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "custom_mutex.h" + +// Test that Broadcast does not induce parasitic synchronization. + +Mutex mu; +long data; + +void *thr(void *arg) { + barrier_wait(&barrier); + mu.Lock(); + data++; + mu.Unlock(); + data++; + mu.Broadcast(); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t th; + pthread_create(&th, 0, thr, 0); + mu.Lock(); + barrier_wait(&barrier); + while (data == 0) + mu.Wait(); + mu.Unlock(); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: ThreadSanitizer: data race +// CHECK: DONE |