[TSan][compiler-rt] Defer symbolization of Reports to as late as possible (#151120)

This is the refactoring portion of:
https://github.com/llvm/llvm-project/pull/149516. My aim is for this
change to replicate current behaviour - just with Symbolization done
explicitly (and later than previously).

This change will enable us to perform symboliaztion after releasing the
locks in `OutputReport`; this is necessary on Apple platforms in order
to avoid a deadlock.
This commit is contained in:
Dan Blackwell 2025-07-30 17:20:33 +01:00 committed by GitHub
parent 6be3cc5ad5
commit 26f9166ca1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 80 additions and 19 deletions

View File

@ -12,6 +12,8 @@
#ifndef TSAN_REPORT_H #ifndef TSAN_REPORT_H
#define TSAN_REPORT_H #define TSAN_REPORT_H
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h" #include "sanitizer_common/sanitizer_symbolizer.h"
#include "sanitizer_common/sanitizer_thread_registry.h" #include "sanitizer_common/sanitizer_thread_registry.h"
#include "sanitizer_common/sanitizer_vector.h" #include "sanitizer_common/sanitizer_vector.h"
@ -56,6 +58,7 @@ struct ReportMop {
bool atomic; bool atomic;
uptr external_tag; uptr external_tag;
Vector<ReportMopMutex> mset; Vector<ReportMopMutex> mset;
StackTrace stack_trace;
ReportStack *stack; ReportStack *stack;
ReportMop(); ReportMop();
@ -79,6 +82,7 @@ struct ReportLocation {
int fd = 0; int fd = 0;
bool fd_closed = false; bool fd_closed = false;
bool suppressable = false; bool suppressable = false;
StackID stack_id = 0;
ReportStack *stack = nullptr; ReportStack *stack = nullptr;
}; };
@ -89,15 +93,23 @@ struct ReportThread {
ThreadType thread_type; ThreadType thread_type;
char *name; char *name;
Tid parent_tid; Tid parent_tid;
StackID stack_id;
ReportStack *stack; ReportStack *stack;
bool suppressable;
}; };
struct ReportMutex { struct ReportMutex {
int id; int id;
uptr addr; uptr addr;
StackID stack_id;
ReportStack *stack; ReportStack *stack;
}; };
struct AddedLocationAddr {
uptr addr;
usize locs_idx;
};
class ReportDesc { class ReportDesc {
public: public:
ReportType typ; ReportType typ;
@ -105,6 +117,7 @@ class ReportDesc {
Vector<ReportStack*> stacks; Vector<ReportStack*> stacks;
Vector<ReportMop*> mops; Vector<ReportMop*> mops;
Vector<ReportLocation*> locs; Vector<ReportLocation*> locs;
Vector<AddedLocationAddr> added_location_addrs;
Vector<ReportMutex*> mutexes; Vector<ReportMutex*> mutexes;
Vector<ReportThread*> threads; Vector<ReportThread*> threads;
Vector<Tid> unique_tids; Vector<Tid> unique_tids;

View File

@ -420,6 +420,7 @@ class ScopedReportBase {
void AddSleep(StackID stack_id); void AddSleep(StackID stack_id);
void SetCount(int count); void SetCount(int count);
void SetSigNum(int sig); void SetSigNum(int sig);
void SymbolizeStackElems(void);
const ReportDesc *GetReport() const; const ReportDesc *GetReport() const;
@ -498,7 +499,7 @@ void ForkChildAfter(ThreadState *thr, uptr pc, bool start_thread);
void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old, void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old,
AccessType typ); AccessType typ);
bool OutputReport(ThreadState *thr, const ScopedReport &srep); bool OutputReport(ThreadState *thr, ScopedReport &srep);
bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace); bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace);
bool IsExpectedReport(uptr addr, uptr size); bool IsExpectedReport(uptr addr, uptr size);

View File

@ -539,13 +539,15 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
for (int i = 0; i < r->n; i++) { for (int i = 0; i < r->n; i++) {
for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) { for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
u32 stk = r->loop[i].stk[j]; u32 stk = r->loop[i].stk[j];
StackTrace stack;
if (stk && stk != kInvalidStackID) { if (stk && stk != kInvalidStackID) {
rep.AddStack(StackDepotGet(stk), true); stack = StackDepotGet(stk);
} else { } else {
// Sometimes we fail to extract the stack trace (FIXME: investigate), // Sometimes we fail to extract the stack trace (FIXME: investigate),
// but we should still produce some stack trace in the report. // but we should still produce some stack trace in the report.
rep.AddStack(StackTrace(&dummy_pc, 1), true); stack = StackTrace(&dummy_pc, 1);
} }
rep.AddStack(stack, true);
} }
} }
OutputReport(thr, rep); OutputReport(thr, rep);

View File

