1 //===-- sanitizer_deadlock_detector2.cc -----------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Deadlock detector implementation based on adjacency lists.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "sanitizer_deadlock_detector_interface.h"
15 #include "sanitizer_common.h"
16 #include "sanitizer_allocator_internal.h"
17 #include "sanitizer_placement_new.h"
18 #include "sanitizer_mutex.h"
19
20 #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
21
22 namespace __sanitizer {
23
24 const int kMaxNesting = 64;
25 const u32 kNoId = -1;
26 const u32 kEndId = -2;
27 const int kMaxLink = 8;
28 const int kL1Size = 1024;
29 const int kL2Size = 1024;
30 const int kMaxMutex = kL1Size * kL2Size;
31
32 struct Id {
33 u32 id;
34 u32 seq;
35
Id__sanitizer::Id36 explicit Id(u32 id = 0, u32 seq = 0)
37 : id(id)
38 , seq(seq) {
39 }
40 };
41
42 struct Link {
43 u32 id;
44 u32 seq;
45 u32 tid;
46 u32 stk0;
47 u32 stk1;
48
Link__sanitizer::Link49 explicit Link(u32 id = 0, u32 seq = 0, u32 tid = 0, u32 s0 = 0, u32 s1 = 0)
50 : id(id)
51 , seq(seq)
52 , tid(tid)
53 , stk0(s0)
54 , stk1(s1) {
55 }
56 };
57
58 struct DDPhysicalThread {
59 DDReport rep;
60 bool report_pending;
61 bool visited[kMaxMutex];
62 Link pending[kMaxMutex];
63 Link path[kMaxMutex];
64 };
65
66 struct ThreadMutex {
67 u32 id;
68 u32 stk;
69 };
70
71 struct DDLogicalThread {
72 u64 ctx;
73 ThreadMutex locked[kMaxNesting];
74 int nlocked;
75 };
76
77 struct Mutex {
78 StaticSpinMutex mtx;
79 u32 seq;
80 int nlink;
81 Link link[kMaxLink];
82 };
83
84 struct DD : public DDetector {
85 explicit DD(const DDFlags *flags);
86
87 DDPhysicalThread* CreatePhysicalThread();
88 void DestroyPhysicalThread(DDPhysicalThread *pt);
89
90 DDLogicalThread* CreateLogicalThread(u64 ctx);
91 void DestroyLogicalThread(DDLogicalThread *lt);
92
93 void MutexInit(DDCallback *cb, DDMutex *m);
94 void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
95 void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
96 bool trylock);
97 void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
98 void MutexDestroy(DDCallback *cb, DDMutex *m);
99
100 DDReport *GetReport(DDCallback *cb);
101
102 void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx);
103 void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath);
104 u32 allocateId(DDCallback *cb);
105 Mutex *getMutex(u32 id);
106 u32 getMutexId(Mutex *m);
107
108 DDFlags flags;
109
110 Mutex* mutex[kL1Size];
111
112 SpinMutex mtx;
113 InternalMmapVector<u32> free_id;
114 int id_gen = 0;
115 };
116
Create(const DDFlags * flags)117 DDetector *DDetector::Create(const DDFlags *flags) {
118 (void)flags;
119 void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
120 return new(mem) DD(flags);
121 }
122
DD(const DDFlags * flags)123 DD::DD(const DDFlags *flags) : flags(*flags) { free_id.reserve(1024); }
124
CreatePhysicalThread()125 DDPhysicalThread* DD::CreatePhysicalThread() {
126 DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread),
127 "deadlock detector (physical thread)");
128 return pt;
129 }
130
DestroyPhysicalThread(DDPhysicalThread * pt)131 void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
132 pt->~DDPhysicalThread();
133 UnmapOrDie(pt, sizeof(DDPhysicalThread));
134 }
135
CreateLogicalThread(u64 ctx)136 DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
137 DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(
138 sizeof(DDLogicalThread));
139 lt->ctx = ctx;
140 lt->nlocked = 0;
141 return lt;
142 }
143
DestroyLogicalThread(DDLogicalThread * lt)144 void DD::DestroyLogicalThread(DDLogicalThread *lt) {
145 lt->~DDLogicalThread();
146 InternalFree(lt);
147 }
148
MutexInit(DDCallback * cb,DDMutex * m)149 void DD::MutexInit(DDCallback *cb, DDMutex *m) {
150 VPrintf(2, "#%llu: DD::MutexInit(%p)\n", cb->lt->ctx, m);
151 m->id = kNoId;
152 m->recursion = 0;
153 atomic_store(&m->owner, 0, memory_order_relaxed);
154 }
155
getMutex(u32 id)156 Mutex *DD::getMutex(u32 id) {
157 return &mutex[id / kL2Size][id % kL2Size];
158 }
159
getMutexId(Mutex * m)160 u32 DD::getMutexId(Mutex *m) {
161 for (int i = 0; i < kL1Size; i++) {
162 Mutex *tab = mutex[i];
163 if (tab == 0)
164 break;
165 if (m >= tab && m < tab + kL2Size)
166 return i * kL2Size + (m - tab);
167 }
168 return -1;
169 }
170
allocateId(DDCallback * cb)171 u32 DD::allocateId(DDCallback *cb) {
172 u32 id = -1;
173 SpinMutexLock l(&mtx);
174 if (free_id.size() > 0) {
175 id = free_id.back();
176 free_id.pop_back();
177 } else {
178 CHECK_LT(id_gen, kMaxMutex);
179 if ((id_gen % kL2Size) == 0) {
180 mutex[id_gen / kL2Size] = (Mutex*)MmapOrDie(kL2Size * sizeof(Mutex),
181 "deadlock detector (mutex table)");
182 }
183 id = id_gen++;
184 }
185 CHECK_LE(id, kMaxMutex);
186 VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
187 return id;
188 }
189
MutexBeforeLock(DDCallback * cb,DDMutex * m,bool wlock)190 void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {
191 VPrintf(2, "#%llu: DD::MutexBeforeLock(%p, wlock=%d) nlocked=%d\n",
192 cb->lt->ctx, m, wlock, cb->lt->nlocked);
193 DDPhysicalThread *pt = cb->pt;
194 DDLogicalThread *lt = cb->lt;
195
196 uptr owner = atomic_load(&m->owner, memory_order_relaxed);
197 if (owner == (uptr)cb->lt) {
198 VPrintf(3, "#%llu: DD::MutexBeforeLock recursive\n",
199 cb->lt->ctx);
200 return;
201 }
202
203 CHECK_LE(lt->nlocked, kMaxNesting);
204
205 // FIXME(dvyukov): don't allocate id if lt->nlocked == 0?
206 if (m->id == kNoId)
207 m->id = allocateId(cb);
208
209 ThreadMutex *tm = <->locked[lt->nlocked++];
210 tm->id = m->id;
211 if (flags.second_deadlock_stack)
212 tm->stk = cb->Unwind();
213 if (lt->nlocked == 1) {
214 VPrintf(3, "#%llu: DD::MutexBeforeLock first mutex\n",
215 cb->lt->ctx);
216 return;
217 }
218
219 bool added = false;
220 Mutex *mtx = getMutex(m->id);
221 for (int i = 0; i < lt->nlocked - 1; i++) {
222 u32 id1 = lt->locked[i].id;
223 u32 stk1 = lt->locked[i].stk;
224 Mutex *mtx1 = getMutex(id1);
225 SpinMutexLock l(&mtx1->mtx);
226 if (mtx1->nlink == kMaxLink) {
227 // FIXME(dvyukov): check stale links
228 continue;
229 }
230 int li = 0;
231 for (; li < mtx1->nlink; li++) {
232 Link *link = &mtx1->link[li];
233 if (link->id == m->id) {
234 if (link->seq != mtx->seq) {
235 link->seq = mtx->seq;
236 link->tid = lt->ctx;
237 link->stk0 = stk1;
238 link->stk1 = cb->Unwind();
239 added = true;
240 VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
241 cb->lt->ctx, getMutexId(mtx1), m->id);
242 }
243 break;
244 }
245 }
246 if (li == mtx1->nlink) {
247 // FIXME(dvyukov): check stale links
248 Link *link = &mtx1->link[mtx1->nlink++];
249 link->id = m->id;
250 link->seq = mtx->seq;
251 link->tid = lt->ctx;
252 link->stk0 = stk1;
253 link->stk1 = cb->Unwind();
254 added = true;
255 VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
256 cb->lt->ctx, getMutexId(mtx1), m->id);
257 }
258 }
259
260 if (!added || mtx->nlink == 0) {
261 VPrintf(3, "#%llu: DD::MutexBeforeLock don't check\n",
262 cb->lt->ctx);
263 return;
264 }
265
266 CycleCheck(pt, lt, m);
267 }
268
MutexAfterLock(DDCallback * cb,DDMutex * m,bool wlock,bool trylock)269 void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
270 bool trylock) {
271 VPrintf(2, "#%llu: DD::MutexAfterLock(%p, wlock=%d, try=%d) nlocked=%d\n",
272 cb->lt->ctx, m, wlock, trylock, cb->lt->nlocked);
273 DDLogicalThread *lt = cb->lt;
274
275 uptr owner = atomic_load(&m->owner, memory_order_relaxed);
276 if (owner == (uptr)cb->lt) {
277 VPrintf(3, "#%llu: DD::MutexAfterLock recursive\n", cb->lt->ctx);
278 CHECK(wlock);
279 m->recursion++;
280 return;
281 }
282 CHECK_EQ(owner, 0);
283 if (wlock) {
284 VPrintf(3, "#%llu: DD::MutexAfterLock set owner\n", cb->lt->ctx);
285 CHECK_EQ(m->recursion, 0);
286 m->recursion = 1;
287 atomic_store(&m->owner, (uptr)cb->lt, memory_order_relaxed);
288 }
289
290 if (!trylock)
291 return;
292
293 CHECK_LE(lt->nlocked, kMaxNesting);
294 if (m->id == kNoId)
295 m->id = allocateId(cb);
296 ThreadMutex *tm = <->locked[lt->nlocked++];
297 tm->id = m->id;
298 if (flags.second_deadlock_stack)
299 tm->stk = cb->Unwind();
300 }
301
MutexBeforeUnlock(DDCallback * cb,DDMutex * m,bool wlock)302 void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
303 VPrintf(2, "#%llu: DD::MutexBeforeUnlock(%p, wlock=%d) nlocked=%d\n",
304 cb->lt->ctx, m, wlock, cb->lt->nlocked);
305 DDLogicalThread *lt = cb->lt;
306
307 uptr owner = atomic_load(&m->owner, memory_order_relaxed);
308 if (owner == (uptr)cb->lt) {
309 VPrintf(3, "#%llu: DD::MutexBeforeUnlock recursive\n", cb->lt->ctx);
310 if (--m->recursion > 0)
311 return;
312 VPrintf(3, "#%llu: DD::MutexBeforeUnlock reset owner\n", cb->lt->ctx);
313 atomic_store(&m->owner, 0, memory_order_relaxed);
314 }
315 CHECK_NE(m->id, kNoId);
316 int last = lt->nlocked - 1;
317 for (int i = last; i >= 0; i--) {
318 if (cb->lt->locked[i].id == m->id) {
319 lt->locked[i] = lt->locked[last];
320 lt->nlocked--;
321 break;
322 }
323 }
324 }
325
MutexDestroy(DDCallback * cb,DDMutex * m)326 void DD::MutexDestroy(DDCallback *cb, DDMutex *m) {
327 VPrintf(2, "#%llu: DD::MutexDestroy(%p)\n",
328 cb->lt->ctx, m);
329 DDLogicalThread *lt = cb->lt;
330
331 if (m->id == kNoId)
332 return;
333
334 // Remove the mutex from lt->locked if there.
335 int last = lt->nlocked - 1;
336 for (int i = last; i >= 0; i--) {
337 if (lt->locked[i].id == m->id) {
338 lt->locked[i] = lt->locked[last];
339 lt->nlocked--;
340 break;
341 }
342 }
343
344 // Clear and invalidate the mutex descriptor.
345 {
346 Mutex *mtx = getMutex(m->id);
347 SpinMutexLock l(&mtx->mtx);
348 mtx->seq++;
349 mtx->nlink = 0;
350 }
351
352 // Return id to cache.
353 {
354 SpinMutexLock l(&mtx);
355 free_id.push_back(m->id);
356 }
357 }
358
CycleCheck(DDPhysicalThread * pt,DDLogicalThread * lt,DDMutex * m)359 void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
360 DDMutex *m) {
361 internal_memset(pt->visited, 0, sizeof(pt->visited));
362 int npath = 0;
363 int npending = 0;
364 {
365 Mutex *mtx = getMutex(m->id);
366 SpinMutexLock l(&mtx->mtx);
367 for (int li = 0; li < mtx->nlink; li++)
368 pt->pending[npending++] = mtx->link[li];
369 }
370 while (npending > 0) {
371 Link link = pt->pending[--npending];
372 if (link.id == kEndId) {
373 npath--;
374 continue;
375 }
376 if (pt->visited[link.id])
377 continue;
378 Mutex *mtx1 = getMutex(link.id);
379 SpinMutexLock l(&mtx1->mtx);
380 if (mtx1->seq != link.seq)
381 continue;
382 pt->visited[link.id] = true;
383 if (mtx1->nlink == 0)
384 continue;
385 pt->path[npath++] = link;
386 pt->pending[npending++] = Link(kEndId);
387 if (link.id == m->id)
388 return Report(pt, lt, npath); // Bingo!
389 for (int li = 0; li < mtx1->nlink; li++) {
390 Link *link1 = &mtx1->link[li];
391 // Mutex *mtx2 = getMutex(link->id);
392 // FIXME(dvyukov): fast seq check
393 // FIXME(dvyukov): fast nlink != 0 check
394 // FIXME(dvyukov): fast pending check?
395 // FIXME(dvyukov): npending can be larger than kMaxMutex
396 pt->pending[npending++] = *link1;
397 }
398 }
399 }
400
Report(DDPhysicalThread * pt,DDLogicalThread * lt,int npath)401 void DD::Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath) {
402 DDReport *rep = &pt->rep;
403 rep->n = npath;
404 for (int i = 0; i < npath; i++) {
405 Link *link = &pt->path[i];
406 Link *link0 = &pt->path[i ? i - 1 : npath - 1];
407 rep->loop[i].thr_ctx = link->tid;
408 rep->loop[i].mtx_ctx0 = link0->id;
409 rep->loop[i].mtx_ctx1 = link->id;
410 rep->loop[i].stk[0] = flags.second_deadlock_stack ? link->stk0 : 0;
411 rep->loop[i].stk[1] = link->stk1;
412 }
413 pt->report_pending = true;
414 }
415
GetReport(DDCallback * cb)416 DDReport *DD::GetReport(DDCallback *cb) {
417 if (!cb->pt->report_pending)
418 return 0;
419 cb->pt->report_pending = false;
420 return &cb->pt->rep;
421 }
422
423 } // namespace __sanitizer
424 #endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
425