199451b44SJordan Rupprecht""" 299451b44SJordan RupprechtTest how many times newly loaded binaries are notified; 399451b44SJordan Rupprechtthey should be delivered in batches instead of one-by-one. 499451b44SJordan Rupprecht""" 599451b44SJordan Rupprecht 699451b44SJordan Rupprechtimport lldb 799451b44SJordan Rupprechtfrom lldbsuite.test.decorators import * 899451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import * 999451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil 1099451b44SJordan Rupprecht 112238dcc3SJonas Devlieghere 12*3e39328bSDmitry Vasilyev@skipUnlessPlatform(["linux"] + lldbplatformutil.getDarwinOSTriples()) 1399451b44SJordan Rupprechtclass ModuleLoadedNotifysTestCase(TestBase): 1499451b44SJordan Rupprecht NO_DEBUG_INFO_TESTCASE = True 1599451b44SJordan Rupprecht 16195a8b97SLuboš Luňák # At least DynamicLoaderDarwin and DynamicLoaderPOSIXDYLD should batch up 17195a8b97SLuboš Luňák # notifications about newly added/removed libraries. Other DynamicLoaders may 1899451b44SJordan Rupprecht # not be written this way. 1999451b44SJordan Rupprecht def setUp(self): 2099451b44SJordan Rupprecht # Call super's setUp(). 2199451b44SJordan Rupprecht TestBase.setUp(self) 2299451b44SJordan Rupprecht # Find the line number to break inside main(). 232238dcc3SJonas Devlieghere self.line = line_number("main.cpp", "// breakpoint") 2499451b44SJordan Rupprecht 25*3e39328bSDmitry Vasilyev def setup_test(self, solibs): 26*3e39328bSDmitry Vasilyev if lldb.remote_platform: 27*3e39328bSDmitry Vasilyev path = lldb.remote_platform.GetWorkingDirectory() 28*3e39328bSDmitry Vasilyev for f in solibs: 29*3e39328bSDmitry Vasilyev lldbutil.install_to_target(self, self.getBuildArtifact(f)) 30*3e39328bSDmitry Vasilyev else: 31*3e39328bSDmitry Vasilyev path = self.getBuildDir() 32*3e39328bSDmitry Vasilyev if self.dylibPath in os.environ: 33*3e39328bSDmitry Vasilyev sep = self.platformContext.shlib_path_separator 34*3e39328bSDmitry Vasilyev path = os.environ[self.dylibPath] + sep + path 35*3e39328bSDmitry Vasilyev self.runCmd( 36*3e39328bSDmitry Vasilyev "settings append target.env-vars '{}={}'".format(self.dylibPath, path) 37*3e39328bSDmitry Vasilyev ) 38*3e39328bSDmitry Vasilyev self.default_path = path 39*3e39328bSDmitry Vasilyev 4099451b44SJordan Rupprecht def test_launch_notifications(self): 4199451b44SJordan Rupprecht """Test that lldb broadcasts newly loaded libraries in batches.""" 42*3e39328bSDmitry Vasilyev 43*3e39328bSDmitry Vasilyev expected_solibs = [ 44*3e39328bSDmitry Vasilyev "lib_a." + self.platformContext.shlib_extension, 45*3e39328bSDmitry Vasilyev "lib_b." + self.platformContext.shlib_extension, 46*3e39328bSDmitry Vasilyev "lib_c." + self.platformContext.shlib_extension, 47*3e39328bSDmitry Vasilyev "lib_d." + self.platformContext.shlib_extension, 48*3e39328bSDmitry Vasilyev ] 49*3e39328bSDmitry Vasilyev 5099451b44SJordan Rupprecht self.build() 51*3e39328bSDmitry Vasilyev self.setup_test(expected_solibs) 52*3e39328bSDmitry Vasilyev 5399451b44SJordan Rupprecht exe = self.getBuildArtifact("a.out") 5499451b44SJordan Rupprecht self.dbg.SetAsync(False) 5599451b44SJordan Rupprecht 5699451b44SJordan Rupprecht listener = self.dbg.GetListener() 5799451b44SJordan Rupprecht listener.StartListeningForEventClass( 5899451b44SJordan Rupprecht self.dbg, 5999451b44SJordan Rupprecht lldb.SBTarget.GetBroadcasterClassName(), 602238dcc3SJonas Devlieghere lldb.SBTarget.eBroadcastBitModulesLoaded 612238dcc3SJonas Devlieghere | lldb.SBTarget.eBroadcastBitModulesUnloaded, 622238dcc3SJonas Devlieghere ) 6399451b44SJordan Rupprecht 6499451b44SJordan Rupprecht # Create a target by the debugger. 6599451b44SJordan Rupprecht target = self.dbg.CreateTarget(exe) 6699451b44SJordan Rupprecht self.assertTrue(target, VALID_TARGET) 6799451b44SJordan Rupprecht 6899451b44SJordan Rupprecht # break on main 692238dcc3SJonas Devlieghere breakpoint = target.BreakpointCreateByName("main", "a.out") 7099451b44SJordan Rupprecht 7199451b44SJordan Rupprecht event = lldb.SBEvent() 7299451b44SJordan Rupprecht # CreateTarget() generated modules-loaded events; consume them & toss 7399451b44SJordan Rupprecht while listener.GetNextEvent(event): 7499451b44SJordan Rupprecht True 7599451b44SJordan Rupprecht 7699451b44SJordan Rupprecht error = lldb.SBError() 772ddba09eSJonas Devlieghere flags = target.GetLaunchInfo().GetLaunchFlags() 782238dcc3SJonas Devlieghere process = target.Launch( 792238dcc3SJonas Devlieghere listener, 8099451b44SJordan Rupprecht None, # argv 8199451b44SJordan Rupprecht None, # envp 8299451b44SJordan Rupprecht None, # stdin_path 8399451b44SJordan Rupprecht None, # stdout_path 8499451b44SJordan Rupprecht None, # stderr_path 8599451b44SJordan Rupprecht None, # working directory 86254e0abfSJonas Devlieghere flags, # launch flags 8799451b44SJordan Rupprecht False, # Stop at entry 882238dcc3SJonas Devlieghere error, 892238dcc3SJonas Devlieghere ) # error 9099451b44SJordan Rupprecht 912238dcc3SJonas Devlieghere self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) 9299451b44SJordan Rupprecht 9399451b44SJordan Rupprecht total_solibs_added = 0 9499451b44SJordan Rupprecht total_solibs_removed = 0 9599451b44SJordan Rupprecht total_modules_added_events = 0 9699451b44SJordan Rupprecht total_modules_removed_events = 0 97195a8b97SLuboš Luňák already_loaded_modules = [] 98*3e39328bSDmitry Vasilyev max_solibs_per_event = 0 99*3e39328bSDmitry Vasilyev max_solib_chunk_per_event = [] 10099451b44SJordan Rupprecht while listener.GetNextEvent(event): 10199451b44SJordan Rupprecht if lldb.SBTarget.EventIsTargetEvent(event): 10299451b44SJordan Rupprecht if event.GetType() == lldb.SBTarget.eBroadcastBitModulesLoaded: 10399451b44SJordan Rupprecht solib_count = lldb.SBTarget.GetNumModulesFromEvent(event) 10499451b44SJordan Rupprecht total_modules_added_events += 1 10599451b44SJordan Rupprecht total_solibs_added += solib_count 10699451b44SJordan Rupprecht added_files = [] 107c9b55eb8SJonas Devlieghere for i in range(solib_count): 10899451b44SJordan Rupprecht module = lldb.SBTarget.GetModuleAtIndexFromEvent(i, event) 109c9b55eb8SJonas Devlieghere # On macOS Ventura and later, dyld and the main binary 110c9b55eb8SJonas Devlieghere # will be loaded again when dyld moves itself into the 1112b61b770SJonas Devlieghere # shared cache. Use the basename so this also works 1122b61b770SJonas Devlieghere # when reading dyld from the expanded shared cache. 1132b61b770SJonas Devlieghere exe_basename = lldb.SBFileSpec(exe).basename 1142238dcc3SJonas Devlieghere if module.file.basename not in ["dyld", exe_basename]: 1159c246882SJordan Rupprecht self.assertNotIn( 1169c246882SJordan Rupprecht module, 1179c246882SJordan Rupprecht already_loaded_modules, 1182238dcc3SJonas Devlieghere "{} is already loaded".format(module), 1192238dcc3SJonas Devlieghere ) 120195a8b97SLuboš Luňák already_loaded_modules.append(module) 12199451b44SJordan Rupprecht added_files.append(module.GetFileSpec().GetFilename()) 122195a8b97SLuboš Luňák if self.TraceOn(): 123195a8b97SLuboš Luňák # print all of the binaries that have been added 1242238dcc3SJonas Devlieghere print("Loaded files: %s" % (", ".join(added_files))) 12599451b44SJordan Rupprecht 126*3e39328bSDmitry Vasilyev # We will check the latest biggest chunk of loaded solibs. 127*3e39328bSDmitry Vasilyev # We expect all of our solibs in the last chunk of loaded modules. 128*3e39328bSDmitry Vasilyev if solib_count >= max_solibs_per_event: 129*3e39328bSDmitry Vasilyev max_solib_chunk_per_event = added_files.copy() 130*3e39328bSDmitry Vasilyev max_solibs_per_event = solib_count 131*3e39328bSDmitry Vasilyev 13299451b44SJordan Rupprecht if event.GetType() == lldb.SBTarget.eBroadcastBitModulesUnloaded: 13399451b44SJordan Rupprecht solib_count = lldb.SBTarget.GetNumModulesFromEvent(event) 13499451b44SJordan Rupprecht total_modules_removed_events += 1 13599451b44SJordan Rupprecht total_solibs_removed += solib_count 13699451b44SJordan Rupprecht if self.TraceOn(): 13799451b44SJordan Rupprecht # print all of the binaries that have been removed 13899451b44SJordan Rupprecht removed_files = [] 13999451b44SJordan Rupprecht i = 0 14099451b44SJordan Rupprecht while i < solib_count: 14199451b44SJordan Rupprecht module = lldb.SBTarget.GetModuleAtIndexFromEvent(i, event) 14299451b44SJordan Rupprecht removed_files.append(module.GetFileSpec().GetFilename()) 14399451b44SJordan Rupprecht i = i + 1 1442238dcc3SJonas Devlieghere print("Unloaded files: %s" % (", ".join(removed_files))) 14599451b44SJordan Rupprecht 14699451b44SJordan Rupprecht # This is testing that we get back a small number of events with the loaded 14799451b44SJordan Rupprecht # binaries in batches. Check that we got back more than 1 solib per event. 14899451b44SJordan Rupprecht # In practice on Darwin today, we get back two events for a do-nothing c 14999451b44SJordan Rupprecht # program: a.out and dyld, and then all the rest of the system libraries. 150*3e39328bSDmitry Vasilyev # On Linux we get events for ld.so, [vdso], the binary and then all libraries, 151*3e39328bSDmitry Vasilyev # but the different configurations could load a different number of .so modules 152*3e39328bSDmitry Vasilyev # per event. 153*3e39328bSDmitry Vasilyev self.assertLessEqual(set(expected_solibs), set(max_solib_chunk_per_event)) 154