@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stackdepot.h"
@ -187,10 +188,8 @@ void ScopedReportBase::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
mop->size = size; mop->size = size;
mop->write = !(typ & kAccessRead); mop->write = !(typ & kAccessRead);
mop->atomic = typ & kAccessAtomic; mop->atomic = typ & kAccessAtomic;
mop->stack = SymbolizeStack(stack);
mop->external_tag = external_tag; mop->external_tag = external_tag;
if (mop->stack) mop->stack_trace = stack;
mop->stack->suppressable = true;
for (uptr i = 0; i < mset->Size(); i++) { for (uptr i = 0; i < mset->Size(); i++) {
MutexSet::Desc d = mset->Get(i); MutexSet::Desc d = mset->Get(i);
int id = this->AddMutex(d.addr, d.stack_id); int id = this->AddMutex(d.addr, d.stack_id);
@ -199,6 +198,56 @@ void ScopedReportBase::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
} }
} }
void ScopedReportBase::SymbolizeStackElems() {
// symbolize memory ops
for (usize i = 0, size = rep_->mops.Size(); i < size; i++) {
ReportMop *mop = rep_->mops[i];
mop->stack = SymbolizeStack(mop->stack_trace);
if (mop->stack)
mop->stack->suppressable = true;
}
// symbolize locations
for (usize i = 0, size = rep_->locs.Size(); i < size; i++) {
// added locations have a NULL placeholder - don't dereference them
if (ReportLocation *loc = rep_->locs[i])
loc->stack = SymbolizeStackId(loc->stack_id);
}
// symbolize any added locations
for (usize i = 0, size = rep_->added_location_addrs.Size(); i < size; i++) {
AddedLocationAddr *added_loc = &rep_->added_location_addrs[i];
if (ReportLocation *loc = SymbolizeData(added_loc->addr)) {
loc->suppressable = true;
rep_->locs[added_loc->locs_idx] = loc;
}
}
// Filter out any added location placeholders that could not be symbolized
usize j = 0;
for (usize i = 0, size = rep_->locs.Size(); i < size; i++) {
if (rep_->locs[i] != nullptr) {
rep_->locs[j] = rep_->locs[i];
j++;
}
}
rep_->locs.Resize(j);
// symbolize threads
for (usize i = 0, size = rep_->threads.Size(); i < size; i++) {
ReportThread *rt = rep_->threads[i];
rt->stack = SymbolizeStackId(rt->stack_id);
if (rt->stack)
rt->stack->suppressable = rt->suppressable;
}
// symbolize mutexes
for (usize i = 0, size = rep_->mutexes.Size(); i < size; i++) {
ReportMutex *rm = rep_->mutexes[i];
rm->stack = SymbolizeStackId(rm->stack_id);
}
}
void ScopedReportBase::AddUniqueTid(Tid unique_tid) { void ScopedReportBase::AddUniqueTid(Tid unique_tid) {
rep_->unique_tids.PushBack(unique_tid); rep_->unique_tids.PushBack(unique_tid);
} }
@ -216,10 +265,8 @@ void ScopedReportBase::AddThread(const ThreadContext *tctx, bool suppressable) {
rt->name = internal_strdup(tctx->name); rt->name = internal_strdup(tctx->name);
rt->parent_tid = tctx->parent_tid; rt->parent_tid = tctx->parent_tid;
rt->thread_type = tctx->thread_type; rt->thread_type = tctx->thread_type;
rt->stack = 0; rt->stack_id = tctx->creation_stack_id;
rt->stack = SymbolizeStackId(tctx->creation_stack_id); rt->suppressable = suppressable;
if (rt->stack)
rt->stack->suppressable = suppressable;
} }
#if !SANITIZER_GO #if !SANITIZER_GO
@ -270,7 +317,7 @@ int ScopedReportBase::AddMutex(uptr addr, StackID creation_stack_id) {
rep_->mutexes.PushBack(rm); rep_->mutexes.PushBack(rm);
rm->id = rep_->mutexes.Size() - 1; rm->id = rep_->mutexes.Size() - 1;
rm->addr = addr; rm->addr = addr;
rm->stack = SymbolizeStackId(creation_stack_id); rm->stack_id = creation_stack_id;
return rm->id; return rm->id;
} }
@ -288,7 +335,7 @@ void ScopedReportBase::AddLocation(uptr addr, uptr size) {
loc->fd_closed = closed; loc->fd_closed = closed;
loc->fd = fd; loc->fd = fd;
loc->tid = creat_tid; loc->tid = creat_tid;
loc->stack = SymbolizeStackId(creat_stack); loc->stack_id = creat_stack;
rep_->locs.PushBack(loc); rep_->locs.PushBack(loc);
AddThread(creat_tid); AddThread(creat_tid);
return; return;
@ -310,7 +357,7 @@ void ScopedReportBase::AddLocation(uptr addr, uptr size) {
loc->heap_chunk_size = b->siz; loc->heap_chunk_size = b->siz;
loc->external_tag = b->tag; loc->external_tag = b->tag;
loc->tid = b->tid; loc->tid = b->tid;
loc->stack = SymbolizeStackId(b->stk); loc->stack_id = b->stk;
rep_->locs.PushBack(loc); rep_->locs.PushBack(loc);
AddThread(b->tid); AddThread(b->tid);
return; return;
@ -324,11 +371,8 @@ void ScopedReportBase::AddLocation(uptr addr, uptr size) {
AddThread(tctx); AddThread(tctx);
} }
#endif #endif
if (ReportLocation *loc = SymbolizeData(addr)) { rep_->added_location_addrs.PushBack({addr, rep_->locs.Size()});
loc->suppressable = true; rep_->locs.PushBack(nullptr);
rep_->locs.PushBack(loc);
return;
}
} }
#if !SANITIZER_GO #if !SANITIZER_GO
@ -628,11 +672,12 @@ static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2]) {
return false; return false;
} }
bool OutputReport(ThreadState *thr, const ScopedReport &srep) { bool OutputReport(ThreadState *thr, ScopedReport &srep) {
// These should have been checked in ShouldReport. // These should have been checked in ShouldReport.
// It's too late to check them here, we have already taken locks. // It's too late to check them here, we have already taken locks.
CHECK(flags()->report_bugs); CHECK(flags()->report_bugs);
CHECK(!thr->suppress_reports); CHECK(!thr->suppress_reports);
srep.SymbolizeStackElems();
atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime()); atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime());
const ReportDesc *rep = srep.GetReport(); const ReportDesc *rep = srep.GetReport();
CHECK_EQ(thr->current_report, nullptr); CHECK_EQ(thr->current_report, nullptr);