]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector2.cc
Merge ^/head r337286 through r337585.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / sanitizer_common / sanitizer_deadlock_detector2.cc
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
36   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
49   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
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
123 DD::DD(const DDFlags *flags) : flags(*flags) { free_id.reserve(1024); }
124
125 DDPhysicalThread* DD::CreatePhysicalThread() {
126   DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread),
127       "deadlock detector (physical thread)");
128   return pt;
129 }
130
131 void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
132   pt->~DDPhysicalThread();
133   UnmapOrDie(pt, sizeof(DDPhysicalThread));
134 }
135
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
144 void DD::DestroyLogicalThread(DDLogicalThread *lt) {
145   lt->~DDLogicalThread();
146   InternalFree(lt);
147 }
148
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
156 Mutex *DD::getMutex(u32 id) {
157   return &mutex[id / kL2Size][id % kL2Size];
158 }
159
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
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
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 = &lt->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
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 = &lt->locked[lt->nlocked++];
297   tm->id = m->id;
298   if (flags.second_deadlock_stack)
299     tm->stk = cb->Unwind();
300 }
301
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
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
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
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
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