https://github.com/medismailben created https://github.com/llvm/llvm-project/pull/170762
This adds support for scripted frames without valid PC addresses, allowing them to display properly without showing 0xffffffffffffffff. Changes include: - Make StackFrame::GetSymbolContext() resilient to PC-less frames by adding an early return when the lookup address is invalid, preserving any already-populated SymbolContext fields. - Populate module and compile unit for scripted frames by searching existing modules for matching LineEntry files, falling back to the scripted module - Create synthetic CompileUnits with language type deduced from the script - Fix frame formatting to avoid extra spaces for PC-less frames by removing the space from the frame format string and adding it conditionally in FormatEntity::Format only when the frame has a valid PC. - Add fallbacks in FormatEntity for FunctionName, FunctionNameNoArgs, and FunctionNameWithArgs to use StackFrame methods when SymbolContext lookup fails, enabling proper function name display for scripted frames. - Update test to include a scripted frame pointing to Python source with a LineEntry referencing the Python file containing my_python_function(). >From cb00990f113e8c9a87c749fcab4cbb7adf7a4069 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Thu, 4 Dec 2025 13:02:18 -0800 Subject: [PATCH 1/2] [lldb] Add support for PC-less scripted frames This adds support for scripted frames without valid PC addresses, allowing them to display properly without showing 0xffffffffffffffff. Changes include: - Make StackFrame::GetSymbolContext() resilient to PC-less frames by adding an early return when the lookup address is invalid, preserving any already-populated SymbolContext fields. - Populate module and compile unit for scripted frames by searching existing modules for matching LineEntry files, falling back to the scripted module - Create synthetic CompileUnits with language type deduced from the script - Fix frame formatting to avoid extra spaces for PC-less frames by removing the space from the frame format string and adding it conditionally in FormatEntity::Format only when the frame has a valid PC. - Add fallbacks in FormatEntity for FunctionName, FunctionNameNoArgs, and FunctionNameWithArgs to use StackFrame methods when SymbolContext lookup fails, enabling proper function name display for scripted frames. - Update test to include a scripted frame pointing to Python source with a LineEntry referencing the Python file containing my_python_function(). Signed-off-by: Med Ismail Bennani <[email protected]> --- lldb/source/Core/FormatEntity.cpp | 161 +++++++++++------- .../Process/scripted/ScriptedFrame.cpp | 107 +++++++++++- lldb/source/Target/StackFrame.cpp | 7 + .../dummy_scripted_process.py | 25 +++ 4 files changed, 239 insertions(+), 61 deletions(-) diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index c528a14fa76d0..0b7025409d952 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) diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp index 265bc28a8957f..ee0c36a6b6edb 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" @@ -102,6 +107,104 @@ ScriptedFrame::Create(ThreadSP thread_sp, sc = *maybe_sym_ctx; } + // Ensure the symbol context has at least the scripted frame implementation + // for scripted frames without a valid PC or custom symbol context + if (!sc.module_sp) { + // First, check if we have a valid line entry with a file, and try to find + // an existing module that contains that file + if (sc.line_entry.IsValid() && sc.line_entry.file_sp) { + const FileSpec &line_file = sc.line_entry.GetFile(); + + // Search through all modules to find one that contains this file + ModuleList &module_list = process_sp->GetTarget().GetImages(); + const size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; ++i) { + ModuleSP module_sp = module_list.GetModuleAtIndex(i); + if (module_sp) { + SymbolFile *sym_file = module_sp->GetSymbolFile(); + if (sym_file) { + // Check if this module has compile units with our file + const uint32_t num_comp_units = sym_file->GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < num_comp_units; ++cu_idx) { + CompUnitSP cu_sp = sym_file->GetCompileUnitAtIndex(cu_idx); + if (cu_sp && + FileSpec::Equal(cu_sp->GetPrimarySupportFile()->GetSpecOnly(), + line_file, false)) { + sc.module_sp = module_sp; + sc.comp_unit = cu_sp.get(); + break; + } + } + if (sc.module_sp) + break; + } + } + } + } + + // If no existing module found, try to create one from the scripted module + // path + if (!sc.module_sp) { + auto module_path_or_err = + scripted_frame_interface->GetScriptedModulePath(); + if (!module_path_or_err) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Script), module_path_or_err.takeError(), + "Failed to get scripted module path: {0}."); + } else { + FileSpec module_path = *module_path_or_err; + if (FileSystem::Instance().Exists(module_path)) { + FileSpec module_file_spec = FileSpec(module_path); + const ArchSpec &arch_spec = process_sp->GetTarget().GetArchitecture(); + ModuleSP script_module_sp = + std::make_shared<Module>(module_file_spec, arch_spec); + sc.module_sp = script_module_sp; + } + } + + if (!sc.module_sp) { + // Fall back to using the executable module + sc.module_sp = process_sp->GetTarget() + .GetExecutableModulePointer() + ->shared_from_this(); + } + } + } + + // Create a CompileUnit for the scripted frame if we don't have one + if (!sc.comp_unit && sc.module_sp) { + const FileSpec &module_file_spec = sc.module_sp->GetFileSpec(); + if (module_file_spec) { + // Get the script language and convert to LanguageType + ScriptInterpreter *script_interp = + process_sp->GetTarget().GetDebugger().GetScriptInterpreter(); + lldb::LanguageType language = lldb::eLanguageTypeUnknown; + if (script_interp) { + switch (script_interp->GetLanguage()) { + case lldb::eScriptLanguagePython: + language = lldb::eLanguageTypePython; + break; + case lldb::eScriptLanguageLua: + // NOTE: Lua isn't part of the DWARF Source Languages so we cannot use + // it to create a compile unit. + language = lldb::eLanguageTypeUnknown; + break; + default: + language = lldb::eLanguageTypeUnknown; + break; + } + } + + sc.comp_unit = new CompileUnit( + sc.module_sp, + nullptr, // user_data + std::make_shared<SupportFile>(module_file_spec), // support_file + frame_id, // unique ID + language, + eLazyBoolCalculate // not optimized + ); + } + } + StructuredData::DictionarySP reg_info = scripted_frame_interface->GetRegisterInfo(); @@ -162,7 +265,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 +274,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..b0ececc7d0c19 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 diff --git a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py index a9459682e70a8..4ebf940708223 100644 --- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py @@ -8,6 +8,15 @@ from lldb.plugins.scripted_process import ScriptedFrame +def my_python_function(x, y): + """A sample Python function to demonstrate Python source display in scripted frames.""" + result = x + y + if result > 100: + return result * 2 + else: + return result + + class DummyStopHook: def __init__(self, target, args): self.target = target @@ -88,6 +97,22 @@ def __init__(self, process, args): DummyScriptedFrame(self, args, len(self.frames), "baz", sym_ctx) ) + # Add a frame with Python source + code = my_python_function.__code__ + lineno = code.co_firstlineno + col_offset = getattr(code, "co_firstcol_offset", 0) # Python ≥3.11 has column info + py_le = lldb.SBLineEntry() + py_le.SetFileSpec(lldb.SBFileSpec(__file__, True)) + py_le.SetLine(lineno) # Line where my_python_function is defined + py_le.SetColumn(col_offset) + + py_sym_ctx = lldb.SBSymbolContext() + py_sym_ctx.SetLineEntry(py_le) + + self.frames.append( + DummyScriptedFrame(self, args, len(self.frames), "my_python_function", py_sym_ctx) + ) + def get_thread_id(self) -> int: return 0x19 >From c53a4ec2ce4b5a09562130f9b54cd34a8bfab30b Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Mon, 1 Dec 2025 16:31:30 -0800 Subject: [PATCH 2/2] [lldb] Add test for scripted frame provider producing python frames Signed-off-by: Med Ismail Bennani <[email protected]> --- .../TestScriptedFrameProvider.py | 125 ++++++++++++++++++ .../scripted_frame_provider/python_helper.py | 36 +++++ .../test_frame_providers.py | 96 ++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/python_helper.py 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( + line_entry1.GetLine(), 16, "Frame 1 should point to line 16" + ) + + line_entry2 = frame2.GetLineEntry() + self.assertTrue( + line_entry2.IsValid(), "Frame 2 should have a valid line entry" + ) + self.assertEqual( + line_entry2.GetLine(), 27, "Frame 2 should point to line 27" + ) + + # Verify the frames display properly in backtrace + # This tests that PC-less frames don't show 0xffffffffffffffff + self.runCmd("bt") + output = self.res.GetOutput() + + # Should show function names + self.assertIn("compute_fibonacci", output) + self.assertIn("process_data", output) + self.assertIn("main", output) + + # Should show Python file + self.assertIn("python_helper.py", output) + + # Should show line numbers + self.assertIn(":7", output) # compute_fibonacci line + self.assertIn(":16", output) # process_data line + self.assertIn(":27", output) # main line + + # Should NOT show invalid address (0xffffffffffffffff) + self.assertNotIn("0xffffffffffffffff", output.lower()) + + # Verify frame 3 is the original real frame 0 + frame3 = thread.GetFrameAtIndex(3) + self.assertIsNotNone(frame3) + self.assertIn("thread_func", frame3.GetFunctionName()) diff --git a/lldb/test/API/functionalities/scripted_frame_provider/python_helper.py b/lldb/test/API/functionalities/scripted_frame_provider/python_helper.py new file mode 100644 index 0000000000000..27f38165608db --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/python_helper.py @@ -0,0 +1,36 @@ +""" +Sample Python module to demonstrate Python source display in scripted frames. +""" + + +def compute_fibonacci(n): + """Compute the nth Fibonacci number.""" + if n <= 1: + return n + a, b = 0, 1 + for _ in range(n - 1): + a, b = b, a + b + return b + + +def process_data(data): + """Process some data and return result.""" + result = [] + for item in data: + if isinstance(item, int): + result.append(item * 2) + elif isinstance(item, str): + result.append(item.upper()) + return result + + +def main(): + """Main entry point for testing.""" + fib_10 = compute_fibonacci(10) + data = [1, 2, "hello", 3, "world"] + processed = process_data(data) + return fib_10, processed + + +if __name__ == "__main__": + main() diff --git a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py index b9731fdc0a197..6177f4345321c 100644 --- a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py +++ b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py @@ -10,6 +10,7 @@ index to create stackframes """ +import os import lldb from lldb.plugins.scripted_process import ScriptedFrame from lldb.plugins.scripted_frame_provider import ScriptedFrameProvider @@ -220,3 +221,98 @@ def get_frame_at_index(self, index): # Pass through original frames at indices 1, 2, 3, ... return index - 1 return None + + +class PythonSourceFrame(ScriptedFrame): + """Scripted frame that points to Python source code.""" + + def __init__(self, thread, idx, function_name, python_file, line_number): + args = lldb.SBStructuredData() + super().__init__(thread, args) + + self.idx = idx + self.function_name = function_name + self.python_file = python_file + self.line_number = line_number + + def get_id(self): + """Return the frame index.""" + return self.idx + + def get_pc(self): + """PC-less frame - return invalid address.""" + return lldb.LLDB_INVALID_ADDRESS + + def get_function_name(self): + """Return the function name.""" + return self.function_name + + def get_symbol_context(self): + """Return a symbol context with LineEntry pointing to Python source.""" + # Create a LineEntry pointing to the Python source file + line_entry = lldb.SBLineEntry() + line_entry.SetFileSpec(lldb.SBFileSpec(self.python_file, True)) + line_entry.SetLine(self.line_number) + line_entry.SetColumn(0) + + # Create a symbol context with the line entry + sym_ctx = lldb.SBSymbolContext() + sym_ctx.SetLineEntry(line_entry) + + return sym_ctx + + def is_artificial(self): + """Not artificial.""" + return False + + def is_hidden(self): + """Not hidden.""" + return False + + def get_register_context(self): + """No register context for PC-less frames.""" + return None + + +class PythonSourceFrameProvider(ScriptedFrameProvider): + """ + Provider that demonstrates Python source display in scripted frames. + + This provider prepends frames pointing to Python source code, showing + that PC-less frames can display Python source files with proper line + numbers and module/compile unit information. + """ + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + + # Find the python_helper.py file + current_dir = os.path.dirname(os.path.abspath(__file__)) + self.python_file = os.path.join(current_dir, "python_helper.py") + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Provider that prepends frames pointing to Python source" + + def get_frame_at_index(self, index): + """Return Python source frames followed by original frames.""" + if index == 0: + # Frame pointing to compute_fibonacci function (line 7) + return PythonSourceFrame( + self.thread, 0, "compute_fibonacci", self.python_file, 7 + ) + elif index == 1: + # Frame pointing to process_data function (line 16) + return PythonSourceFrame( + self.thread, 1, "process_data", self.python_file, 16 + ) + elif index == 2: + # Frame pointing to main function (line 27) + return PythonSourceFrame( + self.thread, 2, "main", self.python_file, 27 + ) + elif index - 3 < len(self.input_frames): + # Pass through original frames + return index - 3 + return None _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
