[compiler-rt] [NFCI] ASAN/TSAN stack descriptions available independent of report printing (PR #200736)
Andrew Haberlandt via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 1 00:02:40 PDT 2026
https://github.com/ndrewh created https://github.com/llvm/llvm-project/pull/200736
This refactors the various functions for printing ASAN/TSAN report components into PrintX and DescribeX, where DescribeX can be used to obtain a (unformatted) string description of a report component e.g. the string `Location is heap block of size %zu at %p allocated by %s:`.
This will make it easier to do something like #200526 downstream.
>From 68714bc055759e0922befc0ac90dbcc8776dd7e9 Mon Sep 17 00:00:00 2001
From: Andrew Haberlandt <ahaberlandt at apple.com>
Date: Sun, 31 May 2026 23:51:06 -0700
Subject: [PATCH] [NFC] Make ASAN/TSAN stack descriptions independent of report
printing
This refactors the various functions for printing ASAN/TSAN report
components into PrintX and DescribeX, where DescribeX can be used
to obtain a (unformatted) string description of a report component
e.g. the string `Location is heap block of size %zu at %p allocated by %s:`.
This will make it easier to do something like #200526 downstream.
---
compiler-rt/lib/asan/asan_descriptions.cpp | 32 +++--
compiler-rt/lib/tsan/rtl/tsan_report.cpp | 140 +++++++++++++--------
2 files changed, 110 insertions(+), 62 deletions(-)
diff --git a/compiler-rt/lib/asan/asan_descriptions.cpp b/compiler-rt/lib/asan/asan_descriptions.cpp
index 551b819a4c436..839f9a1ffae33 100644
--- a/compiler-rt/lib/asan/asan_descriptions.cpp
+++ b/compiler-rt/lib/asan/asan_descriptions.cpp
@@ -36,6 +36,25 @@ AsanThreadIdAndName::AsanThreadIdAndName(u32 tid)
asanThreadRegistry().CheckLocked();
}
+// Writes a color-free, single-line description of a heap allocation or
+// deallocation stack, e.g. "freed by thread T1 here:".
+static void DescribeAllocStack(const char *verb, AsanThreadContext *thread,
+ InternalScopedString *s) {
+ s->AppendF("%s by thread %s here:", verb,
+ AsanThreadIdAndName(thread).c_str());
+}
+
+// Prints the description produced by DescribeAllocStack (decorated and followed
+// by a newline) and then the given stack trace.
+static void PrintAllocStack(const char *verb, AsanThreadContext *thread,
+ const StackTrace &stack) {
+ Decorator d;
+ InternalScopedString s;
+ DescribeAllocStack(verb, thread, &s);
+ Printf("%s%s%s\n", d.Allocation(), s.data(), d.Default());
+ stack.Print();
+}
+
// Prints this thread and, if flags()->print_full_thread_history, its ancestors
void DescribeThread(AsanThreadContext *context) {
while (true) {
@@ -424,21 +443,14 @@ void HeapAddressDescription::Print() const {
AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);
- Decorator d;
AsanThreadContext *free_thread = nullptr;
if (free_tid != kInvalidTid) {
free_thread = GetThreadContextByTidLocked(free_tid);
- Printf("%sfreed by thread %s here:%s\n", d.Allocation(),
- AsanThreadIdAndName(free_thread).c_str(), d.Default());
- StackTrace free_stack = GetStackTraceFromId(free_stack_id);
- free_stack.Print();
- Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(),
- AsanThreadIdAndName(alloc_thread).c_str(), d.Default());
+ PrintAllocStack("freed", free_thread, GetStackTraceFromId(free_stack_id));
+ PrintAllocStack("previously allocated", alloc_thread, alloc_stack);
} else {
- Printf("%sallocated by thread %s here:%s\n", d.Allocation(),
- AsanThreadIdAndName(alloc_thread).c_str(), d.Default());
+ PrintAllocStack("allocated", alloc_thread, alloc_stack);
}
- alloc_stack.Print();
DescribeThread(GetCurrentThread());
if (free_thread) DescribeThread(free_thread);
DescribeThread(alloc_thread);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_report.cpp
index 17fd3064fb85a..c6f9c09cd2934 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_report.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_report.cpp
@@ -139,68 +139,88 @@ static const char *ExternalMopDesc(bool first, bool write) {
: (write ? "Previous modifying" : "Previous read-only");
}
-static void PrintMop(const ReportMop *mop, bool first) {
- Decorator d;
+static void DescribeMop(const ReportMop *mop, bool first,
+ InternalScopedString *s) {
char thrbuf[kThreadBufSize];
- Printf("%s", d.Access());
if (mop->external_tag == kExternalTagNone) {
- Printf(" %s of size %d at %p by %s",
- MopDesc(first, mop->write, mop->atomic), mop->size,
- (void *)mop->addr, thread_name(thrbuf, mop->tid));
+ s->AppendF(" %s of size %d at %p by %s",
+ MopDesc(first, mop->write, mop->atomic), mop->size,
+ (void *)mop->addr, thread_name(thrbuf, mop->tid));
} else {
const char *object_type = GetObjectTypeFromTag(mop->external_tag);
if (object_type == nullptr)
object_type = "external object";
- Printf(" %s access of %s at %p by %s",
- ExternalMopDesc(first, mop->write), object_type,
- (void *)mop->addr, thread_name(thrbuf, mop->tid));
+ s->AppendF(" %s access of %s at %p by %s",
+ ExternalMopDesc(first, mop->write), object_type,
+ (void *)mop->addr, thread_name(thrbuf, mop->tid));
}
+}
+
+static void PrintMop(const ReportMop *mop, bool first) {
+ Decorator d;
+ Printf("%s", d.Access());
+ InternalScopedString s;
+ DescribeMop(mop, first, &s);
+ Printf("%s", s.data());
PrintMutexSet(mop->mset);
Printf(":\n");
Printf("%s", d.Default());
PrintStack(mop->stack);
}
-static void PrintLocation(const ReportLocation *loc) {
- Decorator d;
+// Writes the (uncolored) location description and returns whether a stack
+// trace should follow (true only for Heap and FD locations).
+static bool DescribeLocation(const ReportLocation *loc,
+ InternalScopedString *s) {
char thrbuf[kThreadBufSize];
bool print_stack = false;
- Printf("%s", d.Location());
if (loc->type == ReportLocationGlobal) {
const DataInfo &global = loc->global;
if (global.size != 0)
- Printf(" Location is global '%s' of size %zu at %p (%s+0x%zx)\n\n",
- global.name, global.size, reinterpret_cast<void *>(global.start),
- StripModuleName(global.module), global.module_offset);
+ s->AppendF(" Location is global '%s' of size %zu at %p (%s+0x%zx)",
+ global.name, global.size,
+ reinterpret_cast<void *>(global.start),
+ StripModuleName(global.module), global.module_offset);
else
- Printf(" Location is global '%s' at %p (%s+0x%zx)\n\n", global.name,
- reinterpret_cast<void *>(global.start),
- StripModuleName(global.module), global.module_offset);
+ s->AppendF(" Location is global '%s' at %p (%s+0x%zx)", global.name,
+ reinterpret_cast<void *>(global.start),
+ StripModuleName(global.module), global.module_offset);
} else if (loc->type == ReportLocationHeap) {
- char thrbuf[kThreadBufSize];
const char *object_type = GetObjectTypeFromTag(loc->external_tag);
if (!object_type) {
- Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
- loc->heap_chunk_size,
- reinterpret_cast<void *>(loc->heap_chunk_start),
- thread_name(thrbuf, loc->tid));
+ s->AppendF(" Location is heap block of size %zu at %p allocated by %s:",
+ loc->heap_chunk_size,
+ reinterpret_cast<void *>(loc->heap_chunk_start),
+ thread_name(thrbuf, loc->tid));
} else {
- Printf(" Location is %s of size %zu at %p allocated by %s:\n",
- object_type, loc->heap_chunk_size,
- reinterpret_cast<void *>(loc->heap_chunk_start),
- thread_name(thrbuf, loc->tid));
+ s->AppendF(" Location is %s of size %zu at %p allocated by %s:",
+ object_type, loc->heap_chunk_size,
+ reinterpret_cast<void *>(loc->heap_chunk_start),
+ thread_name(thrbuf, loc->tid));
}
print_stack = true;
} else if (loc->type == ReportLocationStack) {
- Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
+ s->AppendF(" Location is stack of %s.", thread_name(thrbuf, loc->tid));
} else if (loc->type == ReportLocationTLS) {
- Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
+ s->AppendF(" Location is TLS of %s.", thread_name(thrbuf, loc->tid));
} else if (loc->type == ReportLocationFD) {
- Printf(" Location is file descriptor %d %s by %s at:\n", loc->fd,
- loc->fd_closed ? "destroyed" : "created",
- thread_name(thrbuf, loc->tid));
+ s->AppendF(" Location is file descriptor %d %s by %s at:", loc->fd,
+ loc->fd_closed ? "destroyed" : "created",
+ thread_name(thrbuf, loc->tid));
print_stack = true;
}
+ return print_stack;
+}
+
+static void PrintLocation(const ReportLocation *loc) {
+ Decorator d;
+ Printf("%s", d.Location());
+ InternalScopedString s;
+ bool print_stack = DescribeLocation(loc, &s);
+ Printf("%s", s.data());
+ // Locations with a following stack are separated from it by a single
+ // newline; the others provide their own trailing blank line.
+ Printf(print_stack ? "\n" : "\n\n");
Printf("%s", d.Default());
if (print_stack)
PrintStack(loc->stack);
@@ -218,39 +238,55 @@ static void PrintMutexShortWithAddress(const ReportMutex *rm,
reinterpret_cast<void *>(rm->addr), d.Default(), after);
}
+static void DescribeMutex(const ReportMutex *rm, InternalScopedString *s) {
+ s->AppendF(" Mutex M%u (%p) created at:", rm->id,
+ reinterpret_cast<void *>(rm->addr));
+}
+
static void PrintMutex(const ReportMutex *rm) {
Decorator d;
Printf("%s", d.Mutex());
- Printf(" Mutex M%u (%p) created at:\n", rm->id,
- reinterpret_cast<void *>(rm->addr));
+ InternalScopedString s;
+ DescribeMutex(rm, &s);
+ Printf("%s\n", s.data());
Printf("%s", d.Default());
PrintStack(rm->stack);
}
-static void PrintThread(const ReportThread *rt) {
- Decorator d;
- if (rt->id == kMainTid) // Little sense in describing the main thread.
- return;
- Printf("%s", d.ThreadDescription());
- Printf(" Thread T%d", rt->id);
- if (rt->name && rt->name[0] != '\0')
- Printf(" '%s'", rt->name);
+// Writes the (uncolored) description for a non-main thread and returns whether
+// a stack trace should follow (false for GCD worker threads).
+static bool DescribeThread(const ReportThread *rt, InternalScopedString *s) {
char thrbuf[kThreadBufSize];
+ s->AppendF(" Thread T%d", rt->id);
+ if (rt->name && rt->name[0] != '\0')
+ s->AppendF(" '%s'", rt->name);
const char *thread_status = rt->running ? "running" : "finished";
if (rt->thread_type == ThreadType::Worker) {
- Printf(" (tid=%llu, %s) is a GCD worker thread\n", rt->os_id,
- thread_status);
- Printf("\n");
- Printf("%s", d.Default());
- return;
+ s->AppendF(" (tid=%llu, %s) is a GCD worker thread", rt->os_id,
+ thread_status);
+ return false;
}
- Printf(" (tid=%llu, %s) created by %s", rt->os_id, thread_status,
- thread_name(thrbuf, rt->parent_tid));
+ s->AppendF(" (tid=%llu, %s) created by %s", rt->os_id, thread_status,
+ thread_name(thrbuf, rt->parent_tid));
if (rt->stack)
- Printf(" at:");
- Printf("\n");
+ s->Append(" at:");
+ return true;
+}
+
+static void PrintThread(const ReportThread *rt) {
+ Decorator d;
+ if (rt->id == kMainTid) // Little sense in describing the main thread.
+ return;
+ Printf("%s", d.ThreadDescription());
+ InternalScopedString s;
+ bool print_stack = DescribeThread(rt, &s);
+ Printf("%s", s.data());
+ // A thread with a following stack is separated from it by a single newline;
+ // otherwise we emit the trailing blank line here.
+ Printf(print_stack ? "\n" : "\n\n");
Printf("%s", d.Default());
- PrintStack(rt->stack);
+ if (print_stack)
+ PrintStack(rt->stack);
}
static void PrintSleep(const ReportStack *s) {
More information about the llvm-commits
mailing list