| """ | |
| Test lldb-dap module request | |
| """ | |
| import platform | |
| import re | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import line_number | |
| from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase | |
| from lldbsuite.test.tools.lldb_dap.types import ( | |
| CompileUnitsArgs, | |
| LaunchArgs, | |
| ModuleEvent, | |
| ModuleReason, | |
| ) | |
| @skipIfTargetDoesNotSupportSharedLibraries() | |
| @skipIf( | |
| oslist=lldbplatformutil.getDarwinOSTriples(), | |
| bugnumber="https://github.com/llvm/llvm-project/issues/216150", | |
| ) | |
| class TestDAP_module(DAPTestCaseBase): | |
| def run_test(self, symbol_basename: str, expect_debug_info_size: bool): | |
| session = self.build_and_create_session() | |
| program_basename = "a.out.stripped" | |
| program = self.getBuildArtifact(program_basename) | |
| with session.configure(LaunchArgs(program)) as ctx: | |
| # This breakpoint will be resolved only when the libfoo module is loaded. | |
| breakpoints = session.set_function_breakpoints(["foo"]).body.breakpoints | |
| self.assertEqual(len(breakpoints), 1, "expect one breakpoint.") | |
| foo_bp_id = self.expect_not_none(breakpoints[0].id) | |
| self.assertFalse(breakpoints[0].verified) | |
| session.verify_stopped_on_breakpoint(foo_bp_id, after=ctx.process_event) | |
| active_modules = session.get_modules() | |
| program_module = active_modules[program_basename] | |
| self.assertIn( | |
| program_basename, | |
| active_modules, | |
| f"{program_basename} module is in active modules", | |
| ) | |
| self.assertEqual(program_basename, program_module.name) | |
| self.assertIsNotNone(program_module.path, "make sure path is in module") | |
| self.assertEqual(program, program_module.path) | |
| self.assertIsNone( | |
| program_module.symbolFilePath, "Make sure a.out.stripped has no debug info" | |
| ) | |
| symbols_path = self.getBuildArtifact(symbol_basename) | |
| event_before_mod_change = session.last_event() | |
| session.evaluate( | |
| f'''`target symbols add -s "{program}" "{symbols_path}"''', context="repl" | |
| ) | |
| # Make sure we got an update event for the program module when the | |
| # symbols got added. | |
| changed_event = session.verify_next_module_event( | |
| ModuleReason.CHANGED, after=event_before_mod_change | |
| ) | |
| changed_module = changed_event.body.module | |
| self.assertEqual(program_module.name, changed_module.name) | |
| self.assertIsNotNone(changed_module.symbolFilePath) | |
| changed_symbols_path = self.expect_not_none(changed_module.symbolFilePath) | |
| self.assertIn(symbols_path, changed_symbols_path) | |
| if expect_debug_info_size: | |
| changed_debug_size = self.expect_not_none(changed_module.debugInfoSize) | |
| size_regex = re.compile(r"[0-9]+(\.[0-9]*)?[KMG]?B") | |
| self.assertRegex( | |
| changed_debug_size, size_regex, "expect has debug info size" | |
| ) | |
| active_modules = session.get_modules() | |
| program_module = active_modules[program_basename] | |
| self.assertEqual(program_basename, program_module.name) | |
| self.assertEqual(program, program_module.path) | |
| self.assertIsNotNone(program_module.addressRange) | |
| # Collect all the modules added before we changed the program module. | |
| new_module_names = [] | |
| def seen_module_changed_event(event: ModuleEvent): | |
| if event.body.reason == ModuleReason.NEW: | |
| new_module_names.append(event.body.module.name) | |
| return event.seq == changed_event.seq | |
| init_resp = ctx.init_response | |
| session.wait_for_module_event(after=init_resp, until=seen_module_changed_event) | |
| # Make sure we got an event for every active module. | |
| self.assertNotEqual(len(new_module_names), 0) | |
| for module in active_modules: | |
| self.assertIn(module, new_module_names) | |
| session.continue_to_exit() | |
| @skipIfWindows | |
| def test_modules(self): | |
| """ | |
| Mac or linux. | |
| On mac, if we load a.out as our symbol file, we will use DWARF with .o files and we will | |
| have debug symbols, but we won't see any debug info size because all of the DWARF | |
| sections are in .o files. | |
| On other platforms, we expect a.out to have debug info, so we will expect a size. | |
| """ | |
| return self.run_test( | |
| "a.out", expect_debug_info_size=platform.system() != "Darwin" | |
| ) | |
| @requireDarwin | |
| def test_modules_dsym(self): | |
| """ | |
| Darwin only test with dSYM file. | |
| On mac, if we load a.out.dSYM as our symbol file, we will have debug symbols and we | |
| will have DWARF sections added to the module, so we will expect a size. | |
| """ | |
| return self.run_test("a.out.dSYM", expect_debug_info_size=True) | |
| @skipIfWindows | |
| def test_compile_units(self): | |
| session = self.build_and_create_session() | |
| program = self.getBuildArtifact("a.out") | |
| source = "main.cpp" | |
| main_source_path = self.getSourcePath(source) | |
| with session.configure(LaunchArgs(program)) as ctx: | |
| breakpoint1_line = line_number(source, "// breakpoint 1") | |
| bp_ids = session.resolve_source_breakpoints(source, [breakpoint1_line]) | |
| process_event = ctx.process_event | |
| session.verify_stopped_on_breakpoint(bp_ids, after=process_event) | |
| module_id = session.get_modules()["a.out"].id | |
| response = session.send_request(CompileUnitsArgs(module_id)).result() | |
| cu_paths = [cu.compileUnitPath for cu in response.body.compileUnits] | |
| self.assertIn(main_source_path, cu_paths, "Real path to main.cpp matches") | |
| session.continue_to_exit() | |
| @skipIfWindows | |
| def test_modules_paging(self): | |
| """Test modules paging using startModule and moduleCount.""" | |
| session = self.build_and_create_session() | |
| program = self.getBuildArtifact("a.out") | |
| source = "main.cpp" | |
| with session.configure(LaunchArgs(program)) as ctx: | |
| breakpoint1_line = line_number(source, "// breakpoint 1") | |
| bp_ids = session.resolve_source_breakpoints(source, [breakpoint1_line]) | |
| session.verify_stopped_on_breakpoint(bp_ids, after=ctx.process_event) | |
| all_modules = list(session.get_modules().values()) | |
| self.assertGreater(len(all_modules), 1, "Expected multiple modules loaded") | |
| page = session.get_modules(startModule=0, moduleCount=1) | |
| self.assertEqual(len(page), 1) | |
| module = next(iter(page.values())) | |
| self.assertEqual(module.name, all_modules[0].name) | |
| self.assertEqual(module.id, all_modules[0].id) | |
| page = session.get_modules(startModule=1, moduleCount=1) | |
| self.assertEqual(len(page), 1) | |
| module = next(iter(page.values())) | |
| self.assertEqual(module.name, all_modules[1].name) | |
| self.assertEqual(module.id, all_modules[1].id) | |
| page = session.get_modules(startModule=1, moduleCount=100) | |
| self.assertEqual(len(page), len(all_modules) - 1) | |
| session.continue_to_exit() |