154 lines · python
1"""2Test how many times newly loaded binaries are notified;3they should be delivered in batches instead of one-by-one.4"""5 6import lldb7from lldbsuite.test.decorators import *8from lldbsuite.test.lldbtest import *9from lldbsuite.test import lldbutil10 11 12@skipUnlessPlatform(["linux"] + lldbplatformutil.getDarwinOSTriples())13class ModuleLoadedNotifysTestCase(TestBase):14 NO_DEBUG_INFO_TESTCASE = True15 16 # At least DynamicLoaderDarwin and DynamicLoaderPOSIXDYLD should batch up17 # notifications about newly added/removed libraries. Other DynamicLoaders may18 # not be written this way.19 def setUp(self):20 # Call super's setUp().21 TestBase.setUp(self)22 # Find the line number to break inside main().23 self.line = line_number("main.cpp", "// breakpoint")24 25 def setup_test(self, solibs):26 if lldb.remote_platform:27 path = lldb.remote_platform.GetWorkingDirectory()28 for f in solibs:29 lldbutil.install_to_target(self, self.getBuildArtifact(f))30 else:31 path = self.getBuildDir()32 if self.dylibPath in os.environ:33 sep = self.platformContext.shlib_path_separator34 path = os.environ[self.dylibPath] + sep + path35 self.runCmd(36 "settings append target.env-vars '{}={}'".format(self.dylibPath, path)37 )38 self.default_path = path39 40 def test_launch_notifications(self):41 """Test that lldb broadcasts newly loaded libraries in batches."""42 43 expected_solibs = [44 "lib_a." + self.platformContext.shlib_extension,45 "lib_b." + self.platformContext.shlib_extension,46 "lib_c." + self.platformContext.shlib_extension,47 "lib_d." + self.platformContext.shlib_extension,48 ]49 50 self.build()51 self.setup_test(expected_solibs)52 53 exe = self.getBuildArtifact("a.out")54 self.dbg.SetAsync(False)55 56 listener = self.dbg.GetListener()57 listener.StartListeningForEventClass(58 self.dbg,59 lldb.SBTarget.GetBroadcasterClassName(),60 lldb.SBTarget.eBroadcastBitModulesLoaded61 | lldb.SBTarget.eBroadcastBitModulesUnloaded,62 )63 64 # Create a target by the debugger.65 target = self.dbg.CreateTarget(exe)66 self.assertTrue(target, VALID_TARGET)67 68 # break on main69 breakpoint = target.BreakpointCreateByName("main", "a.out")70 71 event = lldb.SBEvent()72 # CreateTarget() generated modules-loaded events; consume them & toss73 while listener.GetNextEvent(event):74 True75 76 error = lldb.SBError()77 flags = target.GetLaunchInfo().GetLaunchFlags()78 process = target.Launch(79 listener,80 None, # argv81 None, # envp82 None, # stdin_path83 None, # stdout_path84 None, # stderr_path85 None, # working directory86 flags, # launch flags87 False, # Stop at entry88 error,89 ) # error90 91 self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)92 93 total_solibs_added = 094 total_solibs_removed = 095 total_modules_added_events = 096 total_modules_removed_events = 097 already_loaded_modules = []98 max_solibs_per_event = 099 max_solib_chunk_per_event = []100 while listener.GetNextEvent(event):101 if lldb.SBTarget.EventIsTargetEvent(event):102 if event.GetType() == lldb.SBTarget.eBroadcastBitModulesLoaded:103 solib_count = lldb.SBTarget.GetNumModulesFromEvent(event)104 total_modules_added_events += 1105 total_solibs_added += solib_count106 added_files = []107 for i in range(solib_count):108 module = lldb.SBTarget.GetModuleAtIndexFromEvent(i, event)109 # On macOS Ventura and later, dyld and the main binary110 # will be loaded again when dyld moves itself into the111 # shared cache. Use the basename so this also works112 # when reading dyld from the expanded shared cache.113 exe_basename = lldb.SBFileSpec(exe).basename114 if module.file.basename not in ["dyld", exe_basename]:115 self.assertNotIn(116 module,117 already_loaded_modules,118 "{} is already loaded".format(module),119 )120 already_loaded_modules.append(module)121 added_files.append(module.GetFileSpec().GetFilename())122 if self.TraceOn():123 # print all of the binaries that have been added124 print("Loaded files: %s" % (", ".join(added_files)))125 126 # We will check the latest biggest chunk of loaded solibs.127 # We expect all of our solibs in the last chunk of loaded modules.128 if solib_count >= max_solibs_per_event:129 max_solib_chunk_per_event = added_files.copy()130 max_solibs_per_event = solib_count131 132 if event.GetType() == lldb.SBTarget.eBroadcastBitModulesUnloaded:133 solib_count = lldb.SBTarget.GetNumModulesFromEvent(event)134 total_modules_removed_events += 1135 total_solibs_removed += solib_count136 if self.TraceOn():137 # print all of the binaries that have been removed138 removed_files = []139 i = 0140 while i < solib_count:141 module = lldb.SBTarget.GetModuleAtIndexFromEvent(i, event)142 removed_files.append(module.GetFileSpec().GetFilename())143 i = i + 1144 print("Unloaded files: %s" % (", ".join(removed_files)))145 146 # This is testing that we get back a small number of events with the loaded147 # binaries in batches. Check that we got back more than 1 solib per event.148 # In practice on Darwin today, we get back two events for a do-nothing c149 # program: a.out and dyld, and then all the rest of the system libraries.150 # On Linux we get events for ld.so, [vdso], the binary and then all libraries,151 # but the different configurations could load a different number of .so modules152 # per event.153 self.assertLessEqual(set(expected_solibs), set(max_solib_chunk_per_event))154