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(<->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(<->dd, m->id)) return; // We already have all edges.
10865492d95SNico Weber SpinMutexLock lk(&mtx);
10965492d95SNico Weber MutexEnsureID(lt, m);
11065492d95SNico Weber if (dd.isHeld(<->dd, m->id))
11165492d95SNico Weber return; // FIXME: allow this only for recursive locks.
11265492d95SNico Weber if (dd.onLockBefore(<->dd, m->id)) {
11365492d95SNico Weber // Actually add this edge now so that we have all the stack traces.
11465492d95SNico Weber dd.addEdges(<->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(<->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 = <->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(<->dd, m->id, stk))
15965492d95SNico Weber return;
16065492d95SNico Weber if (dd.onLockFast(<->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(<->dd, m->id));
16765492d95SNico Weber if (!trylock)
16865492d95SNico Weber dd.addEdges(<->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
16965492d95SNico Weber dd.onLockAfter(<->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