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