-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[lldb] Add support for PC-less scripted frames #170805
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-lldb Author: Med Ismail Bennani (medismailben) ChangesScripted 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:
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]
|
|
✅ With the latest revision this PR passed the Python code formatter. |
🐧 Linux x64 Test Results
✅ The build succeeded and all tests passed. |
e8bcacf to
52ad202
Compare
lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
Show resolved
Hide resolved
52ad202 to
1fdd0de
Compare
|
I tried removing the formatting from the |
1fdd0de to
41f0ae8
Compare
jimingham
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
41f0ae8 to
c801e6f
Compare
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>
c801e6f to
a9ff5bd
Compare
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)
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)
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
FormatEntityto detect and suppress invalid address display for PC-less frames, adds fallback to frame methods when symbol context is unavailable, and modifiesStackFrame::GetSymbolContextto 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.