| """ |
| Test how many times newly loaded binaries are notified; |
| they should be delivered in batches instead of one-by-one. |
| """ |
| |
| import lldb |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test import lldbutil |
| |
| |
| class ModuleLoadedNotifysTestCase(TestBase): |
| NO_DEBUG_INFO_TESTCASE = True |
| |
| # At least DynamicLoaderDarwin and DynamicLoaderPOSIXDYLD should batch up |
| # notifications about newly added/removed libraries. Other DynamicLoaders may |
| # not be written this way. |
| @skipUnlessPlatform(["linux"] + lldbplatformutil.getDarwinOSTriples()) |
| def setUp(self): |
| # Call super's setUp(). |
| TestBase.setUp(self) |
| # Find the line number to break inside main(). |
| self.line = line_number("main.cpp", "// breakpoint") |
| |
| def test_launch_notifications(self): |
| """Test that lldb broadcasts newly loaded libraries in batches.""" |
| self.build() |
| exe = self.getBuildArtifact("a.out") |
| self.dbg.SetAsync(False) |
| |
| listener = self.dbg.GetListener() |
| listener.StartListeningForEventClass( |
| self.dbg, |
| lldb.SBTarget.GetBroadcasterClassName(), |
| lldb.SBTarget.eBroadcastBitModulesLoaded |
| | lldb.SBTarget.eBroadcastBitModulesUnloaded, |
| ) |
| |
| # Create a target by the debugger. |
| target = self.dbg.CreateTarget(exe) |
| self.assertTrue(target, VALID_TARGET) |
| |
| # break on main |
| breakpoint = target.BreakpointCreateByName("main", "a.out") |
| |
| event = lldb.SBEvent() |
| # CreateTarget() generated modules-loaded events; consume them & toss |
| while listener.GetNextEvent(event): |
| True |
| |
| error = lldb.SBError() |
| flags = target.GetLaunchInfo().GetLaunchFlags() |
| process = target.Launch( |
| listener, |
| None, # argv |
| None, # envp |
| None, # stdin_path |
| None, # stdout_path |
| None, # stderr_path |
| None, # working directory |
| flags, # launch flags |
| False, # Stop at entry |
| error, |
| ) # error |
| |
| self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) |
| |
| total_solibs_added = 0 |
| total_solibs_removed = 0 |
| total_modules_added_events = 0 |
| total_modules_removed_events = 0 |
| already_loaded_modules = [] |
| while listener.GetNextEvent(event): |
| if lldb.SBTarget.EventIsTargetEvent(event): |
| if event.GetType() == lldb.SBTarget.eBroadcastBitModulesLoaded: |
| solib_count = lldb.SBTarget.GetNumModulesFromEvent(event) |
| total_modules_added_events += 1 |
| total_solibs_added += solib_count |
| added_files = [] |
| for i in range(solib_count): |
| module = lldb.SBTarget.GetModuleAtIndexFromEvent(i, event) |
| # On macOS Ventura and later, dyld and the main binary |
| # will be loaded again when dyld moves itself into the |
| # shared cache. Use the basename so this also works |
| # when reading dyld from the expanded shared cache. |
| exe_basename = lldb.SBFileSpec(exe).basename |
| if module.file.basename not in ["dyld", exe_basename]: |
| self.assertTrue( |
| module not in already_loaded_modules, |
| "{} is already loaded".format(module), |
| ) |
| already_loaded_modules.append(module) |
| if self.TraceOn(): |
| added_files.append(module.GetFileSpec().GetFilename()) |
| if self.TraceOn(): |
| # print all of the binaries that have been added |
| print("Loaded files: %s" % (", ".join(added_files))) |
| |
| if event.GetType() == lldb.SBTarget.eBroadcastBitModulesUnloaded: |
| solib_count = lldb.SBTarget.GetNumModulesFromEvent(event) |
| total_modules_removed_events += 1 |
| total_solibs_removed += solib_count |
| if self.TraceOn(): |
| # print all of the binaries that have been removed |
| removed_files = [] |
| i = 0 |
| while i < solib_count: |
| module = lldb.SBTarget.GetModuleAtIndexFromEvent(i, event) |
| removed_files.append(module.GetFileSpec().GetFilename()) |
| i = i + 1 |
| print("Unloaded files: %s" % (", ".join(removed_files))) |
| |
| # This is testing that we get back a small number of events with the loaded |
| # binaries in batches. Check that we got back more than 1 solib per event. |
| # In practice on Darwin today, we get back two events for a do-nothing c |
| # program: a.out and dyld, and then all the rest of the system libraries. |
| # On Linux we get events for ld.so, [vdso], the binary and then all libraries. |
| |
| avg_solibs_added_per_event = round( |
| float(total_solibs_added) / float(total_modules_added_events) |
| ) |
| self.assertGreater(avg_solibs_added_per_event, 1) |