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 
1368d75effSDimitry Andric #include "sanitizer_common/sanitizer_libc.h"
1468d75effSDimitry Andric #include "sanitizer_common/sanitizer_placement_new.h"
1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
1668d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h"
1868d75effSDimitry Andric #include "tsan_platform.h"
1968d75effSDimitry Andric #include "tsan_rtl.h"
2068d75effSDimitry Andric #include "tsan_suppressions.h"
2168d75effSDimitry Andric #include "tsan_symbolize.h"
2268d75effSDimitry Andric #include "tsan_report.h"
2368d75effSDimitry Andric #include "tsan_sync.h"
2468d75effSDimitry Andric #include "tsan_mman.h"
2568d75effSDimitry Andric #include "tsan_flags.h"
2668d75effSDimitry Andric #include "tsan_fd.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 void TsanCheckFailed(const char *file, int line, const char *cond,
3568d75effSDimitry Andric                      u64 v1, u64 v2) {
3668d75effSDimitry Andric   // There is high probability that interceptors will check-fail as well,
3768d75effSDimitry Andric   // on the other hand there is no sense in processing interceptors
3868d75effSDimitry Andric   // since we are going to die soon.
3968d75effSDimitry Andric   ScopedIgnoreInterceptors ignore;
4068d75effSDimitry Andric #if !SANITIZER_GO
4168d75effSDimitry Andric   cur_thread()->ignore_sync++;
4268d75effSDimitry Andric   cur_thread()->ignore_reads_and_writes++;
4368d75effSDimitry Andric #endif
4468d75effSDimitry Andric   Printf("FATAL: ThreadSanitizer CHECK failed: "
4568d75effSDimitry Andric          "%s:%d \"%s\" (0x%zx, 0x%zx)\n",
4668d75effSDimitry Andric          file, line, cond, (uptr)v1, (uptr)v2);
4768d75effSDimitry Andric   PrintCurrentStackSlow(StackTrace::GetCurrentPc());
4868d75effSDimitry Andric   Die();
4968d75effSDimitry Andric }
5068d75effSDimitry Andric 
5168d75effSDimitry Andric // Can be overriden by an application/test to intercept reports.
5268d75effSDimitry Andric #ifdef TSAN_EXTERNAL_HOOKS
5368d75effSDimitry Andric bool OnReport(const ReportDesc *rep, bool suppressed);
5468d75effSDimitry Andric #else
5568d75effSDimitry Andric SANITIZER_WEAK_CXX_DEFAULT_IMPL
5668d75effSDimitry Andric bool OnReport(const ReportDesc *rep, bool suppressed) {
5768d75effSDimitry Andric   (void)rep;
5868d75effSDimitry Andric   return suppressed;
5968d75effSDimitry Andric }
6068d75effSDimitry Andric #endif
6168d75effSDimitry Andric 
6268d75effSDimitry Andric SANITIZER_WEAK_DEFAULT_IMPL
6368d75effSDimitry Andric void __tsan_on_report(const ReportDesc *rep) {
6468d75effSDimitry Andric   (void)rep;
6568d75effSDimitry Andric }
6668d75effSDimitry Andric 
6768d75effSDimitry Andric static void StackStripMain(SymbolizedStack *frames) {
6868d75effSDimitry Andric   SymbolizedStack *last_frame = nullptr;
6968d75effSDimitry Andric   SymbolizedStack *last_frame2 = nullptr;
7068d75effSDimitry Andric   for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
7168d75effSDimitry Andric     last_frame2 = last_frame;
7268d75effSDimitry Andric     last_frame = cur;
7368d75effSDimitry Andric   }
7468d75effSDimitry Andric 
7568d75effSDimitry Andric   if (last_frame2 == 0)
7668d75effSDimitry Andric     return;
7768d75effSDimitry Andric #if !SANITIZER_GO
7868d75effSDimitry Andric   const char *last = last_frame->info.function;
7968d75effSDimitry Andric   const char *last2 = last_frame2->info.function;
8068d75effSDimitry Andric   // Strip frame above 'main'
8168d75effSDimitry Andric   if (last2 && 0 == internal_strcmp(last2, "main")) {
8268d75effSDimitry Andric     last_frame->ClearAll();
8368d75effSDimitry Andric     last_frame2->next = nullptr;
8468d75effSDimitry Andric   // Strip our internal thread start routine.
8568d75effSDimitry Andric   } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) {
8668d75effSDimitry Andric     last_frame->ClearAll();
8768d75effSDimitry Andric     last_frame2->next = nullptr;
8868d75effSDimitry Andric   // Strip global ctors init.
8968d75effSDimitry Andric   } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) {
9068d75effSDimitry Andric     last_frame->ClearAll();
9168d75effSDimitry Andric     last_frame2->next = nullptr;
9268d75effSDimitry Andric   // If both are 0, then we probably just failed to symbolize.
9368d75effSDimitry Andric   } else if (last || last2) {
9468d75effSDimitry Andric     // Ensure that we recovered stack completely. Trimmed stack
9568d75effSDimitry Andric     // can actually happen if we do not instrument some code,
9668d75effSDimitry Andric     // so it's only a debug print. However we must try hard to not miss it
9768d75effSDimitry Andric     // due to our fault.
9868d75effSDimitry Andric     DPrintf("Bottom stack frame is missed\n");
9968d75effSDimitry Andric   }
10068d75effSDimitry Andric #else
10168d75effSDimitry Andric   // The last frame always point into runtime (gosched0, goexit0, runtime.main).
10268d75effSDimitry Andric   last_frame->ClearAll();
10368d75effSDimitry Andric   last_frame2->next = nullptr;
10468d75effSDimitry Andric #endif
10568d75effSDimitry Andric }
10668d75effSDimitry Andric 
10768d75effSDimitry Andric ReportStack *SymbolizeStackId(u32 stack_id) {
10868d75effSDimitry Andric   if (stack_id == 0)
10968d75effSDimitry Andric     return 0;
11068d75effSDimitry Andric   StackTrace stack = StackDepotGet(stack_id);
11168d75effSDimitry Andric   if (stack.trace == nullptr)
11268d75effSDimitry Andric     return nullptr;
11368d75effSDimitry Andric   return SymbolizeStack(stack);
11468d75effSDimitry Andric }
11568d75effSDimitry Andric 
11668d75effSDimitry Andric static ReportStack *SymbolizeStack(StackTrace trace) {
11768d75effSDimitry Andric   if (trace.size == 0)
11868d75effSDimitry Andric     return 0;
11968d75effSDimitry Andric   SymbolizedStack *top = nullptr;
12068d75effSDimitry Andric   for (uptr si = 0; si < trace.size; si++) {
12168d75effSDimitry Andric     const uptr pc = trace.trace[si];
12268d75effSDimitry Andric     uptr pc1 = pc;
12368d75effSDimitry Andric     // We obtain the return address, but we're interested in the previous
12468d75effSDimitry Andric     // instruction.
12568d75effSDimitry Andric     if ((pc & kExternalPCBit) == 0)
12668d75effSDimitry Andric       pc1 = StackTrace::GetPreviousInstructionPc(pc);
12768d75effSDimitry Andric     SymbolizedStack *ent = SymbolizeCode(pc1);
12868d75effSDimitry Andric     CHECK_NE(ent, 0);
12968d75effSDimitry Andric     SymbolizedStack *last = ent;
13068d75effSDimitry Andric     while (last->next) {
13168d75effSDimitry Andric       last->info.address = pc;  // restore original pc for report
13268d75effSDimitry Andric       last = last->next;
13368d75effSDimitry Andric     }
13468d75effSDimitry Andric     last->info.address = pc;  // restore original pc for report
13568d75effSDimitry Andric     last->next = top;
13668d75effSDimitry Andric     top = ent;
13768d75effSDimitry Andric   }
13868d75effSDimitry Andric   StackStripMain(top);
13968d75effSDimitry Andric 
14068d75effSDimitry Andric   ReportStack *stack = ReportStack::New();
14168d75effSDimitry Andric   stack->frames = top;
14268d75effSDimitry Andric   return stack;
14368d75effSDimitry Andric }
14468d75effSDimitry Andric 
14568d75effSDimitry Andric ScopedReportBase::ScopedReportBase(ReportType typ, uptr tag) {
14668d75effSDimitry Andric   ctx->thread_registry->CheckLocked();
14768d75effSDimitry Andric   void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc));
14868d75effSDimitry Andric   rep_ = new(mem) ReportDesc;
14968d75effSDimitry Andric   rep_->typ = typ;
15068d75effSDimitry Andric   rep_->tag = tag;
15168d75effSDimitry Andric   ctx->report_mtx.Lock();
15268d75effSDimitry Andric }
15368d75effSDimitry Andric 
15468d75effSDimitry Andric ScopedReportBase::~ScopedReportBase() {
15568d75effSDimitry Andric   ctx->report_mtx.Unlock();
15668d75effSDimitry Andric   DestroyAndFree(rep_);
15768d75effSDimitry Andric   rep_ = nullptr;
15868d75effSDimitry Andric }
15968d75effSDimitry Andric 
16068d75effSDimitry Andric void ScopedReportBase::AddStack(StackTrace stack, bool suppressable) {
16168d75effSDimitry Andric   ReportStack **rs = rep_->stacks.PushBack();
16268d75effSDimitry Andric   *rs = SymbolizeStack(stack);
16368d75effSDimitry Andric   (*rs)->suppressable = suppressable;
16468d75effSDimitry Andric }
16568d75effSDimitry Andric 
16668d75effSDimitry Andric void ScopedReportBase::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
16768d75effSDimitry Andric                                        StackTrace stack, const MutexSet *mset) {
16868d75effSDimitry Andric   void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
16968d75effSDimitry Andric   ReportMop *mop = new(mem) ReportMop;
17068d75effSDimitry Andric   rep_->mops.PushBack(mop);
17168d75effSDimitry Andric   mop->tid = s.tid();
17268d75effSDimitry Andric   mop->addr = addr + s.addr0();
17368d75effSDimitry Andric   mop->size = s.size();
17468d75effSDimitry Andric   mop->write = s.IsWrite();
17568d75effSDimitry Andric   mop->atomic = s.IsAtomic();
17668d75effSDimitry Andric   mop->stack = SymbolizeStack(stack);
17768d75effSDimitry Andric   mop->external_tag = external_tag;
17868d75effSDimitry Andric   if (mop->stack)
17968d75effSDimitry Andric     mop->stack->suppressable = true;
18068d75effSDimitry Andric   for (uptr i = 0; i < mset->Size(); i++) {
18168d75effSDimitry Andric     MutexSet::Desc d = mset->Get(i);
18268d75effSDimitry Andric     u64 mid = this->AddMutex(d.id);
18368d75effSDimitry Andric     ReportMopMutex mtx = {mid, d.write};
18468d75effSDimitry Andric     mop->mset.PushBack(mtx);
18568d75effSDimitry Andric   }
18668d75effSDimitry Andric }
18768d75effSDimitry Andric 
18868d75effSDimitry Andric void ScopedReportBase::AddUniqueTid(int unique_tid) {
18968d75effSDimitry Andric   rep_->unique_tids.PushBack(unique_tid);
19068d75effSDimitry Andric }
19168d75effSDimitry Andric 
19268d75effSDimitry Andric void ScopedReportBase::AddThread(const ThreadContext *tctx, bool suppressable) {
19368d75effSDimitry Andric   for (uptr i = 0; i < rep_->threads.Size(); i++) {
19468d75effSDimitry Andric     if ((u32)rep_->threads[i]->id == tctx->tid)
19568d75effSDimitry Andric       return;
19668d75effSDimitry Andric   }
19768d75effSDimitry Andric   void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread));
19868d75effSDimitry Andric   ReportThread *rt = new(mem) ReportThread;
19968d75effSDimitry Andric   rep_->threads.PushBack(rt);
20068d75effSDimitry Andric   rt->id = tctx->tid;
20168d75effSDimitry Andric   rt->os_id = tctx->os_id;
20268d75effSDimitry Andric   rt->running = (tctx->status == ThreadStatusRunning);
20368d75effSDimitry Andric   rt->name = internal_strdup(tctx->name);
20468d75effSDimitry Andric   rt->parent_tid = tctx->parent_tid;
20568d75effSDimitry Andric   rt->thread_type = tctx->thread_type;
20668d75effSDimitry Andric   rt->stack = 0;
20768d75effSDimitry Andric   rt->stack = SymbolizeStackId(tctx->creation_stack_id);
20868d75effSDimitry Andric   if (rt->stack)
20968d75effSDimitry Andric     rt->stack->suppressable = suppressable;
21068d75effSDimitry Andric }
21168d75effSDimitry Andric 
21268d75effSDimitry Andric #if !SANITIZER_GO
21368d75effSDimitry Andric static bool FindThreadByUidLockedCallback(ThreadContextBase *tctx, void *arg) {
21468d75effSDimitry Andric   int unique_id = *(int *)arg;
21568d75effSDimitry Andric   return tctx->unique_id == (u32)unique_id;
21668d75effSDimitry Andric }
21768d75effSDimitry Andric 
21868d75effSDimitry Andric static ThreadContext *FindThreadByUidLocked(int unique_id) {
21968d75effSDimitry Andric   ctx->thread_registry->CheckLocked();
22068d75effSDimitry Andric   return static_cast<ThreadContext *>(
22168d75effSDimitry Andric       ctx->thread_registry->FindThreadContextLocked(
22268d75effSDimitry Andric           FindThreadByUidLockedCallback, &unique_id));
22368d75effSDimitry Andric }
22468d75effSDimitry Andric 
22568d75effSDimitry Andric static ThreadContext *FindThreadByTidLocked(int tid) {
22668d75effSDimitry Andric   ctx->thread_registry->CheckLocked();
22768d75effSDimitry Andric   return static_cast<ThreadContext*>(
22868d75effSDimitry Andric       ctx->thread_registry->GetThreadLocked(tid));
22968d75effSDimitry Andric }
23068d75effSDimitry Andric 
23168d75effSDimitry Andric static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) {
23268d75effSDimitry Andric   uptr addr = (uptr)arg;
23368d75effSDimitry Andric   ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
23468d75effSDimitry Andric   if (tctx->status != ThreadStatusRunning)
23568d75effSDimitry Andric     return false;
23668d75effSDimitry Andric   ThreadState *thr = tctx->thr;
23768d75effSDimitry Andric   CHECK(thr);
23868d75effSDimitry Andric   return ((addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) ||
23968d75effSDimitry Andric           (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size));
24068d75effSDimitry Andric }
24168d75effSDimitry Andric 
24268d75effSDimitry Andric ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
24368d75effSDimitry Andric   ctx->thread_registry->CheckLocked();
24468d75effSDimitry Andric   ThreadContext *tctx = static_cast<ThreadContext*>(
24568d75effSDimitry Andric       ctx->thread_registry->FindThreadContextLocked(IsInStackOrTls,
24668d75effSDimitry Andric                                                     (void*)addr));
24768d75effSDimitry Andric   if (!tctx)
24868d75effSDimitry Andric     return 0;
24968d75effSDimitry Andric   ThreadState *thr = tctx->thr;
25068d75effSDimitry Andric   CHECK(thr);
25168d75effSDimitry Andric   *is_stack = (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size);
25268d75effSDimitry Andric   return tctx;
25368d75effSDimitry Andric }
25468d75effSDimitry Andric #endif
25568d75effSDimitry Andric 
25668d75effSDimitry Andric void ScopedReportBase::AddThread(int unique_tid, bool suppressable) {
25768d75effSDimitry Andric #if !SANITIZER_GO
25868d75effSDimitry Andric   if (const ThreadContext *tctx = FindThreadByUidLocked(unique_tid))
25968d75effSDimitry Andric     AddThread(tctx, suppressable);
26068d75effSDimitry Andric #endif
26168d75effSDimitry Andric }
26268d75effSDimitry Andric 
26368d75effSDimitry Andric void ScopedReportBase::AddMutex(const SyncVar *s) {
26468d75effSDimitry Andric   for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
26568d75effSDimitry Andric     if (rep_->mutexes[i]->id == s->uid)
26668d75effSDimitry Andric       return;
26768d75effSDimitry Andric   }
26868d75effSDimitry Andric   void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
26968d75effSDimitry Andric   ReportMutex *rm = new(mem) ReportMutex;
27068d75effSDimitry Andric   rep_->mutexes.PushBack(rm);
27168d75effSDimitry Andric   rm->id = s->uid;
27268d75effSDimitry Andric   rm->addr = s->addr;
27368d75effSDimitry Andric   rm->destroyed = false;
27468d75effSDimitry Andric   rm->stack = SymbolizeStackId(s->creation_stack_id);
27568d75effSDimitry Andric }
27668d75effSDimitry Andric 
27768d75effSDimitry Andric u64 ScopedReportBase::AddMutex(u64 id) {
27868d75effSDimitry Andric   u64 uid = 0;
27968d75effSDimitry Andric   u64 mid = id;
28068d75effSDimitry Andric   uptr addr = SyncVar::SplitId(id, &uid);
28168d75effSDimitry Andric   SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true);
28268d75effSDimitry Andric   // Check that the mutex is still alive.
28368d75effSDimitry Andric   // Another mutex can be created at the same address,
28468d75effSDimitry Andric   // so check uid as well.
28568d75effSDimitry Andric   if (s && s->CheckId(uid)) {
28668d75effSDimitry Andric     mid = s->uid;
28768d75effSDimitry Andric     AddMutex(s);
28868d75effSDimitry Andric   } else {
28968d75effSDimitry Andric     AddDeadMutex(id);
29068d75effSDimitry Andric   }
29168d75effSDimitry Andric   if (s)
29268d75effSDimitry Andric     s->mtx.Unlock();
29368d75effSDimitry Andric   return mid;
29468d75effSDimitry Andric }
29568d75effSDimitry Andric 
29668d75effSDimitry Andric void ScopedReportBase::AddDeadMutex(u64 id) {
29768d75effSDimitry Andric   for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
29868d75effSDimitry Andric     if (rep_->mutexes[i]->id == id)
29968d75effSDimitry Andric       return;
30068d75effSDimitry Andric   }
30168d75effSDimitry Andric   void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
30268d75effSDimitry Andric   ReportMutex *rm = new(mem) ReportMutex;
30368d75effSDimitry Andric   rep_->mutexes.PushBack(rm);
30468d75effSDimitry Andric   rm->id = id;
30568d75effSDimitry Andric   rm->addr = 0;
30668d75effSDimitry Andric   rm->destroyed = true;
30768d75effSDimitry Andric   rm->stack = 0;
30868d75effSDimitry Andric }
30968d75effSDimitry Andric 
31068d75effSDimitry Andric void ScopedReportBase::AddLocation(uptr addr, uptr size) {
31168d75effSDimitry Andric   if (addr == 0)
31268d75effSDimitry Andric     return;
31368d75effSDimitry Andric #if !SANITIZER_GO
31468d75effSDimitry Andric   int fd = -1;
31568d75effSDimitry Andric   int creat_tid = kInvalidTid;
31668d75effSDimitry Andric   u32 creat_stack = 0;
31768d75effSDimitry Andric   if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) {
31868d75effSDimitry Andric     ReportLocation *loc = ReportLocation::New(ReportLocationFD);
31968d75effSDimitry Andric     loc->fd = fd;
32068d75effSDimitry Andric     loc->tid = creat_tid;
32168d75effSDimitry Andric     loc->stack = SymbolizeStackId(creat_stack);
32268d75effSDimitry Andric     rep_->locs.PushBack(loc);
32368d75effSDimitry Andric     ThreadContext *tctx = FindThreadByUidLocked(creat_tid);
32468d75effSDimitry Andric     if (tctx)
32568d75effSDimitry Andric       AddThread(tctx);
32668d75effSDimitry Andric     return;
32768d75effSDimitry Andric   }
32868d75effSDimitry Andric   MBlock *b = 0;
32968d75effSDimitry Andric   Allocator *a = allocator();
33068d75effSDimitry Andric   if (a->PointerIsMine((void*)addr)) {
33168d75effSDimitry Andric     void *block_begin = a->GetBlockBegin((void*)addr);
33268d75effSDimitry Andric     if (block_begin)
33368d75effSDimitry Andric       b = ctx->metamap.GetBlock((uptr)block_begin);
33468d75effSDimitry Andric   }
33568d75effSDimitry Andric   if (b != 0) {
33668d75effSDimitry Andric     ThreadContext *tctx = FindThreadByTidLocked(b->tid);
33768d75effSDimitry Andric     ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
33868d75effSDimitry Andric     loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr);
33968d75effSDimitry Andric     loc->heap_chunk_size = b->siz;
34068d75effSDimitry Andric     loc->external_tag = b->tag;
34168d75effSDimitry Andric     loc->tid = tctx ? tctx->tid : b->tid;
34268d75effSDimitry Andric     loc->stack = SymbolizeStackId(b->stk);
34368d75effSDimitry Andric     rep_->locs.PushBack(loc);
34468d75effSDimitry Andric     if (tctx)
34568d75effSDimitry Andric       AddThread(tctx);
34668d75effSDimitry Andric     return;
34768d75effSDimitry Andric   }
34868d75effSDimitry Andric   bool is_stack = false;
34968d75effSDimitry Andric   if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
35068d75effSDimitry Andric     ReportLocation *loc =
35168d75effSDimitry Andric         ReportLocation::New(is_stack ? ReportLocationStack : ReportLocationTLS);
35268d75effSDimitry Andric     loc->tid = tctx->tid;
35368d75effSDimitry Andric     rep_->locs.PushBack(loc);
35468d75effSDimitry Andric     AddThread(tctx);
35568d75effSDimitry Andric   }
35668d75effSDimitry Andric #endif
35768d75effSDimitry Andric   if (ReportLocation *loc = SymbolizeData(addr)) {
35868d75effSDimitry Andric     loc->suppressable = true;
35968d75effSDimitry Andric     rep_->locs.PushBack(loc);
36068d75effSDimitry Andric     return;
36168d75effSDimitry Andric   }
36268d75effSDimitry Andric }
36368d75effSDimitry Andric 
36468d75effSDimitry Andric #if !SANITIZER_GO
36568d75effSDimitry Andric void ScopedReportBase::AddSleep(u32 stack_id) {
36668d75effSDimitry Andric   rep_->sleep = SymbolizeStackId(stack_id);
36768d75effSDimitry Andric }
36868d75effSDimitry Andric #endif
36968d75effSDimitry Andric 
37068d75effSDimitry Andric void ScopedReportBase::SetCount(int count) { rep_->count = count; }
37168d75effSDimitry Andric 
37268d75effSDimitry Andric const ReportDesc *ScopedReportBase::GetReport() const { return rep_; }
37368d75effSDimitry Andric 
37468d75effSDimitry Andric ScopedReport::ScopedReport(ReportType typ, uptr tag)
37568d75effSDimitry Andric     : ScopedReportBase(typ, tag) {}
37668d75effSDimitry Andric 
37768d75effSDimitry Andric ScopedReport::~ScopedReport() {}
37868d75effSDimitry Andric 
37968d75effSDimitry Andric void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
38068d75effSDimitry Andric                   MutexSet *mset, uptr *tag) {
38168d75effSDimitry Andric   // This function restores stack trace and mutex set for the thread/epoch.
38268d75effSDimitry Andric   // It does so by getting stack trace and mutex set at the beginning of
38368d75effSDimitry Andric   // trace part, and then replaying the trace till the given epoch.
38468d75effSDimitry Andric   Trace* trace = ThreadTrace(tid);
38568d75effSDimitry Andric   ReadLock l(&trace->mtx);
38668d75effSDimitry Andric   const int partidx = (epoch / kTracePartSize) % TraceParts();
38768d75effSDimitry Andric   TraceHeader* hdr = &trace->headers[partidx];
38868d75effSDimitry Andric   if (epoch < hdr->epoch0 || epoch >= hdr->epoch0 + kTracePartSize)
38968d75effSDimitry Andric     return;
39068d75effSDimitry Andric   CHECK_EQ(RoundDown(epoch, kTracePartSize), hdr->epoch0);
39168d75effSDimitry Andric   const u64 epoch0 = RoundDown(epoch, TraceSize());
39268d75effSDimitry Andric   const u64 eend = epoch % TraceSize();
39368d75effSDimitry Andric   const u64 ebegin = RoundDown(eend, kTracePartSize);
39468d75effSDimitry Andric   DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
39568d75effSDimitry Andric           tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
39668d75effSDimitry Andric   Vector<uptr> stack;
39768d75effSDimitry Andric   stack.Resize(hdr->stack0.size + 64);
39868d75effSDimitry Andric   for (uptr i = 0; i < hdr->stack0.size; i++) {
39968d75effSDimitry Andric     stack[i] = hdr->stack0.trace[i];
40068d75effSDimitry Andric     DPrintf2("  #%02zu: pc=%zx\n", i, stack[i]);
40168d75effSDimitry Andric   }
40268d75effSDimitry Andric   if (mset)
40368d75effSDimitry Andric     *mset = hdr->mset0;
40468d75effSDimitry Andric   uptr pos = hdr->stack0.size;
40568d75effSDimitry Andric   Event *events = (Event*)GetThreadTrace(tid);
40668d75effSDimitry Andric   for (uptr i = ebegin; i <= eend; i++) {
40768d75effSDimitry Andric     Event ev = events[i];
40868d75effSDimitry Andric     EventType typ = (EventType)(ev >> kEventPCBits);
40968d75effSDimitry Andric     uptr pc = (uptr)(ev & ((1ull << kEventPCBits) - 1));
41068d75effSDimitry Andric     DPrintf2("  %zu typ=%d pc=%zx\n", i, typ, pc);
41168d75effSDimitry Andric     if (typ == EventTypeMop) {
41268d75effSDimitry Andric       stack[pos] = pc;
41368d75effSDimitry Andric     } else if (typ == EventTypeFuncEnter) {
41468d75effSDimitry Andric       if (stack.Size() < pos + 2)
41568d75effSDimitry Andric         stack.Resize(pos + 2);
41668d75effSDimitry Andric       stack[pos++] = pc;
41768d75effSDimitry Andric     } else if (typ == EventTypeFuncExit) {
41868d75effSDimitry Andric       if (pos > 0)
41968d75effSDimitry Andric         pos--;
42068d75effSDimitry Andric     }
42168d75effSDimitry Andric     if (mset) {
42268d75effSDimitry Andric       if (typ == EventTypeLock) {
42368d75effSDimitry Andric         mset->Add(pc, true, epoch0 + i);
42468d75effSDimitry Andric       } else if (typ == EventTypeUnlock) {
42568d75effSDimitry Andric         mset->Del(pc, true);
42668d75effSDimitry Andric       } else if (typ == EventTypeRLock) {
42768d75effSDimitry Andric         mset->Add(pc, false, epoch0 + i);
42868d75effSDimitry Andric       } else if (typ == EventTypeRUnlock) {
42968d75effSDimitry Andric         mset->Del(pc, false);
43068d75effSDimitry Andric       }
43168d75effSDimitry Andric     }
43268d75effSDimitry Andric     for (uptr j = 0; j <= pos; j++)
43368d75effSDimitry Andric       DPrintf2("      #%zu: %zx\n", j, stack[j]);
43468d75effSDimitry Andric   }
43568d75effSDimitry Andric   if (pos == 0 && stack[0] == 0)
43668d75effSDimitry Andric     return;
43768d75effSDimitry Andric   pos++;
43868d75effSDimitry Andric   stk->Init(&stack[0], pos);
43968d75effSDimitry Andric   ExtractTagFromStack(stk, tag);
44068d75effSDimitry Andric }
44168d75effSDimitry Andric 
442590d96feSDimitry Andric static bool FindRacyStacks(const RacyStacks &hash) {
44368d75effSDimitry Andric   for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
44468d75effSDimitry Andric     if (hash == ctx->racy_stacks[i]) {
445590d96feSDimitry Andric       VPrintf(2, "ThreadSanitizer: suppressing report as doubled (stack)\n");
446590d96feSDimitry Andric       return true;
44768d75effSDimitry Andric     }
44868d75effSDimitry Andric   }
449590d96feSDimitry Andric   return false;
45068d75effSDimitry Andric }
451590d96feSDimitry Andric 
452590d96feSDimitry Andric static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2]) {
453590d96feSDimitry Andric   if (!flags()->suppress_equal_stacks)
454590d96feSDimitry Andric     return false;
455590d96feSDimitry Andric   RacyStacks hash;
456590d96feSDimitry Andric   hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
457590d96feSDimitry Andric   hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
458590d96feSDimitry Andric   {
459590d96feSDimitry Andric     ReadLock lock(&ctx->racy_mtx);
460590d96feSDimitry Andric     if (FindRacyStacks(hash))
461590d96feSDimitry Andric       return true;
462590d96feSDimitry Andric   }
463590d96feSDimitry Andric   Lock lock(&ctx->racy_mtx);
464590d96feSDimitry Andric   if (FindRacyStacks(hash))
465590d96feSDimitry Andric     return true;
466590d96feSDimitry Andric   ctx->racy_stacks.PushBack(hash);
467590d96feSDimitry Andric   return false;
468590d96feSDimitry Andric }
469590d96feSDimitry Andric 
470590d96feSDimitry Andric static bool FindRacyAddress(const RacyAddress &ra0) {
47168d75effSDimitry Andric   for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
47268d75effSDimitry Andric     RacyAddress ra2 = ctx->racy_addresses[i];
47368d75effSDimitry Andric     uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
47468d75effSDimitry Andric     uptr minend = min(ra0.addr_max, ra2.addr_max);
47568d75effSDimitry Andric     if (maxbeg < minend) {
47668d75effSDimitry Andric       VPrintf(2, "ThreadSanitizer: suppressing report as doubled (addr)\n");
47768d75effSDimitry Andric       return true;
47868d75effSDimitry Andric     }
479590d96feSDimitry Andric   }
480590d96feSDimitry Andric   return false;
481590d96feSDimitry Andric }
48268d75effSDimitry Andric 
483590d96feSDimitry Andric static bool HandleRacyAddress(ThreadState *thr, uptr addr_min, uptr addr_max) {
484590d96feSDimitry Andric   if (!flags()->suppress_equal_addresses)
485590d96feSDimitry Andric     return false;
48668d75effSDimitry Andric   RacyAddress ra0 = {addr_min, addr_max};
487590d96feSDimitry Andric   {
488590d96feSDimitry Andric     ReadLock lock(&ctx->racy_mtx);
489590d96feSDimitry Andric     if (FindRacyAddress(ra0))
490590d96feSDimitry Andric       return true;
49168d75effSDimitry Andric   }
492590d96feSDimitry Andric   Lock lock(&ctx->racy_mtx);
493590d96feSDimitry Andric   if (FindRacyAddress(ra0))
494590d96feSDimitry Andric     return true;
495590d96feSDimitry Andric   ctx->racy_addresses.PushBack(ra0);
496590d96feSDimitry Andric   return false;
49768d75effSDimitry Andric }
49868d75effSDimitry Andric 
49968d75effSDimitry Andric bool OutputReport(ThreadState *thr, const ScopedReport &srep) {
50068d75effSDimitry Andric   if (!flags()->report_bugs || thr->suppress_reports)
50168d75effSDimitry Andric     return false;
50268d75effSDimitry Andric   atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime());
50368d75effSDimitry Andric   const ReportDesc *rep = srep.GetReport();
50468d75effSDimitry Andric   CHECK_EQ(thr->current_report, nullptr);
50568d75effSDimitry Andric   thr->current_report = rep;
50668d75effSDimitry Andric   Suppression *supp = 0;
50768d75effSDimitry Andric   uptr pc_or_addr = 0;
50868d75effSDimitry Andric   for (uptr i = 0; pc_or_addr == 0 && i < rep->mops.Size(); i++)
50968d75effSDimitry Andric     pc_or_addr = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp);
51068d75effSDimitry Andric   for (uptr i = 0; pc_or_addr == 0 && i < rep->stacks.Size(); i++)
51168d75effSDimitry Andric     pc_or_addr = IsSuppressed(rep->typ, rep->stacks[i], &supp);
51268d75effSDimitry Andric   for (uptr i = 0; pc_or_addr == 0 && i < rep->threads.Size(); i++)
51368d75effSDimitry Andric     pc_or_addr = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp);
51468d75effSDimitry Andric   for (uptr i = 0; pc_or_addr == 0 && i < rep->locs.Size(); i++)
51568d75effSDimitry Andric     pc_or_addr = IsSuppressed(rep->typ, rep->locs[i], &supp);
51668d75effSDimitry Andric   if (pc_or_addr != 0) {
51768d75effSDimitry Andric     Lock lock(&ctx->fired_suppressions_mtx);
51868d75effSDimitry Andric     FiredSuppression s = {srep.GetReport()->typ, pc_or_addr, supp};
51968d75effSDimitry Andric     ctx->fired_suppressions.push_back(s);
52068d75effSDimitry Andric   }
52168d75effSDimitry Andric   {
52268d75effSDimitry Andric     bool old_is_freeing = thr->is_freeing;
52368d75effSDimitry Andric     thr->is_freeing = false;
52468d75effSDimitry Andric     bool suppressed = OnReport(rep, pc_or_addr != 0);
52568d75effSDimitry Andric     thr->is_freeing = old_is_freeing;
52668d75effSDimitry Andric     if (suppressed) {
52768d75effSDimitry Andric       thr->current_report = nullptr;
52868d75effSDimitry Andric       return false;
52968d75effSDimitry Andric     }
53068d75effSDimitry Andric   }
53168d75effSDimitry Andric   PrintReport(rep);
53268d75effSDimitry Andric   __tsan_on_report(rep);
53368d75effSDimitry Andric   ctx->nreported++;
53468d75effSDimitry Andric   if (flags()->halt_on_error)
53568d75effSDimitry Andric     Die();
53668d75effSDimitry Andric   thr->current_report = nullptr;
53768d75effSDimitry Andric   return true;
53868d75effSDimitry Andric }
53968d75effSDimitry Andric 
54068d75effSDimitry Andric bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace) {
54168d75effSDimitry Andric   ReadLock lock(&ctx->fired_suppressions_mtx);
54268d75effSDimitry Andric   for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
54368d75effSDimitry Andric     if (ctx->fired_suppressions[k].type != type)
54468d75effSDimitry Andric       continue;
54568d75effSDimitry Andric     for (uptr j = 0; j < trace.size; j++) {
54668d75effSDimitry Andric       FiredSuppression *s = &ctx->fired_suppressions[k];
54768d75effSDimitry Andric       if (trace.trace[j] == s->pc_or_addr) {
54868d75effSDimitry Andric         if (s->supp)
54968d75effSDimitry Andric           atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
55068d75effSDimitry Andric         return true;
55168d75effSDimitry Andric       }
55268d75effSDimitry Andric     }
55368d75effSDimitry Andric   }
55468d75effSDimitry Andric   return false;
55568d75effSDimitry Andric }
55668d75effSDimitry Andric 
55768d75effSDimitry Andric static bool IsFiredSuppression(Context *ctx, ReportType type, uptr addr) {
55868d75effSDimitry Andric   ReadLock lock(&ctx->fired_suppressions_mtx);
55968d75effSDimitry Andric   for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
56068d75effSDimitry Andric     if (ctx->fired_suppressions[k].type != type)
56168d75effSDimitry Andric       continue;
56268d75effSDimitry Andric     FiredSuppression *s = &ctx->fired_suppressions[k];
56368d75effSDimitry Andric     if (addr == s->pc_or_addr) {
56468d75effSDimitry Andric       if (s->supp)
56568d75effSDimitry Andric         atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
56668d75effSDimitry Andric       return true;
56768d75effSDimitry Andric     }
56868d75effSDimitry Andric   }
56968d75effSDimitry Andric   return false;
57068d75effSDimitry Andric }
57168d75effSDimitry Andric 
57268d75effSDimitry Andric static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
57368d75effSDimitry Andric   Shadow s0(thr->racy_state[0]);
57468d75effSDimitry Andric   Shadow s1(thr->racy_state[1]);
57568d75effSDimitry Andric   CHECK(!(s0.IsAtomic() && s1.IsAtomic()));
57668d75effSDimitry Andric   if (!s0.IsAtomic() && !s1.IsAtomic())
57768d75effSDimitry Andric     return true;
57868d75effSDimitry Andric   if (s0.IsAtomic() && s1.IsFreed())
57968d75effSDimitry Andric     return true;
58068d75effSDimitry Andric   if (s1.IsAtomic() && thr->is_freeing)
58168d75effSDimitry Andric     return true;
58268d75effSDimitry Andric   return false;
58368d75effSDimitry Andric }
58468d75effSDimitry Andric 
58568d75effSDimitry Andric void ReportRace(ThreadState *thr) {
58668d75effSDimitry Andric   CheckNoLocks(thr);
58768d75effSDimitry Andric 
58868d75effSDimitry Andric   // Symbolizer makes lots of intercepted calls. If we try to process them,
58968d75effSDimitry Andric   // at best it will cause deadlocks on internal mutexes.
59068d75effSDimitry Andric   ScopedIgnoreInterceptors ignore;
59168d75effSDimitry Andric 
59268d75effSDimitry Andric   if (!flags()->report_bugs)
59368d75effSDimitry Andric     return;
59468d75effSDimitry Andric   if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr))
59568d75effSDimitry Andric     return;
59668d75effSDimitry Andric 
59768d75effSDimitry Andric   bool freed = false;
59868d75effSDimitry Andric   {
59968d75effSDimitry Andric     Shadow s(thr->racy_state[1]);
60068d75effSDimitry Andric     freed = s.GetFreedAndReset();
60168d75effSDimitry Andric     thr->racy_state[1] = s.raw();
60268d75effSDimitry Andric   }
60368d75effSDimitry Andric 
60468d75effSDimitry Andric   uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr);
60568d75effSDimitry Andric   uptr addr_min = 0;
60668d75effSDimitry Andric   uptr addr_max = 0;
60768d75effSDimitry Andric   {
60868d75effSDimitry Andric     uptr a0 = addr + Shadow(thr->racy_state[0]).addr0();
60968d75effSDimitry Andric     uptr a1 = addr + Shadow(thr->racy_state[1]).addr0();
61068d75effSDimitry Andric     uptr e0 = a0 + Shadow(thr->racy_state[0]).size();
61168d75effSDimitry Andric     uptr e1 = a1 + Shadow(thr->racy_state[1]).size();
61268d75effSDimitry Andric     addr_min = min(a0, a1);
61368d75effSDimitry Andric     addr_max = max(e0, e1);
61468d75effSDimitry Andric     if (IsExpectedReport(addr_min, addr_max - addr_min))
61568d75effSDimitry Andric       return;
61668d75effSDimitry Andric   }
617590d96feSDimitry Andric   if (HandleRacyAddress(thr, addr_min, addr_max))
618590d96feSDimitry Andric     return;
61968d75effSDimitry Andric 
62068d75effSDimitry Andric   ReportType typ = ReportTypeRace;
62168d75effSDimitry Andric   if (thr->is_vptr_access && freed)
62268d75effSDimitry Andric     typ = ReportTypeVptrUseAfterFree;
62368d75effSDimitry Andric   else if (thr->is_vptr_access)
62468d75effSDimitry Andric     typ = ReportTypeVptrRace;
62568d75effSDimitry Andric   else if (freed)
62668d75effSDimitry Andric     typ = ReportTypeUseAfterFree;
62768d75effSDimitry Andric 
62868d75effSDimitry Andric   if (IsFiredSuppression(ctx, typ, addr))
62968d75effSDimitry Andric     return;
63068d75effSDimitry Andric 
63168d75effSDimitry Andric   const uptr kMop = 2;
63268d75effSDimitry Andric   VarSizeStackTrace traces[kMop];
63368d75effSDimitry Andric   uptr tags[kMop] = {kExternalTagNone};
63468d75effSDimitry Andric   uptr toppc = TraceTopPC(thr);
63568d75effSDimitry Andric   if (toppc >> kEventPCBits) {
63668d75effSDimitry Andric     // This is a work-around for a known issue.
63768d75effSDimitry Andric     // The scenario where this happens is rather elaborate and requires
63868d75effSDimitry Andric     // an instrumented __sanitizer_report_error_summary callback and
63968d75effSDimitry Andric     // a __tsan_symbolize_external callback and a race during a range memory
64068d75effSDimitry Andric     // access larger than 8 bytes. MemoryAccessRange adds the current PC to
64168d75effSDimitry Andric     // the trace and starts processing memory accesses. A first memory access
64268d75effSDimitry Andric     // triggers a race, we report it and call the instrumented
64368d75effSDimitry Andric     // __sanitizer_report_error_summary, which adds more stuff to the trace
64468d75effSDimitry Andric     // since it is intrumented. Then a second memory access in MemoryAccessRange
64568d75effSDimitry Andric     // also triggers a race and we get here and call TraceTopPC to get the
64668d75effSDimitry Andric     // current PC, however now it contains some unrelated events from the
64768d75effSDimitry Andric     // callback. Most likely, TraceTopPC will now return a EventTypeFuncExit
64868d75effSDimitry Andric     // event. Later we subtract -1 from it (in GetPreviousInstructionPc)
64968d75effSDimitry Andric     // and the resulting PC has kExternalPCBit set, so we pass it to
65068d75effSDimitry Andric     // __tsan_symbolize_external_ex. __tsan_symbolize_external_ex is within its
65168d75effSDimitry Andric     // rights to crash since the PC is completely bogus.
65268d75effSDimitry Andric     // test/tsan/double_race.cpp contains a test case for this.
65368d75effSDimitry Andric     toppc = 0;
65468d75effSDimitry Andric   }
65568d75effSDimitry Andric   ObtainCurrentStack(thr, toppc, &traces[0], &tags[0]);
65668d75effSDimitry Andric   if (IsFiredSuppression(ctx, typ, traces[0]))
65768d75effSDimitry Andric     return;
65868d75effSDimitry Andric 
65968d75effSDimitry Andric   // MutexSet is too large to live on stack.
66068d75effSDimitry Andric   Vector<u64> mset_buffer;
66168d75effSDimitry Andric   mset_buffer.Resize(sizeof(MutexSet) / sizeof(u64) + 1);
66268d75effSDimitry Andric   MutexSet *mset2 = new(&mset_buffer[0]) MutexSet();
66368d75effSDimitry Andric 
66468d75effSDimitry Andric   Shadow s2(thr->racy_state[1]);
66568d75effSDimitry Andric   RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2, &tags[1]);
66668d75effSDimitry Andric   if (IsFiredSuppression(ctx, typ, traces[1]))
66768d75effSDimitry Andric     return;
66868d75effSDimitry Andric 
669590d96feSDimitry Andric   if (HandleRacyStacks(thr, traces))
67068d75effSDimitry Andric     return;
67168d75effSDimitry Andric 
67268d75effSDimitry Andric   // If any of the accesses has a tag, treat this as an "external" race.
67368d75effSDimitry Andric   uptr tag = kExternalTagNone;
67468d75effSDimitry Andric   for (uptr i = 0; i < kMop; i++) {
67568d75effSDimitry Andric     if (tags[i] != kExternalTagNone) {
67668d75effSDimitry Andric       typ = ReportTypeExternalRace;
67768d75effSDimitry Andric       tag = tags[i];
67868d75effSDimitry Andric       break;
67968d75effSDimitry Andric     }
68068d75effSDimitry Andric   }
68168d75effSDimitry Andric 
68268d75effSDimitry Andric   ThreadRegistryLock l0(ctx->thread_registry);
68368d75effSDimitry Andric   ScopedReport rep(typ, tag);
68468d75effSDimitry Andric   for (uptr i = 0; i < kMop; i++) {
68568d75effSDimitry Andric     Shadow s(thr->racy_state[i]);
68668d75effSDimitry Andric     rep.AddMemoryAccess(addr, tags[i], s, traces[i],
68768d75effSDimitry Andric                         i == 0 ? &thr->mset : mset2);
68868d75effSDimitry Andric   }
68968d75effSDimitry Andric 
69068d75effSDimitry Andric   for (uptr i = 0; i < kMop; i++) {
69168d75effSDimitry Andric     FastState s(thr->racy_state[i]);
69268d75effSDimitry Andric     ThreadContext *tctx = static_cast<ThreadContext*>(
69368d75effSDimitry Andric         ctx->thread_registry->GetThreadLocked(s.tid()));
69468d75effSDimitry Andric     if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1)
69568d75effSDimitry Andric       continue;
69668d75effSDimitry Andric     rep.AddThread(tctx);
69768d75effSDimitry Andric   }
69868d75effSDimitry Andric 
69968d75effSDimitry Andric   rep.AddLocation(addr_min, addr_max - addr_min);
70068d75effSDimitry Andric 
70168d75effSDimitry Andric #if !SANITIZER_GO
70268d75effSDimitry Andric   {
70368d75effSDimitry Andric     Shadow s(thr->racy_state[1]);
70468d75effSDimitry Andric     if (s.epoch() <= thr->last_sleep_clock.get(s.tid()))
70568d75effSDimitry Andric       rep.AddSleep(thr->last_sleep_stack_id);
70668d75effSDimitry Andric   }
70768d75effSDimitry Andric #endif
70868d75effSDimitry Andric 
709*e8d8bef9SDimitry Andric   OutputReport(thr, rep);
71068d75effSDimitry Andric }
71168d75effSDimitry Andric 
71268d75effSDimitry Andric void PrintCurrentStack(ThreadState *thr, uptr pc) {
71368d75effSDimitry Andric   VarSizeStackTrace trace;
71468d75effSDimitry Andric   ObtainCurrentStack(thr, pc, &trace);
71568d75effSDimitry Andric   PrintStack(SymbolizeStack(trace));
71668d75effSDimitry Andric }
71768d75effSDimitry Andric 
71868d75effSDimitry Andric // Always inlining PrintCurrentStackSlow, because LocatePcInTrace assumes
71968d75effSDimitry Andric // __sanitizer_print_stack_trace exists in the actual unwinded stack, but
72068d75effSDimitry Andric // tail-call to PrintCurrentStackSlow breaks this assumption because
72168d75effSDimitry Andric // __sanitizer_print_stack_trace disappears after tail-call.
72268d75effSDimitry Andric // However, this solution is not reliable enough, please see dvyukov's comment
72368d75effSDimitry Andric // http://reviews.llvm.org/D19148#406208
72468d75effSDimitry Andric // Also see PR27280 comment 2 and 3 for breaking examples and analysis.
72568d75effSDimitry Andric ALWAYS_INLINE
72668d75effSDimitry Andric void PrintCurrentStackSlow(uptr pc) {
72768d75effSDimitry Andric #if !SANITIZER_GO
72868d75effSDimitry Andric   uptr bp = GET_CURRENT_FRAME();
72968d75effSDimitry Andric   BufferedStackTrace *ptrace =
73068d75effSDimitry Andric       new(internal_alloc(MBlockStackTrace, sizeof(BufferedStackTrace)))
73168d75effSDimitry Andric           BufferedStackTrace();
73268d75effSDimitry Andric   ptrace->Unwind(pc, bp, nullptr, false);
73368d75effSDimitry Andric 
73468d75effSDimitry Andric   for (uptr i = 0; i < ptrace->size / 2; i++) {
73568d75effSDimitry Andric     uptr tmp = ptrace->trace_buffer[i];
73668d75effSDimitry Andric     ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1];
73768d75effSDimitry Andric     ptrace->trace_buffer[ptrace->size - i - 1] = tmp;
73868d75effSDimitry Andric   }
73968d75effSDimitry Andric   PrintStack(SymbolizeStack(*ptrace));
74068d75effSDimitry Andric #endif
74168d75effSDimitry Andric }
74268d75effSDimitry Andric 
74368d75effSDimitry Andric }  // namespace __tsan
74468d75effSDimitry Andric 
74568d75effSDimitry Andric using namespace __tsan;
74668d75effSDimitry Andric 
74768d75effSDimitry Andric extern "C" {
74868d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
74968d75effSDimitry Andric void __sanitizer_print_stack_trace() {
75068d75effSDimitry Andric   PrintCurrentStackSlow(StackTrace::GetCurrentPc());
75168d75effSDimitry Andric }
75268d75effSDimitry Andric }  // extern "C"
753