1 | //===-- sanitizer_deadlock_detector1.cpp ----------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Deadlock detector implementation based on NxN adjacency bit matrix. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "sanitizer_deadlock_detector_interface.h" |
14 | #include "sanitizer_deadlock_detector.h" |
15 | #include "sanitizer_allocator_internal.h" |
16 | #include "sanitizer_placement_new.h" |
17 | #include "sanitizer_mutex.h" |
18 | |
19 | #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1 |
20 | |
21 | namespace __sanitizer { |
22 | |
23 | typedef TwoLevelBitVector<> DDBV; // DeadlockDetector's bit vector. |
24 | |
25 | struct DDPhysicalThread { |
26 | }; |
27 | |
28 | struct DDLogicalThread { |
29 | u64 ctx; |
30 | DeadlockDetectorTLS<DDBV> dd; |
31 | DDReport rep; |
32 | bool report_pending; |
33 | }; |
34 | |
35 | struct DD final : public DDetector { |
36 | SpinMutex mtx; |
37 | DeadlockDetector<DDBV> dd; |
38 | DDFlags flags; |
39 | |
40 | explicit DD(const DDFlags *flags); |
41 | |
42 | DDPhysicalThread *CreatePhysicalThread() override; |
43 | void DestroyPhysicalThread(DDPhysicalThread *pt) override; |
44 | |
45 | DDLogicalThread *CreateLogicalThread(u64 ctx) override; |
46 | void DestroyLogicalThread(DDLogicalThread *lt) override; |
47 | |
48 | void MutexInit(DDCallback *cb, DDMutex *m) override; |
49 | void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override; |
50 | void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, |
51 | bool trylock) override; |
52 | void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override; |
53 | void MutexDestroy(DDCallback *cb, DDMutex *m) override; |
54 | |
55 | DDReport *GetReport(DDCallback *cb) override; |
56 | |
57 | void MutexEnsureID(DDLogicalThread *lt, DDMutex *m); |
58 | void ReportDeadlock(DDCallback *cb, DDMutex *m); |
59 | }; |
60 | |
61 | DDetector *DDetector::Create(const DDFlags *flags) { |
62 | (void)flags; |
63 | void *mem = MmapOrDie(size: sizeof(DD), mem_type: "deadlock detector" ); |
64 | return new(mem) DD(flags); |
65 | } |
66 | |
67 | DD::DD(const DDFlags *flags) |
68 | : flags(*flags) { |
69 | dd.clear(); |
70 | } |
71 | |
72 | DDPhysicalThread* DD::CreatePhysicalThread() { |
73 | return nullptr; |
74 | } |
75 | |
76 | void DD::DestroyPhysicalThread(DDPhysicalThread *pt) { |
77 | } |
78 | |
79 | DDLogicalThread* DD::CreateLogicalThread(u64 ctx) { |
80 | DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(size: sizeof(*lt)); |
81 | lt->ctx = ctx; |
82 | lt->dd.clear(); |
83 | lt->report_pending = false; |
84 | return lt; |
85 | } |
86 | |
87 | void DD::DestroyLogicalThread(DDLogicalThread *lt) { |
88 | lt->~DDLogicalThread(); |
89 | InternalFree(p: lt); |
90 | } |
91 | |
92 | void DD::MutexInit(DDCallback *cb, DDMutex *m) { |
93 | m->id = 0; |
94 | m->stk = cb->Unwind(); |
95 | } |
96 | |
97 | void DD::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) { |
98 | if (!dd.nodeBelongsToCurrentEpoch(node: m->id)) |
99 | m->id = dd.newNode(data: reinterpret_cast<uptr>(m)); |
100 | dd.ensureCurrentEpoch(dtls: <->dd); |
101 | } |
102 | |
103 | void DD::MutexBeforeLock(DDCallback *cb, |
104 | DDMutex *m, bool wlock) { |
105 | DDLogicalThread *lt = cb->lt; |
106 | if (lt->dd.empty()) return; // This will be the first lock held by lt. |
107 | if (dd.hasAllEdges(dtls: <->dd, cur_node: m->id)) return; // We already have all edges. |
108 | SpinMutexLock lk(&mtx); |
109 | MutexEnsureID(lt, m); |
110 | if (dd.isHeld(dtls: <->dd, node: m->id)) |
111 | return; // FIXME: allow this only for recursive locks. |
112 | if (dd.onLockBefore(dtls: <->dd, cur_node: m->id)) { |
113 | // Actually add this edge now so that we have all the stack traces. |
114 | dd.addEdges(dtls: <->dd, cur_node: m->id, stk: cb->Unwind(), unique_tid: cb->UniqueTid()); |
115 | ReportDeadlock(cb, m); |
116 | } |
117 | } |
118 | |
119 | void DD::ReportDeadlock(DDCallback *cb, DDMutex *m) { |
120 | DDLogicalThread *lt = cb->lt; |
121 | uptr path[20]; |
122 | uptr len = dd.findPathToLock(dtls: <->dd, cur_node: m->id, path, ARRAY_SIZE(path)); |
123 | if (len == 0U) { |
124 | // A cycle of 20+ locks? Well, that's a bit odd... |
125 | Printf(format: "WARNING: too long mutex cycle found\n" ); |
126 | return; |
127 | } |
128 | CHECK_EQ(m->id, path[0]); |
129 | lt->report_pending = true; |
130 | len = Min<uptr>(a: len, b: DDReport::kMaxLoopSize); |
131 | DDReport *rep = <->rep; |
132 | rep->n = len; |
133 | for (uptr i = 0; i < len; i++) { |
134 | uptr from = path[i]; |
135 | uptr to = path[(i + 1) % len]; |
136 | DDMutex *m0 = (DDMutex*)dd.getData(node: from); |
137 | DDMutex *m1 = (DDMutex*)dd.getData(node: to); |
138 | |
139 | u32 stk_from = 0, stk_to = 0; |
140 | int unique_tid = 0; |
141 | dd.findEdge(from_node: from, to_node: to, stk_from: &stk_from, stk_to: &stk_to, unique_tid: &unique_tid); |
142 | // Printf("Edge: %zd=>%zd: %u/%u T%d\n", from, to, stk_from, stk_to, |
143 | // unique_tid); |
144 | rep->loop[i].thr_ctx = unique_tid; |
145 | rep->loop[i].mtx_ctx0 = m0->ctx; |
146 | rep->loop[i].mtx_ctx1 = m1->ctx; |
147 | rep->loop[i].stk[0] = stk_to; |
148 | rep->loop[i].stk[1] = stk_from; |
149 | } |
150 | } |
151 | |
152 | void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) { |
153 | DDLogicalThread *lt = cb->lt; |
154 | u32 stk = 0; |
155 | if (flags.second_deadlock_stack) |
156 | stk = cb->Unwind(); |
157 | // Printf("T%p MutexLock: %zx stk %u\n", lt, m->id, stk); |
158 | if (dd.onFirstLock(dtls: <->dd, node: m->id, stk)) |
159 | return; |
160 | if (dd.onLockFast(dtls: <->dd, node: m->id, stk)) |
161 | return; |
162 | |
163 | SpinMutexLock lk(&mtx); |
164 | MutexEnsureID(lt, m); |
165 | if (wlock) // Only a recursive rlock may be held. |
166 | CHECK(!dd.isHeld(<->dd, m->id)); |
167 | if (!trylock) |
168 | dd.addEdges(dtls: <->dd, cur_node: m->id, stk: stk ? stk : cb->Unwind(), unique_tid: cb->UniqueTid()); |
169 | dd.onLockAfter(dtls: <->dd, cur_node: m->id, stk); |
170 | } |
171 | |
172 | void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) { |
173 | // Printf("T%p MutexUnLock: %zx\n", cb->lt, m->id); |
174 | dd.onUnlock(dtls: &cb->lt->dd, node: m->id); |
175 | } |
176 | |
177 | void DD::MutexDestroy(DDCallback *cb, |
178 | DDMutex *m) { |
179 | if (!m->id) return; |
180 | SpinMutexLock lk(&mtx); |
181 | if (dd.nodeBelongsToCurrentEpoch(node: m->id)) |
182 | dd.removeNode(node: m->id); |
183 | m->id = 0; |
184 | } |
185 | |
186 | DDReport *DD::GetReport(DDCallback *cb) { |
187 | if (!cb->lt->report_pending) |
188 | return nullptr; |
189 | cb->lt->report_pending = false; |
190 | return &cb->lt->rep; |
191 | } |
192 | |
193 | } // namespace __sanitizer |
194 | #endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1 |
195 | |