| """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): | |
| 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", | |
| ) |