168d75effSDimitry Andric //===-- sanitizer_deadlock_detector2.cpp ----------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // Deadlock detector implementation based on adjacency lists.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric //===----------------------------------------------------------------------===//
1268d75effSDimitry Andric 
1368d75effSDimitry Andric #include "sanitizer_deadlock_detector_interface.h"
1468d75effSDimitry Andric #include "sanitizer_common.h"
1568d75effSDimitry Andric #include "sanitizer_allocator_internal.h"
1668d75effSDimitry Andric #include "sanitizer_placement_new.h"
1768d75effSDimitry Andric #include "sanitizer_mutex.h"
1868d75effSDimitry Andric 
1968d75effSDimitry Andric #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
2068d75effSDimitry Andric 
2168d75effSDimitry Andric namespace __sanitizer {
2268d75effSDimitry Andric 
2368d75effSDimitry Andric const int kMaxNesting = 64;
2468d75effSDimitry Andric const u32 kNoId = -1;
2568d75effSDimitry Andric const u32 kEndId = -2;
2668d75effSDimitry Andric const int kMaxLink = 8;
2768d75effSDimitry Andric const int kL1Size = 1024;
2868d75effSDimitry Andric const int kL2Size = 1024;
2968d75effSDimitry Andric const int kMaxMutex = kL1Size * kL2Size;
3068d75effSDimitry Andric 
3168d75effSDimitry Andric struct Id {
3268d75effSDimitry Andric   u32 id;
3368d75effSDimitry Andric   u32 seq;
3468d75effSDimitry Andric 
Id__sanitizer::Id3568d75effSDimitry Andric   explicit Id(u32 id = 0, u32 seq = 0)
3668d75effSDimitry Andric       : id(id)
3768d75effSDimitry Andric       , seq(seq) {
3868d75effSDimitry Andric   }
3968d75effSDimitry Andric };
4068d75effSDimitry Andric 
4168d75effSDimitry Andric struct Link {
4268d75effSDimitry Andric   u32 id;
4368d75effSDimitry Andric   u32 seq;
4468d75effSDimitry Andric   u32 tid;
4568d75effSDimitry Andric   u32 stk0;
4668d75effSDimitry Andric   u32 stk1;
4768d75effSDimitry Andric 
Link__sanitizer::Link4868d75effSDimitry Andric   explicit Link(u32 id = 0, u32 seq = 0, u32 tid = 0, u32 s0 = 0, u32 s1 = 0)
4968d75effSDimitry Andric       : id(id)
5068d75effSDimitry Andric       , seq(seq)
5168d75effSDimitry Andric       , tid(tid)
5268d75effSDimitry Andric       , stk0(s0)
5368d75effSDimitry Andric       , stk1(s1) {
5468d75effSDimitry Andric   }
5568d75effSDimitry Andric };
5668d75effSDimitry Andric 
5768d75effSDimitry Andric struct DDPhysicalThread {
5868d75effSDimitry Andric   DDReport rep;
5968d75effSDimitry Andric   bool report_pending;
6068d75effSDimitry Andric   bool visited[kMaxMutex];
6168d75effSDimitry Andric   Link pending[kMaxMutex];
6268d75effSDimitry Andric   Link path[kMaxMutex];
6368d75effSDimitry Andric };
6468d75effSDimitry Andric 
6568d75effSDimitry Andric struct ThreadMutex {
6668d75effSDimitry Andric   u32 id;
6768d75effSDimitry Andric   u32 stk;
6868d75effSDimitry Andric };
6968d75effSDimitry Andric 
7068d75effSDimitry Andric struct DDLogicalThread {
7168d75effSDimitry Andric   u64         ctx;
7268d75effSDimitry Andric   ThreadMutex locked[kMaxNesting];
7368d75effSDimitry Andric   int         nlocked;
7468d75effSDimitry Andric };
7568d75effSDimitry Andric 
76*fe6060f1SDimitry Andric struct MutexState {
7768d75effSDimitry Andric   StaticSpinMutex mtx;
7868d75effSDimitry Andric   u32 seq;
7968d75effSDimitry Andric   int nlink;
8068d75effSDimitry Andric   Link link[kMaxLink];
8168d75effSDimitry Andric };
8268d75effSDimitry Andric 
83e8d8bef9SDimitry Andric struct DD final : public DDetector {
8468d75effSDimitry Andric   explicit DD(const DDFlags *flags);
8568d75effSDimitry Andric 
8668d75effSDimitry Andric   DDPhysicalThread* CreatePhysicalThread();
8768d75effSDimitry Andric   void DestroyPhysicalThread(DDPhysicalThread *pt);
8868d75effSDimitry Andric 
8968d75effSDimitry Andric   DDLogicalThread* CreateLogicalThread(u64 ctx);
9068d75effSDimitry Andric   void DestroyLogicalThread(DDLogicalThread *lt);
9168d75effSDimitry Andric 
9268d75effSDimitry Andric   void MutexInit(DDCallback *cb, DDMutex *m);
9368d75effSDimitry Andric   void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
9468d75effSDimitry Andric   void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
9568d75effSDimitry Andric       bool trylock);
9668d75effSDimitry Andric   void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
9768d75effSDimitry Andric   void MutexDestroy(DDCallback *cb, DDMutex *m);
9868d75effSDimitry Andric 
9968d75effSDimitry Andric   DDReport *GetReport(DDCallback *cb);
10068d75effSDimitry Andric 
10168d75effSDimitry Andric   void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx);
10268d75effSDimitry Andric   void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath);
10368d75effSDimitry Andric   u32 allocateId(DDCallback *cb);
104*fe6060f1SDimitry Andric   MutexState *getMutex(u32 id);
105*fe6060f1SDimitry Andric   u32 getMutexId(MutexState *m);
10668d75effSDimitry Andric 
10768d75effSDimitry Andric   DDFlags flags;
10868d75effSDimitry Andric 
109*fe6060f1SDimitry Andric   MutexState *mutex[kL1Size];
11068d75effSDimitry Andric 
11168d75effSDimitry Andric   SpinMutex mtx;
11268d75effSDimitry Andric   InternalMmapVector<u32> free_id;
11368d75effSDimitry Andric   int id_gen = 0;
11468d75effSDimitry Andric };
11568d75effSDimitry Andric 
Create(const DDFlags * flags)11668d75effSDimitry Andric DDetector *DDetector::Create(const DDFlags *flags) {
11768d75effSDimitry Andric   (void)flags;
11868d75effSDimitry Andric   void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
11968d75effSDimitry Andric   return new(mem) DD(flags);
12068d75effSDimitry Andric }
12168d75effSDimitry Andric 
DD(const DDFlags * flags)12268d75effSDimitry Andric DD::DD(const DDFlags *flags) : flags(*flags) { free_id.reserve(1024); }
12368d75effSDimitry Andric 
CreatePhysicalThread()12468d75effSDimitry Andric DDPhysicalThread* DD::CreatePhysicalThread() {
12568d75effSDimitry Andric   DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread),
12668d75effSDimitry Andric       "deadlock detector (physical thread)");
12768d75effSDimitry Andric   return pt;
12868d75effSDimitry Andric }
12968d75effSDimitry Andric 
DestroyPhysicalThread(DDPhysicalThread * pt)13068d75effSDimitry Andric void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
13168d75effSDimitry Andric   pt->~DDPhysicalThread();
13268d75effSDimitry Andric   UnmapOrDie(pt, sizeof(DDPhysicalThread));
13368d75effSDimitry Andric }
13468d75effSDimitry Andric 
CreateLogicalThread(u64 ctx)13568d75effSDimitry Andric DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
13668d75effSDimitry Andric   DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(
13768d75effSDimitry Andric       sizeof(DDLogicalThread));
13868d75effSDimitry Andric   lt->ctx = ctx;
13968d75effSDimitry Andric   lt->nlocked = 0;
14068d75effSDimitry Andric   return lt;
14168d75effSDimitry Andric }
14268d75effSDimitry Andric 
DestroyLogicalThread(DDLogicalThread * lt)14368d75effSDimitry Andric void DD::DestroyLogicalThread(DDLogicalThread *lt) {
14468d75effSDimitry Andric   lt->~DDLogicalThread();
14568d75effSDimitry Andric   InternalFree(lt);
14668d75effSDimitry Andric }
14768d75effSDimitry Andric 
MutexInit(DDCallback * cb,DDMutex * m)14868d75effSDimitry Andric void DD::MutexInit(DDCallback *cb, DDMutex *m) {
14968d75effSDimitry Andric   VPrintf(2, "#%llu: DD::MutexInit(%p)\n", cb->lt->ctx, m);
15068d75effSDimitry Andric   m->id = kNoId;
15168d75effSDimitry Andric   m->recursion = 0;
15268d75effSDimitry Andric   atomic_store(&m->owner, 0, memory_order_relaxed);
15368d75effSDimitry Andric }
15468d75effSDimitry Andric 
getMutex(u32 id)155*fe6060f1SDimitry Andric MutexState *DD::getMutex(u32 id) { return &mutex[id / kL2Size][id % kL2Size]; }
15668d75effSDimitry Andric 
getMutexId(MutexState * m)157*fe6060f1SDimitry Andric u32 DD::getMutexId(MutexState *m) {
15868d75effSDimitry Andric   for (int i = 0; i < kL1Size; i++) {
159*fe6060f1SDimitry Andric     MutexState *tab = mutex[i];
16068d75effSDimitry Andric     if (tab == 0)
16168d75effSDimitry Andric       break;
16268d75effSDimitry Andric     if (m >= tab && m < tab + kL2Size)
16368d75effSDimitry Andric       return i * kL2Size + (m - tab);
16468d75effSDimitry Andric   }
16568d75effSDimitry Andric   return -1;
16668d75effSDimitry Andric }
16768d75effSDimitry Andric 
allocateId(DDCallback * cb)16868d75effSDimitry Andric u32 DD::allocateId(DDCallback *cb) {
16968d75effSDimitry Andric   u32 id = -1;
17068d75effSDimitry Andric   SpinMutexLock l(&mtx);
17168d75effSDimitry Andric   if (free_id.size() > 0) {
17268d75effSDimitry Andric     id = free_id.back();
17368d75effSDimitry Andric     free_id.pop_back();
17468d75effSDimitry Andric   } else {
17568d75effSDimitry Andric     CHECK_LT(id_gen, kMaxMutex);
17668d75effSDimitry Andric     if ((id_gen % kL2Size) == 0) {
177*fe6060f1SDimitry Andric       mutex[id_gen / kL2Size] = (MutexState *)MmapOrDie(
178*fe6060f1SDimitry Andric           kL2Size * sizeof(MutexState), "deadlock detector (mutex table)");
17968d75effSDimitry Andric     }
18068d75effSDimitry Andric     id = id_gen++;
18168d75effSDimitry Andric   }
18268d75effSDimitry Andric   CHECK_LE(id, kMaxMutex);
18368d75effSDimitry Andric   VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
18468d75effSDimitry Andric   return id;
18568d75effSDimitry Andric }
18668d75effSDimitry Andric 
MutexBeforeLock(DDCallback * cb,DDMutex * m,bool wlock)18768d75effSDimitry Andric void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {
18868d75effSDimitry Andric   VPrintf(2, "#%llu: DD::MutexBeforeLock(%p, wlock=%d) nlocked=%d\n",
18968d75effSDimitry Andric       cb->lt->ctx, m, wlock, cb->lt->nlocked);
19068d75effSDimitry Andric   DDPhysicalThread *pt = cb->pt;
19168d75effSDimitry Andric   DDLogicalThread *lt = cb->lt;
19268d75effSDimitry Andric 
19368d75effSDimitry Andric   uptr owner = atomic_load(&m->owner, memory_order_relaxed);
19468d75effSDimitry Andric   if (owner == (uptr)cb->lt) {
19568d75effSDimitry Andric     VPrintf(3, "#%llu: DD::MutexBeforeLock recursive\n",
19668d75effSDimitry Andric         cb->lt->ctx);
19768d75effSDimitry Andric     return;
19868d75effSDimitry Andric   }
19968d75effSDimitry Andric 
20068d75effSDimitry Andric   CHECK_LE(lt->nlocked, kMaxNesting);
20168d75effSDimitry Andric 
20268d75effSDimitry Andric   // FIXME(dvyukov): don't allocate id if lt->nlocked == 0?
20368d75effSDimitry Andric   if (m->id == kNoId)
20468d75effSDimitry Andric     m->id = allocateId(cb);
20568d75effSDimitry Andric 
20668d75effSDimitry Andric   ThreadMutex *tm = &lt->locked[lt->nlocked++];
20768d75effSDimitry Andric   tm->id = m->id;
20868d75effSDimitry Andric   if (flags.second_deadlock_stack)
20968d75effSDimitry Andric     tm->stk = cb->Unwind();
21068d75effSDimitry Andric   if (lt->nlocked == 1) {
21168d75effSDimitry Andric     VPrintf(3, "#%llu: DD::MutexBeforeLock first mutex\n",
21268d75effSDimitry Andric         cb->lt->ctx);
21368d75effSDimitry Andric     return;
21468d75effSDimitry Andric   }
21568d75effSDimitry Andric 
21668d75effSDimitry Andric   bool added = false;
217*fe6060f1SDimitry Andric   MutexState *mtx = getMutex(m->id);
21868d75effSDimitry Andric   for (int i = 0; i < lt->nlocked - 1; i++) {
21968d75effSDimitry Andric     u32 id1 = lt->locked[i].id;
22068d75effSDimitry Andric     u32 stk1 = lt->locked[i].stk;
221*fe6060f1SDimitry Andric     MutexState *mtx1 = getMutex(id1);
22268d75effSDimitry Andric     SpinMutexLock l(&mtx1->mtx);
22368d75effSDimitry Andric     if (mtx1->nlink == kMaxLink) {
22468d75effSDimitry Andric       // FIXME(dvyukov): check stale links
22568d75effSDimitry Andric       continue;
22668d75effSDimitry Andric     }
22768d75effSDimitry Andric     int li = 0;
22868d75effSDimitry Andric     for (; li < mtx1->nlink; li++) {
22968d75effSDimitry Andric       Link *link = &mtx1->link[li];
23068d75effSDimitry Andric       if (link->id == m->id) {
23168d75effSDimitry Andric         if (link->seq != mtx->seq) {
23268d75effSDimitry Andric           link->seq = mtx->seq;
23368d75effSDimitry Andric           link->tid = lt->ctx;
23468d75effSDimitry Andric           link->stk0 = stk1;
23568d75effSDimitry Andric           link->stk1 = cb->Unwind();
23668d75effSDimitry Andric           added = true;
23768d75effSDimitry Andric           VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
23868d75effSDimitry Andric               cb->lt->ctx, getMutexId(mtx1), m->id);
23968d75effSDimitry Andric         }
24068d75effSDimitry Andric         break;
24168d75effSDimitry Andric       }
24268d75effSDimitry Andric     }
24368d75effSDimitry Andric     if (li == mtx1->nlink) {
24468d75effSDimitry Andric       // FIXME(dvyukov): check stale links
24568d75effSDimitry Andric       Link *link = &mtx1->link[mtx1->nlink++];
24668d75effSDimitry Andric       link->id = m->id;
24768d75effSDimitry Andric       link->seq = mtx->seq;
24868d75effSDimitry Andric       link->tid = lt->ctx;
24968d75effSDimitry Andric       link->stk0 = stk1;
25068d75effSDimitry Andric       link->stk1 = cb->Unwind();
25168d75effSDimitry Andric       added = true;
25268d75effSDimitry Andric       VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
25368d75effSDimitry Andric           cb->lt->ctx, getMutexId(mtx1), m->id);
25468d75effSDimitry Andric     }
25568d75effSDimitry Andric   }
25668d75effSDimitry Andric 
25768d75effSDimitry Andric   if (!added || mtx->nlink == 0) {
25868d75effSDimitry Andric     VPrintf(3, "#%llu: DD::MutexBeforeLock don't check\n",
25968d75effSDimitry Andric         cb->lt->ctx);
26068d75effSDimitry Andric     return;
26168d75effSDimitry Andric   }
26268d75effSDimitry Andric 
26368d75effSDimitry Andric   CycleCheck(pt, lt, m);
26468d75effSDimitry Andric }
26568d75effSDimitry Andric 
MutexAfterLock(DDCallback * cb,DDMutex * m,bool wlock,bool trylock)26668d75effSDimitry Andric void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
26768d75effSDimitry Andric     bool trylock) {
26868d75effSDimitry Andric   VPrintf(2, "#%llu: DD::MutexAfterLock(%p, wlock=%d, try=%d) nlocked=%d\n",
26968d75effSDimitry Andric       cb->lt->ctx, m, wlock, trylock, cb->lt->nlocked);
27068d75effSDimitry Andric   DDLogicalThread *lt = cb->lt;
27168d75effSDimitry Andric 
27268d75effSDimitry Andric   uptr owner = atomic_load(&m->owner, memory_order_relaxed);
27368d75effSDimitry Andric   if (owner == (uptr)cb->lt) {
27468d75effSDimitry Andric     VPrintf(3, "#%llu: DD::MutexAfterLock recursive\n", cb->lt->ctx);
27568d75effSDimitry Andric     CHECK(wlock);
27668d75effSDimitry Andric     m->recursion++;
27768d75effSDimitry Andric     return;
27868d75effSDimitry Andric   }
27968d75effSDimitry Andric   CHECK_EQ(owner, 0);
28068d75effSDimitry Andric   if (wlock) {
28168d75effSDimitry Andric     VPrintf(3, "#%llu: DD::MutexAfterLock set owner\n", cb->lt->ctx);
28268d75effSDimitry Andric     CHECK_EQ(m->recursion, 0);
28368d75effSDimitry Andric     m->recursion = 1;
28468d75effSDimitry Andric     atomic_store(&m->owner, (uptr)cb->lt, memory_order_relaxed);
28568d75effSDimitry Andric   }
28668d75effSDimitry Andric 
28768d75effSDimitry Andric   if (!trylock)
28868d75effSDimitry Andric     return;
28968d75effSDimitry Andric 
29068d75effSDimitry Andric   CHECK_LE(lt->nlocked, kMaxNesting);
29168d75effSDimitry Andric   if (m->id == kNoId)
29268d75effSDimitry Andric     m->id = allocateId(cb);
29368d75effSDimitry Andric   ThreadMutex *tm = &lt->locked[lt->nlocked++];
29468d75effSDimitry Andric   tm->id = m->id;
29568d75effSDimitry Andric   if (flags.second_deadlock_stack)
29668d75effSDimitry Andric     tm->stk = cb->Unwind();
29768d75effSDimitry Andric }
29868d75effSDimitry Andric 
MutexBeforeUnlock(DDCallback * cb,DDMutex * m,bool wlock)29968d75effSDimitry Andric void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
30068d75effSDimitry Andric   VPrintf(2, "#%llu: DD::MutexBeforeUnlock(%p, wlock=%d) nlocked=%d\n",
30168d75effSDimitry Andric       cb->lt->ctx, m, wlock, cb->lt->nlocked);
30268d75effSDimitry Andric   DDLogicalThread *lt = cb->lt;
30368d75effSDimitry Andric 
30468d75effSDimitry Andric   uptr owner = atomic_load(&m->owner, memory_order_relaxed);
30568d75effSDimitry Andric   if (owner == (uptr)cb->lt) {
30668d75effSDimitry Andric     VPrintf(3, "#%llu: DD::MutexBeforeUnlock recursive\n", cb->lt->ctx);
30768d75effSDimitry Andric     if (--m->recursion > 0)
30868d75effSDimitry Andric       return;
30968d75effSDimitry Andric     VPrintf(3, "#%llu: DD::MutexBeforeUnlock reset owner\n", cb->lt->ctx);
31068d75effSDimitry Andric     atomic_store(&m->owner, 0, memory_order_relaxed);
31168d75effSDimitry Andric   }
31268d75effSDimitry Andric   CHECK_NE(m->id, kNoId);
31368d75effSDimitry Andric   int last = lt->nlocked - 1;
31468d75effSDimitry Andric   for (int i = last; i >= 0; i--) {
31568d75effSDimitry Andric     if (cb->lt->locked[i].id == m->id) {
31668d75effSDimitry Andric       lt->locked[i] = lt->locked[last];
31768d75effSDimitry Andric       lt->nlocked--;
31868d75effSDimitry Andric       break;
31968d75effSDimitry Andric     }
32068d75effSDimitry Andric   }
32168d75effSDimitry Andric }
32268d75effSDimitry Andric 
MutexDestroy(DDCallback * cb,DDMutex * m)32368d75effSDimitry Andric void DD::MutexDestroy(DDCallback *cb, DDMutex *m) {
32468d75effSDimitry Andric   VPrintf(2, "#%llu: DD::MutexDestroy(%p)\n",
32568d75effSDimitry Andric       cb->lt->ctx, m);
32668d75effSDimitry Andric   DDLogicalThread *lt = cb->lt;
32768d75effSDimitry Andric 
32868d75effSDimitry Andric   if (m->id == kNoId)
32968d75effSDimitry Andric     return;
33068d75effSDimitry Andric 
33168d75effSDimitry Andric   // Remove the mutex from lt->locked if there.
33268d75effSDimitry Andric   int last = lt->nlocked - 1;
33368d75effSDimitry Andric   for (int i = last; i >= 0; i--) {
33468d75effSDimitry Andric     if (lt->locked[i].id == m->id) {
33568d75effSDimitry Andric       lt->locked[i] = lt->locked[last];
33668d75effSDimitry Andric       lt->nlocked--;
33768d75effSDimitry Andric       break;
33868d75effSDimitry Andric     }
33968d75effSDimitry Andric   }
34068d75effSDimitry Andric 
34168d75effSDimitry Andric   // Clear and invalidate the mutex descriptor.
34268d75effSDimitry Andric   {
343*fe6060f1SDimitry Andric     MutexState *mtx = getMutex(m->id);
34468d75effSDimitry Andric     SpinMutexLock l(&mtx->mtx);
34568d75effSDimitry Andric     mtx->seq++;
34668d75effSDimitry Andric     mtx->nlink = 0;
34768d75effSDimitry Andric   }
34868d75effSDimitry Andric 
34968d75effSDimitry Andric   // Return id to cache.
35068d75effSDimitry Andric   {
35168d75effSDimitry Andric     SpinMutexLock l(&mtx);
35268d75effSDimitry Andric     free_id.push_back(m->id);
35368d75effSDimitry Andric   }
35468d75effSDimitry Andric }
35568d75effSDimitry Andric 
CycleCheck(DDPhysicalThread * pt,DDLogicalThread * lt,DDMutex * m)35668d75effSDimitry Andric void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
35768d75effSDimitry Andric     DDMutex *m) {
35868d75effSDimitry Andric   internal_memset(pt->visited, 0, sizeof(pt->visited));
35968d75effSDimitry Andric   int npath = 0;
36068d75effSDimitry Andric   int npending = 0;
36168d75effSDimitry Andric   {
362*fe6060f1SDimitry Andric     MutexState *mtx = getMutex(m->id);
36368d75effSDimitry Andric     SpinMutexLock l(&mtx->mtx);
36468d75effSDimitry Andric     for (int li = 0; li < mtx->nlink; li++)
36568d75effSDimitry Andric       pt->pending[npending++] = mtx->link[li];
36668d75effSDimitry Andric   }
36768d75effSDimitry Andric   while (npending > 0) {
36868d75effSDimitry Andric     Link link = pt->pending[--npending];
36968d75effSDimitry Andric     if (link.id == kEndId) {
37068d75effSDimitry Andric       npath--;
37168d75effSDimitry Andric       continue;
37268d75effSDimitry Andric     }
37368d75effSDimitry Andric     if (pt->visited[link.id])
37468d75effSDimitry Andric       continue;
375*fe6060f1SDimitry Andric     MutexState *mtx1 = getMutex(link.id);
37668d75effSDimitry Andric     SpinMutexLock l(&mtx1->mtx);
37768d75effSDimitry Andric     if (mtx1->seq != link.seq)
37868d75effSDimitry Andric       continue;
37968d75effSDimitry Andric     pt->visited[link.id] = true;
38068d75effSDimitry Andric     if (mtx1->nlink == 0)
38168d75effSDimitry Andric       continue;
38268d75effSDimitry Andric     pt->path[npath++] = link;
38368d75effSDimitry Andric     pt->pending[npending++] = Link(kEndId);
38468d75effSDimitry Andric     if (link.id == m->id)
38568d75effSDimitry Andric       return Report(pt, lt, npath);  // Bingo!
38668d75effSDimitry Andric     for (int li = 0; li < mtx1->nlink; li++) {
38768d75effSDimitry Andric       Link *link1 = &mtx1->link[li];
388*fe6060f1SDimitry Andric       // MutexState *mtx2 = getMutex(link->id);
38968d75effSDimitry Andric       // FIXME(dvyukov): fast seq check
39068d75effSDimitry Andric       // FIXME(dvyukov): fast nlink != 0 check
39168d75effSDimitry Andric       // FIXME(dvyukov): fast pending check?
39268d75effSDimitry Andric       // FIXME(dvyukov): npending can be larger than kMaxMutex
39368d75effSDimitry Andric       pt->pending[npending++] = *link1;
39468d75effSDimitry Andric     }
39568d75effSDimitry Andric   }
39668d75effSDimitry Andric }
39768d75effSDimitry Andric 
Report(DDPhysicalThread * pt,DDLogicalThread * lt,int npath)39868d75effSDimitry Andric void DD::Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath) {
39968d75effSDimitry Andric   DDReport *rep = &pt->rep;
40068d75effSDimitry Andric   rep->n = npath;
40168d75effSDimitry Andric   for (int i = 0; i < npath; i++) {
40268d75effSDimitry Andric     Link *link = &pt->path[i];
40368d75effSDimitry Andric     Link *link0 = &pt->path[i ? i - 1 : npath - 1];
40468d75effSDimitry Andric     rep->loop[i].thr_ctx = link->tid;
40568d75effSDimitry Andric     rep->loop[i].mtx_ctx0 = link0->id;
40668d75effSDimitry Andric     rep->loop[i].mtx_ctx1 = link->id;
40768d75effSDimitry Andric     rep->loop[i].stk[0] = flags.second_deadlock_stack ? link->stk0 : 0;
40868d75effSDimitry Andric     rep->loop[i].stk[1] = link->stk1;
40968d75effSDimitry Andric   }
41068d75effSDimitry Andric   pt->report_pending = true;
41168d75effSDimitry Andric }
41268d75effSDimitry Andric 
GetReport(DDCallback * cb)41368d75effSDimitry Andric DDReport *DD::GetReport(DDCallback *cb) {
41468d75effSDimitry Andric   if (!cb->pt->report_pending)
41568d75effSDimitry Andric     return 0;
41668d75effSDimitry Andric   cb->pt->report_pending = false;
41768d75effSDimitry Andric   return &cb->pt->rep;
41868d75effSDimitry Andric }
41968d75effSDimitry Andric 
42068d75effSDimitry Andric }  // namespace __sanitizer
42168d75effSDimitry Andric #endif  // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
422