https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/158811
>From 1fcece5ddecb84c87c341b1ed79bc7aebc982a1a Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Thu, 4 Dec 2025 10:22:11 -0800 Subject: [PATCH] [lldb] Add support for synthetic LineEntry objects without valid address ranges Scripted frames that materialize Python functions are PC-less by design, meaning they don't have valid address ranges. Previously, LineEntry::IsValid() required both a valid address range and a line number, preventing scripted frames from creating valid line entries for these synthetic stack frames. This change introduces an `is_synthetic` flag that gets set when LineEntry objects are created or modified through the SBAPI (specifically via SetLine). When this flag is set, IsValid() no longer requires a valid address range, only a valid line number. This is risk-free because the flag is only set for LineEntry objects created through the SBAPI, which are primarily used by scripted processes and frames. Regular debug information-derived line entries continue to require valid address ranges. Signed-off-by: Med Ismail Bennani <[email protected]> --- lldb/include/lldb/Symbol/LineEntry.h | 5 + lldb/source/API/SBLineEntry.cpp | 2 + lldb/source/Symbol/LineEntry.cpp | 5 +- .../dummy_scripted_process.py | 14 ++ .../python_api/sblineentry/TestSBLineEntry.py | 144 ++++++++++++++++++ 5 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 lldb/test/API/python_api/sblineentry/TestSBLineEntry.py diff --git a/lldb/include/lldb/Symbol/LineEntry.h b/lldb/include/lldb/Symbol/LineEntry.h index a61b72f253dd7..e370d9accdd6c 100644 --- a/lldb/include/lldb/Symbol/LineEntry.h +++ b/lldb/include/lldb/Symbol/LineEntry.h @@ -136,6 +136,11 @@ struct LineEntry { /// The section offset address range for this line entry. AddressRange range; + /// This gets set for LineEntries created from the SBAPI, where we don't + /// necessary have a valid address range. When set, LineEntry::IsValid doesn't + /// check the `range` validity. + bool is_synthetic; + /// The source file, possibly mapped by the target.source-map setting. SupportFileNSP file_sp; diff --git a/lldb/source/API/SBLineEntry.cpp b/lldb/source/API/SBLineEntry.cpp index 0f4936f32a074..1c9401d420545 100644 --- a/lldb/source/API/SBLineEntry.cpp +++ b/lldb/source/API/SBLineEntry.cpp @@ -132,6 +132,8 @@ void SBLineEntry::SetLine(uint32_t line) { LLDB_INSTRUMENT_VA(this, line); ref().line = line; + if (!ref().range.IsValid()) + ref().is_synthetic = true; } void SBLineEntry::SetColumn(uint32_t column) { diff --git a/lldb/source/Symbol/LineEntry.cpp b/lldb/source/Symbol/LineEntry.cpp index c941a6927cb93..a68dbe63cf3f8 100644 --- a/lldb/source/Symbol/LineEntry.cpp +++ b/lldb/source/Symbol/LineEntry.cpp @@ -14,7 +14,7 @@ using namespace lldb_private; LineEntry::LineEntry() - : range(), file_sp(std::make_shared<SupportFile>()), + : range(), is_synthetic(false), file_sp(std::make_shared<SupportFile>()), original_file_sp(std::make_shared<SupportFile>()), is_start_of_statement(0), is_start_of_basic_block(0), is_prologue_end(0), is_epilogue_begin(0), is_terminal_entry(0) {} @@ -33,7 +33,8 @@ void LineEntry::Clear() { } bool LineEntry::IsValid() const { - return range.GetBaseAddress().IsValid() && line != LLDB_INVALID_LINE_NUMBER; + return (range.GetBaseAddress().IsValid() || is_synthetic) && + line != LLDB_INVALID_LINE_NUMBER; } bool LineEntry::DumpStopContext(Stream *s, bool show_fullpaths) const { 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 835267221ddb4..a9459682e70a8 100644 --- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py @@ -74,6 +74,20 @@ def __init__(self, process, args): self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "bar")) self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "foo")) + cwd = os.path.dirname(os.path.abspath(__file__)) + + le = lldb.SBLineEntry() + le.SetFileSpec(lldb.SBFileSpec(os.path.join(cwd, "baz.cpp"), True)) + le.SetLine(9) + le.SetColumn(10) + + sym_ctx = lldb.SBSymbolContext() + sym_ctx.SetLineEntry(le) + + self.frames.append( + DummyScriptedFrame(self, args, len(self.frames), "baz", sym_ctx) + ) + def get_thread_id(self) -> int: return 0x19 diff --git a/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py b/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py new file mode 100644 index 0000000000000..89da61f58956c --- /dev/null +++ b/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py @@ -0,0 +1,144 @@ +""" +Test SBLineEntry APIs, particularly synthetic line entries. +""" + +import os +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class SBLineEntryTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_synthetic_line_entry(self): + """Test that synthetic LineEntry objects (created via SBAPI) can be + valid without a valid address range and can be set in SBSymbolContext.""" + + # Test creating a synthetic line entry via SBAPI + line_entry = lldb.SBLineEntry() + self.assertFalse( + line_entry.IsValid(), "Default constructed line entry should be invalid" + ) + + # Set line number - this should mark the line entry as synthetic + line_entry.SetLine(42) + self.assertTrue( + line_entry.IsValid(), + "Line entry should be valid after setting line, even without address", + ) + self.assertEqual(line_entry.GetLine(), 42) + + # Set file and column + file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "test.cpp"), True) + line_entry.SetFileSpec(file_spec) + line_entry.SetColumn(10) + + self.assertEqual(line_entry.GetColumn(), 10) + self.assertEqual(line_entry.GetFileSpec().GetFilename(), "test.cpp") + + # Verify address range is still invalid (synthetic) + start_addr = line_entry.GetStartAddress() + self.assertFalse( + start_addr.IsValid(), "Synthetic line entry should not have valid address" + ) + + # Test setting synthetic line entry in symbol context + sym_ctx = lldb.SBSymbolContext() + sym_ctx.SetLineEntry(line_entry) + + retrieved_line_entry = sym_ctx.GetLineEntry() + self.assertTrue( + retrieved_line_entry.IsValid(), "Retrieved line entry should be valid" + ) + self.assertEqual(retrieved_line_entry.GetLine(), 42) + self.assertEqual(retrieved_line_entry.GetColumn(), 10) + self.assertEqual(retrieved_line_entry.GetFileSpec().GetFilename(), "test.cpp") + + def test_line_entry_validity_without_address(self): + """Test that line entries created via SBAPI are valid without addresses.""" + + line_entry = lldb.SBLineEntry() + + # Initially invalid + self.assertFalse(line_entry.IsValid()) + + # Still invalid with just a file spec + file_spec = lldb.SBFileSpec("foo.cpp", True) + line_entry.SetFileSpec(file_spec) + self.assertFalse( + line_entry.IsValid(), "Line entry should be invalid without line number" + ) + + # Valid once line number is set (marks as synthetic) + line_entry.SetLine(100) + self.assertTrue( + line_entry.IsValid(), "Line entry should be valid with line number set" + ) + + # Verify no valid address range + self.assertFalse(line_entry.GetStartAddress().IsValid()) + self.assertFalse(line_entry.GetEndAddress().IsValid()) + + def test_line_entry_column(self): + """Test setting and getting column information on synthetic line entries.""" + + line_entry = lldb.SBLineEntry() + line_entry.SetLine(50) + + # Default column should be 0 + self.assertEqual(line_entry.GetColumn(), 0) + + # Set column + line_entry.SetColumn(25) + self.assertEqual(line_entry.GetColumn(), 25) + + # Verify line entry is still valid + self.assertTrue(line_entry.IsValid()) + + def test_non_synthetic_line_entry_requires_line_number(self): + """Test that non-synthetic line entries with addresses still require a line number to be valid.""" + + # A line entry is always invalid without a line number, regardless of whether it has an address + line_entry = lldb.SBLineEntry() + self.assertFalse( + line_entry.IsValid(), "Line entry should be invalid without line number" + ) + + # Even with a file spec, it's still invalid + file_spec = lldb.SBFileSpec("test.cpp", True) + line_entry.SetFileSpec(file_spec) + self.assertFalse( + line_entry.IsValid(), "Line entry should be invalid without line number" + ) + + # Only after setting a line number does it become valid + line_entry.SetLine(42) + self.assertTrue( + line_entry.IsValid(), "Line entry should be valid with line number" + ) + + def test_symbol_context_with_synthetic_line_entry(self): + """Test that SBSymbolContext correctly stores and retrieves synthetic line entries.""" + + # Create a synthetic line entry + line_entry = lldb.SBLineEntry() + line_entry.SetLine(123) + line_entry.SetColumn(45) + file_spec = lldb.SBFileSpec("source.cpp", True) + line_entry.SetFileSpec(file_spec) + + # Create symbol context and set line entry + sym_ctx = lldb.SBSymbolContext() + sym_ctx.SetLineEntry(line_entry) + + # Retrieve and verify + retrieved = sym_ctx.GetLineEntry() + self.assertTrue(retrieved.IsValid()) + self.assertEqual(retrieved.GetLine(), 123) + self.assertEqual(retrieved.GetColumn(), 45) + self.assertEqual(retrieved.GetFileSpec().GetFilename(), "source.cpp") + + # Verify it's still synthetic (no valid address) + self.assertFalse(retrieved.GetStartAddress().IsValid()) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
