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