[compiler-rt] [sanitizer_common] [Darwin] Add inline frame support for AtosSymbolizer (PR #170815)
Andrew Haberlandt via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 5 00:44:28 PST 2025
https://github.com/ndrewh created https://github.com/llvm/llvm-project/pull/170815
When the `symbolize_inline_frames` option is set, we should use the `-i` atos option and show inline frames.
>From 77f1cb17523bae27050be97e30549cf2a0b8f8fe Mon Sep 17 00:00:00 2001
From: Andrew Haberlandt <ahaberlandt at apple.com>
Date: Fri, 5 Dec 2025 00:34:34 -0800
Subject: [PATCH] Add inline frame support for AtosSymbolizer
---
.../sanitizer_symbolizer_mac.cpp | 89 +++++++++++++------
...-stack-trace-in-code-loaded-after-fork.cpp | 2 +-
.../TestCases/symbolize_pc_inline.cpp | 13 ++-
3 files changed, 68 insertions(+), 36 deletions(-)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp
index 88536fc4e6222..ebfab5a032fe9 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp
@@ -78,13 +78,20 @@ class AtosSymbolizerProcess final : public SymbolizerProcess {
}
bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
- return (length >= 1 && buffer[length - 1] == '\n');
+ if (common_flags()->symbolize_inline_frames) {
+ return length >= 2 && buffer[length - 1] == '\n' &&
+ buffer[length - 2] == '\n';
+ } else {
+ return length >= 1 && buffer[length - 1] == '\n';
+ }
}
void GetArgV(const char *path_to_binary,
const char *(&argv)[kArgVMax]) const override {
int i = 0;
argv[i++] = path_to_binary;
+ if (common_flags()->symbolize_inline_frames)
+ argv[i++] = "-i";
argv[i++] = "-p";
argv[i++] = &pid_str_[0];
if (GetMacosAlignedVersion() == MacosVersion(10, 9)) {
@@ -102,12 +109,12 @@ class AtosSymbolizerProcess final : public SymbolizerProcess {
#undef K_ATOS_ENV_VAR
-static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
- char **out_module, char **out_file, uptr *line,
- uptr *start_address) {
+static bool ParseCommandOutput(const char** str, uptr addr, char** out_name,
+ char** out_module, char** out_file, uptr* line,
+ uptr* start_address) {
// Trim ending newlines.
char *trim;
- ExtractTokenUpToDelimiter(str, "\n", &trim);
+ *str = ExtractTokenUpToDelimiter(*str, "\n", &trim);
// The line from `atos` is in one of these formats:
// myfunction (in library.dylib) (sourcefile.c:17)
@@ -161,31 +168,57 @@ bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
char command[32];
internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
const char *buf = process_->SendCommand(command);
- if (!buf) return false;
- uptr line;
- uptr start_address = AddressInfo::kUnknown;
- if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
- &stack->info.file, &line, &start_address)) {
- Report("WARNING: atos failed to symbolize address \"0x%zx\"\n", addr);
+ if (!buf)
return false;
- }
- stack->info.line = (int)line;
-
- if (start_address == AddressInfo::kUnknown) {
- // Fallback to dladdr() to get function start address if atos doesn't report
- // it.
- Dl_info info;
- int result = dladdr((const void *)addr, &info);
- if (result)
- start_address = reinterpret_cast<uptr>(info.dli_saddr);
- }
- // Only assign to `function_offset` if we were able to get the function's
- // start address and we got a sensible `start_address` (dladdr doesn't always
- // ensure that `addr >= sym_addr`).
- if (start_address != AddressInfo::kUnknown && addr >= start_address) {
- stack->info.function_offset = addr - start_address;
+ SymbolizedStack* last = stack;
+ bool top_frame = true;
+
+ while (*buf != '\n') {
+ uptr line;
+ uptr start_address = AddressInfo::kUnknown;
+
+ SymbolizedStack* cur;
+ if (top_frame) {
+ cur = stack;
+ } else {
+ cur = SymbolizedStack::New(stack->info.address);
+ cur->info.FillModuleInfo(stack->info.module, stack->info.module_offset,
+ stack->info.module_arch);
+ last->next = cur;
+ last = cur;
+ }
+
+ if (!ParseCommandOutput(&buf, addr, &cur->info.function, &cur->info.module,
+ &cur->info.file, &line, &start_address)) {
+ Report("WARNING: atos failed to symbolize buf address \"0x%zx\"\n", addr);
+ break;
+ // return false;
+ }
+ cur->info.line = (int)line;
+
+ if (top_frame && start_address == AddressInfo::kUnknown) {
+ // Fallback to dladdr() to get function start address if atos doesn't
+ // report it.
+ Dl_info info;
+ int result = dladdr((const void*)addr, &info);
+ if (result)
+ start_address = reinterpret_cast<uptr>(info.dli_saddr);
+ }
+
+ // Only assign to `function_offset` if we were able to get the function's
+ // start address and we got a sensible `start_address` (dladdr doesn't
+ // always ensure that `addr >= sym_addr`).
+ if (start_address != AddressInfo::kUnknown && addr >= start_address) {
+ cur->info.function_offset = addr - start_address;
+ }
+
+ if (!common_flags()->symbolize_inline_frames)
+ break;
+
+ top_frame = false;
}
+
return true;
}
@@ -195,7 +228,7 @@ bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
const char *buf = process_->SendCommand(command);
if (!buf) return false;
- if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
+ if (!ParseCommandOutput(&buf, addr, &info->name, &info->module, nullptr,
nullptr, &info->start)) {
process_ = nullptr;
return false;
diff --git a/compiler-rt/test/sanitizer_common/TestCases/Darwin/print-stack-trace-in-code-loaded-after-fork.cpp b/compiler-rt/test/sanitizer_common/TestCases/Darwin/print-stack-trace-in-code-loaded-after-fork.cpp
index ec8155365496e..7e57f2b1fbfa5 100644
--- a/compiler-rt/test/sanitizer_common/TestCases/Darwin/print-stack-trace-in-code-loaded-after-fork.cpp
+++ b/compiler-rt/test/sanitizer_common/TestCases/Darwin/print-stack-trace-in-code-loaded-after-fork.cpp
@@ -53,7 +53,7 @@ int main(int argc, char **argv) {
PrintStackFnPtrTy PrintStackFnPtr = (PrintStackFnPtrTy)dlsym(handle, "PrintStack");
assert(PrintStackFnPtr);
// Check that the symbolizer is told examine the child process.
- // CHECK: Launching Symbolizer process: {{.+}}atos -p [[CHILD_PID]]
+ // CHECK: Launching Symbolizer process: {{.+}}atos -i -p [[CHILD_PID]]
// CHECK-STACKTRACE: #2{{( *0x.* *in *)?}} main {{.*}}print-stack-trace-in-code-loaded-after-fork.cpp:[[@LINE+1]]
PrintStackFnPtr();
return 0;
diff --git a/compiler-rt/test/sanitizer_common/TestCases/symbolize_pc_inline.cpp b/compiler-rt/test/sanitizer_common/TestCases/symbolize_pc_inline.cpp
index e95ef324db652..d52f5d0846b85 100644
--- a/compiler-rt/test/sanitizer_common/TestCases/symbolize_pc_inline.cpp
+++ b/compiler-rt/test/sanitizer_common/TestCases/symbolize_pc_inline.cpp
@@ -1,10 +1,8 @@
// RUN: %clangxx -O3 %s -o %t
-// RUN: %env_tool_opts=strip_path_prefix=/TestCases/ %run %t 2>&1 | FileCheck %s
-// RUN: %env_tool_opts=strip_path_prefix=/TestCases/:symbolize_inline_frames=0 %run %t 2>&1 | FileCheck %s --check-prefixes=NOINLINE
+// RUN: %env_tool_opts=strip_path_prefix=/TestCases/:verbosity=3 %run %t 2>&1 | FileCheck %s
+// RUN: %env_tool_opts=strip_path_prefix=/TestCases/:symbolize_inline_frames=0 %run %t 2>&1 | FileCheck %s --check-prefixes=%if darwin %{NOINLINE-ATOS%} %else %{NOINLINE%}
// RUN: %env_tool_opts=strip_path_prefix=/TestCases/:symbolize_inline_frames=1 %run %t 2>&1 | FileCheck %s
-// XFAIL: darwin
-
#include <sanitizer/common_interface_defs.h>
#include <stdio.h>
#include <string.h>
@@ -19,14 +17,15 @@ __attribute__((noinline)) static void Symbolize() {
}
// NOINLINE: {{0x[0-9a-f]+}} in main symbolize_pc_inline.cpp:[[@LINE+2]]
-// CHECK: [[ADDR:0x[0-9a-f]+]] in C2 symbolize_pc_inline.cpp:[[@LINE+1]]
+// CHECK: [[ADDR:0x[0-9a-f]+]] in C2{{(\(\))?}} symbolize_pc_inline.cpp:[[@LINE+1]]
static inline void C2() { Symbolize(); }
-// CHECK: [[ADDR]] in C3 symbolize_pc_inline.cpp:[[@LINE+1]]
+// CHECK: [[ADDR]] in C3{{(\(\))?}} symbolize_pc_inline.cpp:[[@LINE+1]]
static inline void C3() { C2(); }
-// CHECK: [[ADDR]] in C4 symbolize_pc_inline.cpp:[[@LINE+1]]
+// CHECK: [[ADDR]] in C4{{(\(\))?}} symbolize_pc_inline.cpp:[[@LINE+1]]
static inline void C4() { C3(); }
+// NOINLINE-ATOS: {{0x[0-9a-f]+}} in main symbolize_pc_inline.cpp:[[@LINE+2]]
// CHECK: [[ADDR]] in main symbolize_pc_inline.cpp:[[@LINE+1]]
int main() { C4(); }
More information about the llvm-commits
mailing list