blob: 7f26e5e53a40d7a1e9e0cc3e93c58e33191616fd [file] [log] [blame]
"""
Test how many times newly loaded binaries are notified;
they should be delivered in batches instead of one-by-one.
"""
from __future__ import print_function
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)