xref: /llvm-project/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py (revision fa6377119c0624773cb698935692d46843e9f6ec)
1"""
2Test lldb-dap logpoints feature.
3"""
4
5
6import dap_server
7import shutil
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11import lldbdap_testcase
12import os
13
14
15class TestDAP_logpoints(lldbdap_testcase.DAPTestCaseBase):
16    def setUp(self):
17        lldbdap_testcase.DAPTestCaseBase.setUp(self)
18
19        self.main_basename = "main-copy.cpp"
20        self.main_path = os.path.realpath(self.getBuildArtifact(self.main_basename))
21
22    @skipIfWindows
23    def test_logmessage_basic(self):
24        """Tests breakpoint logmessage basic functionality."""
25        before_loop_line = line_number("main.cpp", "// before loop")
26        loop_line = line_number("main.cpp", "// break loop")
27        after_loop_line = line_number("main.cpp", "// after loop")
28
29        program = self.getBuildArtifact("a.out")
30        self.build_and_launch(program)
31
32        # Set a breakpoint at a line before loop
33        before_loop_breakpoint_ids = self.set_source_breakpoints(
34            self.main_path, [before_loop_line]
35        )
36        self.assertEqual(len(before_loop_breakpoint_ids), 1, "expect one breakpoint")
37
38        self.dap_server.request_continue()
39
40        # Verify we hit the breakpoint before loop line
41        self.verify_breakpoint_hit(before_loop_breakpoint_ids)
42
43        # Swallow old console output
44        self.get_console()
45
46        # Set two breakpoints:
47        # 1. First at the loop line with logMessage
48        # 2. Second guard breakpoint at a line after loop
49        logMessage_prefix = "This is log message for { -- "
50        logMessage = logMessage_prefix + "{i + 3}, {message}"
51        [loop_breakpoint_id, post_loop_breakpoint_id] = self.set_source_breakpoints(
52            self.main_path,
53            [loop_line, after_loop_line],
54            [{"logMessage": logMessage}, {}],
55        )
56
57        # Continue to trigger the breakpoint with log messages
58        self.dap_server.request_continue()
59
60        # Verify we hit the breakpoint after loop line
61        self.verify_breakpoint_hit([post_loop_breakpoint_id])
62
63        output = self.get_console()
64        lines = output.splitlines()
65        logMessage_output = []
66        for line in lines:
67            if line.startswith(logMessage_prefix):
68                logMessage_output.append(line)
69
70        # Verify logMessage count
71        loop_count = 10
72        self.assertEqual(len(logMessage_output), loop_count)
73
74        message_addr_pattern = r"\b0x[0-9A-Fa-f]+\b"
75        message_content = '"Hello from main!"'
76        # Verify log message match
77        for idx, logMessage_line in enumerate(logMessage_output):
78            result = idx + 3
79            reg_str = (
80                f"{logMessage_prefix}{result}, {message_addr_pattern} {message_content}"
81            )
82            self.assertRegex(logMessage_line, reg_str)
83
84    @skipIfWindows
85    def test_logmessage_advanced(self):
86        """Tests breakpoint logmessage functionality for complex expression."""
87        before_loop_line = line_number("main.cpp", "// before loop")
88        loop_line = line_number("main.cpp", "// break loop")
89        after_loop_line = line_number("main.cpp", "// after loop")
90
91        program = self.getBuildArtifact("a.out")
92        self.build_and_launch(program)
93
94        # Set a breakpoint at a line before loop
95        before_loop_breakpoint_ids = self.set_source_breakpoints(
96            self.main_path, [before_loop_line]
97        )
98        self.assertEqual(len(before_loop_breakpoint_ids), 1, "expect one breakpoint")
99
100        self.dap_server.request_continue()
101
102        # Verify we hit the breakpoint before loop line
103        self.verify_breakpoint_hit(before_loop_breakpoint_ids)
104
105        # Swallow old console output
106        self.get_console()
107
108        # Set two breakpoints:
109        # 1. First at the loop line with logMessage
110        # 2. Second guard breakpoint at a line after loop
111        logMessage_prefix = "This is log message for { -- "
112        logMessage = (
113            logMessage_prefix
114            + "{int y = 0; if (i % 3 == 0) { y = i + 3;} else {y = i * 3;} y}"
115        )
116        [loop_breakpoint_id, post_loop_breakpoint_id] = self.set_source_breakpoints(
117            self.main_path,
118            [loop_line, after_loop_line],
119            [{"logMessage": logMessage}, {}],
120        )
121
122        # Continue to trigger the breakpoint with log messages
123        self.dap_server.request_continue()
124
125        # Verify we hit the breakpoint after loop line
126        self.verify_breakpoint_hit([post_loop_breakpoint_id])
127
128        output = self.get_console()
129        lines = output.splitlines()
130        logMessage_output = []
131        for line in lines:
132            if line.startswith(logMessage_prefix):
133                logMessage_output.append(line)
134
135        # Verify logMessage count
136        loop_count = 10
137        self.assertEqual(len(logMessage_output), loop_count)
138
139        # Verify log message match
140        for idx, logMessage_line in enumerate(logMessage_output):
141            result = idx + 3 if idx % 3 == 0 else idx * 3
142            self.assertEqual(logMessage_line, logMessage_prefix + str(result))
143
144    @skipIfWindows
145    def test_logmessage_format(self):
146        """
147        Tests breakpoint logmessage functionality with format.
148        """
149        before_loop_line = line_number("main.cpp", "// before loop")
150        loop_line = line_number("main.cpp", "// break loop")
151        after_loop_line = line_number("main.cpp", "// after loop")
152
153        program = self.getBuildArtifact("a.out")
154        self.build_and_launch(program)
155
156        # Set a breakpoint at a line before loop
157        before_loop_breakpoint_ids = self.set_source_breakpoints(
158            self.main_path, [before_loop_line]
159        )
160        self.assertEqual(len(before_loop_breakpoint_ids), 1, "expect one breakpoint")
161
162        self.dap_server.request_continue()
163
164        # Verify we hit the breakpoint before loop line
165        self.verify_breakpoint_hit(before_loop_breakpoint_ids)
166
167        # Swallow old console output
168        self.get_console()
169
170        # Set two breakpoints:
171        # 1. First at the loop line with logMessage
172        # 2. Second guard breakpoint at a line after loop
173        logMessage_prefix = "This is log message for -- "
174        logMessage_with_format = "part1\tpart2\bpart3\x64part4"
175        logMessage_with_format_raw = r"part1\tpart2\bpart3\x64part4"
176        logMessage = logMessage_prefix + logMessage_with_format_raw + "{i - 1}"
177        [loop_breakpoint_id, post_loop_breakpoint_id] = self.set_source_breakpoints(
178            self.main_path,
179            [loop_line, after_loop_line],
180            [{"logMessage": logMessage}, {}],
181        )
182
183        # Continue to trigger the breakpoint with log messages
184        self.dap_server.request_continue()
185
186        # Verify we hit the breakpoint after loop line
187        self.verify_breakpoint_hit([post_loop_breakpoint_id])
188
189        output = self.get_console()
190        lines = output.splitlines()
191        logMessage_output = []
192        for line in lines:
193            if line.startswith(logMessage_prefix):
194                logMessage_output.append(line)
195
196        # Verify logMessage count
197        loop_count = 10
198        self.assertEqual(len(logMessage_output), loop_count)
199
200        # Verify log message match
201        for idx, logMessage_line in enumerate(logMessage_output):
202            result = idx - 1
203            self.assertEqual(
204                logMessage_line,
205                logMessage_prefix + logMessage_with_format + str(result),
206            )
207
208    @skipIfWindows
209    def test_logmessage_format_failure(self):
210        """
211        Tests breakpoint logmessage format with parsing failure.
212        """
213        before_loop_line = line_number("main.cpp", "// before loop")
214        loop_line = line_number("main.cpp", "// break loop")
215        after_loop_line = line_number("main.cpp", "// after loop")
216
217        program = self.getBuildArtifact("a.out")
218        self.build_and_launch(program)
219
220        # Set a breakpoint at a line before loop
221        before_loop_breakpoint_ids = self.set_source_breakpoints(
222            self.main_path, [before_loop_line]
223        )
224        self.assertEqual(len(before_loop_breakpoint_ids), 1, "expect one breakpoint")
225
226        self.dap_server.request_continue()
227
228        # Verify we hit the breakpoint before loop line
229        self.verify_breakpoint_hit(before_loop_breakpoint_ids)
230
231        # Swallow old console output
232        self.get_console()
233
234        # Set two breakpoints:
235        # 1. First at the loop line with logMessage
236        # 2. Second guard breakpoint at a line after loop
237        logMessage_prefix = "This is log message for -- "
238        # log message missing hex number.
239        logMessage_with_format_raw = r"part1\x"
240        logMessage = logMessage_prefix + logMessage_with_format_raw
241        [loop_breakpoint_id, post_loop_breakpoint_id] = self.set_source_breakpoints(
242            self.main_path,
243            [loop_line, after_loop_line],
244            [{"logMessage": logMessage}, {}],
245        )
246
247        # Continue to trigger the breakpoint with log messages
248        self.dap_server.request_continue()
249
250        # Verify we hit logpoint breakpoint if it's format has error.
251        self.verify_breakpoint_hit([loop_breakpoint_id])
252
253        output = self.get_console()
254        lines = output.splitlines()
255
256        failure_prefix = "Log message has error:"
257        logMessage_output = []
258        logMessage_failure_output = []
259        for line in lines:
260            if line.startswith(logMessage_prefix):
261                logMessage_output.append(line)
262            elif line.startswith(failure_prefix):
263                logMessage_failure_output.append(line)
264
265        # Verify logMessage failure message
266        self.assertEqual(len(logMessage_failure_output), 1)
267        self.assertEqual(
268            logMessage_failure_output[0].strip(),
269            failure_prefix + " missing hex number following '\\x'",
270        )
271