[tsan] rudimentary support for deadlock detector in tsan (nothing really works yet except for a single tiny test). Also rename tsan's DeadlockDetector to InternalDeadlockDetector
llvm-svn: 201407
This commit is contained in:
parent
127e93e4dc
commit
a63632a5c6
|
@ -0,0 +1,25 @@
|
|||
// RUN: %clangxx_tsan %s -o %t
|
||||
// RUN: TSAN_OPTIONS=detect_deadlocks=1 %t 2>&1 | FileCheck %s
|
||||
#include <pthread.h>
|
||||
|
||||
int main() {
|
||||
pthread_mutex_t mu1, mu2;
|
||||
pthread_mutex_init(&mu1, NULL);
|
||||
pthread_mutex_init(&mu2, NULL);
|
||||
|
||||
// mu1 => mu2
|
||||
pthread_mutex_lock(&mu1);
|
||||
pthread_mutex_lock(&mu2);
|
||||
pthread_mutex_unlock(&mu2);
|
||||
pthread_mutex_unlock(&mu1);
|
||||
|
||||
// mu2 => mu1
|
||||
pthread_mutex_lock(&mu2);
|
||||
pthread_mutex_lock(&mu1);
|
||||
// CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock)
|
||||
pthread_mutex_unlock(&mu1);
|
||||
pthread_mutex_unlock(&mu2);
|
||||
|
||||
pthread_mutex_destroy(&mu1);
|
||||
pthread_mutex_destroy(&mu2);
|
||||
}
|
|
@ -123,12 +123,12 @@ void InitializeMutex() {
|
|||
#endif
|
||||
}
|
||||
|
||||
DeadlockDetector::DeadlockDetector() {
|
||||
InternalDeadlockDetector::InternalDeadlockDetector() {
|
||||
// Rely on zero initialization because some mutexes can be locked before ctor.
|
||||
}
|
||||
|
||||
#if TSAN_DEBUG && !TSAN_GO
|
||||
void DeadlockDetector::Lock(MutexType t) {
|
||||
void InternalDeadlockDetector::Lock(MutexType t) {
|
||||
// Printf("LOCK %d @%zu\n", t, seq_ + 1);
|
||||
CHECK_GT(t, MutexTypeInvalid);
|
||||
CHECK_LT(t, MutexTypeCount);
|
||||
|
@ -155,7 +155,7 @@ void DeadlockDetector::Lock(MutexType t) {
|
|||
}
|
||||
}
|
||||
|
||||
void DeadlockDetector::Unlock(MutexType t) {
|
||||
void InternalDeadlockDetector::Unlock(MutexType t) {
|
||||
// Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
|
||||
CHECK(locked_[t]);
|
||||
locked_[t] = 0;
|
||||
|
@ -210,7 +210,7 @@ Mutex::~Mutex() {
|
|||
|
||||
void Mutex::Lock() {
|
||||
#if TSAN_DEBUG && !TSAN_GO
|
||||
cur_thread()->deadlock_detector.Lock(type_);
|
||||
cur_thread()->internal_deadlock_detector.Lock(type_);
|
||||
#endif
|
||||
uptr cmp = kUnlocked;
|
||||
if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
|
||||
|
@ -235,13 +235,13 @@ void Mutex::Unlock() {
|
|||
(void)prev;
|
||||
DCHECK_NE(prev & kWriteLock, 0);
|
||||
#if TSAN_DEBUG && !TSAN_GO
|
||||
cur_thread()->deadlock_detector.Unlock(type_);
|
||||
cur_thread()->internal_deadlock_detector.Unlock(type_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::ReadLock() {
|
||||
#if TSAN_DEBUG && !TSAN_GO
|
||||
cur_thread()->deadlock_detector.Lock(type_);
|
||||
cur_thread()->internal_deadlock_detector.Lock(type_);
|
||||
#endif
|
||||
uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
|
||||
if ((prev & kWriteLock) == 0)
|
||||
|
@ -263,7 +263,7 @@ void Mutex::ReadUnlock() {
|
|||
DCHECK_EQ(prev & kWriteLock, 0);
|
||||
DCHECK_GT(prev & ~kWriteLock, 0);
|
||||
#if TSAN_DEBUG && !TSAN_GO
|
||||
cur_thread()->deadlock_detector.Unlock(type_);
|
||||
cur_thread()->internal_deadlock_detector.Unlock(type_);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -65,9 +65,9 @@ class Mutex {
|
|||
typedef GenericScopedLock<Mutex> Lock;
|
||||
typedef GenericScopedReadLock<Mutex> ReadLock;
|
||||
|
||||
class DeadlockDetector {
|
||||
class InternalDeadlockDetector {
|
||||
public:
|
||||
DeadlockDetector();
|
||||
InternalDeadlockDetector();
|
||||
void Lock(MutexType t);
|
||||
void Unlock(MutexType t);
|
||||
private:
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "sanitizer_common/sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common/sanitizer_asm.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_deadlock_detector.h"
|
||||
#include "sanitizer_common/sanitizer_libignore.h"
|
||||
#include "sanitizer_common/sanitizer_suppressions.h"
|
||||
#include "sanitizer_common/sanitizer_thread_registry.h"
|
||||
|
@ -449,7 +450,8 @@ struct ThreadState {
|
|||
const uptr tls_size;
|
||||
ThreadContext *tctx;
|
||||
|
||||
DeadlockDetector deadlock_detector;
|
||||
InternalDeadlockDetector internal_deadlock_detector;
|
||||
__sanitizer::DeadlockDetectorTLS deadlock_detector_tls;
|
||||
|
||||
bool in_signal_handler;
|
||||
SignalContext *signal_ctx;
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <sanitizer_common/sanitizer_deadlock_detector.h>
|
||||
|
||||
#include "tsan_rtl.h"
|
||||
#include "tsan_flags.h"
|
||||
#include "tsan_sync.h"
|
||||
|
@ -20,6 +22,14 @@
|
|||
|
||||
namespace __tsan {
|
||||
|
||||
static __sanitizer::DeadlockDetector<TwoLevelBitVector<> > g_deadlock_detector;
|
||||
|
||||
static void EnsureDeadlockDetectorID(ThreadState *thr, SyncVar *s) {
|
||||
if (!s->deadlock_detector_id)
|
||||
s->deadlock_detector_id =
|
||||
g_deadlock_detector.newNode(reinterpret_cast<uptr>(s));
|
||||
}
|
||||
|
||||
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
|
||||
bool rw, bool recursive, bool linker_init) {
|
||||
Context *ctx = CTX();
|
||||
|
@ -35,6 +45,10 @@ void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
|
|||
s->is_rw = rw;
|
||||
s->is_recursive = recursive;
|
||||
s->is_linker_init = linker_init;
|
||||
if (common_flags()->detect_deadlocks) {
|
||||
EnsureDeadlockDetectorID(thr, s);
|
||||
Printf("MutexCreate: %zx\n", s->deadlock_detector_id);
|
||||
}
|
||||
s->mtx.Unlock();
|
||||
}
|
||||
|
||||
|
@ -51,6 +65,10 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
|
|||
SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr);
|
||||
if (s == 0)
|
||||
return;
|
||||
if (common_flags()->detect_deadlocks) {
|
||||
EnsureDeadlockDetectorID(thr, s);
|
||||
Printf("MutexDestroy: %zx\n", s->deadlock_detector_id);
|
||||
}
|
||||
if (IsAppMem(addr)) {
|
||||
CHECK(!thr->is_freeing);
|
||||
thr->is_freeing = true;
|
||||
|
@ -104,6 +122,15 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) {
|
|||
}
|
||||
s->recursion += rec;
|
||||
thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
|
||||
if (common_flags()->detect_deadlocks) {
|
||||
EnsureDeadlockDetectorID(thr, s);
|
||||
bool has_deadlock = g_deadlock_detector.onLock(&thr->deadlock_detector_tls,
|
||||
s->deadlock_detector_id);
|
||||
Printf("MutexLock: %zx;%s\n", s->deadlock_detector_id,
|
||||
has_deadlock
|
||||
? " ThreadSanitizer: lock-order-inversion (potential deadlock)"
|
||||
: "");
|
||||
}
|
||||
s->mtx.Unlock();
|
||||
}
|
||||
|
||||
|
@ -140,6 +167,12 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) {
|
|||
}
|
||||
}
|
||||
thr->mset.Del(s->GetId(), true);
|
||||
if (common_flags()->detect_deadlocks) {
|
||||
EnsureDeadlockDetectorID(thr, s);
|
||||
Printf("MutexUnlock: %zx\n", s->deadlock_detector_id);
|
||||
g_deadlock_detector.onUnlock(&thr->deadlock_detector_tls,
|
||||
s->deadlock_detector_id);
|
||||
}
|
||||
s->mtx.Unlock();
|
||||
return rec;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ SyncVar* SyncTab::Create(ThreadState *thr, uptr pc, uptr addr) {
|
|||
void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
|
||||
const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
|
||||
SyncVar *res = new(mem) SyncVar(addr, uid);
|
||||
res->deadlock_detector_id = 0;
|
||||
#ifndef TSAN_GO
|
||||
res->creation_stack_id = CurrentStackId(thr, pc);
|
||||
#endif
|
||||
|
|
|
@ -61,6 +61,7 @@ struct SyncVar {
|
|||
SyncClock read_clock; // Used for rw mutexes only.
|
||||
u32 creation_stack_id;
|
||||
int owner_tid; // Set only by exclusive owners.
|
||||
uptr deadlock_detector_id;
|
||||
u64 last_lock;
|
||||
int recursion;
|
||||
bool is_rw;
|
||||
|
|
Loading…
Reference in New Issue