Files
Pavel Labath 021b93e7e7 [lldb] Make TestSBModule a no-SHARED_BUILD_TESTCASE (#188265)
Building libfoo.a twice (quickly) confuses the cache in
ObjectContainerBSDArchive (which works only with second granularity),
and causes the second test to fail due to accessing wrong parts of the
file.

I'm also making this a NO_DEBUG_INFO_TESTCASE as the debug info
distinction is not particularly relevant for this test. Technically,
this would be enough as the collision is between two debug info variants
of the same test case, but SHARED_BUILD_TESTCASE=False seems like a more
principled fix.
2026-05-29 07:29:37 +02:00

178 lines
6.5 KiB
Python

"""Test the SBDModule APIs."""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import os, signal, subprocess
class SBModuleAPICase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
# Shared build causes collisions in the ObjectArchive file cache
SHARED_BUILD_TESTCASE = False
def setUp(self):
TestBase.setUp(self)
self.background_pid = None
def tearDown(self):
TestBase.tearDown(self)
if self.background_pid:
os.kill(self.background_pid, signal.SIGKILL)
@skipIfRemote
def test_GetObjectName(self):
"""Test the SBModule::GetObjectName() method"""
self.build()
exe = self.getBuildArtifact("a.out")
libfoo_path = self.getBuildArtifact("libfoo.a")
target_exe = self.dbg.CreateTarget(exe)
self.assertTrue(target_exe.IsValid(), "Target for a.out is valid")
# Test that the executable module has no object name (usually the first module in the target)
exe_module = target_exe.GetModuleAtIndex(0)
self.assertTrue(exe_module.IsValid(), "Executable module is valid")
self.assertIsNone(
exe_module.GetObjectName(), "a.out should have no object name"
)
# check archive member names
module_specs = lldb.SBModuleSpecList.GetModuleSpecifications(libfoo_path)
self.assertGreater(
module_specs.GetSize(), 0, "Archive should have at least one module spec"
)
self.assertEqual(
module_specs[0].GetObjectName(),
module_specs.GetSpecAtIndex(0).GetObjectName(),
"subscript [0] matches GetSpecAtIndex(0)",
)
self.assertEqual(
module_specs[-1].GetObjectName(),
module_specs.GetSpecAtIndex(module_specs.GetSize() - 1).GetObjectName(),
"subscript [-1] matches last item",
)
found = set()
expected = {"a.o", "b.o"}
for i in range(module_specs.GetSize()):
spec = module_specs.GetSpecAtIndex(i)
obj_name = spec.GetObjectName()
self.assertIsInstance(obj_name, str)
self.assertIn(obj_name, expected, f"Unexpected object name: {obj_name}")
# create a module from the arhive using the sepc
module = lldb.SBModule(spec)
self.assertTrue(module.IsValid(), "Module is valid")
self.assertTrue(module.IsValid(), f"Module for {obj_name} is valid")
self.assertEqual(
module.GetObjectName(), obj_name, f"Object name for {obj_name} matches"
)
found.add(obj_name)
self.assertEqual(found, expected, "Did not find all expected archive members")
@skipUnlessDarwin
@skipIfRemote
def test_module_is_file_backed(self):
"""Test the SBModule::IsFileBacked() method"""
self.build()
target, _, _, _ = lldbutil.run_to_source_breakpoint(
self, "// break here", lldb.SBFileSpec("main.c")
)
self.assertGreater(target.GetNumModules(), 0)
main_module = target.GetModuleAtIndex(0)
self.assertEqual(main_module.GetFileSpec().GetFilename(), "a.out")
self.assertTrue(
main_module.IsFileBacked(), "The module should be backed by a file on disk"
)
self.dbg.DeleteTarget(target)
self.assertEqual(self.dbg.GetNumTargets(), 0)
exe = self.getBuildArtifact("a.out")
background_process = subprocess.Popen([exe])
self.assertTrue(background_process, "process is not valid")
self.background_pid = background_process.pid
os.unlink(exe)
target = self.dbg.CreateTarget("")
self.assertEqual(self.dbg.GetNumTargets(), 1)
error = lldb.SBError()
process = target.AttachToProcessWithID(
self.dbg.GetListener(), self.background_pid, error
)
self.assertTrue(error.Success() and process, PROCESS_IS_VALID)
main_module = target.FindModule(lldb.SBFileSpec("a.out"))
self.assertIsNotNone(main_module)
self.assertFalse(
main_module.IsFileBacked(),
"The module should not be backed by a file on disk.",
)
error = process.Destroy()
self.assertSuccess(
error, "couldn't destroy process %s" % background_process.pid
)
@skipIfRemote
def test_module_spec_list_indexing(self):
"""Test that SBModuleSpecList supports Pythonic indexing."""
self.build()
libfoo_path = self.getBuildArtifact("libfoo.a")
specs = lldb.SBModuleSpecList.GetModuleSpecifications(libfoo_path)
count = specs.GetSize()
self.assertGreater(count, 0, "Archive should have at least one module spec")
# Integer indexing: positive indices
for i in range(count):
self.assertEqual(
str(specs[i]),
str(specs.GetSpecAtIndex(i)),
"specs[%d] should match GetSpecAtIndex(%d)" % (i, i),
)
# Integer indexing: negative indices
self.assertEqual(
str(specs[-1]),
str(specs.GetSpecAtIndex(count - 1)),
"specs[-1] should match last element",
)
self.assertEqual(
str(specs[-count]),
str(specs.GetSpecAtIndex(0)),
"specs[-count] should match first element",
)
# Integer indexing: out of bounds raises IndexError
self.assertRaises(IndexError, lambda: specs[count])
self.assertRaises(IndexError, lambda: specs[-count - 1])
# Unsupported key type raises TypeError
self.assertRaises(TypeError, lambda: specs[1.5])
# String indexing: lookup by file basename
spec0 = specs.GetSpecAtIndex(0)
basename = spec0.GetFileSpec().GetFilename()
if basename:
found = specs[basename]
self.assertIsNotNone(found, "Should find spec by basename '%s'" % basename)
self.assertEqual(
found.GetFileSpec().GetFilename(),
basename,
"Found spec basename should match",
)
# String indexing: lookup by partial path (endswith matching)
fullpath = str(spec0.GetFileSpec())
if fullpath:
found = specs[fullpath]
self.assertIsNotNone(found, "Should find spec by full path '%s'" % fullpath)
# String indexing: missing basename returns None
self.assertIsNone(
specs["nonexistent_file.xyz"],
"Lookup of nonexistent basename should return None",
)