[compiler-rt] [NFC][TSan] Remove redundant ThreadRegistryLock debug check in ShouldReport (PR #228815)

Vitaly Buka via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 6 09:59:38 PDT 2026


https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/228815

>From 6b38839ddc637773820b25383e14b8cbc1b806be Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Sat, 3 Oct 2026 20:15:00 -0700
Subject: [PATCH 1/2] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
 =?UTF-8?q?anges=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7

[skip ci]
---
 .../lib/tsan/rtl/tsan_interceptors_posix.cpp  |  24 +---
 .../lib/tsan/rtl/tsan_interface_ann.cpp       |  26 ++--
 compiler-rt/lib/tsan/rtl/tsan_mman.cpp        |  16 +--
 compiler-rt/lib/tsan/rtl/tsan_report.cpp      |  22 +--
 compiler-rt/lib/tsan/rtl/tsan_report.h        |  64 +++++----
 compiler-rt/lib/tsan/rtl/tsan_rtl.cpp         |   4 +-
 compiler-rt/lib/tsan/rtl/tsan_rtl.h           |   2 +
 compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp   |  98 +++++--------
 compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp  | 133 +++++++++---------
 compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp  |  18 +--
 10 files changed, 168 insertions(+), 239 deletions(-)

diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index b2ba666741e891..b0eb92fbd6a507 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -2176,24 +2176,12 @@ 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));
-  bool suppressed;
-  // Release locks before symbolizing and outputting the report to avoid
-  // deadlocks.
-  {
-    ThreadRegistryLock l(&ctx->thread_registry);
-    new (rep) ScopedReport(ReportTypeErrnoInSignal);
-    rep->SetSigNum(sig);
-    suppressed = IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack);
-    if (!suppressed)
-      rep->AddStack(stack, true);
-  }
-  if (!suppressed)
-    OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  if (IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack))
+    return;
+  ScopedReport rep(ReportTypeErrnoInSignal);
+  rep.SetSigNum(sig);
+  rep.AddStack(stack, true);
+  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 526dc5a753c24f..445e84c843237b 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
@@ -441,25 +441,15 @@ 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));
-  // Release locks before symbolizing and outputting the report to avoid
-  // deadlocks.
-  {
-    ThreadRegistryLock l(&ctx->thread_registry);
-    new (rep) ScopedReport(ReportTypeMutexHeldWrongContext);
-    for (uptr i = 0; i < thr->mset.Size(); ++i) {
-      MutexSet::Desc desc = thr->mset.Get(i);
-      rep->AddMutex(desc.addr, desc.stack_id);
-    }
-    VarSizeStackTrace trace;
-    ObtainCurrentStack(thr, pc, &trace);
-    rep->AddStack(trace, true);
+  VarSizeStackTrace trace;
+  ObtainCurrentStack(thr, pc, &trace);
+  ScopedReport rep(ReportTypeMutexHeldWrongContext);
+  for (uptr i = 0; i < thr->mset.Size(); ++i) {
+    MutexSet::Desc desc = thr->mset.Get(i);
+    rep.AddMutex(desc.addr, desc.stack_id);
   }
-  OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  rep.AddStack(trace, true);
+  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 cea833054551d3..fda49d9fb8f428 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
@@ -182,19 +182,9 @@ 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));
-  // Release locks before symbolizing and outputting the report to avoid
-  // deadlocks.
-  {
-    ThreadRegistryLock l(&ctx->thread_registry);
-    new (rep) ScopedReport(ReportTypeSignalUnsafe);
-    rep->AddStack(stack, true);
-  }
-  OutputReport(thr, *rep);
-
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  ScopedReport rep(ReportTypeSignalUnsafe);
+  rep.AddStack(stack, true);
+  OutputReport(thr, rep);
 }
 
 
diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_report.cpp
index 17fd3064fb85a1..ca04f9e1faf074 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_report.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_report.cpp
@@ -29,25 +29,13 @@ class Decorator: public __sanitizer::SanitizerCommonDecorator {
   const char *Mutex()   { return Magenta(); }
 };
 
-ReportDesc::ReportDesc()
-    : tag(kExternalTagNone)
-    , stacks()
-    , mops()
-    , locs()
-    , mutexes()
-    , threads()
-    , unique_tids()
-    , sleep()
-    , count() {
-}
+ReportDesc::ReportDesc() = default;
 
-ReportMop::ReportMop()
-    : mset() {
-}
+ReportMop::ReportMop() = default;
+ReportMop::~ReportMop() = default;
 
-ReportDesc::~ReportDesc() {
-  // FIXME(dvyukov): it must be leaking a lot of memory.
-}
+// FIXME(dvyukov): it must be leaking a lot of memory.
+ReportDesc::~ReportDesc() = default;
 
 #if !SANITIZER_GO
 
diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.h b/compiler-rt/lib/tsan/rtl/tsan_report.h
index 53bb21964dbba9..13171aa2706843 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_report.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_report.h
@@ -46,22 +46,23 @@ struct ReportStack {
 };
 
 struct ReportMopMutex {
-  int id;
-  bool write;
+  int id = 0;
+  bool write = false;
 };
 
 struct ReportMop {
-  int tid;
-  uptr addr;
-  int size;
-  bool write;
-  bool atomic;
-  uptr external_tag;
+  int tid = kInvalidTid;
+  uptr addr = 0;
+  int size = 0;
+  bool write = false;
+  bool atomic = false;
+  uptr external_tag = 0;
   Vector<ReportMopMutex> mset;
   StackTrace stack_trace;
-  ReportStack *stack;
+  ReportStack* stack = nullptr;
 
   ReportMop();
+  ~ReportMop();
 };
 
 enum ReportLocationType {
@@ -87,42 +88,49 @@ struct ReportLocation {
 };
 
 struct ReportThread {
-  Tid id;
-  ThreadID os_id;
-  bool running;
-  ThreadType thread_type;
-  char *name;
-  Tid parent_tid;
-  StackID stack_id;
-  ReportStack *stack;
-  bool suppressable;
+  Tid id = kInvalidTid;
+  ThreadID os_id = 0;
+  bool running = false;
+  ThreadType thread_type = ThreadType::Regular;
+  char* name = nullptr;
+  Tid parent_tid = kInvalidTid;
+  StackID stack_id = 0;
+  ReportStack* stack = nullptr;
+  bool suppressable = false;
 };
 
 struct ReportMutex {
-  int id;
-  uptr addr;
-  StackID stack_id;
-  ReportStack *stack;
+  int id = 0;
+  uptr addr = 0;
+  StackID stack_id = 0;
+  ReportStack* stack = nullptr;
 };
 
 struct AddedLocationAddr {
-  uptr addr;
-  usize locs_idx;
+  uptr addr = 0;
+  usize locs_idx = 0;
+};
+
+struct AddedStack {
+  StackTrace stack_trace;
+  bool suppressable = false;
 };
 
 class ReportDesc {
  public:
-  ReportType typ;
-  uptr tag;
+  ReportType typ = ReportTypeRace;
+  uptr tag = kExternalTagNone;
   Vector<ReportStack*> stacks;
+  Vector<AddedStack> added_stacks;
   Vector<ReportMop*> mops;
   Vector<ReportLocation*> locs;
   Vector<AddedLocationAddr> added_location_addrs;
   Vector<ReportMutex*> mutexes;
   Vector<ReportThread*> threads;
   Vector<Tid> unique_tids;
-  ReportStack *sleep;
-  int count;
+  ReportStack* sleep = nullptr;
+  StackID sleep_stack_id = 0;
+  int count = 0;
   int signum = 0;
 
   ReportDesc();
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
index c6e7dce5ecb181..7328caab0a9186 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.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
index a4bf790d5980bc..5b4e5362779e78 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
@@ -440,6 +440,8 @@ class ScopedReport {
   ScopedIgnoreInterceptors ignore_interceptors_;
   ScopedErrorReportLock lock_;
 
+  void AddThreadLocked(const ThreadContext* tctx, bool suppressable = false);
+
   ScopedReport(const ScopedReport&) = delete;
   void operator=(const ScopedReport&) = delete;
 };
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
index 271412f4c40885..8d5bad370a5a0e 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,13 @@ 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));
-  // Release locks before symbolizing and outputting the report to avoid
-  // deadlocks.
-  {
-    ThreadRegistryLock l(&ctx->thread_registry);
-    new (rep) ScopedReport(typ);
-    rep->AddMutex(addr, creation_stack_id);
-    VarSizeStackTrace trace;
-    ObtainCurrentStack(thr, pc, &trace);
-    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();
+  VarSizeStackTrace trace;
+  ObtainCurrentStack(thr, pc, &trace);
+  ScopedReport rep(typ);
+  rep.AddMutex(addr, creation_stack_id);
+  rep.AddStack(trace, true);
+  rep.AddLocation(addr, 1);
+  OutputReport(thr, rep);
 }
 
 static void RecordMutexLock(ThreadState *thr, uptr pc, uptr addr,
@@ -538,38 +527,28 @@ 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));
-  // Release locks before symbolizing and outputting the report to avoid
-  // deadlocks.
-  {
-    ThreadRegistryLock l(&ctx->thread_registry);
-    new (rep) ScopedReport(ReportTypeDeadlock);
-    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);
-    }
-    uptr dummy_pc = 0x42;
-    for (int i = 0; i < r->n; i++) {
-      for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
-        u32 stk = r->loop[i].stk[j];
-        StackTrace stack;
-        if (stk && stk != kInvalidStackID) {
-          stack = StackDepotGet(stk);
-        } else {
-          // Sometimes we fail to extract the stack trace (FIXME: investigate),
-          // but we should still produce some stack trace in the report.
-          stack = StackTrace(&dummy_pc, 1);
-        }
-        rep->AddStack(stack, true);
+  uptr dummy_pc = 0x42;
+  ScopedReport rep(ReportTypeDeadlock);
+  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);
+  }
+  for (int i = 0; i < r->n; i++) {
+    for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
+      u32 stk = r->loop[i].stk[j];
+      StackTrace stack;
+      if (stk && stk != kInvalidStackID) {
+        stack = StackDepotGet(stk);
+      } else {
+        // Sometimes we fail to extract the stack trace (FIXME: investigate),
+        // but we should still produce some stack trace in the report.
+        stack = StackTrace(&dummy_pc, 1);
       }
+      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,
@@ -590,24 +569,15 @@ void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
       return;
   }
 
