Skip to content

Conversation

@medismailben
Copy link
Member

@medismailben medismailben commented Dec 5, 2025

Scripted frames that materialize Python functions or other non-native code are PC-less by design, meaning they don't have valid program counter values. Previously, these frames would display invalid addresses (0xffffffffffffffff) in backtrace output.

This patch updates FormatEntity to detect and suppress invalid address display for PC-less frames, adds fallback to frame methods when symbol context is unavailable, and modifies StackFrame::GetSymbolContext to skip PC-based symbol resolution for invalid addresses.

The changes enable PC-less frames to display cleanly with proper function names, file paths, and line numbers, and allow for source display of foreign sources (like Python). Includes comprehensive test coverage demonstrating frames pointing to Python source files.

@llvmbot
Copy link
Member

llvmbot commented Dec 5, 2025

@llvm/pr-subscribers-lldb

Author: Med Ismail Bennani (medismailben)

Changes

Scripted frames that materialize Python functions or other non-native code are PC-less by design, meaning they don't have valid program counter values. Previously, these frames would display invalid addresses (0xffffffffffffffff) in backtrace output.

This patch updates FormatEntity to detect and suppress invalid address display for PC-less frames, adds fallback to frame methods when symbol context is unavailable, and modifies StackFrame::GetSymbolContext to skip PC-based symbol resolution for invalid addresses.

The changes enable PC-less frames to display cleanly with proper function names, file paths, and line numbers, and allow for source display of foreign sources (like Python). Includes comprehensive test coverage demonstrating frames pointing to Python source files.


Patch is 27.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/170805.diff

10 Files Affected:

  • (modified) lldb/include/lldb/Target/StackID.h (+1)
  • (modified) lldb/source/Core/CoreProperties.td (+28-8)
  • (modified) lldb/source/Core/FormatEntity.cpp (+106-64)
  • (modified) lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp (+8-4)
  • (modified) lldb/source/Target/StackFrame.cpp (+8-1)
  • (modified) lldb/source/Target/StackFrameList.cpp (+5)
  • (modified) lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py (+125)
  • (added) lldb/test/API/functionalities/scripted_frame_provider/python_helper.py (+36)
  • (modified) lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py (+96)
  • (modified) lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py (+25)
diff --git a/lldb/include/lldb/Target/StackID.h b/lldb/include/lldb/Target/StackID.h
index 18461533d648a..3f6a83b5e2fa5 100644
--- a/lldb/include/lldb/Target/StackID.h
+++ b/lldb/include/lldb/Target/StackID.h
@@ -52,6 +52,7 @@ class StackID {
 
 protected:
   friend class StackFrame;
+  friend class SyntheticStackFrameList;
 
   void SetPC(lldb::addr_t pc, Process *process);
   void SetCFA(lldb::addr_t cfa, Process *process);
diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td
index 1be911c291703..8d8343aedb3e2 100644
--- a/lldb/source/Core/CoreProperties.td
+++ b/lldb/source/Core/CoreProperties.td
@@ -57,10 +57,20 @@ let Definition = "debugger" in {
     Global,
     DefaultStringValue<"{${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\\\\n}{${function.changed}\\\\n{${module.file.basename}`}{${function.name-without-args}}:\\\\n}{${ansi.fg.yellow}${current-pc-arrow}${ansi.normal} }${addr-file-or-load}{ <${function.concrete-only-addr-offset-no-padding}>}: ">,
     Desc<"The default disassembly format string to use when disassembling instruction sequences.">;
-  def FrameFormat: Property<"frame-format", "FormatEntity">,
-    Global,
-    DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
-    Desc<"The default frame format string to use when displaying stack frame information for threads.">;
+  def FrameFormat
+      : Property<"frame-format", "FormatEntity">,
+        Global,
+        DefaultStringValue<
+            "frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ "
+            "${module.file.basename}{`}}{${function.name-with-args}{${frame.no-"
+            "debug}${function.pc-offset}}}{ at "
+            "${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg."
+            "yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line."
+            "column}${ansi.normal}}}${frame.kind}{${function.is-optimized} "
+            "[opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} "
+            "[artificial]}\\\\n">,
+        Desc<"The default frame format string to use when displaying stack "
+             "frame information for threads.">;
   def NotiftVoid: Property<"notify-void", "Boolean">,
     Global,
     DefaultFalse,
@@ -233,10 +243,20 @@ let Definition = "debugger" in {
     Global,
     DefaultTrue,
     Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">;
-  def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">,
-    Global,
-    DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
-    Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">;
+  def FrameFormatUnique
+      : Property<"frame-format-unique", "FormatEntity">,
+        Global,
+        DefaultStringValue<
+            "frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ "
+            "${module.file.basename}{`}}{${function.name-without-args}{${frame."
+            "no-debug}${function.pc-offset}}}{ at "
+            "${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg."
+            "yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line."
+            "column}${ansi.normal}}}${frame.kind}{${function.is-optimized} "
+            "[opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} "
+            "[artificial]}\\\\n">,
+        Desc<"The default frame format string to use when displaying stack "
+             "frame information for threads from thread backtrace unique.">;
   def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">,
     Global,
     DefaultFalse,
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index c528a14fa76d0..9ddbf59ebdfe1 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -1636,6 +1636,14 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
     if (sc) {
       Module *module = sc->module_sp.get();
       if (module) {
+        // Add a space before module name if frame has a valid PC
+        // (so "PC module" but ": module" for PC-less frames)
+        if (exe_ctx) {
+          StackFrame *frame = exe_ctx->GetFramePtr();
+          if (frame && frame->GetFrameCodeAddress().IsValid()) {
+            s.PutChar(' ');
+          }
+        }
         if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number))
           return true;
       }
@@ -1684,10 +1692,11 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
       StackFrame *frame = exe_ctx->GetFramePtr();
       if (frame) {
         const Address &pc_addr = frame->GetFrameCodeAddress();
-        if (pc_addr.IsValid() || frame->IsSynthetic()) {
+        if (pc_addr.IsValid()) {
           if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false))
             return true;
-        }
+        } else if (frame->IsSynthetic())
+          return true;
       }
     }
     return false;
