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
21 namespace __tsan {
22
MutexCreate(ThreadState * thr,uptr pc,uptr addr,bool rw,bool recursive,bool linker_init)23 void 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
MutexDestroy(ThreadState * thr,uptr pc,uptr addr)42 void 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
MutexLock(ThreadState * thr,uptr pc,uptr addr,int rec)82 void 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
MutexUnlock(ThreadState * thr,uptr pc,uptr addr,bool all)116 int 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
MutexReadLock(ThreadState * thr,uptr pc,uptr addr)156 void 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
MutexReadUnlock(ThreadState * thr,uptr pc,uptr addr)177 void 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
MutexReadOrWriteUnlock(ThreadState * thr,uptr pc,uptr addr)199 void 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
Acquire(ThreadState * thr,uptr pc,uptr addr)245 void 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
UpdateClockCallback(ThreadContextBase * tctx_base,void * arg)255 static 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
AcquireGlobal(ThreadState * thr,uptr pc)264 void AcquireGlobal(ThreadState *thr, uptr pc) {
265 ThreadRegistryLock l(CTX()->thread_registry);
266 CTX()->thread_registry->RunCallbackForEachThreadLocked(
267 UpdateClockCallback, thr);
268 }
269
Release(ThreadState * thr,uptr pc,uptr addr)270 void 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
ReleaseStore(ThreadState * thr,uptr pc,uptr addr)280 void 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
UpdateSleepClockCallback(ThreadContextBase * tctx_base,void * arg)291 static 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
AfterSleep(ThreadState * thr,uptr pc)300 void 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