-  // 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.
-  {
-    ThreadRegistryLock l0(&ctx->thread_registry);
-    new (rep) ScopedReport(ReportTypeMutexDestroyLocked);
-    rep->AddMutex(addr, creation_stack_id);
-    VarSizeStackTrace trace;
-    ObtainCurrentStack(thr, pc, &trace);
-    rep->AddStack(trace, true);
-    rep->AddStack(last_lock_stack, true);
-    rep->AddLocation(addr, 1);
-  }
-  OutputReport(thr, *rep);
+  VarSizeStackTrace trace;
+  ObtainCurrentStack(thr, pc, &trace);
 
-  // Need to manually destroy this because we used placement new to allocate
-  rep->~ScopedReport();
+  ScopedReport rep(ReportTypeMutexDestroyLocked);
+  rep.AddMutex(addr, creation_stack_id);
+  rep.AddStack(trace, true);
+  rep.AddStack(last_lock_stack, true);
+  rep.AddLocation(addr, 1);
+  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 be444eec3a4b78..1d76a886dc5361 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) {
 }
 
 ScopedReport::ScopedReport(ReportType typ, uptr tag) {
-  ctx->thread_registry.CheckLocked();
+  CheckedMutex::CheckNoLocks();
   rep_ = New<ReportDesc>();
   rep_->typ = typ;
   rep_->tag = tag;
@@ -173,9 +173,7 @@ ScopedReport::ScopedReport(ReportType typ, uptr tag) {
 ScopedReport::~ScopedReport() { DestroyAndFree(rep_); }
 
 void ScopedReport::AddStack(StackTrace stack, bool suppressable) {
-  ReportStack **rs = rep_->stacks.PushBack();
-  *rs = SymbolizeStack(stack);
-  (*rs)->suppressable = suppressable;
+  rep_->added_stacks.PushBack({stack, suppressable});
 }
 
 void ScopedReport::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
@@ -202,6 +200,15 @@ void ScopedReport::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
 }
 
 void ScopedReport::SymbolizeStackElems() {
+  // symbolize stacks
+  for (usize i = 0, size = rep_->added_stacks.Size(); i < size; i++) {
+    AddedStack& as = rep_->added_stacks[i];
+    ReportStack* rs = SymbolizeStack(as.stack_trace);
+    if (rs)
+      rs->suppressable = as.suppressable;
+    rep_->stacks.PushBack(rs);
+  }
+
   // symbolize memory ops
   for (usize i = 0, size = rep_->mops.Size(); i < size; i++) {
     ReportMop *mop = rep_->mops[i];
@@ -249,6 +256,10 @@ void ScopedReport::SymbolizeStackElems() {
     ReportMutex *rm = rep_->mutexes[i];
     rm->stack = SymbolizeStackId(rm->stack_id);
   }
+
+#if !SANITIZER_GO
+  rep_->sleep = SymbolizeStackId(rep_->sleep_stack_id);
+#endif
 }
 
 void ScopedReport::AddUniqueTid(Tid unique_tid) {
@@ -256,6 +267,13 @@ void ScopedReport::AddUniqueTid(Tid unique_tid) {
 }
 
 void ScopedReport::AddThread(const ThreadContext* tctx, bool suppressable) {
+  ThreadRegistryLock l(&ctx->thread_registry);
+  AddThreadLocked(tctx, suppressable);
+}
+
+void ScopedReport::AddThreadLocked(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;
@@ -273,12 +291,6 @@ void ScopedReport::AddThread(const ThreadContext* tctx, bool suppressable) {
 }
 
 #if !SANITIZER_GO
-static ThreadContext *FindThreadByTidLocked(Tid tid) {
-  ctx->thread_registry.CheckLocked();
-  return static_cast<ThreadContext *>(
-      ctx->thread_registry.GetThreadLocked(tid));
-}
-
 static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) {
   uptr addr = (uptr)arg;
   ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
@@ -305,10 +317,10 @@ ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
 #endif
 
 void ScopedReport::AddThread(Tid tid, bool suppressable) {
-#if !SANITIZER_GO
-  if (const ThreadContext *tctx = FindThreadByTidLocked(tid))
-    AddThread(tctx, suppressable);
-#endif
+  ThreadRegistryLock l(&ctx->thread_registry);
+  if (const auto* tctx = static_cast<ThreadContext*>(
+          ctx->thread_registry.GetThreadLocked(tid)))
+    AddThreadLocked(tctx, suppressable);
 }
 
 int ScopedReport::AddMutex(uptr addr, StackID creation_stack_id) {
@@ -366,12 +378,15 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
     return;
   }
   bool is_stack = false;
-  if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
-    auto *loc = New<ReportLocation>();
-    loc->type = is_stack ? ReportLocationStack : ReportLocationTLS;
-    loc->tid = tctx->tid;
-    rep_->locs.PushBack(loc);
-    AddThread(tctx);
+  {
+    ThreadRegistryLock l(&ctx->thread_registry);
+    if (ThreadContext* tctx = IsThreadStackOrTls(addr, &is_stack)) {
+      auto* loc = New<ReportLocation>();
+      loc->type = is_stack ? ReportLocationStack : ReportLocationTLS;
+      loc->tid = tctx->tid;
+      rep_->locs.PushBack(loc);
+      AddThreadLocked(tctx);
+    }
   }
 #endif
   rep_->added_location_addrs.PushBack({addr, rep_->locs.Size()});
@@ -380,7 +395,7 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
 
 #if !SANITIZER_GO
 void ScopedReport::AddSleep(StackID stack_id) {
-  rep_->sleep = SymbolizeStackId(stack_id);
+  rep_->sleep_stack_id = stack_id;
 }
 #endif
 
@@ -671,6 +686,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);
@@ -804,10 +820,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.
@@ -821,57 +833,48 @@ 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;
     }
+  }
 
-    new (rep) ScopedReport(rep_typ, tag);
-    for (uptr i = 0; i < kMop; i++)
-      rep->AddMemoryAccess(addr, tags[i], s[i], tids[i], traces[i], mset[i]);
+  ScopedReport rep(rep_typ, tag);
+  for (uptr i = 0; i < kMop; 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);
-    }
+  for (uptr i = 0; i < kMop; i++) rep.AddThread(tids[i]);
 
