summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2014-03-05 13:41:21 +0000
committerDmitry Vyukov <dvyukov@google.com>2014-03-05 13:41:21 +0000
commitbf58bb628aa3b4f4eab3a34b46659046f7290a10 (patch)
tree9811c33578174bc3617ef5b326f92bf1998081da
parent38fdd502f35f5597e2da1981d8724c35f6f4569a (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.cc79
-rw-r--r--lib/sanitizer_common/sanitizer_deadlock_detector2.cc374
-rw-r--r--lib/sanitizer_common/sanitizer_deadlock_detector_interface.h43
-rw-r--r--lib/tsan/dd/CMakeLists.txt1
-rw-r--r--lib/tsan/dd/dd_interceptors.cc113
-rw-r--r--lib/tsan/dd/dd_rtl.cc102
-rw-r--r--lib/tsan/dd/dd_rtl.h17
-rw-r--r--lib/tsan/rtl/tsan_rtl_mutex.cc105
-rw-r--r--lib/tsan/rtl/tsan_sync.cc5
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(&lt->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(&lt->dd, m->id))
- return 0;
+ return;
SpinMutexLock lk(&mtx);
MutexEnsureID(lt, m);
CHECK(!dd.isHeld(&lt->dd, m->id));
@@ -106,13 +111,13 @@ DDReport *DDetectorImpl::MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt,
bool has_deadlock = trylock
? dd.onTryLock(&lt->dd, m->id)
: dd.onLock(&lt->dd, m->id);
- DDReport *rep = 0;
if (has_deadlock) {
uptr path[10];
uptr len = dd.findPathToHeldLock(&lt->dd, m->id,
path, ARRAY_SIZE(path));
CHECK_GT(len, 0U); // Hm.. cycle of 10 locks? I'd like to see that.
- rep = &lt->rep;
+ lt->report_pending = true;
+ DDReport *rep = &lt->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(&lt->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(&lt->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(&lt->dd, m->id));
- // Printf("T%d MutexLock: %zx\n", thr->tid, s->deadlock_detector_id);
- bool has_deadlock = trylock
- ? dd.onTryLock(&lt->dd, m->id)
- : dd.onLock(&lt->dd, m->id);
- DDReport *rep = 0;
- if (has_deadlock) {
- uptr path[10];
- uptr len = dd.findPathToHeldLock(&lt->dd, m->id,
- path, ARRAY_SIZE(path));
- CHECK_GT(len, 0U); // Hm.. cycle of 10 locks? I'd like to see that.
- rep = &lt->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(&lt->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;
}