brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.8 KiB · cfbfaff Raw
466 lines · python
1"""2Test that breakpoint by symbol name works correctly with dynamic libs.3"""4 5import os6import re7import lldb8from lldbsuite.test.decorators import *9from lldbsuite.test.lldbtest import *10from lldbsuite.test import lldbutil11 12 13class LoadUnloadTestCase(TestBase):14    NO_DEBUG_INFO_TESTCASE = True15 16    def setUp(self):17        # Call super's setUp().18        TestBase.setUp(self)19        self.setup_test()20        # Invoke the default build rule.21        self.build()22        # Find the line number to break for main.cpp.23        self.line = line_number(24            "main.cpp",25            "// Set break point at this line for test_lldb_process_load_and_unload_commands().",26        )27        self.line_d_function = line_number(28            "d.cpp", "// Find this line number within d_dunction()."29        )30 31    def setup_test(self):32        lldbutil.mkdir_p(self.getBuildArtifact("hidden"))33        if lldb.remote_platform:34            path = lldb.remote_platform.GetWorkingDirectory()35        else:36            path = self.getBuildDir()37            if self.dylibPath in os.environ:38                sep = self.platformContext.shlib_path_separator39                path = os.environ[self.dylibPath] + sep + path40        self.runCmd(41            "settings append target.env-vars '{}={}'".format(self.dylibPath, path)42        )43        self.default_path = path44 45    def copy_shlibs_to_remote(self, hidden_dir=False):46        """Copies the shared libs required by this test suite to remote.47        Does nothing in case of non-remote platforms.48        """49        if lldb.remote_platform:50            ext = "so"51            if self.platformIsDarwin():52                ext = "dylib"53 54            shlibs = [55                "libloadunload_a." + ext,56                "libloadunload_b." + ext,57                "libloadunload_c." + ext,58                "libloadunload_d." + ext,59            ]60            wd = lldb.remote_platform.GetWorkingDirectory()61            cwd = os.getcwd()62            for f in shlibs:63                err = lldb.remote_platform.Put(64                    lldb.SBFileSpec(self.getBuildArtifact(f)),65                    lldb.SBFileSpec(lldbutil.join_remote_paths(wd, f)),66                )67                if err.Fail():68                    raise RuntimeError(69                        "Unable copy '%s' to '%s'.\n>>> %s" % (f, wd, err.GetCString())70                    )71            if hidden_dir:72                shlib = "libloadunload_d." + ext73                hidden_dir = os.path.join(wd, "hidden")74                hidden_file = lldbutil.join_remote_paths(hidden_dir, shlib)75                err = lldb.remote_platform.MakeDirectory(hidden_dir)76                if err.Fail():77                    raise RuntimeError(78                        "Unable to create a directory '%s'." % hidden_dir79                    )80                err = lldb.remote_platform.Put(81                    lldb.SBFileSpec(os.path.join("hidden", shlib)),82                    lldb.SBFileSpec(hidden_file),83                )84                if err.Fail():85                    raise RuntimeError(86                        "Unable copy 'libloadunload_d.so' to '%s'.\n>>> %s"87                        % (wd, err.GetCString())88                    )89 90    def setSvr4Support(self, enabled):91        self.runCmd(92            "settings set plugin.process.gdb-remote.use-libraries-svr4 {enabled}".format(93                enabled="true" if enabled else "false"94            )95        )96 97    # libloadunload_d.so does not appear in the image list because executable98    # dependencies are resolved relative to the debuggers PWD. Bug?99    @expectedFailureAll(oslist=["freebsd", "linux", "netbsd"])100    @skipIfRemote101    @skipIfWindows  # Windows doesn't have dlopen and friends, dynamic libraries work differently102    def test_modules_search_paths(self):103        """Test target modules list after loading a different copy of the library libd.dylib, and verifies that it works with 'target modules search-paths add'."""104        if self.platformIsDarwin():105            dylibName = "libloadunload_d.dylib"106        else:107            dylibName = "libloadunload_d.so"108 109        # The directory with the dynamic library we did not link to.110        new_dir = os.path.join(self.getBuildDir(), "hidden")111 112        old_dylib = os.path.join(self.getBuildDir(), dylibName)113        new_dylib = os.path.join(new_dir, dylibName)114        exe = self.getBuildArtifact("a.out")115        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)116 117        self.expect("target modules list", substrs=[old_dylib])118        # self.expect("target modules list -t 3",119        #    patterns = ["%s-[^-]*-[^-]*" % self.getArchitecture()])120        # Add an image search path substitution pair.121        self.runCmd(122            "target modules search-paths add %s %s" % (self.getBuildDir(), new_dir)123        )124 125        self.expect(126            "target modules search-paths list", substrs=[self.getBuildDir(), new_dir]127        )128 129        self.expect(130            "target modules search-paths query %s" % self.getBuildDir(),131            "Image search path successfully transformed",132            substrs=[new_dir],133        )134 135        # Obliterate traces of libd from the old location.136        os.remove(old_dylib)137        # Inform (DY)LD_LIBRARY_PATH of the new path, too.138        env_cmd_string = (139            "settings replace target.env-vars " + self.dylibPath + "=" + new_dir140        )141        if self.TraceOn():142            print("Set environment to: ", env_cmd_string)143        self.runCmd(env_cmd_string)144        self.runCmd("settings show target.env-vars")145 146        self.runCmd("run")147 148        self.expect(149            "target modules list",150            "LLDB successfully locates the relocated dynamic library",151            substrs=[new_dylib],152        )153 154    # libloadunload_d.so does not appear in the image list because executable155    # dependencies are resolved relative to the debuggers PWD. Bug?156    @expectedFailureAll(oslist=["freebsd", "linux", "netbsd"])157    @expectedFailureAndroid  # wrong source file shows up for hidden library158    @skipIfWindows  # Windows doesn't have dlopen and friends, dynamic libraries work differently159    @skipIfDarwinEmbedded160    def test_dyld_library_path(self):161        """Test (DY)LD_LIBRARY_PATH after moving libd.dylib, which defines d_function, somewhere else."""162        self.copy_shlibs_to_remote(hidden_dir=True)163 164        exe = self.getBuildArtifact("a.out")165        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)166 167        # Shut off ANSI color usage so we don't get ANSI escape sequences168        # mixed in with stop locations.169        self.dbg.SetUseColor(False)170 171        if self.platformIsDarwin():172            dylibName = "libloadunload_d.dylib"173            dsymName = "libloadunload_d.dylib.dSYM"174        else:175            dylibName = "libloadunload_d.so"176 177        # The directory to relocate the dynamic library and its debugging info.178        special_dir = "hidden"179        if lldb.remote_platform:180            wd = lldb.remote_platform.GetWorkingDirectory()181        else:182            wd = self.getBuildDir()183 184        old_dir = wd185        new_dir = os.path.join(wd, special_dir)186        old_dylib = os.path.join(old_dir, dylibName)187 188        # For now we don't track (DY)LD_LIBRARY_PATH, so the old189        # library will be in the modules list.190        self.expect(191            "target modules list", substrs=[os.path.basename(old_dylib)], matching=True192        )193 194        lldbutil.run_break_set_by_file_and_line(195            self, "d.cpp", self.line_d_function, num_expected_locations=1196        )197        # After run, make sure the non-hidden library is picked up.198        self.expect("run", substrs=["return", "700"])199 200        self.runCmd("continue")201 202        # Add the hidden directory first in the search path.203        env_cmd_string = "settings set target.env-vars %s=%s%s%s" % (204            self.dylibPath,205            new_dir,206            self.platformContext.shlib_path_separator,207            self.default_path,208        )209        self.runCmd(env_cmd_string)210 211        # This time, the hidden library should be picked up.212        self.expect("run", substrs=["return", "12345"])213 214    @expectedFailureAll(215        bugnumber="llvm.org/pr25805", hostoslist=["windows"], triple=".*-android"216    )217    @expectedFailureAll(oslist=["windows"])  # process load not implemented218    def test_lldb_process_load_and_unload_commands(self):219        self.setSvr4Support(False)220        self.run_lldb_process_load_and_unload_commands()221 222    @expectedFailureAll(223        bugnumber="llvm.org/pr25805", hostoslist=["windows"], triple=".*-android"224    )225    @expectedFailureAll(oslist=["windows"])  # process load not implemented226    def test_lldb_process_load_and_unload_commands_with_svr4(self):227        self.setSvr4Support(True)228        self.run_lldb_process_load_and_unload_commands()229 230    def run_lldb_process_load_and_unload_commands(self):231        """Test that lldb process load/unload command work correctly."""232        self.copy_shlibs_to_remote()233 234        exe = self.getBuildArtifact("a.out")235        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)236 237        # Break at main.cpp before the call to dlopen().238        # Use lldb's process load command to load the dylib, instead.239 240        lldbutil.run_break_set_by_file_and_line(241            self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True242        )243 244        self.runCmd("run", RUN_SUCCEEDED)245 246        ctx = self.platformContext247        dylibName = ctx.shlib_prefix + "loadunload_a." + ctx.shlib_extension248        localDylibPath = self.getBuildArtifact(dylibName)249        if lldb.remote_platform:250            wd = lldb.remote_platform.GetWorkingDirectory()251            remoteDylibPath = lldbutil.join_remote_paths(wd, dylibName)252        else:253            remoteDylibPath = localDylibPath254 255        # First make sure that we get some kind of error if process load fails.256        # We print some error even if the load fails, which isn't formalized.257        # The only plugin at present (Posix) that supports this says "unknown reasons".258        # If another plugin shows up, let's require it uses "unknown error" as well.259        non_existant_shlib = "/NoSuchDir/NoSuchSubdir/ReallyNo/NotAFile"260        self.expect(261            "process load %s" % (non_existant_shlib),262            error=True,263            matching=False,264            patterns=["unknown reasons"],265        )266 267        # Make sure that a_function does not exist at this point.268        self.expect(269            "image lookup -n a_function",270            "a_function should not exist yet",271            error=True,272            matching=False,273            patterns=["1 match found"],274        )275 276        # Use lldb 'process load' to load the dylib.277        self.expect(278            "process load %s --install=%s" % (localDylibPath, remoteDylibPath),279            "%s loaded correctly" % dylibName,280            patterns=[281                'Loading "%s".*ok' % re.escape(localDylibPath),282                "Image [0-9]+ loaded",283            ],284        )285 286        # Search for and match the "Image ([0-9]+) loaded" pattern.287        output = self.res.GetOutput()288        pattern = re.compile("Image ([0-9]+) loaded")289        for l in output.split(os.linesep):290            self.trace("l:", l)291            match = pattern.search(l)292            if match:293                break294        index = match.group(1)295 296        # Now we should have an entry for a_function.297        self.expect(298            "image lookup -n a_function",299            "a_function should now exist",300            patterns=["1 match found .*%s" % dylibName],301        )302 303        # Use lldb 'process unload' to unload the dylib.304        self.expect(305            "process unload %s" % index,306            "%s unloaded correctly" % dylibName,307            patterns=["Unloading .* with index %s.*ok" % index],308        )309 310        # Confirm that we unloaded properly.311        self.expect(312            "image lookup -n a_function",313            "a_function should not exist after unload",314            error=True,315            matching=False,316            patterns=["1 match found"],317        )318 319        self.runCmd("process continue")320 321    @expectedFailureAll(oslist=["windows"])  # breakpoint not hit322    def test_load_unload(self):323        self.setSvr4Support(False)324        self.run_load_unload()325 326    @expectedFailureAll(oslist=["windows"])  # breakpoint not hit327    def test_load_unload_with_svr4(self):328        self.setSvr4Support(True)329        self.run_load_unload()330 331    def run_load_unload(self):332        """Test breakpoint by name works correctly with dlopen'ing."""333        self.copy_shlibs_to_remote()334 335        exe = self.getBuildArtifact("a.out")336        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)337 338        # Break by function name a_function (not yet loaded).339        lldbutil.run_break_set_by_symbol(self, "a_function", num_expected_locations=0)340 341        self.runCmd("run", RUN_SUCCEEDED)342 343        # The stop reason of the thread should be breakpoint and at a_function.344        self.expect(345            "thread list",346            STOPPED_DUE_TO_BREAKPOINT,347            substrs=["stopped", "a_function", "stop reason = breakpoint"],348        )349 350        # The breakpoint should have a hit count of 1.351        lldbutil.check_breakpoint(self, bpno=1, expected_hit_count=1)352 353        # Issue the 'continue' command.  We should stop again at a_function.354        # The stop reason of the thread should be breakpoint and at a_function.355        self.runCmd("continue")356 357        # rdar://problem/8508987358        # The a_function breakpoint should be encountered twice.359        self.expect(360            "thread list",361            STOPPED_DUE_TO_BREAKPOINT,362            substrs=["stopped", "a_function", "stop reason = breakpoint"],363        )364 365        # The breakpoint should have a hit count of 2.366        lldbutil.check_breakpoint(self, bpno=1, expected_hit_count=2)367 368    def test_step_over_load(self):369        self.setSvr4Support(False)370        self.run_step_over_load()371 372    def test_step_over_load_with_svr4(self):373        self.setSvr4Support(True)374        self.run_step_over_load()375 376    def run_step_over_load(self):377        """Test stepping over code that loads a shared library works correctly."""378        self.copy_shlibs_to_remote()379 380        exe = self.getBuildArtifact("a.out")381        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)382 383        # Break by function name a_function (not yet loaded).384        lldbutil.run_break_set_by_file_and_line(385            self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True386        )387 388        self.runCmd("run", RUN_SUCCEEDED)389 390        # The stop reason of the thread should be breakpoint and at a_function.391        self.expect(392            "thread list",393            STOPPED_DUE_TO_BREAKPOINT,394            substrs=["stopped", "stop reason = breakpoint"],395        )396 397        self.runCmd("thread step-over", "Stepping over function that loads library")398 399        # The stop reason should be step end.400        self.expect(401            "thread list",402            "step over succeeded.",403            substrs=["stopped", "stop reason = step over"],404        )405 406    # We can't find a breakpoint location for d_init before launching because407    # executable dependencies are resolved relative to the debuggers PWD. Bug?408    # The remote lldb server resolves the executable dependencies correctly.409    @expectedFailureAll(410        oslist=["freebsd", "linux", "netbsd"],411        remote=False,412    )413    @expectedFailureAll(oslist=["windows"], archs=["aarch64"])414    def test_static_init_during_load(self):415        """Test that we can set breakpoints correctly in static initializers"""416        self.copy_shlibs_to_remote()417 418        exe = self.getBuildArtifact("a.out")419        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)420 421        a_init_bp_num = lldbutil.run_break_set_by_symbol(422            self, "a_init", num_expected_locations=0423        )424        b_init_bp_num = lldbutil.run_break_set_by_symbol(425            self, "b_init", num_expected_locations=0426        )427        d_init_bp_num = lldbutil.run_break_set_by_symbol(428            self, "d_init", num_expected_locations=1429        )430 431        self.runCmd("run", RUN_SUCCEEDED)432 433        self.expect(434            "thread list",435            STOPPED_DUE_TO_BREAKPOINT,436            substrs=[437                "stopped",438                "d_init",439                "stop reason = breakpoint %d" % d_init_bp_num,440            ],441        )442 443        self.runCmd("continue")444        self.expect(445            "thread list",446            STOPPED_DUE_TO_BREAKPOINT,447            substrs=[448                "stopped",449                "b_init",450                "stop reason = breakpoint %d" % b_init_bp_num,451            ],452        )453        self.expect("thread backtrace", substrs=["b_init", "dylib_open", "main"])454 455        self.runCmd("continue")456        self.expect(457            "thread list",458            STOPPED_DUE_TO_BREAKPOINT,459            substrs=[460                "stopped",461                "a_init",462                "stop reason = breakpoint %d" % a_init_bp_num,463            ],464        )465        self.expect("thread backtrace", substrs=["a_init", "dylib_open", "main"])466