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