@@ -1808,70 +1817,91 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
     return initial_function;
 
   case Entry::Type::FunctionName: {
-    if (!sc)
-      return false;
+    if (sc) {
+      Language *language_plugin = nullptr;
+      bool language_plugin_handled = false;
+      StreamString ss;
 
-    Language *language_plugin = nullptr;
-    bool language_plugin_handled = false;
-    StreamString ss;
+      if (sc->function)
+        language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+      else if (sc->symbol)
+        language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
 
-    if (sc->function)
-      language_plugin = Language::FindPlugin(sc->function->GetLanguage());
-    else if (sc->symbol)
-      language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+      if (language_plugin)
+        language_plugin_handled = language_plugin->GetFunctionDisplayName(
+            *sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
 
-    if (language_plugin)
-      language_plugin_handled = language_plugin->GetFunctionDisplayName(
-          *sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
+      if (language_plugin_handled) {
+        s << ss.GetString();
+        return true;
+      }
 
-    if (language_plugin_handled) {
-      s << ss.GetString();
-      return true;
+      const char *name = sc->GetPossiblyInlinedFunctionName()
+                             .GetName(Mangled::NamePreference::ePreferDemangled)
+                             .AsCString();
+      if (name) {
+        s.PutCString(name);
+        return true;
+      }
     }
 
-    const char *name = sc->GetPossiblyInlinedFunctionName()
-                           .GetName(Mangled::NamePreference::ePreferDemangled)
-                           .AsCString();
-    if (!name)
-      return false;
-
-    s.PutCString(name);
-
-    return true;
+    // Fallback to frame methods if available
+    if (exe_ctx) {
+      StackFrame *frame = exe_ctx->GetFramePtr();
+      if (frame) {
+        const char *name = frame->GetFunctionName();
+        if (name) {
+          s.PutCString(name);
+          return true;
+        }
+      }
+    }
+    return false;
   }
 
   case Entry::Type::FunctionNameNoArgs: {
-    if (!sc)
-      return false;
-
-    Language *language_plugin = nullptr;
-    bool language_plugin_handled = false;
-    StreamString ss;
-    if (sc->function)
-      language_plugin = Language::FindPlugin(sc->function->GetLanguage());
-    else if (sc->symbol)
-      language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
-
-    if (language_plugin)
-      language_plugin_handled = language_plugin->GetFunctionDisplayName(
-          *sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
-          ss);
+    if (sc) {
+      Language *language_plugin = nullptr;
+      bool language_plugin_handled = false;
+      StreamString ss;
+      if (sc->function)
+        language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+      else if (sc->symbol)
+        language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+
+      if (language_plugin)
+        language_plugin_handled = language_plugin->GetFunctionDisplayName(
+            *sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
+            ss);
+
+      if (language_plugin_handled) {
+        s << ss.GetString();
+        return true;
+      }
 
-    if (language_plugin_handled) {
-      s << ss.GetString();
-      return true;
+      const char *name =
+          sc->GetPossiblyInlinedFunctionName()
+              .GetName(
+                  Mangled::NamePreference::ePreferDemangledWithoutArguments)
+              .AsCString();
+      if (name) {
+        s.PutCString(name);
+        return true;
+      }
     }
 
-    const char *name =
-        sc->GetPossiblyInlinedFunctionName()
-            .GetName(Mangled::NamePreference::ePreferDemangledWithoutArguments)
-            .AsCString();
-    if (!name)
-      return false;
-
-    s.PutCString(name);
-
-    return true;
+    // Fallback to frame methods if available
+    if (exe_ctx) {
+      StackFrame *frame = exe_ctx->GetFramePtr();
+      if (frame) {
+        const char *name = frame->GetFunctionName();
+        if (name) {
+          s.PutCString(name);
+          return true;
+        }
+      }
+    }
+    return false;
   }
 
   case Entry::Type::FunctionPrefix:
@@ -1898,13 +1928,26 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
   }
 
   case Entry::Type::FunctionNameWithArgs: {
-    if (!sc)
-      return false;
+    if (sc) {
+      if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
+        return true;
 
-    if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
-      return true;
+      if (HandleFunctionNameWithArgs(s, exe_ctx, *sc))
+        return true;
+    }
 
-    return HandleFunctionNameWithArgs(s, exe_ctx, *sc);
+    // Fallback to frame methods if available
+    if (exe_ctx) {
+      StackFrame *frame = exe_ctx->GetFramePtr();
+      if (frame) {
+        const char *name = frame->GetDisplayFunctionName();
+        if (name) {
+          s.PutCString(name);
+          return true;
+        }
+      }
+    }
+    return false;
   }
   case Entry::Type::FunctionMangledName: {
     if (!sc)
@@ -1951,6 +1994,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
                                           frame->GetFrameCodeAddress(), false,
                                           false, false))
           return true;
+        else if (frame->IsSynthetic())
+          return true;
       }
     }
     return false;
