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