168d75effSDimitry Andric //===-- tsan_rtl_report.cpp -----------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of ThreadSanitizer (TSan), a race detector.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric //===----------------------------------------------------------------------===//
1268d75effSDimitry Andric 
13*06c3fb27SDimitry Andric #include "sanitizer_common/sanitizer_common.h"
1468d75effSDimitry Andric #include "sanitizer_common/sanitizer_libc.h"
1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_placement_new.h"
1668d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h"
18*06c3fb27SDimitry Andric #include "tsan_fd.h"
19*06c3fb27SDimitry Andric #include "tsan_flags.h"
20*06c3fb27SDimitry Andric #include "tsan_mman.h"
2168d75effSDimitry Andric #include "tsan_platform.h"
22*06c3fb27SDimitry Andric #include "tsan_report.h"
2368d75effSDimitry Andric #include "tsan_rtl.h"
2468d75effSDimitry Andric #include "tsan_suppressions.h"
2568d75effSDimitry Andric #include "tsan_symbolize.h"
2668d75effSDimitry Andric #include "tsan_sync.h"
2768d75effSDimitry Andric 
2868d75effSDimitry Andric namespace __tsan {
2968d75effSDimitry Andric 
3068d75effSDimitry Andric using namespace __sanitizer;
3168d75effSDimitry Andric 
3268d75effSDimitry Andric static ReportStack *SymbolizeStack(StackTrace trace);
3368d75effSDimitry Andric 
3468d75effSDimitry Andric // Can be overriden by an application/test to intercept reports.
3568d75effSDimitry Andric #ifdef TSAN_EXTERNAL_HOOKS
3668d75effSDimitry Andric bool OnReport(const ReportDesc *rep, bool suppressed);
3768d75effSDimitry Andric #else
3868d75effSDimitry Andric SANITIZER_WEAK_CXX_DEFAULT_IMPL
OnReport(const ReportDesc * rep,bool suppressed)3968d75effSDimitry Andric bool OnReport(const ReportDesc *rep, bool suppressed) {
4068d75effSDimitry Andric   (void)rep;
4168d75effSDimitry Andric   return suppressed;
4268d75effSDimitry Andric }
4368d75effSDimitry Andric #endif
4468d75effSDimitry Andric 
4568d75effSDimitry Andric SANITIZER_WEAK_DEFAULT_IMPL
__tsan_on_report(const ReportDesc * rep)4668d75effSDimitry Andric void __tsan_on_report(const ReportDesc *rep) {
4768d75effSDimitry Andric   (void)rep;
4868d75effSDimitry Andric }
4968d75effSDimitry Andric 
StackStripMain(SymbolizedStack * frames)5068d75effSDimitry Andric static void StackStripMain(SymbolizedStack *frames) {
5168d75effSDimitry Andric   SymbolizedStack *last_frame = nullptr;
5268d75effSDimitry Andric   SymbolizedStack *last_frame2 = nullptr;
5368d75effSDimitry Andric   for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
5468d75effSDimitry Andric     last_frame2 = last_frame;
5568d75effSDimitry Andric     last_frame = cur;
5668d75effSDimitry Andric   }
5768d75effSDimitry Andric 
5868d75effSDimitry Andric   if (last_frame2 == 0)
5968d75effSDimitry Andric     return;
6068d75effSDimitry Andric #if !SANITIZER_GO
6168d75effSDimitry Andric   const char *last = last_frame->info.function;
6268d75effSDimitry Andric   const char *last2 = last_frame2->info.function;
6368d75effSDimitry Andric   // Strip frame above 'main'
6468d75effSDimitry Andric   if (last2 && 0 == internal_strcmp(last2, "main")) {
6568d75effSDimitry Andric     last_frame->ClearAll();
6668d75effSDimitry Andric     last_frame2->next = nullptr;
6768d75effSDimitry Andric   // Strip our internal thread start routine.
6868d75effSDimitry Andric   } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) {
6968d75effSDimitry Andric     last_frame->ClearAll();
7068d75effSDimitry Andric     last_frame2->next = nullptr;
71349cc55cSDimitry Andric     // Strip global ctors init, .preinit_array and main caller.
72349cc55cSDimitry Andric   } else if (last && (0 == internal_strcmp(last, "__do_global_ctors_aux") ||
73349cc55cSDimitry Andric                       0 == internal_strcmp(last, "__libc_csu_init") ||
74349cc55cSDimitry Andric                       0 == internal_strcmp(last, "__libc_start_main"))) {
7568d75effSDimitry Andric     last_frame->ClearAll();
7668d75effSDimitry Andric     last_frame2->next = nullptr;
7768d75effSDimitry Andric   // If both are 0, then we probably just failed to symbolize.
7868d75effSDimitry Andric   } else if (last || last2) {
7968d75effSDimitry Andric     // Ensure that we recovered stack completely. Trimmed stack
8068d75effSDimitry Andric     // can actually happen if we do not instrument some code,
8168d75effSDimitry Andric     // so it's only a debug print. However we must try hard to not miss it
8268d75effSDimitry Andric     // due to our fault.
8368d75effSDimitry Andric     DPrintf("Bottom stack frame is missed\n");
8468d75effSDimitry Andric   }
8568d75effSDimitry Andric #else
8668d75effSDimitry Andric   // The last frame always point into runtime (gosched0, goexit0, runtime.main).
8768d75effSDimitry Andric   last_frame->ClearAll();
8868d75effSDimitry Andric   last_frame2->next = nullptr;
8968d75effSDimitry Andric #endif
9068d75effSDimitry Andric }
9168d75effSDimitry Andric 
SymbolizeStackId(u32 stack_id)9268d75effSDimitry Andric ReportStack *SymbolizeStackId(u32 stack_id) {
9368d75effSDimitry Andric   if (stack_id == 0)
9468d75effSDimitry Andric     return 0;
9568d75effSDimitry Andric   StackTrace stack = StackDepotGet(stack_id);
9668d75effSDimitry Andric   if (stack.trace == nullptr)
9768d75effSDimitry Andric     return nullptr;
9868d75effSDimitry Andric   return SymbolizeStack(stack);
9968d75effSDimitry Andric }
10068d75effSDimitry Andric 
SymbolizeStack(StackTrace trace)10168d75effSDimitry Andric static ReportStack *SymbolizeStack(StackTrace trace) {
10268d75effSDimitry Andric   if (trace.size == 0)
10368d75effSDimitry Andric     return 0;
10468d75effSDimitry Andric   SymbolizedStack *top = nullptr;
10568d75effSDimitry Andric   for (uptr si = 0; si < trace.size; si++) {
10668d75effSDimitry Andric     const uptr pc = trace.trace[si];
10768d75effSDimitry Andric     uptr pc1 = pc;
10868d75effSDimitry Andric     // We obtain the return address, but we're interested in the previous
10968d75effSDimitry Andric     // instruction.
11068d75effSDimitry Andric     if ((pc & kExternalPCBit) == 0)
11168d75effSDimitry Andric       pc1 = StackTrace::GetPreviousInstructionPc(pc);
11268d75effSDimitry Andric     SymbolizedStack *ent = SymbolizeCode(pc1);
11368d75effSDimitry Andric     CHECK_NE(ent, 0);
11468d75effSDimitry Andric     SymbolizedStack *last = ent;
11568d75effSDimitry Andric     while (last->next) {
11668d75effSDimitry Andric       last->info.address = pc;  // restore original pc for report
11768d75effSDimitry Andric       last = last->next;
11868d75effSDimitry Andric     }
11968d75effSDimitry Andric     last->info.address = pc;  // restore original pc for report
12068d75effSDimitry Andric     last->next = top;
12168d75effSDimitry Andric     top = ent;
12268d75effSDimitry Andric   }
12368d75effSDimitry Andric   StackStripMain(top);
12468d75effSDimitry Andric 
125349cc55cSDimitry Andric   auto *stack = New<ReportStack>();
12668d75effSDimitry Andric   stack->frames = top;
12768d75effSDimitry Andric   return stack;
12868d75effSDimitry Andric }
12968d75effSDimitry Andric 
ShouldReport(ThreadState * thr,ReportType typ)130fe6060f1SDimitry Andric bool ShouldReport(ThreadState *thr, ReportType typ) {
131fe6060f1SDimitry Andric   // We set thr->suppress_reports in the fork context.
132fe6060f1SDimitry Andric   // Taking any locking in the fork context can lead to deadlocks.
133fe6060f1SDimitry Andric   // If any locks are already taken, it's too late to do this check.
134fe6060f1SDimitry Andric   CheckedMutex::CheckNoLocks();
135fe6060f1SDimitry Andric   // For the same reason check we didn't lock thread_registry yet.
136fe6060f1SDimitry Andric   if (SANITIZER_DEBUG)
137349cc55cSDimitry Andric     ThreadRegistryLock l(&ctx->thread_registry);
138fe6060f1SDimitry Andric   if (!flags()->report_bugs || thr->suppress_reports)
139fe6060f1SDimitry Andric     return false;
140fe6060f1SDimitry Andric   switch (typ) {
141fe6060f1SDimitry Andric     case ReportTypeSignalUnsafe:
142fe6060f1SDimitry Andric       return flags()->report_signal_unsafe;
143fe6060f1SDimitry Andric     case ReportTypeThreadLeak:
144fe6060f1SDimitry Andric #if !SANITIZER_GO
145fe6060f1SDimitry Andric       // It's impossible to join phantom threads
146fe6060f1SDimitry Andric       // in the child after fork.
147fe6060f1SDimitry Andric       if (ctx->after_multithreaded_fork)
148fe6060f1SDimitry Andric         return false;
149fe6060f1SDimitry Andric #endif
150fe6060f1SDimitry Andric       return flags()->report_thread_leaks;
151fe6060f1SDimitry Andric     case ReportTypeMutexDestroyLocked:
152fe6060f1SDimitry Andric       return flags()->report_destroy_locked;
153fe6060f1SDimitry Andric     default:
154fe6060f1SDimitry Andric       return true;
155fe6060f1SDimitry Andric   }
156fe6060f1SDimitry Andric }
157fe6060f1SDimitry Andric 
ScopedReportBase(ReportType typ,uptr tag)15868d75effSDimitry Andric ScopedReportBase::ScopedReportBase(ReportType typ, uptr tag) {
159349cc55cSDimitry Andric   ctx->thread_registry.CheckLocked();
160349cc55cSDimitry Andric   rep_ = New<ReportDesc>();
16168d75effSDimitry Andric   rep_->typ = typ;
16268d75effSDimitry Andric   rep_->tag = tag;
16368d75effSDimitry Andric   ctx->report_mtx.Lock();
16468d75effSDimitry Andric }
16568d75effSDimitry Andric 
~ScopedReportBase()16668d75effSDimitry Andric ScopedReportBase::~ScopedReportBase() {
16768d75effSDimitry Andric   ctx->report_mtx.Unlock();
16868d75effSDimitry Andric   DestroyAndFree(rep_);
16968d75effSDimitry Andric }
17068d75effSDimitry Andric 
AddStack(StackTrace stack,bool suppressable)17168d75effSDimitry Andric void ScopedReportBase::AddStack(StackTrace stack, bool suppressable) {
17268d75effSDimitry Andric   ReportStack **rs = rep_->stacks.PushBack();
17368d75effSDimitry Andric   *rs = SymbolizeStack(stack);
17468d75effSDimitry Andric   (*rs)->suppressable = suppressable;
17568d75effSDimitry Andric }
17668d75effSDimitry Andric 
AddMemoryAccess(uptr addr,uptr external_tag,Shadow s,Tid tid,StackTrace stack,const MutexSet * mset)17768d75effSDimitry Andric void ScopedReportBase::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
1780eae32dcSDimitry Andric                                        Tid tid, StackTrace stack,
1790eae32dcSDimitry Andric                                        const MutexSet *mset) {
1800eae32dcSDimitry Andric   uptr addr0, size;
1810eae32dcSDimitry Andric   AccessType typ;
1820eae32dcSDimitry Andric   s.GetAccess(&addr0, &size, &typ);
183349cc55cSDimitry Andric   auto *mop = New<ReportMop>();
18468d75effSDimitry Andric   rep_->mops.PushBack(mop);
1850eae32dcSDimitry Andric   mop->tid = tid;
1860eae32dcSDimitry Andric   mop->addr = addr + addr0;
1870eae32dcSDimitry Andric   mop->size = size;
1880eae32dcSDimitry Andric   mop->write = !(typ & kAccessRead);
1890eae32dcSDimitry Andric   mop->atomic = typ & kAccessAtomic;
19068d75effSDimitry Andric   mop->stack = SymbolizeStack(stack);
19168d75effSDimitry Andric   mop->external_tag = external_tag;
19268d75effSDimitry Andric   if (mop->stack)
19368d75effSDimitry Andric     mop->stack->suppressable = true;
19468d75effSDimitry Andric   for (uptr i = 0; i < mset->Size(); i++) {
19568d75effSDimitry Andric     MutexSet::Desc d = mset->Get(i);
1960eae32dcSDimitry Andric     int id = this->AddMutex(d.addr, d.stack_id);
1970eae32dcSDimitry Andric     ReportMopMutex mtx = {id, d.write};
19868d75effSDimitry Andric     mop->mset.PushBack(mtx);
19968d75effSDimitry Andric   }
20068d75effSDimitry Andric }
20168d75effSDimitry Andric 
AddUniqueTid(Tid unique_tid)202349cc55cSDimitry Andric void ScopedReportBase::AddUniqueTid(Tid unique_tid) {
20368d75effSDimitry Andric   rep_->unique_tids.PushBack(unique_tid);
20468d75effSDimitry Andric }
20568d75effSDimitry Andric 
AddThread(const ThreadContext * tctx,bool suppressable)20668d75effSDimitry Andric void ScopedReportBase::AddThread(const ThreadContext *tctx, bool suppressable) {
20768d75effSDimitry Andric   for (uptr i = 0; i < rep_->threads.Size(); i++) {
20868d75effSDimitry Andric     if ((u32)rep_->threads[i]->id == tctx->tid)
20968d75effSDimitry Andric       return;
21068d75effSDimitry Andric   }
211349cc55cSDimitry Andric   auto *rt = New<ReportThread>();
21268d75effSDimitry Andric   rep_->threads.PushBack(rt);
21368d75effSDimitry Andric   rt->id = tctx->tid;
21468d75effSDimitry Andric   rt->os_id = tctx->os_id;
21568d75effSDimitry Andric   rt->running = (tctx->status == ThreadStatusRunning);
21668d75effSDimitry Andric   rt->name = internal_strdup(tctx->name);
21768d75effSDimitry Andric   rt->parent_tid = tctx->parent_tid;
21868d75effSDimitry Andric   rt->thread_type = tctx->thread_type;
21968d75effSDimitry Andric   rt->stack = 0;
22068d75effSDimitry Andric   rt->stack = SymbolizeStackId(tctx->creation_stack_id);
22168d75effSDimitry Andric   if (rt->stack)
22268d75effSDimitry Andric     rt->stack->suppressable = suppressable;
22368d75effSDimitry Andric }
22468d75effSDimitry Andric 
22568d75effSDimitry Andric #if !SANITIZER_GO
FindThreadByTidLocked(Tid tid)226349cc55cSDimitry Andric static ThreadContext *FindThreadByTidLocked(Tid tid) {
227349cc55cSDimitry Andric   ctx->thread_registry.CheckLocked();
22868d75effSDimitry Andric   return static_cast<ThreadContext *>(
229349cc55cSDimitry Andric       ctx->thread_registry.GetThreadLocked(tid));
23068d75effSDimitry Andric }
23168d75effSDimitry Andric 
IsInStackOrTls(ThreadContextBase * tctx_base,void * arg)23268d75effSDimitry Andric static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) {
23368d75effSDimitry Andric   uptr addr = (uptr)arg;
23468d75effSDimitry Andric   ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
23568d75effSDimitry Andric   if (tctx->status != ThreadStatusRunning)
23668d75effSDimitry Andric     return false;
23768d75effSDimitry Andric   ThreadState *thr = tctx->thr;
23868d75effSDimitry Andric   CHECK(thr);
23968d75effSDimitry Andric   return ((addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) ||
24068d75effSDimitry Andric           (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size));
24168d75effSDimitry Andric }
24268d75effSDimitry Andric 
IsThreadStackOrTls(uptr addr,bool * is_stack)24368d75effSDimitry Andric ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
244349cc55cSDimitry Andric   ctx->thread_registry.CheckLocked();
245349cc55cSDimitry Andric   ThreadContext *tctx =
246349cc55cSDimitry Andric       static_cast<ThreadContext *>(ctx->thread_registry.FindThreadContextLocked(
247349cc55cSDimitry Andric           IsInStackOrTls, (void *)addr));
24868d75effSDimitry Andric   if (!tctx)
24968d75effSDimitry Andric     return 0;
25068d75effSDimitry Andric   ThreadState *thr = tctx->thr;
25168d75effSDimitry Andric   CHECK(thr);
25268d75effSDimitry Andric   *is_stack = (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size);
25368d75effSDimitry Andric   return tctx;
25468d75effSDimitry Andric }
25568d75effSDimitry Andric #endif
25668d75effSDimitry Andric 
AddThread(Tid tid,bool suppressable)2570eae32dcSDimitry Andric void ScopedReportBase::AddThread(Tid tid, bool suppressable) {
25868d75effSDimitry Andric #if !SANITIZER_GO
2590eae32dcSDimitry Andric   if (const ThreadContext *tctx = FindThreadByTidLocked(tid))
26068d75effSDimitry Andric     AddThread(tctx, suppressable);
26168d75effSDimitry Andric #endif
26268d75effSDimitry Andric }
26368d75effSDimitry Andric 
AddMutex(uptr addr,StackID creation_stack_id)2640eae32dcSDimitry Andric int ScopedReportBase::AddMutex(uptr addr, StackID creation_stack_id) {
26568d75effSDimitry Andric   for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
2660eae32dcSDimitry Andric     if (rep_->mutexes[i]->addr == addr)
2670eae32dcSDimitry Andric       return rep_->mutexes[i]->id;
26868d75effSDimitry Andric   }
269349cc55cSDimitry Andric   auto *rm = New<ReportMutex>();
27068d75effSDimitry Andric   rep_->mutexes.PushBack(rm);
2710eae32dcSDimitry Andric   rm->id = rep_->mutexes.Size() - 1;
2720eae32dcSDimitry Andric   rm->addr = addr;
2730eae32dcSDimitry Andric   rm->stack = SymbolizeStackId(creation_stack_id);
2740eae32dcSDimitry Andric   return rm->id;
27568d75effSDimitry Andric }
27668d75effSDimitry Andric 
AddLocation(uptr addr,uptr size)27768d75effSDimitry Andric void ScopedReportBase::AddLocation(uptr addr, uptr size) {
27868d75effSDimitry Andric   if (addr == 0)
27968d75effSDimitry Andric     return;
28068d75effSDimitry Andric #if !SANITIZER_GO
28168d75effSDimitry Andric   int fd = -1;
282349cc55cSDimitry Andric   Tid creat_tid = kInvalidTid;
283349cc55cSDimitry Andric   StackID creat_stack = 0;
284bdd1243dSDimitry Andric   bool closed = false;
285bdd1243dSDimitry Andric   if (FdLocation(addr, &fd, &creat_tid, &creat_stack, &closed)) {
286349cc55cSDimitry Andric     auto *loc = New<ReportLocation>();
287349cc55cSDimitry Andric     loc->type = ReportLocationFD;
288bdd1243dSDimitry Andric     loc->fd_closed = closed;
28968d75effSDimitry Andric     loc->fd = fd;
29068d75effSDimitry Andric     loc->tid = creat_tid;
29168d75effSDimitry Andric     loc->stack = SymbolizeStackId(creat_stack);
29268d75effSDimitry Andric     rep_->locs.PushBack(loc);
293bdd1243dSDimitry Andric     AddThread(creat_tid);
29468d75effSDimitry Andric     return;
29568d75effSDimitry Andric   }
29668d75effSDimitry Andric   MBlock *b = 0;
297349cc55cSDimitry Andric   uptr block_begin = 0;
29868d75effSDimitry Andric   Allocator *a = allocator();
29968d75effSDimitry Andric   if (a->PointerIsMine((void*)addr)) {
300349cc55cSDimitry Andric     block_begin = (uptr)a->GetBlockBegin((void *)addr);
30168d75effSDimitry Andric     if (block_begin)
302349cc55cSDimitry Andric       b = ctx->metamap.GetBlock(block_begin);
30368d75effSDimitry Andric   }
304349cc55cSDimitry Andric   if (!b)
305349cc55cSDimitry Andric     b = JavaHeapBlock(addr, &block_begin);
30668d75effSDimitry Andric   if (b != 0) {
307349cc55cSDimitry Andric     auto *loc = New<ReportLocation>();
308349cc55cSDimitry Andric     loc->type = ReportLocationHeap;
3094824e7fdSDimitry Andric     loc->heap_chunk_start = block_begin;
31068d75effSDimitry Andric     loc->heap_chunk_size = b->siz;
31168d75effSDimitry Andric     loc->external_tag = b->tag;
3120eae32dcSDimitry Andric     loc->tid = b->tid;
31368d75effSDimitry Andric     loc->stack = SymbolizeStackId(b->stk);
31468d75effSDimitry Andric     rep_->locs.PushBack(loc);
315bdd1243dSDimitry Andric     AddThread(b->tid);
31668d75effSDimitry Andric     return;
31768d75effSDimitry Andric   }
31868d75effSDimitry Andric   bool is_stack = false;
31968d75effSDimitry Andric   if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
320349cc55cSDimitry Andric     auto *loc = New<ReportLocation>();
321349cc55cSDimitry Andric     loc->type = is_stack ? ReportLocationStack : ReportLocationTLS;
32268d75effSDimitry Andric     loc->tid = tctx->tid;
32368d75effSDimitry Andric     rep_->locs.PushBack(loc);
32468d75effSDimitry Andric     AddThread(tctx);
32568d75effSDimitry Andric   }
32668d75effSDimitry Andric #endif
32768d75effSDimitry Andric   if (ReportLocation *loc = SymbolizeData(addr)) {
32868d75effSDimitry Andric     loc->suppressable = true;
32968d75effSDimitry Andric     rep_->locs.PushBack(loc);
33068d75effSDimitry Andric     return;
33168d75effSDimitry Andric   }
33268d75effSDimitry Andric }
33368d75effSDimitry Andric 
33468d75effSDimitry Andric #if !SANITIZER_GO
AddSleep(StackID stack_id)335349cc55cSDimitry Andric void ScopedReportBase::AddSleep(StackID stack_id) {
33668d75effSDimitry Andric   rep_->sleep = SymbolizeStackId(stack_id);
33768d75effSDimitry Andric }
33868d75effSDimitry Andric #endif
33968d75effSDimitry Andric 
SetCount(int count)34068d75effSDimitry Andric void ScopedReportBase::SetCount(int count) { rep_->count = count; }
34168d75effSDimitry Andric 
SetSigNum(int sig)34281ad6265SDimitry Andric void ScopedReportBase::SetSigNum(int sig) { rep_->signum = sig; }
34381ad6265SDimitry Andric 
GetReport() const34468d75effSDimitry Andric const ReportDesc *ScopedReportBase::GetReport() const { return rep_; }
34568d75effSDimitry Andric 
ScopedReport(ReportType typ,uptr tag)34668d75effSDimitry Andric ScopedReport::ScopedReport(ReportType typ, uptr tag)
34768d75effSDimitry Andric     : ScopedReportBase(typ, tag) {}
34868d75effSDimitry Andric 
~ScopedReport()34968d75effSDimitry Andric ScopedReport::~ScopedReport() {}
35068d75effSDimitry Andric 
351349cc55cSDimitry Andric // Replays the trace up to last_pos position in the last part
352349cc55cSDimitry Andric // or up to the provided epoch/sid (whichever is earlier)
353349cc55cSDimitry Andric // and calls the provided function f for each event.
354349cc55cSDimitry Andric template <typename Func>
TraceReplay(Trace * trace,TracePart * last,Event * last_pos,Sid sid,Epoch epoch,Func f)355349cc55cSDimitry Andric void TraceReplay(Trace *trace, TracePart *last, Event *last_pos, Sid sid,
356349cc55cSDimitry Andric                  Epoch epoch, Func f) {
357349cc55cSDimitry Andric   TracePart *part = trace->parts.Front();
358349cc55cSDimitry Andric   Sid ev_sid = kFreeSid;
359349cc55cSDimitry Andric   Epoch ev_epoch = kEpochOver;
360349cc55cSDimitry Andric   for (;;) {
361349cc55cSDimitry Andric     DCHECK_EQ(part->trace, trace);
362349cc55cSDimitry Andric     // Note: an event can't start in the last element.
363349cc55cSDimitry Andric     // Since an event can take up to 2 elements,
364349cc55cSDimitry Andric     // we ensure we have at least 2 before adding an event.
365349cc55cSDimitry Andric     Event *end = &part->events[TracePart::kSize - 1];
366349cc55cSDimitry Andric     if (part == last)
367349cc55cSDimitry Andric       end = last_pos;
3680eae32dcSDimitry Andric     f(kFreeSid, kEpochOver, nullptr);  // notify about part start
369349cc55cSDimitry Andric     for (Event *evp = &part->events[0]; evp < end; evp++) {
370349cc55cSDimitry Andric       Event *evp0 = evp;
371349cc55cSDimitry Andric       if (!evp->is_access && !evp->is_func) {
372349cc55cSDimitry Andric         switch (evp->type) {
373349cc55cSDimitry Andric           case EventType::kTime: {
374349cc55cSDimitry Andric             auto *ev = reinterpret_cast<EventTime *>(evp);
375349cc55cSDimitry Andric             ev_sid = static_cast<Sid>(ev->sid);
376349cc55cSDimitry Andric             ev_epoch = static_cast<Epoch>(ev->epoch);
377349cc55cSDimitry Andric             if (ev_sid == sid && ev_epoch > epoch)
378349cc55cSDimitry Andric               return;
379349cc55cSDimitry Andric             break;
380349cc55cSDimitry Andric           }
381349cc55cSDimitry Andric           case EventType::kAccessExt:
382349cc55cSDimitry Andric             FALLTHROUGH;
383349cc55cSDimitry Andric           case EventType::kAccessRange:
384349cc55cSDimitry Andric             FALLTHROUGH;
385349cc55cSDimitry Andric           case EventType::kLock:
386349cc55cSDimitry Andric             FALLTHROUGH;
387349cc55cSDimitry Andric           case EventType::kRLock:
388349cc55cSDimitry Andric             // These take 2 Event elements.
389349cc55cSDimitry Andric             evp++;
390349cc55cSDimitry Andric             break;
391349cc55cSDimitry Andric           case EventType::kUnlock:
392349cc55cSDimitry Andric             // This takes 1 Event element.
393349cc55cSDimitry Andric             break;
394349cc55cSDimitry Andric         }
395349cc55cSDimitry Andric       }
396349cc55cSDimitry Andric       CHECK_NE(ev_sid, kFreeSid);
397349cc55cSDimitry Andric       CHECK_NE(ev_epoch, kEpochOver);
398349cc55cSDimitry Andric       f(ev_sid, ev_epoch, evp0);
399349cc55cSDimitry Andric     }
400349cc55cSDimitry Andric     if (part == last)
401349cc55cSDimitry Andric       return;
402349cc55cSDimitry Andric     part = trace->parts.Next(part);
403349cc55cSDimitry Andric     CHECK(part);
404349cc55cSDimitry Andric   }
405349cc55cSDimitry Andric   CHECK(0);
406349cc55cSDimitry Andric }
407349cc55cSDimitry Andric 
RestoreStackMatch(VarSizeStackTrace * pstk,MutexSet * pmset,Vector<uptr> * stack,MutexSet * mset,uptr pc,bool * found)408349cc55cSDimitry Andric static void RestoreStackMatch(VarSizeStackTrace *pstk, MutexSet *pmset,
409349cc55cSDimitry Andric                               Vector<uptr> *stack, MutexSet *mset, uptr pc,
410349cc55cSDimitry Andric                               bool *found) {
411349cc55cSDimitry Andric   DPrintf2("    MATCHED\n");
412349cc55cSDimitry Andric   *pmset = *mset;
413349cc55cSDimitry Andric   stack->PushBack(pc);
414349cc55cSDimitry Andric   pstk->Init(&(*stack)[0], stack->Size());
415349cc55cSDimitry Andric   stack->PopBack();
416349cc55cSDimitry Andric   *found = true;
417349cc55cSDimitry Andric }
418349cc55cSDimitry Andric 
419349cc55cSDimitry Andric // Checks if addr1|size1 is fully contained in addr2|size2.
420349cc55cSDimitry Andric // We check for fully contained instread of just overlapping
421349cc55cSDimitry Andric // because a memory access is always traced once, but can be
422349cc55cSDimitry Andric // split into multiple accesses in the shadow.
IsWithinAccess(uptr addr1,uptr size1,uptr addr2,uptr size2)423349cc55cSDimitry Andric static constexpr bool IsWithinAccess(uptr addr1, uptr size1, uptr addr2,
424349cc55cSDimitry Andric                                      uptr size2) {
425349cc55cSDimitry Andric   return addr1 >= addr2 && addr1 + size1 <= addr2 + size2;
426349cc55cSDimitry Andric }
427349cc55cSDimitry Andric 
4280eae32dcSDimitry Andric // Replays the trace of slot sid up to the target event identified
4290eae32dcSDimitry Andric // by epoch/addr/size/typ and restores and returns tid, stack, mutex set
430349cc55cSDimitry Andric // and tag for that event. If there are multiple such events, it returns
431349cc55cSDimitry Andric // the last one. Returns false if the event is not present in the trace.
RestoreStack(EventType type,Sid sid,Epoch epoch,uptr addr,uptr size,AccessType typ,Tid * ptid,VarSizeStackTrace * pstk,MutexSet * pmset,uptr * ptag)4320eae32dcSDimitry Andric bool RestoreStack(EventType type, Sid sid, Epoch epoch, uptr addr, uptr size,
4330eae32dcSDimitry Andric                   AccessType typ, Tid *ptid, VarSizeStackTrace *pstk,
434349cc55cSDimitry Andric                   MutexSet *pmset, uptr *ptag) {
435349cc55cSDimitry Andric   // This function restores stack trace and mutex set for the thread/epoch.
436349cc55cSDimitry Andric   // It does so by getting stack trace and mutex set at the beginning of
437349cc55cSDimitry Andric   // trace part, and then replaying the trace till the given epoch.
4380eae32dcSDimitry Andric   DPrintf2("RestoreStack: sid=%u@%u addr=0x%zx/%zu typ=%x\n",
439349cc55cSDimitry Andric            static_cast<int>(sid), static_cast<int>(epoch), addr, size,
440349cc55cSDimitry Andric            static_cast<int>(typ));
441349cc55cSDimitry Andric   ctx->slot_mtx.CheckLocked();  // needed to prevent trace part recycling
442349cc55cSDimitry Andric   ctx->thread_registry.CheckLocked();
4430eae32dcSDimitry Andric   TidSlot *slot = &ctx->slots[static_cast<uptr>(sid)];
4440eae32dcSDimitry Andric   Tid tid = kInvalidTid;
4450eae32dcSDimitry Andric   // Need to lock the slot mutex as it protects slot->journal.
4460eae32dcSDimitry Andric   slot->mtx.CheckLocked();
4470eae32dcSDimitry Andric   for (uptr i = 0; i < slot->journal.Size(); i++) {
4480eae32dcSDimitry Andric     DPrintf2("  journal: epoch=%d tid=%d\n",
4490eae32dcSDimitry Andric              static_cast<int>(slot->journal[i].epoch), slot->journal[i].tid);
4500eae32dcSDimitry Andric     if (i == slot->journal.Size() - 1 || slot->journal[i + 1].epoch > epoch) {
4510eae32dcSDimitry Andric       tid = slot->journal[i].tid;
4520eae32dcSDimitry Andric       break;
4530eae32dcSDimitry Andric     }
4540eae32dcSDimitry Andric   }
4550eae32dcSDimitry Andric   if (tid == kInvalidTid)
4560eae32dcSDimitry Andric     return false;
4570eae32dcSDimitry Andric   *ptid = tid;
458349cc55cSDimitry Andric   ThreadContext *tctx =
459349cc55cSDimitry Andric       static_cast<ThreadContext *>(ctx->thread_registry.GetThreadLocked(tid));
460349cc55cSDimitry Andric   Trace *trace = &tctx->trace;
461349cc55cSDimitry Andric   // Snapshot first/last parts and the current position in the last part.
462349cc55cSDimitry Andric   TracePart *first_part;
463349cc55cSDimitry Andric   TracePart *last_part;
464349cc55cSDimitry Andric   Event *last_pos;
465349cc55cSDimitry Andric   {
466349cc55cSDimitry Andric     Lock lock(&trace->mtx);
467349cc55cSDimitry Andric     first_part = trace->parts.Front();
4680eae32dcSDimitry Andric     if (!first_part) {
4690eae32dcSDimitry Andric       DPrintf2("RestoreStack: tid=%d trace=%p no trace parts\n", tid, trace);
470349cc55cSDimitry Andric       return false;
4710eae32dcSDimitry Andric     }
472349cc55cSDimitry Andric     last_part = trace->parts.Back();
473349cc55cSDimitry Andric     last_pos = trace->final_pos;
474349cc55cSDimitry Andric     if (tctx->thr)
475349cc55cSDimitry Andric       last_pos = (Event *)atomic_load_relaxed(&tctx->thr->trace_pos);
476349cc55cSDimitry Andric   }
477349cc55cSDimitry Andric   DynamicMutexSet mset;
478349cc55cSDimitry Andric   Vector<uptr> stack;
479349cc55cSDimitry Andric   uptr prev_pc = 0;
480349cc55cSDimitry Andric   bool found = false;
481349cc55cSDimitry Andric   bool is_read = typ & kAccessRead;
482349cc55cSDimitry Andric   bool is_atomic = typ & kAccessAtomic;
483349cc55cSDimitry Andric   bool is_free = typ & kAccessFree;
4840eae32dcSDimitry Andric   DPrintf2("RestoreStack: tid=%d parts=[%p-%p] last_pos=%p\n", tid,
4850eae32dcSDimitry Andric            trace->parts.Front(), last_part, last_pos);
486349cc55cSDimitry Andric   TraceReplay(
487349cc55cSDimitry Andric       trace, last_part, last_pos, sid, epoch,
488349cc55cSDimitry Andric       [&](Sid ev_sid, Epoch ev_epoch, Event *evp) {
4890eae32dcSDimitry Andric         if (evp == nullptr) {
4900eae32dcSDimitry Andric           // Each trace part is self-consistent, so we reset state.
4910eae32dcSDimitry Andric           stack.Resize(0);
4920eae32dcSDimitry Andric           mset->Reset();
4930eae32dcSDimitry Andric           prev_pc = 0;
4940eae32dcSDimitry Andric           return;
4950eae32dcSDimitry Andric         }
496349cc55cSDimitry Andric         bool match = ev_sid == sid && ev_epoch == epoch;
497349cc55cSDimitry Andric         if (evp->is_access) {
498349cc55cSDimitry Andric           if (evp->is_func == 0 && evp->type == EventType::kAccessExt &&
499349cc55cSDimitry Andric               evp->_ == 0)  // NopEvent
500349cc55cSDimitry Andric             return;
501349cc55cSDimitry Andric           auto *ev = reinterpret_cast<EventAccess *>(evp);
502349cc55cSDimitry Andric           uptr ev_addr = RestoreAddr(ev->addr);
503349cc55cSDimitry Andric           uptr ev_size = 1 << ev->size_log;
504349cc55cSDimitry Andric           uptr ev_pc =
505349cc55cSDimitry Andric               prev_pc + ev->pc_delta - (1 << (EventAccess::kPCBits - 1));
506349cc55cSDimitry Andric           prev_pc = ev_pc;
507349cc55cSDimitry Andric           DPrintf2("  Access: pc=0x%zx addr=0x%zx/%zu type=%u/%u\n", ev_pc,
508349cc55cSDimitry Andric                    ev_addr, ev_size, ev->is_read, ev->is_atomic);
509349cc55cSDimitry Andric           if (match && type == EventType::kAccessExt &&
510349cc55cSDimitry Andric               IsWithinAccess(addr, size, ev_addr, ev_size) &&
511349cc55cSDimitry Andric               is_read == ev->is_read && is_atomic == ev->is_atomic && !is_free)
512349cc55cSDimitry Andric             RestoreStackMatch(pstk, pmset, &stack, mset, ev_pc, &found);
513349cc55cSDimitry Andric           return;
514349cc55cSDimitry Andric         }
515349cc55cSDimitry Andric         if (evp->is_func) {
516349cc55cSDimitry Andric           auto *ev = reinterpret_cast<EventFunc *>(evp);
517349cc55cSDimitry Andric           if (ev->pc) {
518349cc55cSDimitry Andric             DPrintf2(" FuncEnter: pc=0x%llx\n", ev->pc);
519349cc55cSDimitry Andric             stack.PushBack(ev->pc);
520349cc55cSDimitry Andric           } else {
521349cc55cSDimitry Andric             DPrintf2(" FuncExit\n");
5220eae32dcSDimitry Andric             // We don't log pathologically large stacks in each part,
5230eae32dcSDimitry Andric             // if the stack was truncated we can have more func exits than
5240eae32dcSDimitry Andric             // entries.
5250eae32dcSDimitry Andric             if (stack.Size())
526349cc55cSDimitry Andric               stack.PopBack();
527349cc55cSDimitry Andric           }
528349cc55cSDimitry Andric           return;
529349cc55cSDimitry Andric         }
530349cc55cSDimitry Andric         switch (evp->type) {
531349cc55cSDimitry Andric           case EventType::kAccessExt: {
532349cc55cSDimitry Andric             auto *ev = reinterpret_cast<EventAccessExt *>(evp);
533349cc55cSDimitry Andric             uptr ev_addr = RestoreAddr(ev->addr);
534349cc55cSDimitry Andric             uptr ev_size = 1 << ev->size_log;
535349cc55cSDimitry Andric             prev_pc = ev->pc;
536349cc55cSDimitry Andric             DPrintf2("  AccessExt: pc=0x%llx addr=0x%zx/%zu type=%u/%u\n",
537349cc55cSDimitry Andric                      ev->pc, ev_addr, ev_size, ev->is_read, ev->is_atomic);
538349cc55cSDimitry Andric             if (match && type == EventType::kAccessExt &&
539349cc55cSDimitry Andric                 IsWithinAccess(addr, size, ev_addr, ev_size) &&
540349cc55cSDimitry Andric                 is_read == ev->is_read && is_atomic == ev->is_atomic &&
541349cc55cSDimitry Andric                 !is_free)
542349cc55cSDimitry Andric               RestoreStackMatch(pstk, pmset, &stack, mset, ev->pc, &found);
543349cc55cSDimitry Andric             break;
544349cc55cSDimitry Andric           }
545349cc55cSDimitry Andric           case EventType::kAccessRange: {
546349cc55cSDimitry Andric             auto *ev = reinterpret_cast<EventAccessRange *>(evp);
547349cc55cSDimitry Andric             uptr ev_addr = RestoreAddr(ev->addr);
548349cc55cSDimitry Andric             uptr ev_size =
549349cc55cSDimitry Andric                 (ev->size_hi << EventAccessRange::kSizeLoBits) + ev->size_lo;
550349cc55cSDimitry Andric             uptr ev_pc = RestoreAddr(ev->pc);
551349cc55cSDimitry Andric             prev_pc = ev_pc;
552349cc55cSDimitry Andric             DPrintf2("  Range: pc=0x%zx addr=0x%zx/%zu type=%u/%u\n", ev_pc,
553349cc55cSDimitry Andric                      ev_addr, ev_size, ev->is_read, ev->is_free);
554349cc55cSDimitry Andric             if (match && type == EventType::kAccessExt &&
555349cc55cSDimitry Andric                 IsWithinAccess(addr, size, ev_addr, ev_size) &&
556349cc55cSDimitry Andric                 is_read == ev->is_read && !is_atomic && is_free == ev->is_free)
557349cc55cSDimitry Andric               RestoreStackMatch(pstk, pmset, &stack, mset, ev_pc, &found);
558349cc55cSDimitry Andric             break;
559349cc55cSDimitry Andric           }
560349cc55cSDimitry Andric           case EventType::kLock:
561349cc55cSDimitry Andric             FALLTHROUGH;
562349cc55cSDimitry Andric           case EventType::kRLock: {
563349cc55cSDimitry Andric             auto *ev = reinterpret_cast<EventLock *>(evp);
564349cc55cSDimitry Andric             bool is_write = ev->type == EventType::kLock;
565349cc55cSDimitry Andric             uptr ev_addr = RestoreAddr(ev->addr);
566349cc55cSDimitry Andric             uptr ev_pc = RestoreAddr(ev->pc);
567349cc55cSDimitry Andric             StackID stack_id =
568349cc55cSDimitry Andric                 (ev->stack_hi << EventLock::kStackIDLoBits) + ev->stack_lo;
569349cc55cSDimitry Andric             DPrintf2("  Lock: pc=0x%zx addr=0x%zx stack=%u write=%d\n", ev_pc,
570349cc55cSDimitry Andric                      ev_addr, stack_id, is_write);
571349cc55cSDimitry Andric             mset->AddAddr(ev_addr, stack_id, is_write);
572349cc55cSDimitry Andric             // Events with ev_pc == 0 are written to the beginning of trace
573349cc55cSDimitry Andric             // part as initial mutex set (are not real).
574349cc55cSDimitry Andric             if (match && type == EventType::kLock && addr == ev_addr && ev_pc)
575349cc55cSDimitry Andric               RestoreStackMatch(pstk, pmset, &stack, mset, ev_pc, &found);
576349cc55cSDimitry Andric             break;
577349cc55cSDimitry Andric           }
578349cc55cSDimitry Andric           case EventType::kUnlock: {
579349cc55cSDimitry Andric             auto *ev = reinterpret_cast<EventUnlock *>(evp);
580349cc55cSDimitry Andric             uptr ev_addr = RestoreAddr(ev->addr);
581349cc55cSDimitry Andric             DPrintf2("  Unlock: addr=0x%zx\n", ev_addr);
582349cc55cSDimitry Andric             mset->DelAddr(ev_addr);
583349cc55cSDimitry Andric             break;
584349cc55cSDimitry Andric           }
585349cc55cSDimitry Andric           case EventType::kTime:
586349cc55cSDimitry Andric             // TraceReplay already extracted sid/epoch from it,
587349cc55cSDimitry Andric             // nothing else to do here.
588349cc55cSDimitry Andric             break;
589349cc55cSDimitry Andric         }
590349cc55cSDimitry Andric       });
591349cc55cSDimitry Andric   ExtractTagFromStack(pstk, ptag);
592349cc55cSDimitry Andric   return found;
593349cc55cSDimitry Andric }
594349cc55cSDimitry Andric 
operator ==(const RacyStacks & other) const595349cc55cSDimitry Andric bool RacyStacks::operator==(const RacyStacks &other) const {
596349cc55cSDimitry Andric   if (hash[0] == other.hash[0] && hash[1] == other.hash[1])
597349cc55cSDimitry Andric     return true;
598349cc55cSDimitry Andric   if (hash[0] == other.hash[1] && hash[1] == other.hash[0])
599349cc55cSDimitry Andric     return true;
600349cc55cSDimitry Andric   return false;
601349cc55cSDimitry Andric }
602349cc55cSDimitry Andric 
FindRacyStacks(const RacyStacks & hash)603590d96feSDimitry Andric static bool FindRacyStacks(const RacyStacks &hash) {
60468d75effSDimitry Andric   for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
60568d75effSDimitry Andric     if (hash == ctx->racy_stacks[i]) {
606590d96feSDimitry Andric       VPrintf(2, "ThreadSanitizer: suppressing report as doubled (stack)\n");
607590d96feSDimitry Andric       return true;
60868d75effSDimitry Andric     }
60968d75effSDimitry Andric   }
610590d96feSDimitry Andric   return false;
61168d75effSDimitry Andric }
612590d96feSDimitry Andric 
HandleRacyStacks(ThreadState * thr,VarSizeStackTrace traces[2])613590d96feSDimitry Andric static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2]) {
614590d96feSDimitry Andric   if (!flags()->suppress_equal_stacks)
615590d96feSDimitry Andric     return false;
616590d96feSDimitry Andric   RacyStacks hash;
617590d96feSDimitry Andric   hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
618590d96feSDimitry Andric   hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
619590d96feSDimitry Andric   {
620590d96feSDimitry Andric     ReadLock lock(&ctx->racy_mtx);
621590d96feSDimitry Andric     if (FindRacyStacks(hash))
622590d96feSDimitry Andric       return true;
623590d96feSDimitry Andric   }
624590d96feSDimitry Andric   Lock lock(&ctx->racy_mtx);
625590d96feSDimitry Andric   if (FindRacyStacks(hash))
626590d96feSDimitry Andric     return true;
627590d96feSDimitry Andric   ctx->racy_stacks.PushBack(hash);
628590d96feSDimitry Andric   return false;
629590d96feSDimitry Andric }
630590d96feSDimitry Andric 
OutputReport(ThreadState * thr,const ScopedReport & srep)63168d75effSDimitry Andric bool OutputReport(ThreadState *thr, const ScopedReport &srep) {
632fe6060f1SDimitry Andric   // These should have been checked in ShouldReport.
633fe6060f1SDimitry Andric   // It's too late to check them here, we have already taken locks.
634fe6060f1SDimitry Andric   CHECK(flags()->report_bugs);
635fe6060f1SDimitry Andric   CHECK(!thr->suppress_reports);
63668d75effSDimitry Andric   atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime());
63768d75effSDimitry Andric   const ReportDesc *rep = srep.GetReport();
63868d75effSDimitry Andric   CHECK_EQ(thr->current_report, nullptr);
63968d75effSDimitry Andric   thr->current_report = rep;
64068d75effSDimitry Andric   Suppression *supp = 0;
64168d75effSDimitry Andric   uptr pc_or_addr = 0;
64268d75effSDimitry Andric   for (uptr i = 0; pc_or_addr == 0 && i < rep->mops.Size(); i++)
64368d75effSDimitry Andric     pc_or_addr = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp);
64468d75effSDimitry Andric   for (uptr i = 0; pc_or_addr == 0 && i < rep->stacks.Size(); i++)
64568d75effSDimitry Andric     pc_or_addr = IsSuppressed(rep->typ, rep->stacks[i], &supp);
64668d75effSDimitry Andric   for (uptr i = 0; pc_or_addr == 0 && i < rep->threads.Size(); i++)
64768d75effSDimitry Andric     pc_or_addr = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp);
64868d75effSDimitry Andric   for (uptr i = 0; pc_or_addr == 0 && i < rep->locs.Size(); i++)
64968d75effSDimitry Andric     pc_or_addr = IsSuppressed(rep->typ, rep->locs[i], &supp);
65068d75effSDimitry Andric   if (pc_or_addr != 0) {
65168d75effSDimitry Andric     Lock lock(&ctx->fired_suppressions_mtx);
65268d75effSDimitry Andric     FiredSuppression s = {srep.GetReport()->typ, pc_or_addr, supp};
65368d75effSDimitry Andric     ctx->fired_suppressions.push_back(s);
65468d75effSDimitry Andric   }
65568d75effSDimitry Andric   {
65668d75effSDimitry Andric     bool suppressed = OnReport(rep, pc_or_addr != 0);
65768d75effSDimitry Andric     if (suppressed) {
65868d75effSDimitry Andric       thr->current_report = nullptr;
65968d75effSDimitry Andric       return false;
66068d75effSDimitry Andric     }
66168d75effSDimitry Andric   }
66268d75effSDimitry Andric   PrintReport(rep);
66368d75effSDimitry Andric   __tsan_on_report(rep);
66468d75effSDimitry Andric   ctx->nreported++;
66568d75effSDimitry Andric   if (flags()->halt_on_error)
66668d75effSDimitry Andric     Die();
66768d75effSDimitry Andric   thr->current_report = nullptr;
66868d75effSDimitry Andric   return true;
66968d75effSDimitry Andric }
67068d75effSDimitry Andric 
IsFiredSuppression(Context * ctx,ReportType type,StackTrace trace)67168d75effSDimitry Andric bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace) {
67268d75effSDimitry Andric   ReadLock lock(&ctx->fired_suppressions_mtx);
67368d75effSDimitry Andric   for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
67468d75effSDimitry Andric     if (ctx->fired_suppressions[k].type != type)
67568d75effSDimitry Andric       continue;
67668d75effSDimitry Andric     for (uptr j = 0; j < trace.size; j++) {
67768d75effSDimitry Andric       FiredSuppression *s = &ctx->fired_suppressions[k];
67868d75effSDimitry Andric       if (trace.trace[j] == s->pc_or_addr) {
67968d75effSDimitry Andric         if (s->supp)
68068d75effSDimitry Andric           atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
68168d75effSDimitry Andric         return true;
68268d75effSDimitry Andric       }
68368d75effSDimitry Andric     }
68468d75effSDimitry Andric   }
68568d75effSDimitry Andric   return false;
68668d75effSDimitry Andric }
68768d75effSDimitry Andric 
IsFiredSuppression(Context * ctx,ReportType type,uptr addr)68868d75effSDimitry Andric static bool IsFiredSuppression(Context *ctx, ReportType type, uptr addr) {
68968d75effSDimitry Andric   ReadLock lock(&ctx->fired_suppressions_mtx);
69068d75effSDimitry Andric   for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
69168d75effSDimitry Andric     if (ctx->fired_suppressions[k].type != type)
69268d75effSDimitry Andric       continue;
69368d75effSDimitry Andric     FiredSuppression *s = &ctx->fired_suppressions[k];
69468d75effSDimitry Andric     if (addr == s->pc_or_addr) {
69568d75effSDimitry Andric       if (s->supp)
69668d75effSDimitry Andric         atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
69768d75effSDimitry Andric       return true;
69868d75effSDimitry Andric     }
69968d75effSDimitry Andric   }
70068d75effSDimitry Andric   return false;
70168d75effSDimitry Andric }
70268d75effSDimitry Andric 
SpuriousRace(Shadow old)703972a253aSDimitry Andric static bool SpuriousRace(Shadow old) {
704972a253aSDimitry Andric   Shadow last(LoadShadow(&ctx->last_spurious_race));
705972a253aSDimitry Andric   return last.sid() == old.sid() && last.epoch() == old.epoch();
706972a253aSDimitry Andric }
707972a253aSDimitry Andric 
ReportRace(ThreadState * thr,RawShadow * shadow_mem,Shadow cur,Shadow old,AccessType typ0)7080eae32dcSDimitry Andric void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old,
7090eae32dcSDimitry Andric                 AccessType typ0) {
710fe6060f1SDimitry Andric   CheckedMutex::CheckNoLocks();
71168d75effSDimitry Andric 
71268d75effSDimitry Andric   // Symbolizer makes lots of intercepted calls. If we try to process them,
71368d75effSDimitry Andric   // at best it will cause deadlocks on internal mutexes.
71468d75effSDimitry Andric   ScopedIgnoreInterceptors ignore;
71568d75effSDimitry Andric 
7160eae32dcSDimitry Andric   uptr addr = ShadowToMem(shadow_mem);
7170eae32dcSDimitry Andric   DPrintf("#%d: ReportRace %p\n", thr->tid, (void *)addr);
718fe6060f1SDimitry Andric   if (!ShouldReport(thr, ReportTypeRace))
71968d75effSDimitry Andric     return;
7200eae32dcSDimitry Andric   uptr addr_off0, size0;
7210eae32dcSDimitry Andric   cur.GetAccess(&addr_off0, &size0, nullptr);
7220eae32dcSDimitry Andric   uptr addr_off1, size1, typ1;
7230eae32dcSDimitry Andric   old.GetAccess(&addr_off1, &size1, &typ1);
7240eae32dcSDimitry Andric   if (!flags()->report_atomic_races &&
7250eae32dcSDimitry Andric       ((typ0 & kAccessAtomic) || (typ1 & kAccessAtomic)) &&
7260eae32dcSDimitry Andric       !(typ0 & kAccessFree) && !(typ1 & kAccessFree))
72768d75effSDimitry Andric     return;
728972a253aSDimitry Andric   if (SpuriousRace(old))
729972a253aSDimitry Andric     return;
73068d75effSDimitry Andric 
73168d75effSDimitry Andric   const uptr kMop = 2;
7320eae32dcSDimitry Andric   Shadow s[kMop] = {cur, old};
7330eae32dcSDimitry Andric   uptr addr0 = addr + addr_off0;
7340eae32dcSDimitry Andric   uptr addr1 = addr + addr_off1;
7350eae32dcSDimitry Andric   uptr end0 = addr0 + size0;
7360eae32dcSDimitry Andric   uptr end1 = addr1 + size1;
7370eae32dcSDimitry Andric   uptr addr_min = min(addr0, addr1);
7380eae32dcSDimitry Andric   uptr addr_max = max(end0, end1);
7390eae32dcSDimitry Andric   if (IsExpectedReport(addr_min, addr_max - addr_min))
7400eae32dcSDimitry Andric     return;
74168d75effSDimitry Andric 
7420eae32dcSDimitry Andric   ReportType rep_typ = ReportTypeRace;
7430eae32dcSDimitry Andric   if ((typ0 & kAccessVptr) && (typ1 & kAccessFree))
7440eae32dcSDimitry Andric     rep_typ = ReportTypeVptrUseAfterFree;
7450eae32dcSDimitry Andric   else if (typ0 & kAccessVptr)
7460eae32dcSDimitry Andric     rep_typ = ReportTypeVptrRace;
7470eae32dcSDimitry Andric   else if (typ1 & kAccessFree)
7480eae32dcSDimitry Andric     rep_typ = ReportTypeUseAfterFree;
7490eae32dcSDimitry Andric 
7500eae32dcSDimitry Andric   if (IsFiredSuppression(ctx, rep_typ, addr))
7510eae32dcSDimitry Andric     return;
7520eae32dcSDimitry Andric 
7530eae32dcSDimitry Andric   VarSizeStackTrace traces[kMop];
7540eae32dcSDimitry Andric   Tid tids[kMop] = {thr->tid, kInvalidTid};
7550eae32dcSDimitry Andric   uptr tags[kMop] = {kExternalTagNone, kExternalTagNone};
7560eae32dcSDimitry Andric 
7570eae32dcSDimitry Andric   ObtainCurrentStack(thr, thr->trace_prev_pc, &traces[0], &tags[0]);
7580eae32dcSDimitry Andric   if (IsFiredSuppression(ctx, rep_typ, traces[0]))
7590eae32dcSDimitry Andric     return;
7600eae32dcSDimitry Andric 
7610eae32dcSDimitry Andric   DynamicMutexSet mset1;
7620eae32dcSDimitry Andric   MutexSet *mset[kMop] = {&thr->mset, mset1};
7630eae32dcSDimitry Andric 
7640eae32dcSDimitry Andric   // We need to lock the slot during RestoreStack because it protects
7650eae32dcSDimitry Andric   // the slot journal.
7660eae32dcSDimitry Andric   Lock slot_lock(&ctx->slots[static_cast<uptr>(s[1].sid())].mtx);
7670eae32dcSDimitry Andric   ThreadRegistryLock l0(&ctx->thread_registry);
7680eae32dcSDimitry Andric   Lock slots_lock(&ctx->slot_mtx);
769972a253aSDimitry Andric   if (SpuriousRace(old))
7700eae32dcSDimitry Andric     return;
771972a253aSDimitry Andric   if (!RestoreStack(EventType::kAccessExt, s[1].sid(), s[1].epoch(), addr1,
772972a253aSDimitry Andric                     size1, typ1, &tids[1], &traces[1], mset[1], &tags[1])) {
773972a253aSDimitry Andric     StoreShadow(&ctx->last_spurious_race, old.raw());
774972a253aSDimitry Andric     return;
775972a253aSDimitry Andric   }
7760eae32dcSDimitry Andric 
7770eae32dcSDimitry Andric   if (IsFiredSuppression(ctx, rep_typ, traces[1]))
77868d75effSDimitry Andric     return;
77968d75effSDimitry Andric 
780590d96feSDimitry Andric   if (HandleRacyStacks(thr, traces))
78168d75effSDimitry Andric     return;
78268d75effSDimitry Andric 
78368d75effSDimitry Andric   // If any of the accesses has a tag, treat this as an "external" race.
78468d75effSDimitry Andric   uptr tag = kExternalTagNone;
78568d75effSDimitry Andric   for (uptr i = 0; i < kMop; i++) {
78668d75effSDimitry Andric     if (tags[i] != kExternalTagNone) {
7870eae32dcSDimitry Andric       rep_typ = ReportTypeExternalRace;
78868d75effSDimitry Andric       tag = tags[i];
78968d75effSDimitry Andric       break;
79068d75effSDimitry Andric     }
79168d75effSDimitry Andric   }
79268d75effSDimitry Andric 
7930eae32dcSDimitry Andric   ScopedReport rep(rep_typ, tag);
7940eae32dcSDimitry Andric   for (uptr i = 0; i < kMop; i++)
7950eae32dcSDimitry Andric     rep.AddMemoryAccess(addr, tags[i], s[i], tids[i], traces[i], mset[i]);
79668d75effSDimitry Andric 
79768d75effSDimitry Andric   for (uptr i = 0; i < kMop; i++) {
79868d75effSDimitry Andric     ThreadContext *tctx = static_cast<ThreadContext *>(
7990eae32dcSDimitry Andric         ctx->thread_registry.GetThreadLocked(tids[i]));
80068d75effSDimitry Andric     rep.AddThread(tctx);
80168d75effSDimitry Andric   }
80268d75effSDimitry Andric 
80368d75effSDimitry Andric   rep.AddLocation(addr_min, addr_max - addr_min);
80468d75effSDimitry Andric 
80581ad6265SDimitry Andric   if (flags()->print_full_thread_history) {
80681ad6265SDimitry Andric     const ReportDesc *rep_desc = rep.GetReport();
80781ad6265SDimitry Andric     for (uptr i = 0; i < rep_desc->threads.Size(); i++) {
80881ad6265SDimitry Andric       Tid parent_tid = rep_desc->threads[i]->parent_tid;
80981ad6265SDimitry Andric       if (parent_tid == kMainTid || parent_tid == kInvalidTid)
81081ad6265SDimitry Andric         continue;
81181ad6265SDimitry Andric       ThreadContext *parent_tctx = static_cast<ThreadContext *>(
81281ad6265SDimitry Andric           ctx->thread_registry.GetThreadLocked(parent_tid));
81381ad6265SDimitry Andric       rep.AddThread(parent_tctx);
81481ad6265SDimitry Andric     }
81581ad6265SDimitry Andric   }
81681ad6265SDimitry Andric 
81768d75effSDimitry Andric #if !SANITIZER_GO
8180eae32dcSDimitry Andric   if (!((typ0 | typ1) & kAccessFree) &&
8190eae32dcSDimitry Andric       s[1].epoch() <= thr->last_sleep_clock.Get(s[1].sid()))
82068d75effSDimitry Andric     rep.AddSleep(thr->last_sleep_stack_id);
82168d75effSDimitry Andric #endif
822e8d8bef9SDimitry Andric   OutputReport(thr, rep);
82368d75effSDimitry Andric }
82468d75effSDimitry Andric 
PrintCurrentStack(ThreadState * thr,uptr pc)82568d75effSDimitry Andric void PrintCurrentStack(ThreadState *thr, uptr pc) {
82668d75effSDimitry Andric   VarSizeStackTrace trace;
82768d75effSDimitry Andric   ObtainCurrentStack(thr, pc, &trace);
82868d75effSDimitry Andric   PrintStack(SymbolizeStack(trace));
82968d75effSDimitry Andric }
83068d75effSDimitry Andric 
83168d75effSDimitry Andric // Always inlining PrintCurrentStackSlow, because LocatePcInTrace assumes
83268d75effSDimitry Andric // __sanitizer_print_stack_trace exists in the actual unwinded stack, but
83368d75effSDimitry Andric // tail-call to PrintCurrentStackSlow breaks this assumption because
83468d75effSDimitry Andric // __sanitizer_print_stack_trace disappears after tail-call.
83568d75effSDimitry Andric // However, this solution is not reliable enough, please see dvyukov's comment
83668d75effSDimitry Andric // http://reviews.llvm.org/D19148#406208
83768d75effSDimitry Andric // Also see PR27280 comment 2 and 3 for breaking examples and analysis.
PrintCurrentStackSlow(uptr pc)838fe6060f1SDimitry Andric ALWAYS_INLINE USED void PrintCurrentStackSlow(uptr pc) {
83968d75effSDimitry Andric #if !SANITIZER_GO
84068d75effSDimitry Andric   uptr bp = GET_CURRENT_FRAME();
841349cc55cSDimitry Andric   auto *ptrace = New<BufferedStackTrace>();
84268d75effSDimitry Andric   ptrace->Unwind(pc, bp, nullptr, false);
84368d75effSDimitry Andric 
84468d75effSDimitry Andric   for (uptr i = 0; i < ptrace->size / 2; i++) {
84568d75effSDimitry Andric     uptr tmp = ptrace->trace_buffer[i];
84668d75effSDimitry Andric     ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1];
84768d75effSDimitry Andric     ptrace->trace_buffer[ptrace->size - i - 1] = tmp;
84868d75effSDimitry Andric   }
84968d75effSDimitry Andric   PrintStack(SymbolizeStack(*ptrace));
85068d75effSDimitry Andric #endif
85168d75effSDimitry Andric }
85268d75effSDimitry Andric 
85368d75effSDimitry Andric }  // namespace __tsan
85468d75effSDimitry Andric 
85568d75effSDimitry Andric using namespace __tsan;
85668d75effSDimitry Andric 
85768d75effSDimitry Andric extern "C" {
85868d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_print_stack_trace()85968d75effSDimitry Andric void __sanitizer_print_stack_trace() {
86068d75effSDimitry Andric   PrintCurrentStackSlow(StackTrace::GetCurrentPc());
86168d75effSDimitry Andric }
86268d75effSDimitry Andric }  // extern "C"
863