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