xref: /freebsd/contrib/llvm-project/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp (revision 5e801ac66d24704442eba426ed13c3effb8a34e7)
1 //===-- tsan_rtl_mutex.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 // This file is a part of ThreadSanitizer (TSan), a race detector.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include <sanitizer_common/sanitizer_deadlock_detector_interface.h>
14 #include <sanitizer_common/sanitizer_stackdepot.h>
15 
16 #include "tsan_rtl.h"
17 #include "tsan_flags.h"
18 #include "tsan_sync.h"
19 #include "tsan_report.h"
20 #include "tsan_symbolize.h"
21 #include "tsan_platform.h"
22 
23 namespace __tsan {
24 
25 void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r);
26 
27 struct Callback final : public DDCallback {
28   ThreadState *thr;
29   uptr pc;
30 
31   Callback(ThreadState *thr, uptr pc)
32       : thr(thr)
33       , pc(pc) {
34     DDCallback::pt = thr->proc()->dd_pt;
35     DDCallback::lt = thr->dd_lt;
36   }
37 
38   StackID Unwind() override { return CurrentStackId(thr, pc); }
39   int UniqueTid() override { return thr->unique_id; }
40 };
41 
42 void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) {
43   Callback cb(thr, pc);
44   ctx->dd->MutexInit(&cb, &s->dd);
45   s->dd.ctx = s->GetId();
46 }
47 
48 static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
49     uptr addr, u64 mid) {
50   // In Go, these misuses are either impossible, or detected by std lib,
51   // or false positives (e.g. unlock in a different thread).
52   if (SANITIZER_GO)
53     return;
54   if (!ShouldReport(thr, typ))
55     return;
56   ThreadRegistryLock l(&ctx->thread_registry);
57   ScopedReport rep(typ);
58   rep.AddMutex(mid);
59   VarSizeStackTrace trace;
60   ObtainCurrentStack(thr, pc, &trace);
61   rep.AddStack(trace, true);
62   rep.AddLocation(addr, 1);
63   OutputReport(thr, rep);
64 }
65 
66 void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
67   DPrintf("#%d: MutexCreate %zx flagz=0x%x\n", thr->tid, addr, flagz);
68   if (!(flagz & MutexFlagLinkerInit) && IsAppMem(addr)) {
69     CHECK(!thr->is_freeing);
70     thr->is_freeing = true;
71     MemoryAccess(thr, pc, addr, 1, kAccessWrite);
72     thr->is_freeing = false;
73   }
74   SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
75   Lock l(&s->mtx);
76   s->SetFlags(flagz & MutexCreationFlagMask);
77   // Save stack in the case the sync object was created before as atomic.
78   if (!SANITIZER_GO && s->creation_stack_id == 0)
79     s->creation_stack_id = CurrentStackId(thr, pc);
80 }
81 
82 void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
83   DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
84   bool unlock_locked = false;
85   u64 mid = 0;
86   u64 last_lock = 0;
87   {
88     SyncVar *s = ctx->metamap.GetSyncIfExists(addr);
89     if (s == 0)
90       return;
91     Lock l(&s->mtx);
92     if ((flagz & MutexFlagLinkerInit) || s->IsFlagSet(MutexFlagLinkerInit) ||
93         ((flagz & MutexFlagNotStatic) && !s->IsFlagSet(MutexFlagNotStatic))) {
94       // Destroy is no-op for linker-initialized mutexes.
95       return;
96     }
97     if (common_flags()->detect_deadlocks) {
98       Callback cb(thr, pc);
99       ctx->dd->MutexDestroy(&cb, &s->dd);
100       ctx->dd->MutexInit(&cb, &s->dd);
101     }
102     if (flags()->report_destroy_locked && s->owner_tid != kInvalidTid &&
103         !s->IsFlagSet(MutexFlagBroken)) {
104       s->SetFlags(MutexFlagBroken);
105       unlock_locked = true;
106     }
107     mid = s->GetId();
108     last_lock = s->last_lock;
109     if (!unlock_locked)
110       s->Reset(thr->proc());  // must not reset it before the report is printed
111   }
112   if (unlock_locked && ShouldReport(thr, ReportTypeMutexDestroyLocked)) {
113     ThreadRegistryLock l(&ctx->thread_registry);
114     ScopedReport rep(ReportTypeMutexDestroyLocked);
115     rep.AddMutex(mid);
116     VarSizeStackTrace trace;
117     ObtainCurrentStack(thr, pc, &trace);
118     rep.AddStack(trace, true);
119     FastState last(last_lock);
120     RestoreStack(last.tid(), last.epoch(), &trace, 0);
121     rep.AddStack(trace, true);
122     rep.AddLocation(addr, 1);
123     OutputReport(thr, rep);
124 
125     SyncVar *s = ctx->metamap.GetSyncIfExists(addr);
126     if (s != 0) {
127       Lock l(&s->mtx);
128       s->Reset(thr->proc());
129     }
130   }
131   thr->mset.Remove(mid);
132   // Imitate a memory write to catch unlock-destroy races.
133   // Do this outside of sync mutex, because it can report a race which locks
134   // sync mutexes.
135   if (IsAppMem(addr))
136     MemoryAccess(thr, pc, addr, 1, kAccessWrite | kAccessFree);
137   // s will be destroyed and freed in MetaMap::FreeBlock.
138 }
139 
140 void MutexPreLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
141   DPrintf("#%d: MutexPreLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
142   if (!(flagz & MutexFlagTryLock) && common_flags()->detect_deadlocks) {
143     SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
144     {
145       ReadLock l(&s->mtx);
146       s->UpdateFlags(flagz);
147       if (s->owner_tid != thr->tid) {
148         Callback cb(thr, pc);
149         ctx->dd->MutexBeforeLock(&cb, &s->dd, true);
150       }
151     }
152     Callback cb(thr, pc);
153     ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
154   }
155 }
156 
157 void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz, int rec) {
158   DPrintf("#%d: MutexPostLock %zx flag=0x%x rec=%d\n",
159       thr->tid, addr, flagz, rec);
160   if (flagz & MutexFlagRecursiveLock)
161     CHECK_GT(rec, 0);
162   else
163     rec = 1;
164   if (IsAppMem(addr))
165     MemoryAccess(thr, pc, addr, 1, kAccessRead | kAccessAtomic);
166   u64 mid = 0;
167   bool pre_lock = false;
168   bool first = false;
169   bool report_double_lock = false;
170   {
171     SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
172     Lock l(&s->mtx);
173     s->UpdateFlags(flagz);
174     thr->fast_state.IncrementEpoch();
175     TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
176     if (s->owner_tid == kInvalidTid) {
177       CHECK_EQ(s->recursion, 0);
178       s->owner_tid = thr->tid;
179       s->last_lock = thr->fast_state.raw();
180     } else if (s->owner_tid == thr->tid) {
181       CHECK_GT(s->recursion, 0);
182     } else if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
183       s->SetFlags(MutexFlagBroken);
184       report_double_lock = true;
185     }
186     first = s->recursion == 0;
187     s->recursion += rec;
188     if (first) {
189       AcquireImpl(thr, pc, &s->clock);
190       AcquireImpl(thr, pc, &s->read_clock);
191     } else if (!s->IsFlagSet(MutexFlagWriteReentrant)) {
192     }
193     thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
194     if (first && common_flags()->detect_deadlocks) {
195       pre_lock =
196           (flagz & MutexFlagDoPreLockOnPostLock) && !(flagz & MutexFlagTryLock);
197       Callback cb(thr, pc);
198       if (pre_lock)
199         ctx->dd->MutexBeforeLock(&cb, &s->dd, true);
200       ctx->dd->MutexAfterLock(&cb, &s->dd, true, flagz & MutexFlagTryLock);
201     }
202     mid = s->GetId();
203   }
204   if (report_double_lock)
205     ReportMutexMisuse(thr, pc, ReportTypeMutexDoubleLock, addr, mid);
206   if (first && pre_lock && common_flags()->detect_deadlocks) {
207     Callback cb(thr, pc);
208     ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
209   }
210 }
211 
212 int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
213   DPrintf("#%d: MutexUnlock %zx flagz=0x%x\n", thr->tid, addr, flagz);
214   if (IsAppMem(addr))
215     MemoryAccess(thr, pc, addr, 1, kAccessRead | kAccessAtomic);
216   u64 mid = 0;
217   bool report_bad_unlock = false;
218   int rec = 0;
219   {
220     SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
221     Lock l(&s->mtx);
222     thr->fast_state.IncrementEpoch();
223     TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
224     if (!SANITIZER_GO && (s->recursion == 0 || s->owner_tid != thr->tid)) {
225       if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
226         s->SetFlags(MutexFlagBroken);
227         report_bad_unlock = true;
228       }
229     } else {
230       rec = (flagz & MutexFlagRecursiveUnlock) ? s->recursion : 1;
231       s->recursion -= rec;
232       if (s->recursion == 0) {
233         s->owner_tid = kInvalidTid;
234         ReleaseStoreImpl(thr, pc, &s->clock);
235       } else {
236       }
237     }
238     thr->mset.Del(s->GetId(), true);
239     if (common_flags()->detect_deadlocks && s->recursion == 0 &&
240         !report_bad_unlock) {
241       Callback cb(thr, pc);
242       ctx->dd->MutexBeforeUnlock(&cb, &s->dd, true);
243     }
244     mid = s->GetId();
245   }
246   if (report_bad_unlock)
247     ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
248   if (common_flags()->detect_deadlocks && !report_bad_unlock) {
249     Callback cb(thr, pc);
250     ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
251   }
252   return rec;
253 }
254 
255 void MutexPreReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
256   DPrintf("#%d: MutexPreReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
257   if (!(flagz & MutexFlagTryLock) && common_flags()->detect_deadlocks) {
258     {
259       SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
260       ReadLock l(&s->mtx);
261       s->UpdateFlags(flagz);
262       Callback cb(thr, pc);
263       ctx->dd->MutexBeforeLock(&cb, &s->dd, false);
264     }
265     Callback cb(thr, pc);
266     ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
267   }
268 }
269 
270 void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
271   DPrintf("#%d: MutexPostReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
272   if (IsAppMem(addr))
273     MemoryAccess(thr, pc, addr, 1, kAccessRead | kAccessAtomic);
274   u64 mid = 0;
275   bool report_bad_lock = false;
276   bool pre_lock = false;
277   {
278     SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
279     ReadLock l(&s->mtx);
280     s->UpdateFlags(flagz);
281     thr->fast_state.IncrementEpoch();
282     TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
283     if (s->owner_tid != kInvalidTid) {
284       if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
285         s->SetFlags(MutexFlagBroken);
286         report_bad_lock = true;
287       }
288     }
289     AcquireImpl(thr, pc, &s->clock);
290     s->last_lock = thr->fast_state.raw();
291     thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
292     if (common_flags()->detect_deadlocks) {
293       pre_lock =
294           (flagz & MutexFlagDoPreLockOnPostLock) && !(flagz & MutexFlagTryLock);
295       Callback cb(thr, pc);
296       if (pre_lock)
297         ctx->dd->MutexBeforeLock(&cb, &s->dd, false);
298       ctx->dd->MutexAfterLock(&cb, &s->dd, false, flagz & MutexFlagTryLock);
299     }
300     mid = s->GetId();
301   }
302   if (report_bad_lock)
303     ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadLock, addr, mid);
304   if (pre_lock  && common_flags()->detect_deadlocks) {
305     Callback cb(thr, pc);
306     ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
307   }
308 }
309 
310 void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
311   DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
312   if (IsAppMem(addr))
313     MemoryAccess(thr, pc, addr, 1, kAccessRead | kAccessAtomic);
314   u64 mid = 0;
315   bool report_bad_unlock = false;
316   {
317     SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
318     Lock l(&s->mtx);
319     thr->fast_state.IncrementEpoch();
320     TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
321     if (s->owner_tid != kInvalidTid) {
322       if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
323         s->SetFlags(MutexFlagBroken);
324         report_bad_unlock = true;
325       }
326     }
327     ReleaseImpl(thr, pc, &s->read_clock);
328     if (common_flags()->detect_deadlocks && s->recursion == 0) {
329       Callback cb(thr, pc);
330       ctx->dd->MutexBeforeUnlock(&cb, &s->dd, false);
331     }
332     mid = s->GetId();
333   }
334   thr->mset.Del(mid, false);
335   if (report_bad_unlock)
336     ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadUnlock, addr, mid);
337   if (common_flags()->detect_deadlocks) {
338     Callback cb(thr, pc);
339     ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
340   }
341 }
342 
343 void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
344   DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
345   if (IsAppMem(addr))
346     MemoryAccess(thr, pc, addr, 1, kAccessRead | kAccessAtomic);
347   u64 mid = 0;
348   bool report_bad_unlock = false;
349   {
350     SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
351     Lock l(&s->mtx);
352     bool write = true;
353     if (s->owner_tid == kInvalidTid) {
354       // Seems to be read unlock.
355       write = false;
356       thr->fast_state.IncrementEpoch();
357       TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
358       ReleaseImpl(thr, pc, &s->read_clock);
359     } else if (s->owner_tid == thr->tid) {
360       // Seems to be write unlock.
361       thr->fast_state.IncrementEpoch();
362       TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
363       CHECK_GT(s->recursion, 0);
364       s->recursion--;
365       if (s->recursion == 0) {
366         s->owner_tid = kInvalidTid;
367         ReleaseStoreImpl(thr, pc, &s->clock);
368       } else {
369       }
370     } else if (!s->IsFlagSet(MutexFlagBroken)) {
371       s->SetFlags(MutexFlagBroken);
372       report_bad_unlock = true;
373     }
374     thr->mset.Del(s->GetId(), write);
375     if (common_flags()->detect_deadlocks && s->recursion == 0) {
376       Callback cb(thr, pc);
377       ctx->dd->MutexBeforeUnlock(&cb, &s->dd, write);
378     }
379     mid = s->GetId();
380   }
381   if (report_bad_unlock)
382     ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
383   if (common_flags()->detect_deadlocks) {
384     Callback cb(thr, pc);
385     ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
386   }
387 }
388 
389 void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
390   DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
391   SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
392   Lock l(&s->mtx);
393   s->owner_tid = kInvalidTid;
394   s->recursion = 0;
395 }
396 
397 void MutexInvalidAccess(ThreadState *thr, uptr pc, uptr addr) {
398   DPrintf("#%d: MutexInvalidAccess %zx\n", thr->tid, addr);
399   SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, true);
400   ReportMutexMisuse(thr, pc, ReportTypeMutexInvalidAccess, addr, s->GetId());
401 }
402 
403 void Acquire(ThreadState *thr, uptr pc, uptr addr) {
404   DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
405   if (thr->ignore_sync)
406     return;
407   SyncVar *s = ctx->metamap.GetSyncIfExists(addr);
408   if (!s)
409     return;
410   ReadLock l(&s->mtx);
411   AcquireImpl(thr, pc, &s->clock);
412 }
413 
414 static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) {
415   ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
416   ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
417   u64 epoch = tctx->epoch1;
418   if (tctx->status == ThreadStatusRunning) {
419     epoch = tctx->thr->fast_state.epoch();
420     tctx->thr->clock.NoteGlobalAcquire(epoch);
421   }
422   thr->clock.set(&thr->proc()->clock_cache, tctx->tid, epoch);
423 }
424 
425 void AcquireGlobal(ThreadState *thr) {
426   DPrintf("#%d: AcquireGlobal\n", thr->tid);
427   if (thr->ignore_sync)
428     return;
429   ThreadRegistryLock l(&ctx->thread_registry);
430   ctx->thread_registry.RunCallbackForEachThreadLocked(UpdateClockCallback, thr);
431 }
432 
433 void ReleaseStoreAcquire(ThreadState *thr, uptr pc, uptr addr) {
434   DPrintf("#%d: ReleaseStoreAcquire %zx\n", thr->tid, addr);
435   if (thr->ignore_sync)
436     return;
437   SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, false);
438   Lock l(&s->mtx);
439   thr->fast_state.IncrementEpoch();
440   // Can't increment epoch w/o writing to the trace as well.
441   TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
442   ReleaseStoreAcquireImpl(thr, pc, &s->clock);
443 }
444 
445 void Release(ThreadState *thr, uptr pc, uptr addr) {
446   DPrintf("#%d: Release %zx\n", thr->tid, addr);
447   if (thr->ignore_sync)
448     return;
449   SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, false);
450   Lock l(&s->mtx);
451   thr->fast_state.IncrementEpoch();
452   // Can't increment epoch w/o writing to the trace as well.
453   TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
454   ReleaseImpl(thr, pc, &s->clock);
455 }
456 
457 void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
458   DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
459   if (thr->ignore_sync)
460     return;
461   SyncVar *s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, false);
462   Lock l(&s->mtx);
463   thr->fast_state.IncrementEpoch();
464   // Can't increment epoch w/o writing to the trace as well.
465   TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
466   ReleaseStoreImpl(thr, pc, &s->clock);
467 }
468 
469 #if !SANITIZER_GO
470 static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) {
471   ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
472   ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
473   u64 epoch = tctx->epoch1;
474   if (tctx->status == ThreadStatusRunning)
475     epoch = tctx->thr->fast_state.epoch();
476   thr->last_sleep_clock.set(&thr->proc()->clock_cache, tctx->tid, epoch);
477 }
478 
479 void AfterSleep(ThreadState *thr, uptr pc) {
480   DPrintf("#%d: AfterSleep\n", thr->tid);
481   if (thr->ignore_sync)
482     return;
483   thr->last_sleep_stack_id = CurrentStackId(thr, pc);
484   ThreadRegistryLock l(&ctx->thread_registry);
485   ctx->thread_registry.RunCallbackForEachThreadLocked(UpdateSleepClockCallback,
486                                                       thr);
487 }
488 #endif
489 
490 void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) {
491   if (thr->ignore_sync)
492     return;
493   thr->clock.set(thr->fast_state.epoch());
494   thr->clock.acquire(&thr->proc()->clock_cache, c);
495 }
496 
497 void ReleaseStoreAcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) {
498   if (thr->ignore_sync)
499     return;
500   thr->clock.set(thr->fast_state.epoch());
501   thr->fast_synch_epoch = thr->fast_state.epoch();
502   thr->clock.releaseStoreAcquire(&thr->proc()->clock_cache, c);
503 }
504 
505 void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
506   if (thr->ignore_sync)
507     return;
508   thr->clock.set(thr->fast_state.epoch());
509   thr->fast_synch_epoch = thr->fast_state.epoch();
510   thr->clock.release(&thr->proc()->clock_cache, c);
511 }
512 
513 void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) {
514   if (thr->ignore_sync)
515     return;
516   thr->clock.set(thr->fast_state.epoch());
517   thr->fast_synch_epoch = thr->fast_state.epoch();
518   thr->clock.ReleaseStore(&thr->proc()->clock_cache, c);
519 }
520 
521 void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
522   if (thr->ignore_sync)
523     return;
524   thr->clock.set(thr->fast_state.epoch());
525   thr->fast_synch_epoch = thr->fast_state.epoch();
526   thr->clock.acq_rel(&thr->proc()->clock_cache, c);
527 }
528 
529 void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
530   if (r == 0 || !ShouldReport(thr, ReportTypeDeadlock))
531     return;
532   ThreadRegistryLock l(&ctx->thread_registry);
533   ScopedReport rep(ReportTypeDeadlock);
534   for (int i = 0; i < r->n; i++) {
535     rep.AddMutex(r->loop[i].mtx_ctx0);
536     rep.AddUniqueTid((int)r->loop[i].thr_ctx);
537     rep.AddThread((int)r->loop[i].thr_ctx);
538   }
539   uptr dummy_pc = 0x42;
540   for (int i = 0; i < r->n; i++) {
541     for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
542       u32 stk = r->loop[i].stk[j];
543       if (stk && stk != 0xffffffff) {
544         rep.AddStack(StackDepotGet(stk), true);
545       } else {
546         // Sometimes we fail to extract the stack trace (FIXME: investigate),
547         // but we should still produce some stack trace in the report.
548         rep.AddStack(StackTrace(&dummy_pc, 1), true);
549       }
550     }
551   }
552   OutputReport(thr, rep);
553 }
554 
555 }  // namespace __tsan
556