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

Reply via email to