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