-    rep->AddLocation(addr_min, addr_max - addr_min);
-
-    if (flags()->print_full_thread_history) {
-      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.AddLocation(addr_min, addr_max - addr_min);
+
+  if (flags()->print_full_thread_history) {
+    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;
+      rep.AddThread(parent_tid);
     }
+  }
 
 #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);
+  if (!((typ0 | typ1) & kAccessFree) &&
+      s[1].epoch() <= thr->last_sleep_clock.Get(s[1].sid()))
+    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 afc6b8d4a0b1cd..da9e56a5b8f8d6 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -96,20 +96,10 @@ 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));
-    // Release locks before symbolizing and outputting the report to avoid
-    // deadlocks.
-    {
-      ThreadRegistryLock l(&ctx->thread_registry);
-      new (rep) ScopedReport(ReportTypeThreadLeak);
-      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();
+    ScopedReport rep(ReportTypeThreadLeak);
+    rep.AddThread(leaks[i].tctx, true);
+    rep.SetCount(leaks[i].count);
+    OutputReport(thr, rep);
   }
 #endif
 }

>From 35887d5baac4fd7eae56506e827cccb4634cef17 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Sat, 3 Oct 2026 22:00:32 -0700
Subject: [PATCH 2/2] Address review: drop EnterSymbolizer CheckNoLocks, fix
 commit messages

Created using spr 1.3.7
---
 compiler-rt/lib/tsan/rtl/tsan_symbolize.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/compiler-rt/lib/tsan/rtl/tsan_symbolize.cpp b/compiler-rt/lib/tsan/rtl/tsan_symbolize.cpp
index 51028e9ce59415..b382b63247e8ed 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_symbolize.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_symbolize.cpp
@@ -22,7 +22,6 @@
 namespace __tsan {
 
 void EnterSymbolizer() {
-  CheckedMutex::CheckNoLocks();
   ThreadState *thr = cur_thread();
   CHECK(!thr->in_symbolizer);
   thr->in_symbolizer = true;



More information about the llvm-commits mailing list