@@ -1975,11 +2020,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
 
   case Entry::Type::LineEntryFile:
     if (sc && sc->line_entry.IsValid()) {
-      Module *module = sc->module_sp.get();
-      if (module) {
-        if (DumpFile(s, sc->line_entry.GetFile(), (FileKind)entry.number))
-          return true;
-      }
+      if (DumpFile(s, sc->line_entry.GetFile(), (FileKind)entry.number))
+        return true;
     }
     return false;
 
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
index 265bc28a8957f..748ca5cf03414 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
@@ -11,10 +11,15 @@
 
 #include "lldb/Core/Address.h"
 #include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Host/FileSystem.h"
 #include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/CompileUnit.h"
 #include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
 #include "lldb/Target/ExecutionContext.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/RegisterContext.h"
@@ -98,9 +103,8 @@ ScriptedFrame::Create(ThreadSP thread_sp,
 
   std::optional<SymbolContext> maybe_sym_ctx =
       scripted_frame_interface->GetSymbolContext();
-  if (maybe_sym_ctx) {
+  if (maybe_sym_ctx)
     sc = *maybe_sym_ctx;
-  }
 
   StructuredData::DictionarySP reg_info =
       scripted_frame_interface->GetRegisterInfo();
@@ -162,7 +166,7 @@ const char *ScriptedFrame::GetFunctionName() {
   CheckInterpreterAndScriptObject();
   std::optional<std::string> function_name = GetInterface()->GetFunctionName();
   if (!function_name)
-    return nullptr;
+    return StackFrame::GetFunctionName();
   return ConstString(function_name->c_str()).AsCString();
 }
 
@@ -171,7 +175,7 @@ const char *ScriptedFrame::GetDisplayFunctionName() {
   std::optional<std::string> function_name =
       GetInterface()->GetDisplayFunctionName();
   if (!function_name)
-    return nullptr;
+    return StackFrame::GetDisplayFunctionName();
   return ConstString(function_name->c_str()).AsCString();
 }
 
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index ca3d4a1a29b59..3bbb851b88007 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -331,6 +331,13 @@ StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) {
     // following the function call instruction...
     Address lookup_addr(GetFrameCodeAddressForSymbolication());
 
+    // For PC-less frames (e.g., scripted frames), skip PC-based symbol
+    // resolution and preserve any already-populated SymbolContext fields.
+    if (!lookup_addr.IsValid()) {
+      m_flags.Set(resolve_scope | resolved);
+      return m_sc;
+    }
+
     if (m_sc.module_sp) {
       // We have something in our stack frame symbol context, lets check if we
       // haven't already tried to lookup one of those things. If we haven't
@@ -2057,7 +2064,7 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
       disasm_display = debugger.GetStopDisassemblyDisplay();
 
       GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry);
-      if (m_sc.comp_unit && m_sc.line_entry.IsValid()) {
+      if (m_sc.comp_unit || m_sc.line_entry.IsValid()) {
         have_debuginfo = true;
         if (source_lines_before > 0 || source_lines_after > 0) {
           SupportFileNSP source_file_sp = m_sc.line_entry.file_sp;
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index 5d1a8a8370414..896a760f61d26 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -64,6 +64,8 @@ SyntheticStackFrameList::SyntheticStackFrameList(
 
 bool SyntheticStackFrameList::FetchFramesUpTo(
     uint32_t end_idx, InterruptionControl allow_interrupt) {
+
+  size_t num_synthetic_frames = 0;
   // Check if the thread has a synthetic frame provider.
   if (auto provider_sp = m_thread.GetFrameProvider()) {
     // Use the synthetic frame provider to generate frames lazily.
@@ -81,6 +83,9 @@ bool SyntheticStackFrameList::FetchFramesUpTo(
         break;
       }
       StackFrameSP frame_sp = *frame_or_err;
+      if (frame_sp->IsSynthetic())
+        frame_sp->GetStackID().SetCFA(num_synthetic_frames++,
+                                      GetThread().GetProcess().get());
       // Set the frame list weak pointer so ExecutionContextRef can resolve
       // the frame without calling Thread::GetStackFrameList().
       frame_sp->m_frame_list_wp = shared_from_this();
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
index caed94f5f93da..06e55e79d538c 100644
--- a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
+++ b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
@@ -426,3 +426,128 @@ def test_circular_dependency_fix(self):
             # These calls should not trigger circular dependency
             pc = frame.GetPC()
             self.assertNotEqual(pc, 0, f"Frame {i} should have valid PC")
+
+    def test_python_source_frames(self):
+        """Test that frames can point to Python source files and display properly."""
+        self.build()
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False
+        )
+
+        # Get original frame count
+        original_frame_count = thread.GetNumFrames()
+        self.assertGreaterEqual(
+            original_frame_count, 2, "Should have at least 2 real frames"
+        )
+
+        # Import the provider
+        script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py")
+        self.runCmd("command script import " + script_path)
+
+        # Register the PythonSourceFrameProvider
+        error = lldb.SBError()
+        provider_id = target.RegisterScriptedFrameProvider(
+            "test_frame_providers.PythonSourceFrameProvider",
+            lldb.SBStructuredData(),
+            error,
+        )
+        self.assertTrue(error.Success(), f"Failed to register provider: {error}")
+        self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero")
+
+        # Verify we have 3 more frames (Python frames)
+        new_frame_count = thread.GetNumFrames()
+        self.assertEqual(
+            new_frame_count,
+            original_frame_count + 3,
+            "Should have original frames + 3 Python frames",
+        )
+
+        # Verify first three frames are Python source frames
+        frame0 = thread.GetFrameAtIndex(0)
+        self.assertIsNotNone(frame0)
+        self.assertEqual(
+            frame0.GetFunctionName(),
+            "compute_fibonacci",
+            "First frame should be compute_fibonacci",
+        )
+        self.assertTrue(frame0.IsSynthetic(), "Frame should be marked as synthetic")
+        # PC-less frames should show invalid address
+        self.assertEqual(
+            frame0.GetPC(),
+            lldb.LLDB_INVALID_ADDRESS,
+            "PC-less frame should have LLDB_INVALID_ADDRESS",
+        )
+
+        frame1 = thread.GetFrameAtIndex(1)
+        self.assertIsNotNone(frame1)
+        self.assertEqual(
+            frame1.GetFunctionName(),
+            "process_data",
+            "Second frame should be process_data",
+        )
+        self.assertTrue(frame1.IsSynthetic(), "Frame should be marked as synthetic")
+
+        frame2 = thread.GetFrameAtIndex(2)
+        self.assertIsNotNone(frame2)
+        self.assertEqual(
+            frame2.GetFunctionName(), "main", "Third frame should be main"
+        )
+        self.assertTrue(frame2.IsSynthetic(), "Frame should be marked as synthetic")
+
+        # Verify line entry information is present
+        line_entry0 = frame0.GetLineEntry()
+        self.assertTrue(
+            line_entry0.IsValid(), "Frame 0 should have a valid line entry"
+        )
+        self.assertEqual(
+            line_entry0.GetLine(), 7, "Frame 0 should point to line 7"
+        )
+        file_spec0 = line_entry0.GetFileSpec()
+        self.assertTrue(file_spec0.IsValid(), "Frame 0 should have valid file spec")
+        self.assertEqual(
+            file_spec0.GetFilename(),
+            "python_helper.py",
+            "Frame 0 should point to python_helper.py",
+        )
+
+        line_entry1 = frame1.GetLineEntry()
+        self.assertTrue(
+            line_entry1.IsValid(), "Frame 1 should have a valid line entry"
+        )
+        self.assertEqual(
+           ...
[truncated]

@github-actions
Copy link

github-actions bot commented Dec 5, 2025

✅ With the latest revision this PR passed the Python code formatter.

@github-actions
Copy link

github-actions bot commented Dec 5, 2025

🐧 Linux x64 Test Results

  • 33216 tests passed
  • 507 tests skipped

✅ The build succeeded and all tests passed.

@medismailben
Copy link
Member Author

I tried removing the formatting from the frame-format strings, lets see if clang-format complains.

Copy link
Collaborator

@jimingham jimingham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@medismailben medismailben enabled auto-merge (squash) December 5, 2025 23:45
Scripted frames that materialize Python functions or other non-native
code are PC-less by design, meaning they don't have valid program counter
values. Previously, these frames would display invalid addresses
(0xffffffffffffffff) in backtrace output.

This patch updates FormatEntity to detect and suppress invalid address
display for PC-less frames, adds fallback to frame methods when symbol
context is unavailable, and modifies StackFrame::GetSymbolContext to
skip PC-based symbol resolution for invalid addresses.

The changes enable PC-less frames to display cleanly with proper
function names, file paths, and line numbers, and allow for source
display of foreign sources (like Python). Includes comprehensive
test coverage demonstrating frames pointing to Python source files.

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
@medismailben medismailben merged commit 96c733e into llvm:main Dec 5, 2025
9 of 10 checks passed
medismailben added a commit to medismailben/llvm-project that referenced this pull request Dec 6, 2025
Scripted frames that materialize Python functions or other non-native
code are PC-less by design, meaning they don't have valid program
counter values. Previously, these frames would display invalid addresses
(`0xffffffffffffffff`) in backtrace output.

This patch updates `FormatEntity` to detect and suppress invalid address
display for PC-less frames, adds fallback to frame methods when symbol
context is unavailable, and modifies `StackFrame::GetSymbolContext` to
skip PC-based symbol resolution for invalid addresses.

The changes enable PC-less frames to display cleanly with proper
function names, file paths, and line numbers, and allow for source
display of foreign sources (like Python). Includes comprehensive test
coverage demonstrating frames pointing to Python source files.

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
(cherry picked from commit 96c733e)
medismailben added a commit to medismailben/llvm-project that referenced this pull request Dec 6, 2025
Scripted frames that materialize Python functions or other non-native
code are PC-less by design, meaning they don't have valid program
counter values. Previously, these frames would display invalid addresses
(`0xffffffffffffffff`) in backtrace output.

This patch updates `FormatEntity` to detect and suppress invalid address
display for PC-less frames, adds fallback to frame methods when symbol
context is unavailable, and modifies `StackFrame::GetSymbolContext` to
skip PC-based symbol resolution for invalid addresses.

The changes enable PC-less frames to display cleanly with proper
function names, file paths, and line numbers, and allow for source
display of foreign sources (like Python). Includes comprehensive test
coverage demonstrating frames pointing to Python source files.

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
(cherry picked from commit 96c733e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants