[compiler-rt] [NFC][TSan] Merge ScopedReportBase into ScopedReport (PR #228640)
Vitaly Buka via llvm-commits
llvm-commits at lists.llvm.org
Sat Oct 3 18:26:40 PDT 2026
https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/228640
>From 4b2ad9cf95aa914fc34d6dc78c2d50841e3288ef Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 2 Oct 2026 16:07:34 -0700
Subject: [PATCH 1/3] [TSan] Lock ScopedErrorReportLock before slot and
thread_registry locks
OutputReport runs while ScopedErrorReportLock is held after slot_mtx and
thread_registry have been unlocked. Because code executed during
OutputReport (symbolizer, callbacks, or signal handlers) can acquire
slot_mtx or thread_registry, ScopedErrorReportLock must precede slot and
thread_registry locks in the lock hierarchy to avoid AB-BA deadlocks
between concurrent reports or fork().
- Move ScopedErrorReportLock::Lock() before slot.mtx, thread_registry,
and slot_mtx in ForkBefore (and unlock in reverse order in ForkAfter).
- Replace ctx->thread_registry.CheckLocked() in ScopedReportBase's
constructor with CheckedMutex::CheckNoLocks(), and add CheckLocked() to
AddThread(const ThreadContext *) and CheckNoLocks() to OutputReport.
- Construct ScopedReport before acquiring ThreadRegistryLock across all
reporting functions, and close the RestoreStack lock scope before
constructing ScopedReport in ReportRace.
Assisted-by: Gemini
Pull Request: https://github.com/llvm/llvm-project/pull/228614
---
.../lib/tsan/rtl/tsan_interceptors_posix.cpp | 2 +-
.../lib/tsan/rtl/tsan_interface_ann.cpp | 2 +-
compiler-rt/lib/tsan/rtl/tsan_mman.cpp | 2 +-
compiler-rt/lib/tsan/rtl/tsan_rtl.cpp | 4 +-
compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp | 6 +--
compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp | 39 +++++++++++--------
compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp | 2 +-
7 files changed, 31 insertions(+), 26 deletions(-)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index b2ba666741e89..a8d502966bcca 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -2182,8 +2182,8 @@ static void ReportErrnoSpoiling(ThreadState *thr, uptr pc, int sig) {
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- ThreadRegistryLock l(&ctx->thread_registry);
new (rep) ScopedReport(ReportTypeErrnoInSignal);
+ ThreadRegistryLock l(&ctx->thread_registry);
rep->SetSigNum(sig);
suppressed = IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack);
if (!suppressed)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
index 526dc5a753c24..390cae67d2170 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
@@ -446,8 +446,8 @@ static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) {
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- ThreadRegistryLock l(&ctx->thread_registry);
new (rep) ScopedReport(ReportTypeMutexHeldWrongContext);
+ ThreadRegistryLock l(&ctx->thread_registry);
for (uptr i = 0; i < thr->mset.Size(); ++i) {
MutexSet::Desc desc = thr->mset.Get(i);
rep->AddMutex(desc.addr, desc.stack_id);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
index cea833054551d..88cdf58af8d58 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
@@ -187,8 +187,8 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- ThreadRegistryLock l(&ctx->thread_registry);
new (rep) ScopedReport(ReportTypeSignalUnsafe);
+ ThreadRegistryLock l(&ctx->thread_registry);
rep->AddStack(stack, true);
}
OutputReport(thr, *rep);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
index c6e7dce5ecb18..7328caab0a918 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
@@ -841,10 +841,10 @@ void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
// Detaching from the slot makes OnUserFree skip writing to the shadow.
// The slot will be locked so any attempts to use it will deadlock anyway.
SlotDetach(thr);
+ ScopedErrorReportLock::Lock();
for (auto& slot : ctx->slots) slot.mtx.Lock();
ctx->thread_registry.Lock();
ctx->slot_mtx.Lock();
- ScopedErrorReportLock::Lock();
AllocatorLockBeforeFork();
// Suppress all reports in the pthread_atfork callbacks.
// Reports may deadlock.
@@ -871,10 +871,10 @@ static void ForkAfter(ThreadState* thr,
thr->ignore_interceptors--;
thr->ignore_reads_and_writes--;
AllocatorUnlockAfterFork(child);
- ScopedErrorReportLock::Unlock();
ctx->slot_mtx.Unlock();
ctx->thread_registry.Unlock();
for (auto& slot : ctx->slots) slot.mtx.Unlock();
+ ScopedErrorReportLock::Unlock();
SlotAttachAndLock(thr);
SlotUnlock(thr);
GlobalProcessorUnlock();
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
index da3604a2b840d..49759ab2974fd 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
@@ -61,8 +61,8 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- ThreadRegistryLock l(&ctx->thread_registry);
new (rep) ScopedReport(typ);
+ ThreadRegistryLock l(&ctx->thread_registry);
rep->AddMutex(addr, creation_stack_id);
VarSizeStackTrace trace;
ObtainCurrentStack(thr, pc, &trace);
@@ -543,8 +543,8 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- ThreadRegistryLock l(&ctx->thread_registry);
new (rep) ScopedReport(ReportTypeDeadlock);
+ ThreadRegistryLock l(&ctx->thread_registry);
for (int i = 0; i < r->n; i++) {
rep->AddMutex(r->loop[i].mtx_ctx0, r->loop[i].stk[0]);
rep->AddUniqueTid((int)r->loop[i].thr_ctx);
@@ -579,12 +579,12 @@ void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
+ new (rep) ScopedReport(ReportTypeMutexDestroyLocked);
// We need to lock the slot during RestoreStack because it protects
// the slot journal.
Lock slot_lock(&ctx->slots[static_cast<uptr>(last_lock.sid())].mtx);
ThreadRegistryLock l0(&ctx->thread_registry);
Lock slots_lock(&ctx->slot_mtx);
- new (rep) ScopedReport(ReportTypeMutexDestroyLocked);
rep->AddMutex(addr, creation_stack_id);
VarSizeStackTrace trace;
ObtainCurrentStack(thr, pc, &trace);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
index 8fd1471b6fa2c..7d302955374de 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
@@ -164,7 +164,7 @@ bool ShouldReport(ThreadState *thr, ReportType typ) {
}
ScopedReportBase::ScopedReportBase(ReportType typ, uptr tag) {
- ctx->thread_registry.CheckLocked();
+ CheckedMutex::CheckNoLocks();
rep_ = New<ReportDesc>();
rep_->typ = typ;
rep_->tag = tag;
@@ -256,6 +256,7 @@ void ScopedReportBase::AddUniqueTid(Tid unique_tid) {
}
void ScopedReportBase::AddThread(const ThreadContext *tctx, bool suppressable) {
+ ctx->thread_registry.CheckLocked();
for (uptr i = 0; i < rep_->threads.Size(); i++) {
if ((u32)rep_->threads[i]->id == tctx->tid)
return;
@@ -676,6 +677,7 @@ static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2]) {
}
bool OutputReport(ThreadState *thr, ScopedReport &srep) {
+ CheckedMutex::CheckNoLocks();
// These should have been checked in ShouldReport.
// It's too late to check them here, we have already taken locks.
CHECK(flags()->report_bugs);
@@ -809,10 +811,6 @@ void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old,
DynamicMutexSet mset1;
MutexSet *mset[kMop] = {&thr->mset, mset1};
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
- // Release locks before symbolizing and outputting the report to avoid
- // deadlocks.
{
// We need to lock the slot during RestoreStack because it protects
// the slot journal.
@@ -826,24 +824,31 @@ void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old,
StoreShadow(&ctx->last_spurious_race, old.raw());
return;
}
+ }
- if (IsFiredSuppression(ctx, rep_typ, traces[1]))
- return;
+ if (IsFiredSuppression(ctx, rep_typ, traces[1]))
+ return;
- if (HandleRacyStacks(thr, traces))
- return;
+ if (HandleRacyStacks(thr, traces))
+ return;
- // If any of the accesses has a tag, treat this as an "external" race.
- uptr tag = kExternalTagNone;
- for (uptr i = 0; i < kMop; i++) {
- if (tags[i] != kExternalTagNone) {
- rep_typ = ReportTypeExternalRace;
- tag = tags[i];
- break;
- }
+ // If any of the accesses has a tag, treat this as an "external" race.
+ uptr tag = kExternalTagNone;
+ for (uptr i = 0; i < kMop; i++) {
+ if (tags[i] != kExternalTagNone) {
+ rep_typ = ReportTypeExternalRace;
+ tag = tags[i];
+ break;
}
+ }
+ // Use alloca, because malloc during signal handling deadlocks
+ ScopedReport* rep = (ScopedReport*)__builtin_alloca(sizeof(ScopedReport));
+ // Release locks before symbolizing and outputting the report to avoid
+ // deadlocks.
+ {
new (rep) ScopedReport(rep_typ, tag);
+ ThreadRegistryLock l0(&ctx->thread_registry);
for (uptr i = 0; i < kMop; i++)
rep->AddMemoryAccess(addr, tags[i], s[i], tids[i], traces[i], mset[i]);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
index afc6b8d4a0b1c..c408024e77e60 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -101,8 +101,8 @@ void ThreadFinalize(ThreadState *thr) {
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- ThreadRegistryLock l(&ctx->thread_registry);
new (rep) ScopedReport(ReportTypeThreadLeak);
+ ThreadRegistryLock l(&ctx->thread_registry);
rep->AddThread(leaks[i].tctx, true);
rep->SetCount(leaks[i].count);
}
>From 20fb2e995b769fe07766dda4eb77b4fa6485998d Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 2 Oct 2026 19:41:27 -0700
Subject: [PATCH 2/3] [NFC][TSan] Allocate ScopedReport as a stack variable
Now that ScopedReport is constructed before acquiring ThreadRegistryLock
or slot locks across all reporting functions, it no longer needs to be
constructed inside the lock scope via placement new on __builtin_alloca
storage.
Declare ScopedReport as a normal stack variable before the lock scope
and remove the manual destructor calls.
Assisted-by: Gemini
Pull Request: https://github.com/llvm/llvm-project/pull/228637
---
.../lib/tsan/rtl/tsan_interceptors_posix.cpp | 13 ++---
.../lib/tsan/rtl/tsan_interface_ann.cpp | 13 ++---
compiler-rt/lib/tsan/rtl/tsan_mman.cpp | 11 ++--
compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp | 54 +++++++------------
compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp | 21 +++-----
compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp | 13 ++---
6 files changed, 41 insertions(+), 84 deletions(-)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index a8d502966bcca..233985c419be1 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -2176,24 +2176,19 @@ static void ReportErrnoSpoiling(ThreadState *thr, uptr pc, int sig) {
// StackTrace::GetNestInstructionPc(pc) is used because return address is
// expected, OutputReport() will undo this.
ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack);
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+ ScopedReport rep(ReportTypeErrnoInSignal);
bool suppressed;
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- new (rep) ScopedReport(ReportTypeErrnoInSignal);
ThreadRegistryLock l(&ctx->thread_registry);
- rep->SetSigNum(sig);
+ rep.SetSigNum(sig);
suppressed = IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack);
if (!suppressed)
- rep->AddStack(stack, true);
+ rep.AddStack(stack, true);
}
if (!suppressed)
- OutputReport(thr, *rep);
-
- // Need to manually destroy this because we used placement new to allocate
- rep->~ScopedReport();
+ OutputReport(thr, rep);
}
static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
index 390cae67d2170..e280f5b91b61d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
@@ -441,25 +441,20 @@ void __tsan_mutex_post_divert(void *addr, unsigned flagz) {
}
static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) {
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+ ScopedReport rep(ReportTypeMutexHeldWrongContext);
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- new (rep) ScopedReport(ReportTypeMutexHeldWrongContext);
ThreadRegistryLock l(&ctx->thread_registry);
for (uptr i = 0; i < thr->mset.Size(); ++i) {
MutexSet::Desc desc = thr->mset.Get(i);
- rep->AddMutex(desc.addr, desc.stack_id);
+ rep.AddMutex(desc.addr, desc.stack_id);
}
VarSizeStackTrace trace;
ObtainCurrentStack(thr, pc, &trace);
- rep->AddStack(trace, true);
+ rep.AddStack(trace, true);
}
- OutputReport(thr, *rep);
-
- // Need to manually destroy this because we used placement new to allocate
- rep->~ScopedReport();
+ OutputReport(thr, rep);
}
INTERFACE_ATTRIBUTE
diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
index 88cdf58af8d58..7a39b8a71a0a7 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
@@ -182,19 +182,14 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
ObtainCurrentStack(thr, pc, &stack);
if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack))
return;
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+ ScopedReport rep(ReportTypeSignalUnsafe);
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- new (rep) ScopedReport(ReportTypeSignalUnsafe);
ThreadRegistryLock l(&ctx->thread_registry);
- rep->AddStack(stack, true);
+ rep.AddStack(stack, true);
}
- OutputReport(thr, *rep);
-
- // Need to manually destroy this because we used placement new to allocate
- rep->~ScopedReport();
+ OutputReport(thr, rep);
}
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
index 49759ab2974fd..ea9260a9486b9 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
@@ -11,7 +11,6 @@
//===----------------------------------------------------------------------===//
#include <sanitizer_common/sanitizer_deadlock_detector_interface.h>
-#include <sanitizer_common/sanitizer_placement_new.h>
#include <sanitizer_common/sanitizer_stackdepot.h>
#include "tsan_flags.h"
@@ -56,23 +55,18 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
return;
if (!ShouldReport(thr, typ))
return;
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+ ScopedReport rep(typ);
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- new (rep) ScopedReport(typ);
ThreadRegistryLock l(&ctx->thread_registry);
- rep->AddMutex(addr, creation_stack_id);
+ rep.AddMutex(addr, creation_stack_id);
VarSizeStackTrace trace;
ObtainCurrentStack(thr, pc, &trace);
- rep->AddStack(trace, true);
- rep->AddLocation(addr, 1);
+ rep.AddStack(trace, true);
+ rep.AddLocation(addr, 1);
}
- OutputReport(thr, *rep);
-
- // Need to manually destroy this because we used placement new to allocate
- rep->~ScopedReport();
+ OutputReport(thr, rep);
}
static void RecordMutexLock(ThreadState *thr, uptr pc, uptr addr,
@@ -538,17 +532,15 @@ void AfterSleep(ThreadState *thr, uptr pc) {
void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
if (r == 0 || !ShouldReport(thr, ReportTypeDeadlock))
return;
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+ ScopedReport rep(ReportTypeDeadlock);
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- new (rep) ScopedReport(ReportTypeDeadlock);
ThreadRegistryLock l(&ctx->thread_registry);
for (int i = 0; i < r->n; i++) {
- rep->AddMutex(r->loop[i].mtx_ctx0, r->loop[i].stk[0]);
- rep->AddUniqueTid((int)r->loop[i].thr_ctx);
- rep->AddThread((int)r->loop[i].thr_ctx);
+ rep.AddMutex(r->loop[i].mtx_ctx0, r->loop[i].stk[0]);
+ rep.AddUniqueTid((int)r->loop[i].thr_ctx);
+ rep.AddThread((int)r->loop[i].thr_ctx);
}
uptr dummy_pc = 0x42;
for (int i = 0; i < r->n; i++) {
@@ -562,49 +554,39 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
// but we should still produce some stack trace in the report.
stack = StackTrace(&dummy_pc, 1);
}
- rep->AddStack(stack, true);
+ rep.AddStack(stack, true);
}
}
}
- OutputReport(thr, *rep);
-
- // Need to manually destroy this because we used placement new to allocate
- rep->~ScopedReport();
+ OutputReport(thr, rep);
}
void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
FastState last_lock, StackID creation_stack_id) {
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+ ScopedReport rep(ReportTypeMutexDestroyLocked);
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- new (rep) ScopedReport(ReportTypeMutexDestroyLocked);
// We need to lock the slot during RestoreStack because it protects
// the slot journal.
Lock slot_lock(&ctx->slots[static_cast<uptr>(last_lock.sid())].mtx);
ThreadRegistryLock l0(&ctx->thread_registry);
Lock slots_lock(&ctx->slot_mtx);
- rep->AddMutex(addr, creation_stack_id);
+ rep.AddMutex(addr, creation_stack_id);
VarSizeStackTrace trace;
ObtainCurrentStack(thr, pc, &trace);
- rep->AddStack(trace, true);
+ rep.AddStack(trace, true);
Tid tid;
DynamicMutexSet mset;
uptr tag;
if (!RestoreStack(EventType::kLock, last_lock.sid(), last_lock.epoch(),
- addr, 0, kAccessWrite, &tid, &trace, mset, &tag)) {
- rep->~ScopedReport();
+ addr, 0, kAccessWrite, &tid, &trace, mset, &tag))
return;
- }
- rep->AddStack(trace, true);
- rep->AddLocation(addr, 1);
+ rep.AddStack(trace, true);
+ rep.AddLocation(addr, 1);
}
- OutputReport(thr, *rep);
-
- // Need to manually destroy this because we used placement new to allocate
- rep->~ScopedReport();
+ OutputReport(thr, rep);
}
} // namespace __tsan
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
index 7d302955374de..4a98b761b9d87 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
@@ -842,46 +842,41 @@ void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old,
}
}
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport* rep = (ScopedReport*)__builtin_alloca(sizeof(ScopedReport));
+ ScopedReport rep(rep_typ, tag);
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- new (rep) ScopedReport(rep_typ, tag);
ThreadRegistryLock l0(&ctx->thread_registry);
for (uptr i = 0; i < kMop; i++)
- rep->AddMemoryAccess(addr, tags[i], s[i], tids[i], traces[i], mset[i]);
+ rep.AddMemoryAccess(addr, tags[i], s[i], tids[i], traces[i], mset[i]);
for (uptr i = 0; i < kMop; i++) {
ThreadContext *tctx = static_cast<ThreadContext *>(
ctx->thread_registry.GetThreadLocked(tids[i]));
- rep->AddThread(tctx);
+ rep.AddThread(tctx);
}
- rep->AddLocation(addr_min, addr_max - addr_min);
+ rep.AddLocation(addr_min, addr_max - addr_min);
if (flags()->print_full_thread_history) {
- const ReportDesc *rep_desc = rep->GetReport();
+ const ReportDesc* rep_desc = rep.GetReport();
for (uptr i = 0; i < rep_desc->threads.Size(); i++) {
Tid parent_tid = rep_desc->threads[i]->parent_tid;
if (parent_tid == kMainTid || parent_tid == kInvalidTid)
continue;
ThreadContext *parent_tctx = static_cast<ThreadContext *>(
ctx->thread_registry.GetThreadLocked(parent_tid));
- rep->AddThread(parent_tctx);
+ rep.AddThread(parent_tctx);
}
}
#if !SANITIZER_GO
if (!((typ0 | typ1) & kAccessFree) &&
s[1].epoch() <= thr->last_sleep_clock.Get(s[1].sid()))
- rep->AddSleep(thr->last_sleep_stack_id);
+ rep.AddSleep(thr->last_sleep_stack_id);
#endif
}
- OutputReport(thr, *rep);
-
- // Need to manually destroy this because we used placement new to allocate
- rep->~ScopedReport();
+ OutputReport(thr, rep);
}
void PrintCurrentStack(ThreadState *thr, uptr pc) {
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
index c408024e77e60..5edf0c16fdd0d 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -96,20 +96,15 @@ void ThreadFinalize(ThreadState *thr) {
}
for (uptr i = 0; i < leaks.Size(); i++) {
- // Use alloca, because malloc during signal handling deadlocks
- ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
+ ScopedReport rep(ReportTypeThreadLeak);
// Release locks before symbolizing and outputting the report to avoid
// deadlocks.
{
- new (rep) ScopedReport(ReportTypeThreadLeak);
ThreadRegistryLock l(&ctx->thread_registry);
- rep->AddThread(leaks[i].tctx, true);
- rep->SetCount(leaks[i].count);
+ rep.AddThread(leaks[i].tctx, true);
+ rep.SetCount(leaks[i].count);
}
- OutputReport(thr, *rep);
-
- // Need to manually destroy this because we used placement new to allocate
- rep->~ScopedReport();
+ OutputReport(thr, rep);
}
#endif
}
>From 02a58c8e135bc21f15d03484c04ede345a2f794c Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 2 Oct 2026 19:51:25 -0700
Subject: [PATCH 3/3] [NFC][TSan] Merge ScopedReportBase into ScopedReport
ScopedReport is the only subclass of ScopedReportBase and nothing uses
ScopedReportBase directly. Merge ScopedReportBase into ScopedReport.
Assisted-by: Gemini
Pull Request: https://github.com/llvm/llvm-project/pull/228640
---
compiler-rt/lib/tsan/rtl/tsan_rtl.h | 23 ++++--------
compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp | 37 +++++++++-----------
2 files changed, 23 insertions(+), 37 deletions(-)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
index fcfbf2f445dfa..a4bf790d5980b 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
@@ -413,8 +413,11 @@ const char *GetObjectTypeFromTag(uptr tag);
const char *GetReportHeaderFromTag(uptr tag);
uptr TagFromShadowStackFrame(uptr pc);
-class ScopedReportBase {
+class ScopedReport {
public:
+ explicit ScopedReport(ReportType typ, uptr tag = kExternalTagNone);
+ ~ScopedReport();
+
void AddMemoryAccess(uptr addr, uptr external_tag, Shadow s, Tid tid,
StackTrace stack, const MutexSet *mset);
void AddStack(StackTrace stack, bool suppressable = false);
@@ -430,27 +433,15 @@ class ScopedReportBase {
const ReportDesc *GetReport() const;
- protected:
- ScopedReportBase(ReportType typ, uptr tag);
- ~ScopedReportBase();
-
private:
ReportDesc *rep_;
// Symbolizer makes lots of intercepted calls. If we try to process them,
// at best it will cause deadlocks on internal mutexes.
ScopedIgnoreInterceptors ignore_interceptors_;
-
- ScopedReportBase(const ScopedReportBase &) = delete;
- void operator=(const ScopedReportBase &) = delete;
-};
-
-class ScopedReport : public ScopedReportBase {
- public:
- explicit ScopedReport(ReportType typ, uptr tag = kExternalTagNone);
- ~ScopedReport();
-
- private:
ScopedErrorReportLock lock_;
+
+ ScopedReport(const ScopedReport&) = delete;
+ void operator=(const ScopedReport&) = delete;
};
bool ShouldReport(ThreadState *thr, ReportType typ);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
index 4a98b761b9d87..52a462617ecb4 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
@@ -163,24 +163,24 @@ bool ShouldReport(ThreadState *thr, ReportType typ) {
}
}
-ScopedReportBase::ScopedReportBase(ReportType typ, uptr tag) {
+ScopedReport::ScopedReport(ReportType typ, uptr tag) {
CheckedMutex::CheckNoLocks();
rep_ = New<ReportDesc>();
rep_->typ = typ;
rep_->tag = tag;
}
-ScopedReportBase::~ScopedReportBase() { DestroyAndFree(rep_); }
+ScopedReport::~ScopedReport() { DestroyAndFree(rep_); }
-void ScopedReportBase::AddStack(StackTrace stack, bool suppressable) {
+void ScopedReport::AddStack(StackTrace stack, bool suppressable) {
ReportStack **rs = rep_->stacks.PushBack();
*rs = SymbolizeStack(stack);
(*rs)->suppressable = suppressable;
}
-void ScopedReportBase::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
- Tid tid, StackTrace stack,
- const MutexSet *mset) {
+void ScopedReport::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
+ Tid tid, StackTrace stack,
+ const MutexSet* mset) {
uptr addr0, size;
AccessType typ;
s.GetAccess(&addr0, &size, &typ);
@@ -201,7 +201,7 @@ void ScopedReportBase::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
}
}
-void ScopedReportBase::SymbolizeStackElems() {
+void ScopedReport::SymbolizeStackElems() {
// symbolize memory ops
for (usize i = 0, size = rep_->mops.Size(); i < size; i++) {
ReportMop *mop = rep_->mops[i];
@@ -251,11 +251,11 @@ void ScopedReportBase::SymbolizeStackElems() {
}
}
-void ScopedReportBase::AddUniqueTid(Tid unique_tid) {
+void ScopedReport::AddUniqueTid(Tid unique_tid) {
rep_->unique_tids.PushBack(unique_tid);
}
-void ScopedReportBase::AddThread(const ThreadContext *tctx, bool suppressable) {
+void ScopedReport::AddThread(const ThreadContext* tctx, bool suppressable) {
ctx->thread_registry.CheckLocked();
for (uptr i = 0; i < rep_->threads.Size(); i++) {
if ((u32)rep_->threads[i]->id == tctx->tid)
@@ -305,14 +305,14 @@ ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
}
#endif
-void ScopedReportBase::AddThread(Tid tid, bool suppressable) {
+void ScopedReport::AddThread(Tid tid, bool suppressable) {
#if !SANITIZER_GO
if (const ThreadContext *tctx = FindThreadByTidLocked(tid))
AddThread(tctx, suppressable);
#endif
}
-int ScopedReportBase::AddMutex(uptr addr, StackID creation_stack_id) {
+int ScopedReport::AddMutex(uptr addr, StackID creation_stack_id) {
for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
if (rep_->mutexes[i]->addr == addr)
return rep_->mutexes[i]->id;
@@ -325,7 +325,7 @@ int ScopedReportBase::AddMutex(uptr addr, StackID creation_stack_id) {
return rm->id;
}
-void ScopedReportBase::AddLocation(uptr addr, uptr size) {
+void ScopedReport::AddLocation(uptr addr, uptr size) {
if (addr == 0)
return;
#if !SANITIZER_GO
@@ -380,21 +380,16 @@ void ScopedReportBase::AddLocation(uptr addr, uptr size) {
}
#if !SANITIZER_GO
-void ScopedReportBase::AddSleep(StackID stack_id) {
+void ScopedReport::AddSleep(StackID stack_id) {
rep_->sleep = SymbolizeStackId(stack_id);
}
#endif
-void ScopedReportBase::SetCount(int count) { rep_->count = count; }
+void ScopedReport::SetCount(int count) { rep_->count = count; }
-void ScopedReportBase::SetSigNum(int sig) { rep_->signum = sig; }
+void ScopedReport::SetSigNum(int sig) { rep_->signum = sig; }
-const ReportDesc *ScopedReportBase::GetReport() const { return rep_; }
-
-ScopedReport::ScopedReport(ReportType typ, uptr tag)
- : ScopedReportBase(typ, tag) {}
-
-ScopedReport::~ScopedReport() {}
+const ReportDesc* ScopedReport::GetReport() const { return rep_; }
// Replays the trace up to last_pos position in the last part
// or up to the provided epoch/sid (whichever is earlier)
More information about the llvm-commits
mailing list