xref: /llvm-project/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py (revision c954306ef763eb25f06432324889bde98735963d)
1"""
2Test lldb-dap setBreakpoints request
3"""
4
5import dap_server
6import lldbdap_testcase
7from lldbsuite.test import lldbutil
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10
11
12def get_subprocess(root_process, process_name):
13    queue = [root_process]
14    while queue:
15        process = queue.pop()
16        if process.name() == process_name:
17            return process
18        queue.extend(process.children())
19
20    self.assertTrue(False, "No subprocess with name %s found" % process_name)
21
22class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
23    def check_lldb_command(
24        self, lldb_command, contains_string, assert_msg, command_escape_prefix="`"
25    ):
26        response = self.dap_server.request_evaluate(
27            f"{command_escape_prefix}{lldb_command}", context="repl"
28        )
29        output = response["body"]["result"]
30        self.assertIn(
31            contains_string,
32            output,
33            (
34                """Verify %s by checking the command output:\n"""
35                """'''\n%s'''\nfor the string: "%s" """
36                % (assert_msg, output, contains_string)
37            ),
38        )
39
40    def test_scopes_variables_setVariable_evaluate(self):
41        """
42        Tests that the "scopes" request causes the currently selected
43        thread and frame to be updated. There are no DAP packets that tell
44        lldb-dap which thread and frame are selected other than the
45        "scopes" request. lldb-dap will now select the thread and frame
46        for the latest "scopes" request that it receives.
47
48        The LLDB command interpreter needs to have the right thread and
49        frame selected so that commands executed in the debug console act
50        on the right scope. This applies both to the expressions that are
51        evaluated and the lldb commands that start with the backtick
52        character.
53        """
54        program = self.getBuildArtifact("a.out")
55        self.build_and_launch(program)
56        source = "main.cpp"
57        breakpoint1_line = line_number(source, "// breakpoint 1")
58        lines = [breakpoint1_line]
59        # Set breakpoint in the thread function so we can step the threads
60        breakpoint_ids = self.set_source_breakpoints(source, lines)
61        self.assertEqual(
62            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
63        )
64        self.continue_to_breakpoints(breakpoint_ids)
65        # Cause a "scopes" to be sent for frame zero which should update the
66        # selected thread and frame to frame 0.
67        self.dap_server.get_local_variables(frameIndex=0)
68        # Verify frame #0 is selected in the command interpreter by running
69        # the "frame select" command with no frame index which will print the
70        # currently selected frame.
71        self.check_lldb_command("frame select", "frame #0", "frame 0 is selected")
72
73        # Cause a "scopes" to be sent for frame one which should update the
74        # selected thread and frame to frame 1.
75        self.dap_server.get_local_variables(frameIndex=1)
76        # Verify frame #1 is selected in the command interpreter by running
77        # the "frame select" command with no frame index which will print the
78        # currently selected frame.
79
80        self.check_lldb_command("frame select", "frame #1", "frame 1 is selected")
81
82    def test_custom_escape_prefix(self):
83        program = self.getBuildArtifact("a.out")
84        self.build_and_launch(program, commandEscapePrefix="::")
85        source = "main.cpp"
86        breakpoint1_line = line_number(source, "// breakpoint 1")
87        breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
88        self.continue_to_breakpoints(breakpoint_ids)
89
90        self.check_lldb_command(
91            "help",
92            "For more information on any command",
93            "Help can be invoked",
94            command_escape_prefix="::",
95        )
96
97    def test_empty_escape_prefix(self):
98        program = self.getBuildArtifact("a.out")
99        self.build_and_launch(program, commandEscapePrefix="")
100        source = "main.cpp"
101        breakpoint1_line = line_number(source, "// breakpoint 1")
102        breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
103        self.continue_to_breakpoints(breakpoint_ids)
104
105        self.check_lldb_command(
106            "help",
107            "For more information on any command",
108            "Help can be invoked",
109            command_escape_prefix="",
110        )
111
112    @skipIfWindows
113    def test_exit_status_message_sigterm(self):
114        source = "main.cpp"
115        program = self.getBuildArtifact("a.out")
116        self.build_and_launch(program, commandEscapePrefix="")
117        breakpoint1_line = line_number(source, "// breakpoint 1")
118        breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
119        self.continue_to_breakpoints(breakpoint_ids)
120
121        # Kill lldb-server process.
122        process_name = (
123            "debugserver" if platform.system() in ["Darwin"] else "lldb-server"
124        )
125
126        try:
127            import psutil
128        except ImportError:
129            print(
130                "psutil not installed, please install using 'pip install psutil'. "
131                "Skipping test_exit_status_message_sigterm test.",
132                file=sys.stderr,
133            )
134            return
135        process = get_subprocess(psutil.Process(os.getpid()), process_name)
136        process.terminate()
137        process.wait()
138
139        # Get the console output
140        console_output = self.collect_console(
141            timeout_secs=10.0, pattern="exited with status"
142        )
143
144        # Verify the exit status message is printed.
145        self.assertIn(
146            "exited with status = -1 (0xffffffff) debugserver died with signal SIGTERM",
147            console_output,
148            "Exit status does not contain message 'exited with status'",
149        )
150
151    def test_exit_status_message_ok(self):
152        program = self.getBuildArtifact("a.out")
153        self.build_and_launch(program, commandEscapePrefix="")
154        self.continue_to_exit()
155
156        # Get the console output
157        console_output = self.collect_console(
158            timeout_secs=10.0, pattern="exited with status"
159        )
160
161        # Verify the exit status message is printed.
162        self.assertIn(
163            "exited with status = 0 (0x00000000)",
164            console_output,
165            "Exit status does not contain message 'exited with status'",
166        )
167