brintos

brintos / llvm-project-archived public Read only

0
0
Text · 26.1 KiB · ca881f1 Raw
662 lines · python
1"""2Test lldb-dap setBreakpoints request3"""4 5from lldbsuite.test.decorators import *6from lldbsuite.test.lldbtest import *7import lldbdap_testcase8import os9import pathlib10import re11import tempfile12 13# Many tests are skipped on Windows because get_stdout() returns None there.14# Despite the test program printing correctly. See15# https://github.com/llvm/llvm-project/issues/137599.16 17 18class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):19    @skipIfWindows20    def test_default(self):21        """22        Tests the default launch of a simple program. No arguments,23        environment, or anything else is specified.24        """25        program = self.getBuildArtifact("a.out")26        self.build_and_launch(program)27        self.continue_to_exit()28        # Now get the STDOUT and verify our program argument is correct29        output = self.get_stdout()30        self.assertTrue(output and len(output) > 0, "expect program output")31        lines = output.splitlines()32        self.assertIn(program, lines[0], "make sure program path is in first argument")33 34    def test_failing_launch_program(self):35        """36        Tests launching with an invalid program.37        """38        program = self.getBuildArtifact("a.out")39        self.create_debug_adapter()40        response = self.launch(program, expectFailure=True)41        self.assertFalse(response["success"])42        self.assertEqual(43            "'{0}' does not exist".format(program), response["body"]["error"]["format"]44        )45 46    def test_failing_launch_commands_and_console(self):47        """48        Tests launching with launch commands in an integrated terminal.49        """50        program = self.getBuildArtifact("a.out")51        self.create_debug_adapter()52        response = self.launch(53            program,54            launchCommands=["a b c"],55            console="integratedTerminal",56            expectFailure=True,57        )58        self.assertFalse(response["success"])59        self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"]))60        self.assertEqual(61            "'launchCommands' and non-internal 'console' are mutually exclusive",62            self.get_dict_value(response, ["body", "error", "format"]),63        )64 65    def test_failing_console(self):66        """67        Tests launching in console with an invalid terminal type.68        """69        program = self.getBuildArtifact("a.out")70        self.create_debug_adapter()71        response = self.launch(program, console="invalid", expectFailure=True)72        self.assertFalse(response["success"])73        self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"]))74        self.assertRegex(75            response["body"]["error"]["format"],76            r"unexpected value, expected 'internalConsole\', 'integratedTerminal\' or 'externalTerminal\' at arguments.console",77        )78 79    @skipIfWindows80    def test_termination(self):81        """82        Tests the correct termination of lldb-dap upon a 'disconnect'83        request.84        """85        self.create_debug_adapter()86        # The underlying lldb-dap process must be alive87        self.assertEqual(self.dap_server.process.poll(), None)88 89        # The lldb-dap process should finish even though90        # we didn't close the communication socket explicitly91        self.dap_server.request_disconnect()92 93        # Wait until the underlying lldb-dap process dies.94        self.dap_server.process.wait(timeout=self.DEFAULT_TIMEOUT)95 96        # Check the return code97        self.assertEqual(self.dap_server.process.poll(), 0)98 99    def test_stopOnEntry(self):100        """101        Tests the default launch of a simple program that stops at the102        entry point instead of continuing.103        """104        program = self.getBuildArtifact("a.out")105        self.build_and_launch(program, stopOnEntry=True)106        self.dap_server.request_configurationDone()107        self.dap_server.wait_for_stopped()108        self.assertTrue(109            len(self.dap_server.thread_stop_reasons) > 0,110            "expected stopped event during launch",111        )112        for _, body in self.dap_server.thread_stop_reasons.items():113            if "reason" in body:114                reason = body["reason"]115                self.assertNotEqual(116                    reason, "breakpoint", 'verify stop isn\'t "main" breakpoint'117                )118 119    @skipIfWindows120    def test_cwd(self):121        """122        Tests the default launch of a simple program with a current working123        directory.124        """125        program = self.getBuildArtifact("a.out")126        program_parent_dir = os.path.realpath(os.path.dirname(os.path.dirname(program)))127        self.build_and_launch(program, cwd=program_parent_dir)128        self.continue_to_exit()129        # Now get the STDOUT and verify our program argument is correct130        output = self.get_stdout()131        self.assertTrue(output and len(output) > 0, "expect program output")132        lines = output.splitlines()133        found = False134        for line in lines:135            if line.startswith('cwd = "'):136                quote_path = '"%s"' % (program_parent_dir)137                found = True138                self.assertIn(139                    quote_path,140                    line,141                    "working directory '%s' not in '%s'" % (program_parent_dir, line),142                )143        self.assertTrue(found, "verified program working directory")144 145    def test_debuggerRoot(self):146        """147        Tests the "debuggerRoot" will change the working directory of148        the lldb-dap debug adapter.149        """150        program = self.getBuildArtifact("a.out")151        program_parent_dir = os.path.realpath(os.path.dirname(os.path.dirname(program)))152 153        var = "%cd%" if lldbplatformutil.getHostPlatform() == "windows" else "$PWD"154        commands = [f"platform shell echo cwd = {var}"]155 156        self.build_and_launch(157            program, debuggerRoot=program_parent_dir, initCommands=commands158        )159        output = self.get_console()160        self.assertTrue(output and len(output) > 0, "expect console output")161        lines = output.splitlines()162        prefix = "cwd = "163        found = False164        for line in lines:165            if line.startswith(prefix):166                found = True167                self.assertEqual(168                    program_parent_dir,169                    line.strip()[len(prefix) :],170                    "lldb-dap working dir '%s' == '%s'"171                    % (program_parent_dir, line[len(prefix) :]),172                )173        self.assertTrue(found, "verified lldb-dap working directory")174        self.continue_to_exit()175 176    def test_sourcePath(self):177        """178        Tests the "sourcePath" will set the target.source-map.179        """180        program = self.getBuildArtifact("a.out")181        program_dir = os.path.dirname(program)182        self.build_and_launch(program, sourcePath=program_dir)183        output = self.get_console()184        self.assertTrue(output and len(output) > 0, "expect console output")185        lines = output.splitlines()186        prefix = '(lldb) settings set target.source-map "." '187        found = False188        for line in lines:189            if line.startswith(prefix):190                found = True191                quoted_path = '"%s"' % (program_dir)192                self.assertEqual(193                    quoted_path,194                    line[len(prefix) :],195                    "lldb-dap working dir %s == %s" % (quoted_path, line[6:]),196                )197        self.assertTrue(found, 'found "sourcePath" in console output')198        self.continue_to_exit()199 200    @skipIfWindows201    def test_disableSTDIO(self):202        """203        Tests the default launch of a simple program with STDIO disabled.204        """205        program = self.getBuildArtifact("a.out")206        self.build_and_launch(program, disableSTDIO=True)207        self.continue_to_exit()208        # Now get the STDOUT and verify our program argument is correct209        output = self.get_stdout()210        self.assertEqual(output, "", "expect no program output")211 212    @skipIfWindows213    @skipIfLinux  # shell argument expansion doesn't seem to work on Linux214    @expectedFailureAll(oslist=["freebsd", "netbsd"], bugnumber="llvm.org/pr48349")215    def test_shellExpandArguments_enabled(self):216        """217        Tests the default launch of a simple program with shell expansion218        enabled.219        """220        program = self.getBuildArtifact("a.out")221        program_dir = os.path.dirname(program)222        glob = os.path.join(program_dir, "*.out")223        self.build_and_launch(program, args=[glob], shellExpandArguments=True)224        self.continue_to_exit()225        # Now get the STDOUT and verify our program argument is correct226        output = self.get_stdout()227        self.assertTrue(output and len(output) > 0, "expect no program output")228        lines = output.splitlines()229        for line in lines:230            quote_path = '"%s"' % (program)231            if line.startswith("arg[1] ="):232                self.assertIn(233                    quote_path, line, 'verify "%s" expanded to "%s"' % (glob, program)234                )235 236    @skipIfWindows237    def test_shellExpandArguments_disabled(self):238        """239        Tests the default launch of a simple program with shell expansion240        disabled.241        """242        program = self.getBuildArtifact("a.out")243        program_dir = os.path.dirname(program)244        glob = os.path.join(program_dir, "*.out")245        self.build_and_launch(program, args=[glob], shellExpandArguments=False)246        self.continue_to_exit()247        # Now get the STDOUT and verify our program argument is correct248        output = self.get_stdout()249        self.assertTrue(output and len(output) > 0, "expect no program output")250        lines = output.splitlines()251        for line in lines:252            quote_path = '"%s"' % (glob)253            if line.startswith("arg[1] ="):254                self.assertIn(255                    quote_path, line, 'verify "%s" stayed to "%s"' % (glob, glob)256                )257 258    @skipIfWindows259    def test_args(self):260        """261        Tests launch of a simple program with arguments262        """263        program = self.getBuildArtifact("a.out")264        args = ["one", "with space", "'with single quotes'", '"with double quotes"']265        self.build_and_launch(program, args=args)266        self.continue_to_exit()267 268        # Now get the STDOUT and verify our arguments got passed correctly269        output = self.get_stdout()270        self.assertTrue(output and len(output) > 0, "expect program output")271        lines = output.splitlines()272        # Skip the first argument that contains the program name273        lines.pop(0)274        # Make sure arguments we specified are correct275        for i, arg in enumerate(args):276            quoted_arg = '"%s"' % (arg)277            self.assertIn(278                quoted_arg,279                lines[i],280                'arg[%i] "%s" not in "%s"' % (i + 1, quoted_arg, lines[i]),281            )282 283    @skipIfWindows284    def test_environment_with_object(self):285        """286        Tests launch of a simple program with environment variables287        """288        program = self.getBuildArtifact("a.out")289        env = {290            "NO_VALUE": "",291            "WITH_VALUE": "BAR",292            "EMPTY_VALUE": "",293            "SPACE": "Hello World",294        }295 296        self.build_and_launch(program, env=env)297        self.continue_to_exit()298 299        # Now get the STDOUT and verify our arguments got passed correctly300        output = self.get_stdout()301        self.assertTrue(output and len(output) > 0, "expect program output")302        lines = output.splitlines()303        # Skip the all arguments so we have only environment vars left304        while len(lines) and lines[0].startswith("arg["):305            lines.pop(0)306        # Make sure each environment variable in "env" is actually set in the307        # program environment that was printed to STDOUT308        for var in env:309            found = False310            for program_var in lines:311                if var in program_var:312                    found = True313                    break314            self.assertTrue(315                found, '"%s" must exist in program environment (%s)' % (var, lines)316            )317 318    @skipIfWindows319    def test_environment_with_array(self):320        """321        Tests launch of a simple program with environment variables322        """323        program = self.getBuildArtifact("a.out")324        env = ["NO_VALUE", "WITH_VALUE=BAR", "EMPTY_VALUE=", "SPACE=Hello World"]325 326        self.build_and_launch(program, env=env)327        self.continue_to_exit()328 329        # Now get the STDOUT and verify our arguments got passed correctly330        output = self.get_stdout()331        self.assertTrue(output and len(output) > 0, "expect program output")332        lines = output.splitlines()333        # Skip the all arguments so we have only environment vars left334        while len(lines) and lines[0].startswith("arg["):335            lines.pop(0)336        # Make sure each environment variable in "env" is actually set in the337        # program environment that was printed to STDOUT338        for var in env:339            found = False340            for program_var in lines:341                if var in program_var:342                    found = True343                    break344            self.assertTrue(345                found, '"%s" must exist in program environment (%s)' % (var, lines)346            )347 348    @skipIf(349        archs=["arm$", "aarch64"]350    )  # failed run https://lab.llvm.org/buildbot/#/builders/96/builds/6933351    def test_commands(self):352        """353        Tests the "initCommands", "preRunCommands", "stopCommands",354        "terminateCommands" and "exitCommands" that can be passed during355        launch.356 357        "initCommands" are a list of LLDB commands that get executed358        before the targt is created.359        "preRunCommands" are a list of LLDB commands that get executed360        after the target has been created and before the launch.361        "stopCommands" are a list of LLDB commands that get executed each362        time the program stops.363        "exitCommands" are a list of LLDB commands that get executed when364        the process exits365        "terminateCommands" are a list of LLDB commands that get executed when366        the debugger session terminates.367        """368        program = self.getBuildArtifact("a.out")369        initCommands = ["target list", "platform list"]370        preRunCommands = ["image list a.out", "image dump sections a.out"]371        postRunCommands = ["help trace", "help process trace"]372        stopCommands = ["frame variable", "bt"]373        exitCommands = ["expr 2+3", "expr 3+4"]374        terminateCommands = ["expr 4+2"]375        self.build_and_launch(376            program,377            initCommands=initCommands,378            preRunCommands=preRunCommands,379            postRunCommands=postRunCommands,380            stopCommands=stopCommands,381            exitCommands=exitCommands,382            terminateCommands=terminateCommands,383        )384 385        # Get output from the console. This should contain both the386        # "initCommands" and the "preRunCommands".387        output = self.get_console()388        # Verify all "initCommands" were found in console output389        self.verify_commands("initCommands", output, initCommands)390        # Verify all "preRunCommands" were found in console output391        self.verify_commands("preRunCommands", output, preRunCommands)392        # Verify all "postRunCommands" were found in console output393        self.verify_commands("postRunCommands", output, postRunCommands)394 395        source = "main.c"396        first_line = line_number(source, "// breakpoint 1")397        second_line = line_number(source, "// breakpoint 2")398        lines = [first_line, second_line]399 400        # Set 2 breakpoints so we can verify that "stopCommands" get run as the401        # breakpoints get hit402        breakpoint_ids = self.set_source_breakpoints(source, lines)403        self.assertEqual(404            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"405        )406 407        # Continue after launch and hit the first breakpoint.408        # Get output from the console. This should contain both the409        # "stopCommands" that were run after the first breakpoint was hit410        self.continue_to_breakpoints(breakpoint_ids)411        output = self.get_console()412        self.verify_commands("stopCommands", output, stopCommands)413 414        # Continue again and hit the second breakpoint.415        # Get output from the console. This should contain both the416        # "stopCommands" that were run after the second breakpoint was hit417        self.continue_to_breakpoints(breakpoint_ids)418        output = self.get_console()419        self.verify_commands("stopCommands", output, stopCommands)420 421        # Continue until the program exits422        self.continue_to_exit()423        # Get output from the console. This should contain both the424        # "exitCommands" that were run after the second breakpoint was hit425        # and the "terminateCommands" due to the debugging session ending426        output = self.collect_console(pattern=terminateCommands[0])427        self.verify_commands("exitCommands", output, exitCommands)428        self.verify_commands("terminateCommands", output, terminateCommands)429 430    # Flakey on 32-bit Arm Linux.431    @skipIf(oslist=["linux"], archs=["arm$"])432    def test_extra_launch_commands(self):433        """434        Tests the "launchCommands" with extra launching settings435        """436        self.build_and_create_debug_adapter()437        program = self.getBuildArtifact("a.out")438 439        source = "main.c"440        first_line = line_number(source, "// breakpoint 1")441        second_line = line_number(source, "// breakpoint 2")442        # Set target binary and 2 breakpoints443        # then we can varify the "launchCommands" get run444        # also we can verify that "stopCommands" get run as the445        # breakpoints get hit446        launchCommands = [447            'target create "%s"' % (program),448            "breakpoint s -f main.c -l %d" % first_line,449            "breakpoint s -f main.c -l %d" % second_line,450            "process launch --stop-at-entry",451        ]452 453        initCommands = ["target list", "platform list"]454        preRunCommands = ["image list a.out", "image dump sections a.out"]455        stopCommands = ["frame variable", "bt"]456        exitCommands = ["expr 2+3", "expr 3+4"]457        self.launch(458            program,459            initCommands=initCommands,460            preRunCommands=preRunCommands,461            stopCommands=stopCommands,462            exitCommands=exitCommands,463            launchCommands=launchCommands,464        )465 466        # Get output from the console. This should contain both the467        # "initCommands" and the "preRunCommands".468        output = self.get_console()469        # Verify all "initCommands" were found in console output470        self.verify_commands("initCommands", output, initCommands)471        # Verify all "preRunCommands" were found in console output472        self.verify_commands("preRunCommands", output, preRunCommands)473 474        # Verify all "launchCommands" were found in console output475        # After execution, program should launch476        self.verify_commands("launchCommands", output, launchCommands)477        # Verify the "stopCommands" here478        self.continue_to_next_stop()479        output = self.get_console()480        self.verify_commands("stopCommands", output, stopCommands)481 482        # Continue and hit the second breakpoint.483        # Get output from the console. This should contain both the484        # "stopCommands" that were run after the first breakpoint was hit485        self.continue_to_next_stop()486        output = self.get_console()487        self.verify_commands("stopCommands", output, stopCommands)488 489        # Continue until the program exits490        self.continue_to_exit()491        # Get output from the console. This should contain both the492        # "exitCommands" that were run after the second breakpoint was hit493        output = self.get_console()494        self.verify_commands("exitCommands", output, exitCommands)495 496    def test_failing_launch_commands(self):497        """498        Tests "launchCommands" failures prevents a launch.499        """500        self.build_and_create_debug_adapter()501        program = self.getBuildArtifact("a.out")502 503        # Run an invalid launch command, in this case a bad path.504        bad_path = os.path.join("bad", "path")505        launchCommands = ['!target create "%s%s"' % (bad_path, program)]506 507        initCommands = ["target list", "platform list"]508        preRunCommands = ["image list a.out", "image dump sections a.out"]509        response = self.launch(510            program,511            initCommands=initCommands,512            preRunCommands=preRunCommands,513            launchCommands=launchCommands,514            expectFailure=True,515        )516 517        self.assertFalse(response["success"])518        self.assertRegex(519            response["body"]["error"]["format"],520            r"Failed to run launch commands\. See the Debug Console for more details",521        )522 523        # Get output from the console. This should contain both the524        # "initCommands" and the "preRunCommands".525        output = self.get_console()526        # Verify all "initCommands" were found in console output527        self.verify_commands("initCommands", output, initCommands)528        # Verify all "preRunCommands" were found in console output529        self.verify_commands("preRunCommands", output, preRunCommands)530 531        # Verify all "launchCommands" were founc in console output532        # The launch should fail due to the invalid command.533        self.verify_commands("launchCommands", output, launchCommands)534        self.assertRegex(output, re.escape(bad_path) + r".*does not exist")535 536    @skipIfNetBSD  # Hangs on NetBSD as well537    @skipIf(archs=["arm$", "aarch64"], oslist=["linux"])538    def test_terminate_commands(self):539        """540        Tests that the "terminateCommands", that can be passed during541        launch, are run when the debugger is disconnected.542        """543        self.build_and_create_debug_adapter()544        program = self.getBuildArtifact("a.out")545 546        terminateCommands = ["expr 4+2"]547        self.launch(548            program,549            stopOnEntry=True,550            terminateCommands=terminateCommands,551            disconnectAutomatically=False,552        )553        self.get_console()554        # Once it's disconnected the console should contain the555        # "terminateCommands"556        self.dap_server.request_disconnect(terminateDebuggee=True)557        output = self.collect_console(pattern=terminateCommands[0])558        self.verify_commands("terminateCommands", output, terminateCommands)559 560    @skipIfWindows561    def test_version(self):562        """563        Tests that "initialize" response contains the "version" string the same564        as the one returned by "version" command.565        """566        program = self.getBuildArtifact("a.out")567        self.build_and_launch(program)568 569        source = "main.c"570        breakpoint_line = line_number(source, "// breakpoint 1")571        lines = [breakpoint_line]572        # Set breakpoint in the thread function so we can step the threads573        breakpoint_ids = self.set_source_breakpoints(source, lines)574        self.continue_to_breakpoints(breakpoint_ids)575 576        version_eval_response = self.dap_server.request_evaluate(577            "`version", context="repl"578        )579        version_eval_output = version_eval_response["body"]["result"]580 581        version_string = self.dap_server.get_capability("$__lldb_version")582        self.assertEqual(583            version_eval_output.splitlines(),584            version_string.splitlines(),585            "version string does not match",586        )587 588    def test_no_lldbinit_flag(self):589        """590        Test that the --no-lldbinit flag prevents sourcing .lldbinit files.591        """592        # Create a temporary .lldbinit file in the home directory593        with tempfile.TemporaryDirectory() as temp_home:594            lldbinit_path = os.path.join(temp_home, ".lldbinit")595 596            # Write a command to the .lldbinit file that would set a unique setting597            with open(lldbinit_path, "w") as f:598                f.write("settings set stop-disassembly-display never\n")599                f.write("settings set target.x86-disassembly-flavor intel\n")600 601            # Test with --no-lldbinit flag (should NOT source .lldbinit)602            self.build_and_create_debug_adapter(603                lldbDAPEnv={"HOME": temp_home}, additional_args=["--no-lldbinit"]604            )605            program = self.getBuildArtifact("a.out")606 607            # Use initCommands to check if .lldbinit was sourced608            initCommands = ["settings show stop-disassembly-display"]609 610            # Launch with initCommands to check the setting611            self.launch(program, initCommands=initCommands, stopOnEntry=True)612 613            # Get console output to verify the setting was NOT set from .lldbinit614            output = self.get_console()615            self.assertTrue(output and len(output) > 0, "expect console output")616 617            # Verify the setting has default value, not "never" from .lldbinit618            self.assertNotIn(619                "never",620                output,621                "Setting should have default value when --no-lldbinit is used",622            )623 624            # Verify the initCommands were executed625            self.verify_commands("initCommands", output, initCommands)626 627    def test_stdio_redirection(self):628        """629        Test stdio redirection.630        """631        self.build_and_create_debug_adapter()632        program = self.getBuildArtifact("a.out")633 634        with tempfile.NamedTemporaryFile("rt") as f:635            self.launch(program, stdio=[None, f.name])636            self.continue_to_exit()637            lines = f.readlines()638            self.assertIn(639                program, lines[0], "make sure program path is in first argument"640            )641 642    @skipIfAsan643    @skipIfWindows644    @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))645    @skipIfBuildType(["debug"])646    def test_stdio_redirection_and_console(self):647        """648        Test stdio redirection and console.649        """650        self.build_and_create_debug_adapter()651        program = self.getBuildArtifact("a.out")652 653        with tempfile.NamedTemporaryFile("rt") as f:654            self.launch(655                program, console="integratedTerminal", stdio=[None, f.name, None]656            )657            self.continue_to_exit()658            lines = f.readlines()659            self.assertIn(660                program, lines[0], "make sure program path is in first argument"661            )662