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 | void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr, |
27 | FastState last_lock, StackID creation_stack_id); |
28 | |
29 | struct Callback final : public DDCallback { |
30 | ThreadState *thr; |
31 | uptr pc; |
32 | |
33 | Callback(ThreadState *thr, uptr pc) |
34 | : thr(thr) |
35 | , pc(pc) { |
36 | DDCallback::pt = thr->proc()->dd_pt; |
37 | DDCallback::lt = thr->dd_lt; |
38 | } |
39 | |
40 | StackID Unwind() override { return CurrentStackId(thr, pc); } |
41 | int UniqueTid() override { return thr->tid; } |
42 | }; |
43 | |
44 | void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) { |
45 | Callback cb(thr, pc); |
46 | ctx->dd->MutexInit(cb: &cb, m: &s->dd); |
47 | s->dd.ctx = s->addr; |
48 | } |
49 | |
50 | static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ, |
51 | uptr addr, StackID creation_stack_id) { |
52 | // In Go, these misuses are either impossible, or detected by std lib, |
53 | // or false positives (e.g. unlock in a different thread). |
54 | if (SANITIZER_GO) |
55 | return; |
56 | if (!ShouldReport(thr, typ)) |
57 | return; |
58 | ThreadRegistryLock l(&ctx->thread_registry); |
59 | ScopedReport rep(typ); |
60 | rep.AddMutex(addr, creation_stack_id); |
61 | VarSizeStackTrace trace; |
62 | ObtainCurrentStack(thr, toppc: pc, stack: &trace); |
63 | rep.AddStack(stack: trace, suppressable: true); |
64 | rep.AddLocation(addr, size: 1); |
65 | OutputReport(thr, srep: rep); |
66 | } |
67 | |
68 | static void RecordMutexLock(ThreadState *thr, uptr pc, uptr addr, |
69 | StackID stack_id, bool write) { |
70 | auto typ = write ? EventType::kLock : EventType::kRLock; |
71 | // Note: it's important to trace before modifying mutex set |
72 | // because tracing can switch trace part and we write the current |
73 | // mutex set in the beginning of each part. |
74 | // If we do it in the opposite order, we will write already reduced |
75 | // mutex set in the beginning of the part and then trace unlock again. |
76 | TraceMutexLock(thr, type: typ, pc, addr, stk: stack_id); |
77 | thr->mset.AddAddr(addr, stack_id, write); |
78 | } |
79 | |
80 | static void RecordMutexUnlock(ThreadState *thr, uptr addr) { |
81 | // See the comment in RecordMutexLock re order of operations. |
82 | TraceMutexUnlock(thr, addr); |
83 | thr->mset.DelAddr(addr); |
84 | } |
85 | |
86 | void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { |
87 | DPrintf("#%d: MutexCreate %zx flagz=0x%x\n" , thr->tid, addr, flagz); |
88 | if (!(flagz & MutexFlagLinkerInit) && pc && IsAppMem(mem: addr)) |
89 | MemoryAccess(thr, pc, addr, size: 1, typ: kAccessWrite); |
90 | SlotLocker locker(thr); |
91 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
92 | s->SetFlags(flagz & MutexCreationFlagMask); |
93 | // Save stack in the case the sync object was created before as atomic. |
94 | if (!SANITIZER_GO && s->creation_stack_id == kInvalidStackID) |
95 | s->creation_stack_id = CurrentStackId(thr, pc); |
96 | } |
97 | |
98 | void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { |
99 | DPrintf("#%d: MutexDestroy %zx\n" , thr->tid, addr); |
100 | bool unlock_locked = false; |
101 | StackID creation_stack_id; |
102 | FastState last_lock; |
103 | { |
104 | auto s = ctx->metamap.GetSyncIfExists(addr); |
105 | if (!s) |
106 | return; |
107 | SlotLocker locker(thr); |
108 | { |
109 | Lock lock(&s->mtx); |
110 | creation_stack_id = s->creation_stack_id; |
111 | last_lock = s->last_lock; |
112 | if ((flagz & MutexFlagLinkerInit) || s->IsFlagSet(f: MutexFlagLinkerInit) || |
113 | ((flagz & MutexFlagNotStatic) && !s->IsFlagSet(f: MutexFlagNotStatic))) { |
114 | // Destroy is no-op for linker-initialized mutexes. |
115 | return; |
116 | } |
117 | if (common_flags()->detect_deadlocks) { |
118 | Callback cb(thr, pc); |
119 | ctx->dd->MutexDestroy(cb: &cb, m: &s->dd); |
120 | ctx->dd->MutexInit(cb: &cb, m: &s->dd); |
121 | } |
122 | if (flags()->report_destroy_locked && s->owner_tid != kInvalidTid && |
123 | !s->IsFlagSet(f: MutexFlagBroken)) { |
124 | s->SetFlags(MutexFlagBroken); |
125 | unlock_locked = true; |
126 | } |
127 | s->Reset(); |
128 | } |
129 | // Imitate a memory write to catch unlock-destroy races. |
130 | if (pc && IsAppMem(mem: addr)) |
131 | MemoryAccess(thr, pc, addr, size: 1, |
132 | typ: kAccessWrite | kAccessFree | kAccessSlotLocked); |
133 | } |
134 | if (unlock_locked && ShouldReport(thr, typ: ReportTypeMutexDestroyLocked)) |
135 | ReportDestroyLocked(thr, pc, addr, last_lock, creation_stack_id); |
136 | thr->mset.DelAddr(addr, destroy: true); |
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) |
143 | return; |
144 | if (!common_flags()->detect_deadlocks) |
145 | return; |
146 | Callback cb(thr, pc); |
147 | { |
148 | SlotLocker locker(thr); |
149 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
150 | ReadLock lock(&s->mtx); |
151 | s->UpdateFlags(flagz); |
152 | if (s->owner_tid != thr->tid) |
153 | ctx->dd->MutexBeforeLock(cb: &cb, m: &s->dd, wlock: true); |
154 | } |
155 | ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb)); |
156 | } |
157 | |
158 | void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz, int rec) { |
159 | DPrintf("#%d: MutexPostLock %zx flag=0x%x rec=%d\n" , |
160 | thr->tid, addr, flagz, rec); |
161 | if (flagz & MutexFlagRecursiveLock) |
162 | CHECK_GT(rec, 0); |
163 | else |
164 | rec = 1; |
165 | if (pc && IsAppMem(mem: addr)) |
166 | MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic); |
167 | bool report_double_lock = false; |
168 | bool pre_lock = false; |
169 | bool first = false; |
170 | StackID creation_stack_id = kInvalidStackID; |
171 | { |
172 | SlotLocker locker(thr); |
173 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
174 | creation_stack_id = s->creation_stack_id; |
175 | RecordMutexLock(thr, pc, addr, stack_id: creation_stack_id, write: true); |
176 | { |
177 | Lock lock(&s->mtx); |
178 | first = s->recursion == 0; |
179 | s->UpdateFlags(flagz); |
180 | if (s->owner_tid == kInvalidTid) { |
181 | CHECK_EQ(s->recursion, 0); |
182 | s->owner_tid = thr->tid; |
183 | s->last_lock = thr->fast_state; |
184 | } else if (s->owner_tid == thr->tid) { |
185 | CHECK_GT(s->recursion, 0); |
186 | } else if (flags()->report_mutex_bugs && !s->IsFlagSet(f: MutexFlagBroken)) { |
187 | s->SetFlags(MutexFlagBroken); |
188 | report_double_lock = true; |
189 | } |
190 | s->recursion += rec; |
191 | if (first) { |
192 | if (!thr->ignore_sync) { |
193 | thr->clock.Acquire(src: s->clock); |
194 | thr->clock.Acquire(src: s->read_clock); |
195 | } |
196 | } |
197 | if (first && common_flags()->detect_deadlocks) { |
198 | pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) && |
199 | !(flagz & MutexFlagTryLock); |
200 | Callback cb(thr, pc); |
201 | if (pre_lock) |
202 | ctx->dd->MutexBeforeLock(cb: &cb, m: &s->dd, wlock: true); |
203 | ctx->dd->MutexAfterLock(cb: &cb, m: &s->dd, wlock: true, trylock: flagz & MutexFlagTryLock); |
204 | } |
205 | } |
206 | } |
207 | if (report_double_lock) |
208 | ReportMutexMisuse(thr, pc, typ: ReportTypeMutexDoubleLock, addr, |
209 | creation_stack_id); |
210 | if (first && pre_lock && common_flags()->detect_deadlocks) { |
211 | Callback cb(thr, pc); |
212 | ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb)); |
213 | } |
214 | } |
215 | |
216 | int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { |
217 | DPrintf("#%d: MutexUnlock %zx flagz=0x%x\n" , thr->tid, addr, flagz); |
218 | if (pc && IsAppMem(mem: addr)) |
219 | MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic); |
220 | StackID creation_stack_id; |
221 | RecordMutexUnlock(thr, addr); |
222 | bool report_bad_unlock = false; |
223 | int rec = 0; |
224 | { |
225 | SlotLocker locker(thr); |
226 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
227 | bool released = false; |
228 | { |
229 | Lock lock(&s->mtx); |
230 | creation_stack_id = s->creation_stack_id; |
231 | if (!SANITIZER_GO && (s->recursion == 0 || s->owner_tid != thr->tid)) { |
232 | if (flags()->report_mutex_bugs && !s->IsFlagSet(f: MutexFlagBroken)) { |
233 | s->SetFlags(MutexFlagBroken); |
234 | report_bad_unlock = true; |
235 | } |
236 | } else { |
237 | rec = (flagz & MutexFlagRecursiveUnlock) ? s->recursion : 1; |
238 | s->recursion -= rec; |
239 | if (s->recursion == 0) { |
240 | s->owner_tid = kInvalidTid; |
241 | if (!thr->ignore_sync) { |
242 | thr->clock.ReleaseStore(dstp: &s->clock); |
243 | released = true; |
244 | } |
245 | } |
246 | } |
247 | if (common_flags()->detect_deadlocks && s->recursion == 0 && |
248 | !report_bad_unlock) { |
249 | Callback cb(thr, pc); |
250 | ctx->dd->MutexBeforeUnlock(cb: &cb, m: &s->dd, wlock: true); |
251 | } |
252 | } |
253 | if (released) |
254 | IncrementEpoch(thr); |
255 | } |
256 | if (report_bad_unlock) |
257 | ReportMutexMisuse(thr, pc, typ: ReportTypeMutexBadUnlock, addr, |
258 | creation_stack_id); |
259 | if (common_flags()->detect_deadlocks && !report_bad_unlock) { |
260 | Callback cb(thr, pc); |
261 | ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb)); |
262 | } |
263 | return rec; |
264 | } |
265 | |
266 | void MutexPreReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { |
267 | DPrintf("#%d: MutexPreReadLock %zx flagz=0x%x\n" , thr->tid, addr, flagz); |
268 | if ((flagz & MutexFlagTryLock) || !common_flags()->detect_deadlocks) |
269 | return; |
270 | Callback cb(thr, pc); |
271 | { |
272 | SlotLocker locker(thr); |
273 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
274 | ReadLock lock(&s->mtx); |
275 | s->UpdateFlags(flagz); |
276 | ctx->dd->MutexBeforeLock(cb: &cb, m: &s->dd, wlock: false); |
277 | } |
278 | ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb)); |
279 | } |
280 | |
281 | void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { |
282 | DPrintf("#%d: MutexPostReadLock %zx flagz=0x%x\n" , thr->tid, addr, flagz); |
283 | if (pc && IsAppMem(mem: addr)) |
284 | MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic); |
285 | bool report_bad_lock = false; |
286 | bool pre_lock = false; |
287 | StackID creation_stack_id = kInvalidStackID; |
288 | { |
289 | SlotLocker locker(thr); |
290 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
291 | creation_stack_id = s->creation_stack_id; |
292 | RecordMutexLock(thr, pc, addr, stack_id: creation_stack_id, write: false); |
293 | { |
294 | ReadLock lock(&s->mtx); |
295 | s->UpdateFlags(flagz); |
296 | if (s->owner_tid != kInvalidTid) { |
297 | if (flags()->report_mutex_bugs && !s->IsFlagSet(f: MutexFlagBroken)) { |
298 | s->SetFlags(MutexFlagBroken); |
299 | report_bad_lock = true; |
300 | } |
301 | } |
302 | if (!thr->ignore_sync) |
303 | thr->clock.Acquire(src: s->clock); |
304 | s->last_lock = thr->fast_state; |
305 | if (common_flags()->detect_deadlocks) { |
306 | pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) && |
307 | !(flagz & MutexFlagTryLock); |
308 | Callback cb(thr, pc); |
309 | if (pre_lock) |
310 | ctx->dd->MutexBeforeLock(cb: &cb, m: &s->dd, wlock: false); |
311 | ctx->dd->MutexAfterLock(cb: &cb, m: &s->dd, wlock: false, trylock: flagz & MutexFlagTryLock); |
312 | } |
313 | } |
314 | } |
315 | if (report_bad_lock) |
316 | ReportMutexMisuse(thr, pc, typ: ReportTypeMutexBadReadLock, addr, |
317 | creation_stack_id); |
318 | if (pre_lock && common_flags()->detect_deadlocks) { |
319 | Callback cb(thr, pc); |
320 | ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb)); |
321 | } |
322 | } |
323 | |
324 | void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { |
325 | DPrintf("#%d: MutexReadUnlock %zx\n" , thr->tid, addr); |
326 | if (pc && IsAppMem(mem: addr)) |
327 | MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic); |
328 | RecordMutexUnlock(thr, addr); |
329 | StackID creation_stack_id; |
330 | bool report_bad_unlock = false; |
331 | { |
332 | SlotLocker locker(thr); |
333 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
334 | bool released = false; |
335 | { |
336 | Lock lock(&s->mtx); |
337 | creation_stack_id = s->creation_stack_id; |
338 | if (s->owner_tid != kInvalidTid) { |
339 | if (flags()->report_mutex_bugs && !s->IsFlagSet(f: MutexFlagBroken)) { |
340 | s->SetFlags(MutexFlagBroken); |
341 | report_bad_unlock = true; |
342 | } |
343 | } |
344 | if (!thr->ignore_sync) { |
345 | thr->clock.Release(dstp: &s->read_clock); |
346 | released = true; |
347 | } |
348 | if (common_flags()->detect_deadlocks && s->recursion == 0) { |
349 | Callback cb(thr, pc); |
350 | ctx->dd->MutexBeforeUnlock(cb: &cb, m: &s->dd, wlock: false); |
351 | } |
352 | } |
353 | if (released) |
354 | IncrementEpoch(thr); |
355 | } |
356 | if (report_bad_unlock) |
357 | ReportMutexMisuse(thr, pc, typ: ReportTypeMutexBadReadUnlock, addr, |
358 | creation_stack_id); |
359 | if (common_flags()->detect_deadlocks) { |
360 | Callback cb(thr, pc); |
361 | ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb)); |
362 | } |
363 | } |
364 | |
365 | void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { |
366 | DPrintf("#%d: MutexReadOrWriteUnlock %zx\n" , thr->tid, addr); |
367 | if (pc && IsAppMem(mem: addr)) |
368 | MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic); |
369 | RecordMutexUnlock(thr, addr); |
370 | StackID creation_stack_id; |
371 | bool report_bad_unlock = false; |
372 | bool write = true; |
373 | { |
374 | SlotLocker locker(thr); |
375 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
376 | bool released = false; |
377 | { |
378 | Lock lock(&s->mtx); |
379 | creation_stack_id = s->creation_stack_id; |
380 | if (s->owner_tid == kInvalidTid) { |
381 | // Seems to be read unlock. |
382 | write = false; |
383 | if (!thr->ignore_sync) { |
384 | thr->clock.Release(dstp: &s->read_clock); |
385 | released = true; |
386 | } |
387 | } else if (s->owner_tid == thr->tid) { |
388 | // Seems to be write unlock. |
389 | CHECK_GT(s->recursion, 0); |
390 | s->recursion--; |
391 | if (s->recursion == 0) { |
392 | s->owner_tid = kInvalidTid; |
393 | if (!thr->ignore_sync) { |
394 | thr->clock.ReleaseStore(dstp: &s->clock); |
395 | released = true; |
396 | } |
397 | } |
398 | } else if (!s->IsFlagSet(f: MutexFlagBroken)) { |
399 | s->SetFlags(MutexFlagBroken); |
400 | report_bad_unlock = true; |
401 | } |
402 | if (common_flags()->detect_deadlocks && s->recursion == 0) { |
403 | Callback cb(thr, pc); |
404 | ctx->dd->MutexBeforeUnlock(cb: &cb, m: &s->dd, wlock: write); |
405 | } |
406 | } |
407 | if (released) |
408 | IncrementEpoch(thr); |
409 | } |
410 | if (report_bad_unlock) |
411 | ReportMutexMisuse(thr, pc, typ: ReportTypeMutexBadUnlock, addr, |
412 | creation_stack_id); |
413 | if (common_flags()->detect_deadlocks) { |
414 | Callback cb(thr, pc); |
415 | ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb)); |
416 | } |
417 | } |
418 | |
419 | void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { |
420 | DPrintf("#%d: MutexRepair %zx\n" , thr->tid, addr); |
421 | SlotLocker locker(thr); |
422 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
423 | Lock lock(&s->mtx); |
424 | s->owner_tid = kInvalidTid; |
425 | s->recursion = 0; |
426 | } |
427 | |
428 | void MutexInvalidAccess(ThreadState *thr, uptr pc, uptr addr) { |
429 | DPrintf("#%d: MutexInvalidAccess %zx\n" , thr->tid, addr); |
430 | StackID creation_stack_id = kInvalidStackID; |
431 | { |
432 | SlotLocker locker(thr); |
433 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true); |
434 | if (s) |
435 | creation_stack_id = s->creation_stack_id; |
436 | } |
437 | ReportMutexMisuse(thr, pc, typ: ReportTypeMutexInvalidAccess, addr, |
438 | creation_stack_id); |
439 | } |
440 | |
441 | void Acquire(ThreadState *thr, uptr pc, uptr addr) { |
442 | DPrintf("#%d: Acquire %zx\n" , thr->tid, addr); |
443 | if (thr->ignore_sync) |
444 | return; |
445 | auto s = ctx->metamap.GetSyncIfExists(addr); |
446 | if (!s) |
447 | return; |
448 | SlotLocker locker(thr); |
449 | ReadLock lock(&s->mtx); |
450 | if (!s->clock) |
451 | return; |
452 | thr->clock.Acquire(src: s->clock); |
453 | } |
454 | |
455 | void AcquireGlobal(ThreadState *thr) { |
456 | DPrintf("#%d: AcquireGlobal\n" , thr->tid); |
457 | if (thr->ignore_sync) |
458 | return; |
459 | SlotLocker locker(thr); |
460 | for (auto &slot : ctx->slots) thr->clock.Set(sid: slot.sid, v: slot.epoch()); |
461 | } |
462 | |
463 | void Release(ThreadState *thr, uptr pc, uptr addr) { |
464 | DPrintf("#%d: Release %zx\n" , thr->tid, addr); |
465 | if (thr->ignore_sync) |
466 | return; |
467 | SlotLocker locker(thr); |
468 | { |
469 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: false); |
470 | Lock lock(&s->mtx); |
471 | thr->clock.Release(dstp: &s->clock); |
472 | } |
473 | IncrementEpoch(thr); |
474 | } |
475 | |
476 | void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { |
477 | DPrintf("#%d: ReleaseStore %zx\n" , thr->tid, addr); |
478 | if (thr->ignore_sync) |
479 | return; |
480 | SlotLocker locker(thr); |
481 | { |
482 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: false); |
483 | Lock lock(&s->mtx); |
484 | thr->clock.ReleaseStore(dstp: &s->clock); |
485 | } |
486 | IncrementEpoch(thr); |
487 | } |
488 | |
489 | void ReleaseStoreAcquire(ThreadState *thr, uptr pc, uptr addr) { |
490 | DPrintf("#%d: ReleaseStoreAcquire %zx\n" , thr->tid, addr); |
491 | if (thr->ignore_sync) |
492 | return; |
493 | SlotLocker locker(thr); |
494 | { |
495 | auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: false); |
496 | Lock lock(&s->mtx); |
497 | thr->clock.ReleaseStoreAcquire(dstp: &s->clock); |
498 | } |
499 | IncrementEpoch(thr); |
500 | } |
501 | |
502 | void IncrementEpoch(ThreadState *thr) { |
503 | DCHECK(!thr->ignore_sync); |
504 | DCHECK(thr->slot_locked); |
505 | Epoch epoch = EpochInc(epoch: thr->fast_state.epoch()); |
506 | if (!EpochOverflow(epoch)) { |
507 | Sid sid = thr->fast_state.sid(); |
508 | thr->clock.Set(sid, v: epoch); |
509 | thr->fast_state.SetEpoch(epoch); |
510 | thr->slot->SetEpoch(epoch); |
511 | TraceTime(thr); |
512 | } |
513 | } |
514 | |
515 | #if !SANITIZER_GO |
516 | void AfterSleep(ThreadState *thr, uptr pc) { |
517 | DPrintf("#%d: AfterSleep\n" , thr->tid); |
518 | if (thr->ignore_sync) |
519 | return; |
520 | thr->last_sleep_stack_id = CurrentStackId(thr, pc); |
521 | thr->last_sleep_clock.Reset(); |
522 | SlotLocker locker(thr); |
523 | for (auto &slot : ctx->slots) |
524 | thr->last_sleep_clock.Set(sid: slot.sid, v: slot.epoch()); |
525 | } |
526 | #endif |
527 | |
528 | void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { |
529 | if (r == 0 || !ShouldReport(thr, typ: ReportTypeDeadlock)) |
530 | return; |
531 | ThreadRegistryLock l(&ctx->thread_registry); |
532 | ScopedReport rep(ReportTypeDeadlock); |
533 | for (int i = 0; i < r->n; i++) { |
534 | rep.AddMutex(addr: r->loop[i].mtx_ctx0, creation_stack_id: r->loop[i].stk[0]); |
535 | rep.AddUniqueTid(unique_tid: (int)r->loop[i].thr_ctx); |
536 | rep.AddThread(tid: (int)r->loop[i].thr_ctx); |
537 | } |
538 | uptr dummy_pc = 0x42; |
539 | for (int i = 0; i < r->n; i++) { |
540 | for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) { |
541 | u32 stk = r->loop[i].stk[j]; |
542 | if (stk && stk != kInvalidStackID) { |
543 | rep.AddStack(stack: StackDepotGet(id: stk), suppressable: true); |
544 | } else { |
545 | // Sometimes we fail to extract the stack trace (FIXME: investigate), |
546 | // but we should still produce some stack trace in the report. |
547 | rep.AddStack(stack: StackTrace(&dummy_pc, 1), suppressable: true); |
548 | } |
549 | } |
550 | } |
551 | OutputReport(thr, srep: rep); |
552 | } |
553 | |
554 | void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr, |
555 | FastState last_lock, StackID creation_stack_id) { |
556 | // We need to lock the slot during RestoreStack because it protects |
557 | // the slot journal. |
558 | Lock slot_lock(&ctx->slots[static_cast<uptr>(last_lock.sid())].mtx); |
559 | ThreadRegistryLock l0(&ctx->thread_registry); |
560 | Lock slots_lock(&ctx->slot_mtx); |
561 | ScopedReport rep(ReportTypeMutexDestroyLocked); |
562 | rep.AddMutex(addr, creation_stack_id); |
563 | VarSizeStackTrace trace; |
564 | ObtainCurrentStack(thr, toppc: pc, stack: &trace); |
565 | rep.AddStack(stack: trace, suppressable: true); |
566 | |
567 | Tid tid; |
568 | DynamicMutexSet mset; |
569 | uptr tag; |
570 | if (!RestoreStack(type: EventType::kLock, sid: last_lock.sid(), epoch: last_lock.epoch(), addr, |
571 | size: 0, typ: kAccessWrite, ptid: &tid, pstk: &trace, pmset: mset, ptag: &tag)) |
572 | return; |
573 | rep.AddStack(stack: trace, suppressable: true); |
574 | rep.AddLocation(addr, size: 1); |
575 | OutputReport(thr, srep: rep); |
576 | } |
577 | |
578 | } // namespace __tsan |
579 | |