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