diff options
author | Dmitry Vyukov <dvyukov@google.com> | 2014-03-05 13:41:21 +0000 |
---|---|---|
committer | Dmitry Vyukov <dvyukov@google.com> | 2014-03-05 13:41:21 +0000 |
commit | bf58bb628aa3b4f4eab3a34b46659046f7290a10 (patch) | |
tree | 9811c33578174bc3617ef5b326f92bf1998081da | |
parent | 38fdd502f35f5597e2da1981d8724c35f6f4569a (diff) |
tsan: implement new version of standalong deadlock detector
intercept pthread_cond (it is required to properly track state of mutexes)
detect cycles in mutex graph
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@202975 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/sanitizer_common/sanitizer_deadlock_detector1.cc | 79 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_deadlock_detector2.cc | 374 | ||||
-rw-r--r-- | lib/sanitizer_common/sanitizer_deadlock_detector_interface.h | 43 | ||||
-rw-r--r-- | lib/tsan/dd/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/tsan/dd/dd_interceptors.cc | 113 | ||||
-rw-r--r-- | lib/tsan/dd/dd_rtl.cc | 102 | ||||
-rw-r--r-- | lib/tsan/dd/dd_rtl.h | 17 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_rtl_mutex.cc | 105 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_sync.cc | 5 |
9 files changed, 635 insertions, 204 deletions
diff --git a/lib/sanitizer_common/sanitizer_deadlock_detector1.cc b/lib/sanitizer_common/sanitizer_deadlock_detector1.cc index ca38493cb..5ecf5e45c 100644 --- a/lib/sanitizer_common/sanitizer_deadlock_detector1.cc +++ b/lib/sanitizer_common/sanitizer_deadlock_detector1.cc @@ -30,75 +30,80 @@ struct DDLogicalThread { u64 ctx; DeadlockDetectorTLS<DDBV> dd; DDReport rep; + bool report_pending; }; -struct DDetectorImpl : public DDetector { +struct DD : public DDetector { SpinMutex mtx; DeadlockDetector<DDBV> dd; - DDetectorImpl(); + DD(); - virtual DDPhysicalThread* CreatePhysicalThread(); - virtual void DestroyPhysicalThread(DDPhysicalThread *pt); + DDPhysicalThread* CreatePhysicalThread(); + void DestroyPhysicalThread(DDPhysicalThread *pt); - virtual DDLogicalThread* CreateLogicalThread(u64 ctx); - virtual void DestroyLogicalThread(DDLogicalThread *lt); + DDLogicalThread* CreateLogicalThread(u64 ctx); + void DestroyLogicalThread(DDLogicalThread *lt); - virtual void MutexInit(DDMutex *m, u32 stk, u64 ctx); - virtual DDReport *MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock, bool trylock); - virtual DDReport *MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock); - virtual void MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m); + void MutexInit(DDCallback *cb, DDMutex *m); + void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock); + void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock); + void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock); + void MutexDestroy(DDCallback *cb, DDMutex *m); + + DDReport *GetReport(DDCallback *cb); void MutexEnsureID(DDLogicalThread *lt, DDMutex *m); }; DDetector *DDetector::Create() { - void *mem = MmapOrDie(sizeof(DDetectorImpl), "deadlock detector"); - return new(mem) DDetectorImpl(); + void *mem = MmapOrDie(sizeof(DD), "deadlock detector"); + return new(mem) DD(); } -DDetectorImpl::DDetectorImpl() { +DD::DD() { dd.clear(); } -DDPhysicalThread* DDetectorImpl::CreatePhysicalThread() { +DDPhysicalThread* DD::CreatePhysicalThread() { return 0; } -void DDetectorImpl::DestroyPhysicalThread(DDPhysicalThread *pt) { +void DD::DestroyPhysicalThread(DDPhysicalThread *pt) { } -DDLogicalThread* DDetectorImpl::CreateLogicalThread(u64 ctx) { +DDLogicalThread* DD::CreateLogicalThread(u64 ctx) { DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt)); lt->ctx = ctx; lt->dd.clear(); + lt->report_pending = false; return lt; } -void DDetectorImpl::DestroyLogicalThread(DDLogicalThread *lt) { +void DD::DestroyLogicalThread(DDLogicalThread *lt) { lt->~DDLogicalThread(); InternalFree(lt); } -void DDetectorImpl::MutexInit(DDMutex *m, u32 stk, u64 ctx) { +void DD::MutexInit(DDCallback *cb, DDMutex *m) { m->id = 0; - m->stk = stk; - m->ctx = ctx; + m->stk = cb->Unwind(); } -void DDetectorImpl::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) { +void DD::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) { if (!dd.nodeBelongsToCurrentEpoch(m->id)) m->id = dd.newNode(reinterpret_cast<uptr>(m)); dd.ensureCurrentEpoch(<->dd); } -DDReport *DDetectorImpl::MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock, bool trylock) { +void DD::MutexBeforeLock(DDCallback *cb, + DDMutex *m, bool wlock) { +} + +void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) { + DDLogicalThread *lt = cb->lt; if (dd.onFirstLock(<->dd, m->id)) - return 0; + return; SpinMutexLock lk(&mtx); MutexEnsureID(lt, m); CHECK(!dd.isHeld(<->dd, m->id)); @@ -106,13 +111,13 @@ DDReport *DDetectorImpl::MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, bool has_deadlock = trylock ? dd.onTryLock(<->dd, m->id) : dd.onLock(<->dd, m->id); - DDReport *rep = 0; if (has_deadlock) { uptr path[10]; uptr len = dd.findPathToHeldLock(<->dd, m->id, path, ARRAY_SIZE(path)); CHECK_GT(len, 0U); // Hm.. cycle of 10 locks? I'd like to see that. - rep = <->rep; + lt->report_pending = true; + DDReport *rep = <->rep; rep->n = len; for (uptr i = 0; i < len; i++) { DDMutex *m0 = (DDMutex*)dd.getData(path[i]); @@ -123,18 +128,15 @@ DDReport *DDetectorImpl::MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, rep->loop[i].stk = m0->stk; } } - return rep; } -DDReport *DDetectorImpl::MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock) { +void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) { // Printf("T%d MutexUnlock: %zx; recursion %d\n", thr->tid, // s->deadlock_detector_id, s->recursion); - dd.onUnlock(<->dd, m->id); - return 0; + dd.onUnlock(&cb->lt->dd, m->id); } -void DDetectorImpl::MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt, +void DD::MutexDestroy(DDCallback *cb, DDMutex *m) { if (!m->id) return; SpinMutexLock lk(&mtx); @@ -143,5 +145,12 @@ void DDetectorImpl::MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt, m->id = 0; } +DDReport *DD::GetReport(DDCallback *cb) { + if (!cb->lt->report_pending) + return 0; + cb->lt->report_pending = false; + return &cb->lt->rep; +} + } // namespace __sanitizer #endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1 diff --git a/lib/sanitizer_common/sanitizer_deadlock_detector2.cc b/lib/sanitizer_common/sanitizer_deadlock_detector2.cc index d68fc12cb..27e4f1d3d 100644 --- a/lib/sanitizer_common/sanitizer_deadlock_detector2.cc +++ b/lib/sanitizer_common/sanitizer_deadlock_detector2.cc @@ -14,125 +14,357 @@ #include "sanitizer_deadlock_detector_interface.h" #include "sanitizer_allocator_internal.h" #include "sanitizer_placement_new.h" -//#include "sanitizer_mutex.h" +#include "sanitizer_mutex.h" #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2 namespace __sanitizer { +const int kMaxMutex = 1024; +const int kMaxNesting = 64; +const u32 kNoId = -1; +const u32 kEndId = -2; +const int kMaxLink = 8; + +struct Id { + u32 id; + u32 seq; + + explicit Id(u32 id = 0, u32 seq = 0) + : id(id) + , seq(seq) { + } +}; + +struct Link { + u32 id; + u32 seq; + u32 tid; + u32 stk; + + explicit Link(u32 id = 0, u32 seq = 0, u32 tid = 0, u32 stk = 0) + : id(id) + , seq(seq) + , tid(tid) + , stk(stk) { + } +}; + struct DDPhysicalThread { DDReport rep; + bool report_pending; + bool visited[kMaxMutex]; + Link pending[kMaxMutex]; + Link path[kMaxMutex]; }; struct DDLogicalThread { u64 ctx; + u32 locked[kMaxNesting]; + int nlocked; +}; + +struct Mutex { + StaticSpinMutex mtx; + u32 seq; + int nlink; + Link link[kMaxLink]; }; -struct DDetectorImpl : public DDetector { - DDetectorImpl(); +struct DD : public DDetector { + explicit DD(); + + DDPhysicalThread* CreatePhysicalThread(); + void DestroyPhysicalThread(DDPhysicalThread *pt); + + DDLogicalThread* CreateLogicalThread(u64 ctx); + void DestroyLogicalThread(DDLogicalThread *lt); + + void MutexInit(DDCallback *cb, DDMutex *m); + void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock); + void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, + bool trylock); + void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock); + void MutexDestroy(DDCallback *cb, DDMutex *m); - virtual DDPhysicalThread* CreatePhysicalThread(); - virtual void DestroyPhysicalThread(DDPhysicalThread *pt); + DDReport *GetReport(DDCallback *cb); - virtual DDLogicalThread* CreateLogicalThread(u64 ctx); - virtual void DestroyLogicalThread(DDLogicalThread *lt); + void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx); + void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath); - virtual void MutexInit(DDMutex *m, u32 stk, u64 ctx); - virtual DDReport *MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock, bool trylock); - virtual DDReport *MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock); - virtual void MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m); + SpinMutex mtx; + u32 free_id[kMaxMutex]; + int nfree_id; + int id_gen; + + Mutex mutex[kMaxMutex]; }; DDetector *DDetector::Create() { - void *mem = MmapOrDie(sizeof(DDetectorImpl), "deadlock detector"); - return new(mem) DDetectorImpl(); + void *mem = MmapOrDie(sizeof(DD), "deadlock detector"); + return new(mem) DD(); } -DDetectorImpl::DDetectorImpl() { +DD::DD() { + nfree_id = 0; + id_gen = 0; } -DDPhysicalThread* DDetectorImpl::CreatePhysicalThread() { - void *mem = InternalAlloc(sizeof(DDPhysicalThread)); - DDPhysicalThread *pt = new(mem) DDPhysicalThread(); +DDPhysicalThread* DD::CreatePhysicalThread() { + DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread), + "deadlock detector (physical thread)"); return pt; } -void DDetectorImpl::DestroyPhysicalThread(DDPhysicalThread *pt) { +void DD::DestroyPhysicalThread(DDPhysicalThread *pt) { pt->~DDPhysicalThread(); InternalFree(pt); } -DDLogicalThread* DDetectorImpl::CreateLogicalThread(u64 ctx) { - void *mem = InternalAlloc(sizeof( - DDLogicalThread)); - DDLogicalThread *lt = new(mem) DDLogicalThread(); +DDLogicalThread* DD::CreateLogicalThread(u64 ctx) { + DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc( + sizeof(DDLogicalThread)); lt->ctx = ctx; + lt->nlocked = 0; return lt; } -void DDetectorImpl::DestroyLogicalThread(DDLogicalThread *lt) { +void DD::DestroyLogicalThread(DDLogicalThread *lt) { lt->~DDLogicalThread(); InternalFree(lt); } -void DDetectorImpl::MutexInit(DDMutex *m, u32 stk, u64 ctx) { - m->id = 0; - m->stk = stk; - m->ctx = ctx; +void DD::MutexInit(DDCallback *cb, DDMutex *m) { + VPrintf(2, "#%llu: DD::MutexInit(%p)\n", cb->lt->ctx, m); + m->id = kNoId; + m->recursion = 0; + atomic_store(&m->owner, 0, memory_order_relaxed); } -DDReport *DDetectorImpl::MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock, bool trylock) { - /* - if (dd.onFirstLock(<->dd, m->id)) +void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) { + VPrintf(2, "#%llu: DD::MutexBeforeLock(%p, wlock=%d) nlocked=%d\n", + cb->lt->ctx, m, wlock, cb->lt->nlocked); + DDPhysicalThread *pt = cb->pt; + DDLogicalThread *lt = cb->lt; + + uptr owner = atomic_load(&m->owner, memory_order_relaxed); + if (owner == (uptr)cb->lt) { + VPrintf(3, "#%llu: DD::MutexBeforeLock recursive\n", + cb->lt->ctx); + return; + } + + CHECK_LE(lt->nlocked, kMaxNesting); + + if (m->id == kNoId) { + // FIXME(dvyukov): don't allocate id if lt->nlocked == 0 + SpinMutexLock l(&mtx); + if (nfree_id > 0) + m->id = free_id[--nfree_id]; + else + m->id = id_gen++; + CHECK_LE(id_gen, kMaxMutex); + VPrintf(3, "#%llu: DD::MutexBeforeLock assign id %d\n", + cb->lt->ctx, m->id); + } + + lt->locked[lt->nlocked++] = m->id; + if (lt->nlocked == 1) { + VPrintf(3, "#%llu: DD::MutexBeforeLock first mutex\n", + cb->lt->ctx); return 0; - SpinMutexLock lk(&mtx); - MutexEnsureID(lt, m); - CHECK(!dd.isHeld(<->dd, m->id)); - // Printf("T%d MutexLock: %zx\n", thr->tid, s->deadlock_detector_id); - bool has_deadlock = trylock - ? dd.onTryLock(<->dd, m->id) - : dd.onLock(<->dd, m->id); - DDReport *rep = 0; - if (has_deadlock) { - uptr path[10]; - uptr len = dd.findPathToHeldLock(<->dd, m->id, - path, ARRAY_SIZE(path)); - CHECK_GT(len, 0U); // Hm.. cycle of 10 locks? I'd like to see that. - rep = <->rep; - rep->n = len; - for (uptr i = 0; i < len; i++) { - DDMutex *m0 = (DDMutex*)dd.getData(path[i]); - DDMutex *m1 = (DDMutex*)dd.getData(path[i < len - 1 ? i + 1 : 0]); - rep->loop[i].thr_ctx = 0; // don't know - rep->loop[i].mtx_ctx0 = m0->ctx; - rep->loop[i].mtx_ctx1 = m1->ctx; - rep->loop[i].stk = m0->stk; + } + + bool added = false; + Mutex *mtx = &mutex[m->id]; + for (int i = 0; i < lt->nlocked - 1; i++) { + u32 id1 = lt->locked[i]; + Mutex *mtx1 = &mutex[id1]; + SpinMutexLock l(&mtx1->mtx); + if (mtx1->nlink == kMaxLink) { + // FIXME(dvyukov): check stale links + continue; + } + int li = 0; + for (; li < mtx1->nlink; li++) { + Link *link = &mtx1->link[li]; + if (link->id == m->id) { + if (link->seq != mtx->seq) { + link->seq = mtx->seq; + link->tid = lt->ctx; + link->stk = cb->Unwind(); + added = true; + VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n", + cb->lt->ctx, mtx1 - mutex, m->id); + } + break; + } + } + if (li == mtx1->nlink) { + // FIXME(dvyukov): check stale links + Link *link = &mtx1->link[mtx1->nlink++]; + link->id = m->id; + link->seq = mtx->seq; + link->tid = lt->ctx; + link->stk = cb->Unwind(); + added = true; + VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n", + cb->lt->ctx, mtx1 - mutex, m->id); } } - return rep; - */ - return 0; + + if (!added || mtx->nlink == 0) { + VPrintf(3, "#%llu: DD::MutexBeforeLock don't check\n", + cb->lt->ctx); + return; + } + + CycleCheck(pt, lt, m); } -DDReport *DDetectorImpl::MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock) { - //dd.onUnlock(<->dd, m->id); - return 0; +void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, + bool trylock) { + VPrintf(2, "#%llu: DD::MutexAfterLock(%p, wlock=%d, try=%d) nlocked=%d\n", + cb->lt->ctx, m, wlock, trylock, cb->lt->nlocked); + DDLogicalThread *lt = cb->lt; + + uptr owner = atomic_load(&m->owner, memory_order_relaxed); + if (owner == (uptr)cb->lt) { + VPrintf(3, "#%llu: DD::MutexAfterLock recursive\n", cb->lt->ctx); + CHECK(wlock); + m->recursion++; + return; + } + CHECK_EQ(owner, 0); + if (wlock) { + VPrintf(3, "#%llu: DD::MutexAfterLock set owner\n", cb->lt->ctx); + CHECK_EQ(m->recursion, 0); + m->recursion = 1; + atomic_store(&m->owner, (uptr)cb->lt, memory_order_relaxed); + } + + if (!trylock) + return; + + CHECK_LE(lt->nlocked, kMaxNesting); + if (m->id == kNoId) { + // FIXME(dvyukov): don't allocate id if lt->nlocked == 0 + SpinMutexLock l(&mtx); + if (nfree_id > 0) + m->id = free_id[--nfree_id]; + else + m->id = id_gen++; + CHECK_LE(id_gen, kMaxMutex); + VPrintf(3, "#%llu: DD::MutexAfterLock assign id %d\n", cb->lt->ctx, m->id); + } + + lt->locked[lt->nlocked++] = m->id; } -void DDetectorImpl::MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt, +void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) { + VPrintf(2, "#%llu: DD::MutexBeforeUnlock(%p, wlock=%d) nlocked=%d\n", + cb->lt->ctx, m, wlock, cb->lt->nlocked); + DDLogicalThread *lt = cb->lt; + + uptr owner = atomic_load(&m->owner, memory_order_relaxed); + if (owner == (uptr)cb->lt) { + VPrintf(3, "#%llu: DD::MutexBeforeUnlock recursive\n", cb->lt->ctx); + if (--m->recursion > 0) + return; + VPrintf(3, "#%llu: DD::MutexBeforeUnlock reset owner\n", cb->lt->ctx); + atomic_store(&m->owner, 0, memory_order_relaxed); + } + CHECK_NE(m->id, kNoId); + int last = lt->nlocked - 1; + for (int i = last; i >= 0; i--) { + if (cb->lt->locked[i] == m->id) { + lt->locked[i] = lt->locked[last]; + lt->nlocked--; + break; + } + } +} + +void DD::MutexDestroy(DDCallback *cb, DDMutex *m) { + VPrintf(2, "#%llu: DD::MutexDestroy(%p)\n", + cb->lt->ctx, m); + if (m->id == kNoId) + return; + { + Mutex *mtx = &mutex[m->id]; + SpinMutexLock l(&mtx->mtx); + mtx->seq++; + mtx->nlink = 0; + } + { + SpinMutexLock l(&mtx); + free_id[nfree_id++] = m->id; + m->id = kNoId; + } +} + +void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *m) { - /* - if (!m->id) return; - SpinMutexLock lk(&mtx); - if (dd.nodeBelongsToCurrentEpoch(m->id)) - dd.removeNode(m->id); - m->id = 0; - */ + // Mutex *mtx = &mutex[m->id]; + internal_memset(pt->visited, 0, sizeof(pt->visited)); + int npath = 0; + int npending = 0; + { + Mutex *mtx = &mutex[m->id]; + SpinMutexLock l(&mtx->mtx); + for (int li = 0; li < mtx->nlink; li++) + pt->pending[npending++] = mtx->link[li]; + } + while (npending > 0) { + Link link = pt->pending[--npending]; + if (link.id == kEndId) { + npath--; + continue; + } + if (pt->visited[link.id]) + continue; + Mutex *mtx1 = &mutex[link.id]; + SpinMutexLock l(&mtx1->mtx); + if (mtx1->seq != link.seq) + continue; + pt->visited[link.id] = true; + if (mtx1->nlink == 0) + continue; + pt->path[npath++] = link; + pt->pending[npending++] = Link(kEndId); + if (link.id == m->id) + return Report(pt, lt, npath); // Bingo! + for (int li = 0; li < mtx1->nlink; li++) { + Link *link1 = &mtx1->link[li]; + // Mutex *mtx2 = &mutex[link->id]; + // FIXME(dvyukov): fast seq check + // FIXME(dvyukov): fast nlink != 0 check + // FIXME(dvyukov): fast pending check? + // FIXME(dvyukov): npending can be larger than kMaxMutex + pt->pending[npending++] = *link1; + } + } +} + +void DD::Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath) { + DDReport *rep = &pt->rep; + rep->n = npath; + for (int i = 0; i < npath; i++) { + Link *link = &pt->path[i]; + Link *link0 = &pt->path[i ? i - 1 : npath - 1]; + rep->loop[i].thr_ctx = link->tid; + rep->loop[i].mtx_ctx0 = link0->id; + rep->loop[i].mtx_ctx1 = link->id; + rep->loop[i].stk = link->stk; + } + pt->report_pending = true; +} + +DDReport *DD::GetReport(DDCallback *cb) { + if (!cb->lt->report_pending) + return 0; + cb->lt->report_pending = false; + return &cb->lt->rep; } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h b/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h index b077247f5..99ddcc4b6 100644 --- a/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h +++ b/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h @@ -16,9 +16,12 @@ #ifndef SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H #define SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H -#define SANITIZER_DEADLOCK_DETECTOR_VERSION 1 +#ifndef SANITIZER_DEADLOCK_DETECTOR_VERSION +# define SANITIZER_DEADLOCK_DETECTOR_VERSION 1 +#endif #include "sanitizer_internal_defs.h" +#include "sanitizer_atomic.h" namespace __sanitizer { @@ -26,10 +29,21 @@ namespace __sanitizer { // lt - logical (user) thread. // pt - physical (OS) thread. +struct DDPhysicalThread; +struct DDLogicalThread; + struct DDMutex { +#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1 uptr id; - u32 stk; // creation (or any other) stack that indentifies the mutex - u64 ctx; // user context + u32 stk; // creation stack + u64 ctx; +#elif SANITIZER_DEADLOCK_DETECTOR_VERSION == 2 + u32 id; + u32 recursion; + atomic_uintptr_t owner; +#else +# error "BAD SANITIZER_DEADLOCK_DETECTOR_VERSION" +#endif }; struct DDReport { @@ -42,8 +56,12 @@ struct DDReport { } loop[16]; }; -struct DDPhysicalThread; -struct DDLogicalThread; +struct DDCallback { + DDPhysicalThread *pt; + DDLogicalThread *lt; + + virtual u32 Unwind() { return 0; } +}; struct DDetector { static DDetector *Create(); @@ -54,13 +72,14 @@ struct DDetector { virtual DDLogicalThread* CreateLogicalThread(u64 ctx) { return 0; } virtual void DestroyLogicalThread(DDLogicalThread *lt) {} - virtual void MutexInit(DDMutex *m, u32 stk, u64 ctx) {} - virtual DDReport *MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock, bool trylock) { return 0; } - virtual DDReport *MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m, bool writelock) { return 0; } - virtual void MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt, - DDMutex *m) {} + virtual void MutexInit(DDCallback *cb, DDMutex *m) {} + virtual void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {} + virtual void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, + bool trylock) {} + virtual void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {} + virtual void MutexDestroy(DDCallback *cb, DDMutex *m) {} + + virtual DDReport *GetReport(DDCallback *cb) { return 0; } }; } // namespace __sanitizer diff --git a/lib/tsan/dd/CMakeLists.txt b/lib/tsan/dd/CMakeLists.txt index 42b16047f..24fd03fea 100644 --- a/lib/tsan/dd/CMakeLists.txt +++ b/lib/tsan/dd/CMakeLists.txt @@ -12,6 +12,7 @@ else() endif() set(DD_DYNAMIC_DEFINITIONS DYNAMIC=1) +set(DD_COMMON_DEFINITIONS SANITIZER_DEADLOCK_DETECTOR_VERSION=2) set(DD_SOURCES dd_rtl.cc diff --git a/lib/tsan/dd/dd_interceptors.cc b/lib/tsan/dd/dd_interceptors.cc index 3374a40bf..c75330e75 100644 --- a/lib/tsan/dd/dd_interceptors.cc +++ b/lib/tsan/dd/dd_interceptors.cc @@ -10,6 +10,7 @@ #include "dd_rtl.h" #include "interception/interception.h" #include <pthread.h> +#include <stdlib.h> using namespace __dsan; @@ -34,21 +35,23 @@ INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) { INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) { InitThread(); - MutexLock(thr, (uptr)m, true, false); - return REAL(pthread_mutex_lock)(m); + MutexBeforeLock(thr, (uptr)m, true); + int res = REAL(pthread_mutex_lock)(m); + MutexAfterLock(thr, (uptr)m, true, false); + return res; } INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) { InitThread(); int res = REAL(pthread_mutex_trylock)(m); if (res == 0) - MutexLock(thr, (uptr)m, true, true); + MutexAfterLock(thr, (uptr)m, true, true); return res; } INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) { InitThread(); - MutexUnlock(thr, (uptr)m, true); + MutexBeforeUnlock(thr, (uptr)m, true); return REAL(pthread_mutex_unlock)(m); } @@ -61,21 +64,23 @@ INTERCEPTOR(int, pthread_spin_destroy, pthread_spinlock_t *m) { INTERCEPTOR(int, pthread_spin_lock, pthread_spinlock_t *m) { InitThread(); - MutexLock(thr, (uptr)m, true, false); - return REAL(pthread_spin_lock)(m); + MutexBeforeLock(thr, (uptr)m, true); + int res = REAL(pthread_spin_lock)(m); + MutexAfterLock(thr, (uptr)m, true, false); + return res; } INTERCEPTOR(int, pthread_spin_trylock, pthread_spinlock_t *m) { InitThread(); int res = REAL(pthread_spin_trylock)(m); if (res == 0) - MutexLock(thr, (uptr)m, true, true); + MutexAfterLock(thr, (uptr)m, true, true); return res; } INTERCEPTOR(int, pthread_spin_unlock, pthread_spinlock_t *m) { InitThread(); - MutexUnlock(thr, (uptr)m, true); + MutexBeforeUnlock(thr, (uptr)m, true); return REAL(pthread_spin_unlock)(m); } @@ -87,15 +92,17 @@ INTERCEPTOR(int, pthread_rwlock_destroy, pthread_rwlock_t *m) { INTERCEPTOR(int, pthread_rwlock_rdlock, pthread_rwlock_t *m) { InitThread(); - MutexLock(thr, (uptr)m, false, false); - return REAL(pthread_rwlock_rdlock)(m); + MutexBeforeLock(thr, (uptr)m, false); + int res = REAL(pthread_rwlock_rdlock)(m); + MutexAfterLock(thr, (uptr)m, false, false); + return res; } INTERCEPTOR(int, pthread_rwlock_tryrdlock, pthread_rwlock_t *m) { InitThread(); int res = REAL(pthread_rwlock_tryrdlock)(m); if (res == 0) - MutexLock(thr, (uptr)m, false, true); + MutexAfterLock(thr, (uptr)m, false, true); return res; } @@ -104,21 +111,23 @@ INTERCEPTOR(int, pthread_rwlock_timedrdlock, pthread_rwlock_t *m, InitThread(); int res = REAL(pthread_rwlock_timedrdlock)(m, abstime); if (res == 0) - MutexLock(thr, (uptr)m, false, true); + MutexAfterLock(thr, (uptr)m, false, true); return res; } INTERCEPTOR(int, pthread_rwlock_wrlock, pthread_rwlock_t *m) { InitThread(); - MutexLock(thr, (uptr)m, true, false); - return REAL(pthread_rwlock_wrlock)(m); + MutexBeforeLock(thr, (uptr)m, true); + int res = REAL(pthread_rwlock_wrlock)(m); + MutexAfterLock(thr, (uptr)m, true, false); + return res; } INTERCEPTOR(int, pthread_rwlock_trywrlock, pthread_rwlock_t *m) { InitThread(); int res = REAL(pthread_rwlock_trywrlock)(m); if (res == 0) - MutexLock(thr, (uptr)m, true, true); + MutexAfterLock(thr, (uptr)m, true, true); return res; } @@ -127,16 +136,79 @@ INTERCEPTOR(int, pthread_rwlock_timedwrlock, pthread_rwlock_t *m, InitThread(); int res = REAL(pthread_rwlock_timedwrlock)(m, abstime); if (res == 0) - MutexLock(thr, (uptr)m, true, true); + MutexAfterLock(thr, (uptr)m, true, true); return res; } INTERCEPTOR(int, pthread_rwlock_unlock, pthread_rwlock_t *m) { InitThread(); - MutexUnlock(thr, (uptr)m, true); // note: not necessary write unlock + MutexBeforeUnlock(thr, (uptr)m, true); // note: not necessary write unlock return REAL(pthread_rwlock_unlock)(m); } +static pthread_cond_t *init_cond(pthread_cond_t *c, bool force = false) { + atomic_uintptr_t *p = (atomic_uintptr_t*)c; + uptr cond = atomic_load(p, memory_order_acquire); + if (!force && cond != 0) + return (pthread_cond_t*)cond; + void *newcond = malloc(sizeof(pthread_cond_t)); + internal_memset(newcond, 0, sizeof(pthread_cond_t)); + if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond, + memory_order_acq_rel)) + return (pthread_cond_t*)newcond; + free(newcond); + return (pthread_cond_t*)cond; +} + +INTERCEPTOR(int, pthread_cond_init, pthread_cond_t *c, + const pthread_condattr_t *a) { + InitThread(); + pthread_cond_t *cond = init_cond(c, true); + return REAL(pthread_cond_init)(cond, a); +} + +INTERCEPTOR(int, pthread_cond_wait, pthread_cond_t *c, pthread_mutex_t *m) { + InitThread(); + pthread_cond_t *cond = init_cond(c); + MutexBeforeUnlock(thr, (uptr)m, true); + MutexBeforeLock(thr, (uptr)m, true); + int res = REAL(pthread_cond_wait)(cond, m); + MutexAfterLock(thr, (uptr)m, true, false); + return res; +} + +INTERCEPTOR(int, pthread_cond_timedwait, pthread_cond_t *c, pthread_mutex_t *m, + const timespec *abstime) { + InitThread(); + pthread_cond_t *cond = init_cond(c); + MutexBeforeUnlock(thr, (uptr)m, true); + MutexBeforeLock(thr, (uptr)m, true); + int res = REAL(pthread_cond_timedwait)(cond, m, abstime); + MutexAfterLock(thr, (uptr)m, true, false); + return res; +} + +INTERCEPTOR(int, pthread_cond_signal, pthread_cond_t *c) { + InitThread(); + pthread_cond_t *cond = init_cond(c); + return REAL(pthread_cond_signal)(cond); +} + +INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *c) { + InitThread(); + pthread_cond_t *cond = init_cond(c); + return REAL(pthread_cond_broadcast)(cond); +} + +INTERCEPTOR(int, pthread_cond_destroy, pthread_cond_t *c) { + InitThread(); + pthread_cond_t *cond = init_cond(c); + int res = REAL(pthread_cond_destroy)(cond); + free(cond); + atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed); + return res; +} + namespace __dsan { void InitializeInterceptors() { @@ -158,6 +230,13 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(pthread_rwlock_trywrlock); INTERCEPT_FUNCTION(pthread_rwlock_timedwrlock); INTERCEPT_FUNCTION(pthread_rwlock_unlock); + + INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2"); } } // namespace __dsan diff --git a/lib/tsan/dd/dd_rtl.cc b/lib/tsan/dd/dd_rtl.cc index 084565403..840a25a2a 100644 --- a/lib/tsan/dd/dd_rtl.cc +++ b/lib/tsan/dd/dd_rtl.cc @@ -18,32 +18,11 @@ namespace __dsan { static Context *ctx; -void Initialize() { - static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1]; - ctx = new(ctx_mem) Context(); - - InitializeInterceptors(); - //common_flags()->allow_addr2line = true; - common_flags()->symbolize = true; - ctx->dd = DDetector::Create(); -} - -void ThreadInit(Thread *thr) { - thr->dd_pt = ctx->dd->CreatePhysicalThread(); - thr->dd_lt = ctx->dd->CreateLogicalThread(0); -} - -void ThreadDestroy(Thread *thr) { - ctx->dd->DestroyPhysicalThread(thr->dd_pt); - ctx->dd->DestroyLogicalThread(thr->dd_lt); -} - -static u32 CurrentStackTrace(Thread *thr) { +static u32 CurrentStackTrace(Thread *thr, uptr skip) { StackTrace trace; thr->ignore_interceptors = true; trace.Unwind(1000, 0, 0, 0, 0, 0, false); thr->ignore_interceptors = false; - const uptr skip = 4; if (trace.size <= skip) return 0; return StackDepotPut(trace.trace + skip, trace.size - skip); @@ -58,45 +37,96 @@ static void PrintStackTrace(Thread *thr, u32 stk) { } static void ReportDeadlock(Thread *thr, DDReport *rep) { + if (rep == 0) + return; Printf("==============================\n"); - Printf("DEADLOCK\n"); - PrintStackTrace(thr, CurrentStackTrace(thr)); + Printf("WARNING: lock-order-inversion (potential deadlock)\n"); for (int i = 0; i < rep->n; i++) { - Printf("Mutex %llu created at:\n", rep->loop[i].mtx_ctx0); + Printf("Thread %d locks mutex %llu under mutex %llu:\n", + rep->loop[i].thr_ctx, rep->loop[i].mtx_ctx1, rep->loop[i].mtx_ctx0); PrintStackTrace(thr, rep->loop[i].stk); } Printf("==============================\n"); } -void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock) { +Callback::Callback(Thread *thr) + : thr(thr) { + lt = thr->dd_lt; + pt = thr->dd_pt; +} + +u32 Callback::Unwind() { + return CurrentStackTrace(thr, 3); +} + +void Initialize() { + static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1]; + ctx = new(ctx_mem) Context(); + + InitializeInterceptors(); + ParseCommonFlagsFromString(flags(), GetEnv("DSAN_OPTIONS")); + //common_flags()->allow_addr2line = true; + common_flags()->symbolize = true; + ctx->dd = DDetector::Create(); +} + +void ThreadInit(Thread *thr) { + static atomic_uintptr_t id_gen; + uptr id = atomic_fetch_add(&id_gen, 1, memory_order_relaxed); + thr->dd_pt = ctx->dd->CreatePhysicalThread(); + thr->dd_lt = ctx->dd->CreateLogicalThread(id); +} + +void ThreadDestroy(Thread *thr) { + ctx->dd->DestroyPhysicalThread(thr->dd_pt); + ctx->dd->DestroyLogicalThread(thr->dd_lt); +} + +void MutexBeforeLock(Thread *thr, uptr m, bool writelock) { if (thr->ignore_interceptors) return; - DDReport *rep = 0; + Callback cb(thr); { MutexHashMap::Handle h(&ctx->mutex_map, m); if (h.created()) - ctx->dd->MutexInit(&h->dd, CurrentStackTrace(thr), m); - rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &h->dd, - writelock, trylock); + ctx->dd->MutexInit(&cb, &h->dd); + ctx->dd->MutexBeforeLock(&cb, &h->dd, writelock); } - if (rep) - ReportDeadlock(thr, rep); + ReportDeadlock(thr, ctx->dd->GetReport(&cb)); } -void MutexUnlock(Thread *thr, uptr m, bool writelock) { +void MutexAfterLock(Thread *thr, uptr m, bool writelock, bool trylock) { if (thr->ignore_interceptors) return; - MutexHashMap::Handle h(&ctx->mutex_map, m); - ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &h->dd, writelock); + Callback cb(thr); + { + MutexHashMap::Handle h(&ctx->mutex_map, m); + if (h.created()) + ctx->dd->MutexInit(&cb, &h->dd); + ctx->dd->MutexAfterLock(&cb, &h->dd, writelock, trylock); + } + ReportDeadlock(thr, ctx->dd->GetReport(&cb)); +} + +void MutexBeforeUnlock(Thread *thr, uptr m, bool writelock) { + if (thr->ignore_interceptors) + return; + Callback cb(thr); + { + MutexHashMap::Handle h(&ctx->mutex_map, m); + ctx->dd->MutexBeforeUnlock(&cb, &h->dd, writelock); + } + ReportDeadlock(thr, ctx->dd->GetReport(&cb)); } void MutexDestroy(Thread *thr, uptr m) { if (thr->ignore_interceptors) return; + Callback cb(thr); MutexHashMap::Handle h(&ctx->mutex_map, m, true); if (!h.exists()) return; - ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &h->dd); + ctx->dd->MutexDestroy(&cb, &h->dd); } } // namespace __dsan diff --git a/lib/tsan/dd/dd_rtl.h b/lib/tsan/dd/dd_rtl.h index 9bd70ebd9..42528862c 100644 --- a/lib/tsan/dd/dd_rtl.h +++ b/lib/tsan/dd/dd_rtl.h @@ -11,6 +11,7 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_deadlock_detector_interface.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_addrhashmap.h" #include "sanitizer_common/sanitizer_mutex.h" @@ -28,6 +29,13 @@ struct Thread { bool ignore_interceptors; }; +struct Callback : DDCallback { + Thread *thr; + + Callback(Thread *thr); + virtual u32 Unwind(); +}; + typedef AddrHashMap<Mutex, 1000003> MutexHashMap; struct Context { @@ -36,14 +44,19 @@ struct Context { MutexHashMap mutex_map; }; +inline CommonFlags* flags() { + return common_flags(); +} + void Initialize(); void InitializeInterceptors(); void ThreadInit(Thread *thr); void ThreadDestroy(Thread *thr); -void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock); -void MutexUnlock(Thread *thr, uptr m, bool writelock); +void MutexBeforeLock(Thread *thr, uptr m, bool writelock); +void MutexAfterLock(Thread *thr, uptr m, bool writelock, bool trylock); +void MutexBeforeUnlock(Thread *thr, uptr m, bool writelock); void MutexDestroy(Thread *thr, uptr m); } // namespace __dsan diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc index fbbd5ee46..87c847cce 100644 --- a/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -22,7 +22,33 @@ namespace __tsan { -void ReportDeadlockReport(ThreadState *thr, uptr pc, DDReport *r); +void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r); + +struct Callback : DDCallback { + ThreadState *thr; + uptr pc; + + Callback(ThreadState *thr, uptr pc) + : thr(thr) + , pc(pc) { + DDCallback::pt = thr->dd_pt; + DDCallback::lt = thr->dd_lt; + } + + virtual u32 Unwind() { +#ifdef TSAN_GO + return 0; +#else + return CurrentStackId(thr, pc); +#endif + } +}; + +void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) { + Callback cb(thr, pc); + CTX()->dd->MutexInit(&cb, &s->dd); + s->dd.ctx = s->GetId(); +} void MutexCreate(ThreadState *thr, uptr pc, uptr addr, bool rw, bool recursive, bool linker_init) { @@ -55,8 +81,10 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr); if (s == 0) return; - if (flags()->detect_deadlocks) - ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &s->dd); + if (flags()->detect_deadlocks) { + Callback cb(thr, pc); + ctx->dd->MutexDestroy(&cb, &s->dd); + } if (IsAppMem(addr)) { CHECK(!thr->is_freeing); thr->is_freeing = true; @@ -111,12 +139,16 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec, bool try_lock) { } s->recursion += rec; thr->mset.Add(s->GetId(), true, thr->fast_state.epoch()); - DDReport *dd_rep = 0; - if (flags()->detect_deadlocks && s->recursion == 1) - dd_rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &s->dd, true, try_lock); + if (flags()->detect_deadlocks && s->recursion == 1) { + Callback cb(thr, pc); + ctx->dd->MutexBeforeLock(&cb, &s->dd, true); + ctx->dd->MutexAfterLock(&cb, &s->dd, true, try_lock); + } s->mtx.Unlock(); - if (dd_rep) - ReportDeadlockReport(thr, pc, dd_rep); + if (flags()->detect_deadlocks) { + Callback cb(thr, pc); + ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); + } } int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { @@ -153,12 +185,15 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { } } thr->mset.Del(s->GetId(), true); - DDReport *dd_rep = 0; - if (flags()->detect_deadlocks && s->recursion == 0) - dd_rep = ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &s->dd, true); + if (flags()->detect_deadlocks && s->recursion == 0) { + Callback cb(thr, pc); + ctx->dd->MutexBeforeUnlock(&cb, &s->dd, true); + } s->mtx.Unlock(); - if (dd_rep) - ReportDeadlockReport(thr, pc, dd_rep); + if (flags()->detect_deadlocks) { + Callback cb(thr, pc); + ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); + } return rec; } @@ -179,12 +214,16 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr, bool trylock) { AcquireImpl(thr, pc, &s->clock); s->last_lock = thr->fast_state.raw(); thr->mset.Add(s->GetId(), false, thr->fast_state.epoch()); - DDReport *dd_rep = 0; - if (flags()->detect_deadlocks && s->recursion == 0) - dd_rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &s->dd, false, trylock); + if (flags()->detect_deadlocks && s->recursion == 0) { + Callback cb(thr, pc); + ctx->dd->MutexBeforeLock(&cb, &s->dd, false); + ctx->dd->MutexAfterLock(&cb, &s->dd, false, trylock); + } s->mtx.ReadUnlock(); - if (dd_rep) - ReportDeadlockReport(thr, pc, dd_rep); + if (flags()->detect_deadlocks) { + Callback cb(thr, pc); + ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); + } } void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { @@ -202,13 +241,16 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { PrintCurrentStack(thr, pc); } ReleaseImpl(thr, pc, &s->read_clock); - DDReport *dd_rep = 0; - if (flags()->detect_deadlocks && s->recursion == 0) - dd_rep = ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &s->dd, false); + if (flags()->detect_deadlocks && s->recursion == 0) { + Callback cb(thr, pc); + ctx->dd->MutexBeforeUnlock(&cb, &s->dd, false); + } s->mtx.Unlock(); thr->mset.Del(s->GetId(), false); - if (dd_rep) - ReportDeadlockReport(thr, pc, dd_rep); + if (flags()->detect_deadlocks) { + Callback cb(thr, pc); + ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); + } } void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { @@ -245,12 +287,15 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { PrintCurrentStack(thr, pc); } thr->mset.Del(s->GetId(), write); - DDReport *dd_rep = 0; - if (flags()->detect_deadlocks && s->recursion == 0) - dd_rep = ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &s->dd, write); + if (flags()->detect_deadlocks && s->recursion == 0) { + Callback cb(thr, pc); + ctx->dd->MutexBeforeUnlock(&cb, &s->dd, write); + } s->mtx.Unlock(); - if (dd_rep) - ReportDeadlockReport(thr, pc, dd_rep); + if (flags()->detect_deadlocks) { + Callback cb(thr, pc); + ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb)); + } } void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { @@ -370,7 +415,9 @@ void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { StatInc(thr, StatSyncRelease); } -void ReportDeadlockReport(ThreadState *thr, uptr pc, DDReport *r) { +void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { + if (r == 0) + return; Context *ctx = CTX(); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeDeadlock); diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc index 1fe72c819..5ca6b1588 100644 --- a/lib/tsan/rtl/tsan_sync.cc +++ b/lib/tsan/rtl/tsan_sync.cc @@ -17,6 +17,8 @@ namespace __tsan { +void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s); + SyncVar::SyncVar(uptr addr, u64 uid) : mtx(MutexTypeSyncVar, StatMtxSyncVar) , addr(addr) @@ -58,7 +60,6 @@ SyncVar* SyncTab::GetIfExistsAndLock(uptr addr, bool write_lock) { } SyncVar* SyncTab::Create(ThreadState *thr, uptr pc, uptr addr) { - Context *ctx = CTX(); StatInc(thr, StatSyncCreated); void *mem = internal_alloc(MBlockSync, sizeof(SyncVar)); const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed); @@ -67,7 +68,7 @@ SyncVar* SyncTab::Create(ThreadState *thr, uptr pc, uptr addr) { res->creation_stack_id = CurrentStackId(thr, pc); #endif if (flags()->detect_deadlocks) - ctx->dd->MutexInit(&res->dd, res->creation_stack_id, res->GetId()); + DDMutexInit(thr, pc, res); return res; } |