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