1//===-- tsan_rtl_mutex.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// This file is a part of ThreadSanitizer (TSan), a race detector.
11//
12//===----------------------------------------------------------------------===//
13
14#include "tsan_rtl.h"
15#include "tsan_flags.h"
16#include "tsan_sync.h"
17#include "tsan_report.h"
18#include "tsan_symbolize.h"
19#include "tsan_platform.h"
20
21namespace __tsan {
22
23void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
24                 bool rw, bool recursive, bool linker_init) {
25  Context *ctx = CTX();
26  CHECK_GT(thr->in_rtl, 0);
27  DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr);
28  StatInc(thr, StatMutexCreate);
29  if (!linker_init && IsAppMem(addr)) {
30    CHECK(!thr->is_freeing);
31    thr->is_freeing = true;
32    MemoryWrite(thr, pc, addr, kSizeLog1);
33    thr->is_freeing = false;
34  }
35  SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
36  s->is_rw = rw;
37  s->is_recursive = recursive;
38  s->is_linker_init = linker_init;
39  s->mtx.Unlock();
40}
41
42void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
43  Context *ctx = CTX();
44  CHECK_GT(thr->in_rtl, 0);
45  DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
46  StatInc(thr, StatMutexDestroy);
47#ifndef TSAN_GO
48  // Global mutexes not marked as LINKER_INITIALIZED
49  // cause tons of not interesting reports, so just ignore it.
50  if (IsGlobalVar(addr))
51    return;
52#endif
53  SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr);
54  if (s == 0)
55    return;
56  if (IsAppMem(addr)) {
57    CHECK(!thr->is_freeing);
58    thr->is_freeing = true;
59    MemoryWrite(thr, pc, addr, kSizeLog1);
60    thr->is_freeing = false;
61  }
62  if (flags()->report_destroy_locked
63      && s->owner_tid != SyncVar::kInvalidTid
64      && !s->is_broken) {
65    s->is_broken = true;
66    ThreadRegistryLock l(ctx->thread_registry);
67    ScopedReport rep(ReportTypeMutexDestroyLocked);
68    rep.AddMutex(s);
69    StackTrace trace;
70    trace.ObtainCurrent(thr, pc);
71    rep.AddStack(&trace);
72    FastState last(s->last_lock);
73    RestoreStack(last.tid(), last.epoch(), &trace, 0);
74    rep.AddStack(&trace);
75    rep.AddLocation(s->addr, 1);
76    OutputReport(ctx, rep);
77  }
78  thr->mset.Remove(s->GetId());
79  DestroyAndFree(s);
80}
81
82void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) {
83  CHECK_GT(thr->in_rtl, 0);
84  DPrintf("#%d: MutexLock %zx rec=%d\n", thr->tid, addr, rec);
85  CHECK_GT(rec, 0);
86  if (IsAppMem(addr))
87    MemoryReadAtomic(thr, pc, addr, kSizeLog1);
88  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
89  thr->fast_state.IncrementEpoch();
90  TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
91  if (s->owner_tid == SyncVar::kInvalidTid) {
92    CHECK_EQ(s->recursion, 0);
93    s->owner_tid = thr->tid;
94    s->last_lock = thr->fast_state.raw();
95  } else if (s->owner_tid == thr->tid) {
96    CHECK_GT(s->recursion, 0);
97  } else {
98    Printf("ThreadSanitizer WARNING: double lock\n");
99    PrintCurrentStack(thr, pc);
100  }
101  if (s->recursion == 0) {
102    StatInc(thr, StatMutexLock);
103    thr->clock.set(thr->tid, thr->fast_state.epoch());
104    thr->clock.acquire(&s->clock);
105    StatInc(thr, StatSyncAcquire);
106    thr->clock.acquire(&s->read_clock);
107    StatInc(thr, StatSyncAcquire);
108  } else if (!s->is_recursive) {
109    StatInc(thr, StatMutexRecLock);
110  }
111  s->recursion += rec;
112  thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
113  s->mtx.Unlock();
114}
115
116int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) {
117  CHECK_GT(thr->in_rtl, 0);
118  DPrintf("#%d: MutexUnlock %zx all=%d\n", thr->tid, addr, all);
119  if (IsAppMem(addr))
120    MemoryReadAtomic(thr, pc, addr, kSizeLog1);
121  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
122  thr->fast_state.IncrementEpoch();
123  TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
124  int rec = 0;
125  if (s->recursion == 0) {
126    if (!s->is_broken) {
127      s->is_broken = true;
128      Printf("ThreadSanitizer WARNING: unlock of unlocked mutex\n");
129      PrintCurrentStack(thr, pc);
130    }
131  } else if (s->owner_tid != thr->tid) {
132    if (!s->is_broken) {
133      s->is_broken = true;
134      Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
135      PrintCurrentStack(thr, pc);
136    }
137  } else {
138    rec = all ? s->recursion : 1;
139    s->recursion -= rec;
140    if (s->recursion == 0) {
141      StatInc(thr, StatMutexUnlock);
142      s->owner_tid = SyncVar::kInvalidTid;
143      thr->clock.set(thr->tid, thr->fast_state.epoch());
144      thr->fast_synch_epoch = thr->fast_state.epoch();
145      thr->clock.ReleaseStore(&s->clock);
146      StatInc(thr, StatSyncRelease);
147    } else {
148      StatInc(thr, StatMutexRecUnlock);
149    }
150  }
151  thr->mset.Del(s->GetId(), true);
152  s->mtx.Unlock();
153  return rec;
154}
155
156void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
157  CHECK_GT(thr->in_rtl, 0);
158  DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr);
159  StatInc(thr, StatMutexReadLock);
160  if (IsAppMem(addr))
161    MemoryReadAtomic(thr, pc, addr, kSizeLog1);
162  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
163  thr->fast_state.IncrementEpoch();
164  TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
165  if (s->owner_tid != SyncVar::kInvalidTid) {
166    Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
167    PrintCurrentStack(thr, pc);
168  }
169  thr->clock.set(thr->tid, thr->fast_state.epoch());
170  thr->clock.acquire(&s->clock);
171  s->last_lock = thr->fast_state.raw();
172  StatInc(thr, StatSyncAcquire);
173  thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
174  s->mtx.ReadUnlock();
175}
176
177void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
178  CHECK_GT(thr->in_rtl, 0);
179  DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
180  StatInc(thr, StatMutexReadUnlock);
181  if (IsAppMem(addr))
182    MemoryReadAtomic(thr, pc, addr, kSizeLog1);
183  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
184  thr->fast_state.IncrementEpoch();
185  TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
186  if (s->owner_tid != SyncVar::kInvalidTid) {
187    Printf("ThreadSanitizer WARNING: read unlock of a write "
188               "locked mutex\n");
189    PrintCurrentStack(thr, pc);
190  }
191  thr->clock.set(thr->tid, thr->fast_state.epoch());
192  thr->fast_synch_epoch = thr->fast_state.epoch();
193  thr->clock.release(&s->read_clock);
194  StatInc(thr, StatSyncRelease);
195  s->mtx.Unlock();
196  thr->mset.Del(s->GetId(), false);
197}
198
199void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
200  CHECK_GT(thr->in_rtl, 0);
201  DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
202  if (IsAppMem(addr))
203    MemoryReadAtomic(thr, pc, addr, kSizeLog1);
204  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
205  bool write = true;
206  if (s->owner_tid == SyncVar::kInvalidTid) {
207    // Seems to be read unlock.
208    write = false;
209    StatInc(thr, StatMutexReadUnlock);
210    thr->fast_state.IncrementEpoch();
211    TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
212    thr->clock.set(thr->tid, thr->fast_state.epoch());
213    thr->fast_synch_epoch = thr->fast_state.epoch();
214    thr->clock.release(&s->read_clock);
215    StatInc(thr, StatSyncRelease);
216  } else if (s->owner_tid == thr->tid) {
217    // Seems to be write unlock.
218    thr->fast_state.IncrementEpoch();
219    TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
220    CHECK_GT(s->recursion, 0);
221    s->recursion--;
222    if (s->recursion == 0) {
223      StatInc(thr, StatMutexUnlock);
224      s->owner_tid = SyncVar::kInvalidTid;
225      // FIXME: Refactor me, plz.
226      // The sequence of events is quite tricky and doubled in several places.
227      // First, it's a bug to increment the epoch w/o writing to the trace.
228      // Then, the acquire/release logic can be factored out as well.
229      thr->clock.set(thr->tid, thr->fast_state.epoch());
230      thr->fast_synch_epoch = thr->fast_state.epoch();
231      thr->clock.ReleaseStore(&s->clock);
232      StatInc(thr, StatSyncRelease);
233    } else {
234      StatInc(thr, StatMutexRecUnlock);
235    }
236  } else if (!s->is_broken) {
237    s->is_broken = true;
238    Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
239    PrintCurrentStack(thr, pc);
240  }
241  thr->mset.Del(s->GetId(), write);
242  s->mtx.Unlock();
243}
244
245void Acquire(ThreadState *thr, uptr pc, uptr addr) {
246  CHECK_GT(thr->in_rtl, 0);
247  DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
248  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
249  thr->clock.set(thr->tid, thr->fast_state.epoch());
250  thr->clock.acquire(&s->clock);
251  StatInc(thr, StatSyncAcquire);
252  s->mtx.ReadUnlock();
253}
254
255static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) {
256  ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
257  ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
258  if (tctx->status == ThreadStatusRunning)
259    thr->clock.set(tctx->tid, tctx->thr->fast_state.epoch());
260  else
261    thr->clock.set(tctx->tid, tctx->epoch1);
262}
263
264void AcquireGlobal(ThreadState *thr, uptr pc) {
265  ThreadRegistryLock l(CTX()->thread_registry);
266  CTX()->thread_registry->RunCallbackForEachThreadLocked(
267      UpdateClockCallback, thr);
268}
269
270void Release(ThreadState *thr, uptr pc, uptr addr) {
271  CHECK_GT(thr->in_rtl, 0);
272  DPrintf("#%d: Release %zx\n", thr->tid, addr);
273  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
274  thr->clock.set(thr->tid, thr->fast_state.epoch());
275  thr->clock.release(&s->clock);
276  StatInc(thr, StatSyncRelease);
277  s->mtx.Unlock();
278}
279
280void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
281  CHECK_GT(thr->in_rtl, 0);
282  DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
283  SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
284  thr->clock.set(thr->tid, thr->fast_state.epoch());
285  thr->clock.ReleaseStore(&s->clock);
286  StatInc(thr, StatSyncRelease);
287  s->mtx.Unlock();
288}
289
290#ifndef TSAN_GO
291static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) {
292  ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
293  ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
294  if (tctx->status == ThreadStatusRunning)
295    thr->last_sleep_clock.set(tctx->tid, tctx->thr->fast_state.epoch());
296  else
297    thr->last_sleep_clock.set(tctx->tid, tctx->epoch1);
298}
299
300void AfterSleep(ThreadState *thr, uptr pc) {
301  thr->last_sleep_stack_id = CurrentStackId(thr, pc);
302  ThreadRegistryLock l(CTX()->thread_registry);
303  CTX()->thread_registry->RunCallbackForEachThreadLocked(
304      UpdateSleepClockCallback, thr);
305}
306#endif
307
308}  // namespace __tsan
309