1""" 2Test how many times newly loaded binaries are notified; 3they should be delivered in batches instead of one-by-one. 4""" 5 6from __future__ import print_function 7 8 9import lldb 10from lldbsuite.test.decorators import * 11from lldbsuite.test.lldbtest import * 12from lldbsuite.test import lldbutil 13 14class ModuleLoadedNotifysTestCase(TestBase): 15 NO_DEBUG_INFO_TESTCASE = True 16 17 # At least DynamicLoaderDarwin and DynamicLoaderPOSIXDYLD should batch up 18 # notifications about newly added/removed libraries. Other DynamicLoaders may 19 # not be written this way. 20 @skipUnlessPlatform(["linux"]+lldbplatformutil.getDarwinOSTriples()) 21 22 def setUp(self): 23 # Call super's setUp(). 24 TestBase.setUp(self) 25 # Find the line number to break inside main(). 26 self.line = line_number('main.cpp', '// breakpoint') 27 28 def test_launch_notifications(self): 29 """Test that lldb broadcasts newly loaded libraries in batches.""" 30 self.build() 31 exe = self.getBuildArtifact("a.out") 32 self.dbg.SetAsync(False) 33 34 listener = self.dbg.GetListener() 35 listener.StartListeningForEventClass( 36 self.dbg, 37 lldb.SBTarget.GetBroadcasterClassName(), 38 lldb.SBTarget.eBroadcastBitModulesLoaded | lldb.SBTarget.eBroadcastBitModulesUnloaded) 39 40 # Create a target by the debugger. 41 target = self.dbg.CreateTarget(exe) 42 self.assertTrue(target, VALID_TARGET) 43 44 # break on main 45 breakpoint = target.BreakpointCreateByName('main', 'a.out') 46 47 event = lldb.SBEvent() 48 # CreateTarget() generated modules-loaded events; consume them & toss 49 while listener.GetNextEvent(event): 50 True 51 52 error = lldb.SBError() 53 flags = target.GetLaunchInfo().GetLaunchFlags() 54 process = target.Launch(listener, 55 None, # argv 56 None, # envp 57 None, # stdin_path 58 None, # stdout_path 59 None, # stderr_path 60 None, # working directory 61 flags, # launch flags 62 False, # Stop at entry 63 error) # error 64 65 self.assertEqual( 66 process.GetState(), lldb.eStateStopped, 67 PROCESS_STOPPED) 68 69 total_solibs_added = 0 70 total_solibs_removed = 0 71 total_modules_added_events = 0 72 total_modules_removed_events = 0 73 already_loaded_modules = [] 74 while listener.GetNextEvent(event): 75 if lldb.SBTarget.EventIsTargetEvent(event): 76 if event.GetType() == lldb.SBTarget.eBroadcastBitModulesLoaded: 77 solib_count = lldb.SBTarget.GetNumModulesFromEvent(event) 78 total_modules_added_events += 1 79 total_solibs_added += solib_count 80 added_files = [] 81 for i in range (solib_count): 82 module = lldb.SBTarget.GetModuleAtIndexFromEvent(i, event) 83 # On macOS Ventura and later, dyld and the main binary 84 # will be loaded again when dyld moves itself into the 85 # shared cache. 86 if module.file.fullpath not in ['/usr/lib/dyld', exe]: 87 self.assertTrue(module not in already_loaded_modules, '{} is already loaded'.format(module)) 88 already_loaded_modules.append(module) 89 if self.TraceOn(): 90 added_files.append(module.GetFileSpec().GetFilename()) 91 if self.TraceOn(): 92 # print all of the binaries that have been added 93 print("Loaded files: %s" % (', '.join(added_files))) 94 95 if event.GetType() == lldb.SBTarget.eBroadcastBitModulesUnloaded: 96 solib_count = lldb.SBTarget.GetNumModulesFromEvent(event) 97 total_modules_removed_events += 1 98 total_solibs_removed += solib_count 99 if self.TraceOn(): 100 # print all of the binaries that have been removed 101 removed_files = [] 102 i = 0 103 while i < solib_count: 104 module = lldb.SBTarget.GetModuleAtIndexFromEvent(i, event) 105 removed_files.append(module.GetFileSpec().GetFilename()) 106 i = i + 1 107 print("Unloaded files: %s" % (', '.join(removed_files))) 108 109 110 # This is testing that we get back a small number of events with the loaded 111 # binaries in batches. Check that we got back more than 1 solib per event. 112 # In practice on Darwin today, we get back two events for a do-nothing c 113 # program: a.out and dyld, and then all the rest of the system libraries. 114 # On Linux we get events for ld.so, [vdso], the binary and then all libraries. 115 116 avg_solibs_added_per_event = round(float(total_solibs_added) / float(total_modules_added_events)) 117 self.assertGreater(avg_solibs_added_per_event, 1) 118