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. Relaxing this requirement is necessary since `SBSymbolContext::SetLineEntry` will first check if the LineEntry is valid and discard it otherwise. This change introduces an `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 <ismail@bennani.ma>
145 lines
5.3 KiB
Python
145 lines
5.3 KiB
Python
"""
|
|
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())
|