xref: /llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cpp (revision 326b0054fd32ecaa6da0c7ae014fb9bbf1e80048)
165492d95SNico Weber //===-- sanitizer_deadlock_detector1.cpp ----------------------------------===//
265492d95SNico Weber //
365492d95SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
465492d95SNico Weber // See https://llvm.org/LICENSE.txt for license information.
565492d95SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
665492d95SNico Weber //
765492d95SNico Weber //===----------------------------------------------------------------------===//
865492d95SNico Weber //
965492d95SNico Weber // Deadlock detector implementation based on NxN adjacency bit matrix.
1065492d95SNico Weber //
1165492d95SNico Weber //===----------------------------------------------------------------------===//
1265492d95SNico Weber 
1365492d95SNico Weber #include "sanitizer_deadlock_detector_interface.h"
1465492d95SNico Weber #include "sanitizer_deadlock_detector.h"
1565492d95SNico Weber #include "sanitizer_allocator_internal.h"
1665492d95SNico Weber #include "sanitizer_placement_new.h"
1765492d95SNico Weber #include "sanitizer_mutex.h"
1865492d95SNico Weber 
1965492d95SNico Weber #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
2065492d95SNico Weber 
2165492d95SNico Weber namespace __sanitizer {
2265492d95SNico Weber 
2365492d95SNico Weber typedef TwoLevelBitVector<> DDBV;  // DeadlockDetector's bit vector.
2465492d95SNico Weber 
2565492d95SNico Weber struct DDPhysicalThread {
2665492d95SNico Weber };
2765492d95SNico Weber 
2865492d95SNico Weber struct DDLogicalThread {
2965492d95SNico Weber   u64 ctx;
3065492d95SNico Weber   DeadlockDetectorTLS<DDBV> dd;
3165492d95SNico Weber   DDReport rep;
3265492d95SNico Weber   bool report_pending;
3365492d95SNico Weber };
3465492d95SNico Weber 
358b37a4e6SVitaly Buka struct DD final : public DDetector {
3665492d95SNico Weber   SpinMutex mtx;
3765492d95SNico Weber   DeadlockDetector<DDBV> dd;
3865492d95SNico Weber   DDFlags flags;
3965492d95SNico Weber 
4065492d95SNico Weber   explicit DD(const DDFlags *flags);
4165492d95SNico Weber 
4265492d95SNico Weber   DDPhysicalThread *CreatePhysicalThread() override;
4365492d95SNico Weber   void DestroyPhysicalThread(DDPhysicalThread *pt) override;
4465492d95SNico Weber 
4565492d95SNico Weber   DDLogicalThread *CreateLogicalThread(u64 ctx) override;
4665492d95SNico Weber   void DestroyLogicalThread(DDLogicalThread *lt) override;
4765492d95SNico Weber 
4865492d95SNico Weber   void MutexInit(DDCallback *cb, DDMutex *m) override;
4965492d95SNico Weber   void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override;
5065492d95SNico Weber   void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
5165492d95SNico Weber                       bool trylock) override;
5265492d95SNico Weber   void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override;
5365492d95SNico Weber   void MutexDestroy(DDCallback *cb, DDMutex *m) override;
5465492d95SNico Weber 
5565492d95SNico Weber   DDReport *GetReport(DDCallback *cb) override;
5665492d95SNico Weber 
5765492d95SNico Weber   void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);
5865492d95SNico Weber   void ReportDeadlock(DDCallback *cb, DDMutex *m);
5965492d95SNico Weber };
6065492d95SNico Weber 
Create(const DDFlags * flags)6165492d95SNico Weber DDetector *DDetector::Create(const DDFlags *flags) {
6265492d95SNico Weber   (void)flags;
6365492d95SNico Weber   void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
6465492d95SNico Weber   return new(mem) DD(flags);
6565492d95SNico Weber }
6665492d95SNico Weber 
DD(const DDFlags * flags)6765492d95SNico Weber DD::DD(const DDFlags *flags)
6865492d95SNico Weber     : flags(*flags) {
6965492d95SNico Weber   dd.clear();
7065492d95SNico Weber }
7165492d95SNico Weber 
CreatePhysicalThread()7265492d95SNico Weber DDPhysicalThread* DD::CreatePhysicalThread() {
7365492d95SNico Weber   return nullptr;
7465492d95SNico Weber }
7565492d95SNico Weber 
DestroyPhysicalThread(DDPhysicalThread * pt)7665492d95SNico Weber void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
7765492d95SNico Weber }
7865492d95SNico Weber 
CreateLogicalThread(u64 ctx)7965492d95SNico Weber DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
8065492d95SNico Weber   DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt));
8165492d95SNico Weber   lt->ctx = ctx;
8265492d95SNico Weber   lt->dd.clear();
8365492d95SNico Weber   lt->report_pending = false;
8465492d95SNico Weber   return lt;
8565492d95SNico Weber }
8665492d95SNico Weber 
DestroyLogicalThread(DDLogicalThread * lt)8765492d95SNico Weber void DD::DestroyLogicalThread(DDLogicalThread *lt) {
8865492d95SNico Weber   lt->~DDLogicalThread();
8965492d95SNico Weber   InternalFree(lt);
9065492d95SNico Weber }
9165492d95SNico Weber 
MutexInit(DDCallback * cb,DDMutex * m)9265492d95SNico Weber void DD::MutexInit(DDCallback *cb, DDMutex *m) {
9365492d95SNico Weber   m->id = 0;
9465492d95SNico Weber   m->stk = cb->Unwind();
9565492d95SNico Weber }
9665492d95SNico Weber 
MutexEnsureID(DDLogicalThread * lt,DDMutex * m)9765492d95SNico Weber void DD::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) {
9865492d95SNico Weber   if (!dd.nodeBelongsToCurrentEpoch(m->id))
9965492d95SNico Weber     m->id = dd.newNode(reinterpret_cast<uptr>(m));
10065492d95SNico Weber   dd.ensureCurrentEpoch(&lt->dd);
10165492d95SNico Weber }
10265492d95SNico Weber 
MutexBeforeLock(DDCallback * cb,DDMutex * m,bool wlock)10365492d95SNico Weber void DD::MutexBeforeLock(DDCallback *cb,
10465492d95SNico Weber     DDMutex *m, bool wlock) {
10565492d95SNico Weber   DDLogicalThread *lt = cb->lt;
10665492d95SNico Weber   if (lt->dd.empty()) return;  // This will be the first lock held by lt.
10765492d95SNico Weber   if (dd.hasAllEdges(&lt->dd, m->id)) return;  // We already have all edges.
10865492d95SNico Weber   SpinMutexLock lk(&mtx);
10965492d95SNico Weber   MutexEnsureID(lt, m);
11065492d95SNico Weber   if (dd.isHeld(&lt->dd, m->id))
11165492d95SNico Weber     return;  // FIXME: allow this only for recursive locks.
11265492d95SNico Weber   if (dd.onLockBefore(&lt->dd, m->id)) {
11365492d95SNico Weber     // Actually add this edge now so that we have all the stack traces.
11465492d95SNico Weber     dd.addEdges(&lt->dd, m->id, cb->Unwind(), cb->UniqueTid());
11565492d95SNico Weber     ReportDeadlock(cb, m);
11665492d95SNico Weber   }
11765492d95SNico Weber }
11865492d95SNico Weber 
ReportDeadlock(DDCallback * cb,DDMutex * m)11965492d95SNico Weber void DD::ReportDeadlock(DDCallback *cb, DDMutex *m) {
12065492d95SNico Weber   DDLogicalThread *lt = cb->lt;
12165492d95SNico Weber   uptr path[20];
12265492d95SNico Weber   uptr len = dd.findPathToLock(&lt->dd, m->id, path, ARRAY_SIZE(path));
12365492d95SNico Weber   if (len == 0U) {
12465492d95SNico Weber     // A cycle of 20+ locks? Well, that's a bit odd...
12565492d95SNico Weber     Printf("WARNING: too long mutex cycle found\n");
12665492d95SNico Weber     return;
12765492d95SNico Weber   }
12865492d95SNico Weber   CHECK_EQ(m->id, path[0]);
12965492d95SNico Weber   lt->report_pending = true;
13065492d95SNico Weber   len = Min<uptr>(len, DDReport::kMaxLoopSize);
13165492d95SNico Weber   DDReport *rep = &lt->rep;
13265492d95SNico Weber   rep->n = len;
13365492d95SNico Weber   for (uptr i = 0; i < len; i++) {
13465492d95SNico Weber     uptr from = path[i];
13565492d95SNico Weber     uptr to = path[(i + 1) % len];
13665492d95SNico Weber     DDMutex *m0 = (DDMutex*)dd.getData(from);
13765492d95SNico Weber     DDMutex *m1 = (DDMutex*)dd.getData(to);
13865492d95SNico Weber 
139*326b0054SDmitry Vyukov     u32 stk_from = 0, stk_to = 0;
14065492d95SNico Weber     int unique_tid = 0;
14165492d95SNico Weber     dd.findEdge(from, to, &stk_from, &stk_to, &unique_tid);
14265492d95SNico Weber     // Printf("Edge: %zd=>%zd: %u/%u T%d\n", from, to, stk_from, stk_to,
14365492d95SNico Weber     //    unique_tid);
14465492d95SNico Weber     rep->loop[i].thr_ctx = unique_tid;
14565492d95SNico Weber     rep->loop[i].mtx_ctx0 = m0->ctx;
14665492d95SNico Weber     rep->loop[i].mtx_ctx1 = m1->ctx;
14765492d95SNico Weber     rep->loop[i].stk[0] = stk_to;
14865492d95SNico Weber     rep->loop[i].stk[1] = stk_from;
14965492d95SNico Weber   }
15065492d95SNico Weber }
15165492d95SNico Weber 
MutexAfterLock(DDCallback * cb,DDMutex * m,bool wlock,bool trylock)15265492d95SNico Weber void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) {
15365492d95SNico Weber   DDLogicalThread *lt = cb->lt;
15465492d95SNico Weber   u32 stk = 0;
15565492d95SNico Weber   if (flags.second_deadlock_stack)
15665492d95SNico Weber     stk = cb->Unwind();
15765492d95SNico Weber   // Printf("T%p MutexLock:   %zx stk %u\n", lt, m->id, stk);
15865492d95SNico Weber   if (dd.onFirstLock(&lt->dd, m->id, stk))
15965492d95SNico Weber     return;
16065492d95SNico Weber   if (dd.onLockFast(&lt->dd, m->id, stk))
16165492d95SNico Weber     return;
16265492d95SNico Weber 
16365492d95SNico Weber   SpinMutexLock lk(&mtx);
16465492d95SNico Weber   MutexEnsureID(lt, m);
16565492d95SNico Weber   if (wlock)  // Only a recursive rlock may be held.
16665492d95SNico Weber     CHECK(!dd.isHeld(&lt->dd, m->id));
16765492d95SNico Weber   if (!trylock)
16865492d95SNico Weber     dd.addEdges(&lt->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
16965492d95SNico Weber   dd.onLockAfter(&lt->dd, m->id, stk);
17065492d95SNico Weber }
17165492d95SNico Weber 
MutexBeforeUnlock(DDCallback * cb,DDMutex * m,bool wlock)17265492d95SNico Weber void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
17365492d95SNico Weber   // Printf("T%p MutexUnLock: %zx\n", cb->lt, m->id);
17465492d95SNico Weber   dd.onUnlock(&cb->lt->dd, m->id);
17565492d95SNico Weber }
17665492d95SNico Weber 
MutexDestroy(DDCallback * cb,DDMutex * m)17765492d95SNico Weber void DD::MutexDestroy(DDCallback *cb,
17865492d95SNico Weber     DDMutex *m) {
17965492d95SNico Weber   if (!m->id) return;
18065492d95SNico Weber   SpinMutexLock lk(&mtx);
18165492d95SNico Weber   if (dd.nodeBelongsToCurrentEpoch(m->id))
18265492d95SNico Weber     dd.removeNode(m->id);
18365492d95SNico Weber   m->id = 0;
18465492d95SNico Weber }
18565492d95SNico Weber 
GetReport(DDCallback * cb)18665492d95SNico Weber DDReport *DD::GetReport(DDCallback *cb) {
18765492d95SNico Weber   if (!cb->lt->report_pending)
18865492d95SNico Weber     return nullptr;
18965492d95SNico Weber   cb->lt->report_pending = false;
19065492d95SNico Weber   return &cb->lt->rep;
19165492d95SNico Weber }
19265492d95SNico Weber 
19365492d95SNico Weber } // namespace __sanitizer
19465492d95SNico Weber #endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
195