[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