1""" 2Test lldb-dap setBreakpoints request 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_setBreakpoints(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_source_map(self): 24 """ 25 This test simulates building two files in a folder, and then moving 26 each source to a different folder. Then, the debug session is started 27 with the corresponding source maps to have breakpoints and frames 28 working. 29 """ 30 self.build_and_create_debug_adaptor() 31 32 other_basename = "other-copy.c" 33 other_path = self.getBuildArtifact(other_basename) 34 35 source_folder = os.path.dirname(self.main_path) 36 37 new_main_folder = os.path.join(source_folder, "moved_main") 38 new_other_folder = os.path.join(source_folder, "moved_other") 39 40 new_main_path = os.path.join(new_main_folder, self.main_basename) 41 new_other_path = os.path.join(new_other_folder, other_basename) 42 43 # move the sources 44 os.mkdir(new_main_folder) 45 os.mkdir(new_other_folder) 46 shutil.move(self.main_path, new_main_path) 47 shutil.move(other_path, new_other_path) 48 49 main_line = line_number("main.cpp", "break 12") 50 other_line = line_number("other.c", "break other") 51 52 program = self.getBuildArtifact("a.out") 53 source_map = [ 54 [source_folder, new_main_folder], 55 [source_folder, new_other_folder], 56 ] 57 self.launch(program, sourceMap=source_map) 58 59 # breakpoint in main.cpp 60 response = self.dap_server.request_setBreakpoints(new_main_path, [main_line]) 61 breakpoints = response["body"]["breakpoints"] 62 self.assertEqual(len(breakpoints), 1) 63 breakpoint = breakpoints[0] 64 self.assertEqual(breakpoint["line"], main_line) 65 self.assertTrue(breakpoint["verified"]) 66 self.assertEqual(self.main_basename, breakpoint["source"]["name"]) 67 self.assertEqual(new_main_path, breakpoint["source"]["path"]) 68 69 # 2nd breakpoint, which is from a dynamically loaded library 70 response = self.dap_server.request_setBreakpoints(new_other_path, [other_line]) 71 breakpoints = response["body"]["breakpoints"] 72 breakpoint = breakpoints[0] 73 self.assertEqual(breakpoint["line"], other_line) 74 self.assertFalse(breakpoint["verified"]) 75 self.assertEqual(other_basename, breakpoint["source"]["name"]) 76 self.assertEqual(new_other_path, breakpoint["source"]["path"]) 77 other_breakpoint_id = breakpoint["id"] 78 79 self.dap_server.request_continue() 80 self.verify_breakpoint_hit([other_breakpoint_id]) 81 82 # 2nd breakpoint again, which should be valid at this point 83 response = self.dap_server.request_setBreakpoints(new_other_path, [other_line]) 84 breakpoints = response["body"]["breakpoints"] 85 breakpoint = breakpoints[0] 86 self.assertEqual(breakpoint["line"], other_line) 87 self.assertTrue(breakpoint["verified"]) 88 self.assertEqual(other_basename, breakpoint["source"]["name"]) 89 self.assertEqual(new_other_path, breakpoint["source"]["path"]) 90 91 # now we check the stack trace making sure that we got mapped source paths 92 frames = self.dap_server.request_stackTrace()["body"]["stackFrames"] 93 94 self.assertEqual(frames[0]["source"]["name"], other_basename) 95 self.assertEqual(frames[0]["source"]["path"], new_other_path) 96 97 self.assertEqual(frames[1]["source"]["name"], self.main_basename) 98 self.assertEqual(frames[1]["source"]["path"], new_main_path) 99 100 @skipIfWindows 101 def test_set_and_clear(self): 102 """Tests setting and clearing source file and line breakpoints. 103 This packet is a bit tricky on the debug adaptor side since there 104 is no "clearBreakpoints" packet. Source file and line breakpoints 105 are set by sending a "setBreakpoints" packet with a source file 106 specified and zero or more source lines. If breakpoints have been 107 set in the source file before, any existing breakpoints must remain 108 set, and any new breakpoints must be created, and any breakpoints 109 that were in previous requests and are not in the current request 110 must be removed. This function tests this setting and clearing 111 and makes sure things happen correctly. It doesn't test hitting 112 breakpoints and the functionality of each breakpoint, like 113 'conditions' and 'hitCondition' settings.""" 114 first_line = line_number("main.cpp", "break 12") 115 second_line = line_number("main.cpp", "break 13") 116 third_line = line_number("main.cpp", "break 14") 117 lines = [first_line, third_line, second_line] 118 119 # Visual Studio Code Debug Adaptors have no way to specify the file 120 # without launching or attaching to a process, so we must start a 121 # process in order to be able to set breakpoints. 122 program = self.getBuildArtifact("a.out") 123 self.build_and_launch(program) 124 125 # Set 3 breakpoints and verify that they got set correctly 126 response = self.dap_server.request_setBreakpoints(self.main_path, lines) 127 line_to_id = {} 128 if response: 129 breakpoints = response["body"]["breakpoints"] 130 self.assertEqual( 131 len(breakpoints), 132 len(lines), 133 "expect %u source breakpoints" % (len(lines)), 134 ) 135 for breakpoint, index in zip(breakpoints, range(len(lines))): 136 line = breakpoint["line"] 137 self.assertTrue(line, lines[index]) 138 # Store the "id" of the breakpoint that was set for later 139 line_to_id[line] = breakpoint["id"] 140 self.assertIn(line, lines, "line expected in lines array") 141 self.assertTrue(breakpoint["verified"], "expect breakpoint verified") 142 143 # There is no breakpoint delete packet, clients just send another 144 # setBreakpoints packet with the same source file with fewer lines. 145 # Below we remove the second line entry and call the setBreakpoints 146 # function again. We want to verify that any breakpoints that were set 147 # before still have the same "id". This means we didn't clear the 148 # breakpoint and set it again at the same location. We also need to 149 # verify that the second line location was actually removed. 150 lines.remove(second_line) 151 # Set 2 breakpoints and verify that the previous breakpoints that were 152 # set above are still set. 153 response = self.dap_server.request_setBreakpoints(self.main_path, lines) 154 if response: 155 breakpoints = response["body"]["breakpoints"] 156 self.assertEqual( 157 len(breakpoints), 158 len(lines), 159 "expect %u source breakpoints" % (len(lines)), 160 ) 161 for breakpoint, index in zip(breakpoints, range(len(lines))): 162 line = breakpoint["line"] 163 self.assertTrue(line, lines[index]) 164 # Verify the same breakpoints are still set within LLDB by 165 # making sure the breakpoint ID didn't change 166 self.assertEqual( 167 line_to_id[line], 168 breakpoint["id"], 169 "verify previous breakpoints stayed the same", 170 ) 171 self.assertIn(line, lines, "line expected in lines array") 172 self.assertTrue( 173 breakpoint["verified"], "expect breakpoint still verified" 174 ) 175 176 # Now get the full list of breakpoints set in the target and verify 177 # we have only 2 breakpoints set. The response above could have told 178 # us about 2 breakpoints, but we want to make sure we don't have the 179 # third one still set in the target 180 response = self.dap_server.request_testGetTargetBreakpoints() 181 if response: 182 breakpoints = response["body"]["breakpoints"] 183 self.assertEqual( 184 len(breakpoints), 185 len(lines), 186 "expect %u source breakpoints" % (len(lines)), 187 ) 188 for breakpoint in breakpoints: 189 line = breakpoint["line"] 190 # Verify the same breakpoints are still set within LLDB by 191 # making sure the breakpoint ID didn't change 192 self.assertEqual( 193 line_to_id[line], 194 breakpoint["id"], 195 "verify previous breakpoints stayed the same", 196 ) 197 self.assertIn(line, lines, "line expected in lines array") 198 self.assertTrue( 199 breakpoint["verified"], "expect breakpoint still verified" 200 ) 201 202 # Now clear all breakpoints for the source file by passing down an 203 # empty lines array 204 lines = [] 205 response = self.dap_server.request_setBreakpoints(self.main_path, lines) 206 if response: 207 breakpoints = response["body"]["breakpoints"] 208 self.assertEqual( 209 len(breakpoints), 210 len(lines), 211 "expect %u source breakpoints" % (len(lines)), 212 ) 213 214 # Verify with the target that all breakpoints have been cleared 215 response = self.dap_server.request_testGetTargetBreakpoints() 216 if response: 217 breakpoints = response["body"]["breakpoints"] 218 self.assertEqual( 219 len(breakpoints), 220 len(lines), 221 "expect %u source breakpoints" % (len(lines)), 222 ) 223 224 # Now set a breakpoint again in the same source file and verify it 225 # was added. 226 lines = [second_line] 227 response = self.dap_server.request_setBreakpoints(self.main_path, lines) 228 if response: 229 breakpoints = response["body"]["breakpoints"] 230 self.assertEqual( 231 len(breakpoints), 232 len(lines), 233 "expect %u source breakpoints" % (len(lines)), 234 ) 235 for breakpoint in breakpoints: 236 line = breakpoint["line"] 237 self.assertIn(line, lines, "line expected in lines array") 238 self.assertTrue( 239 breakpoint["verified"], "expect breakpoint still verified" 240 ) 241 242 # Now get the full list of breakpoints set in the target and verify 243 # we have only 2 breakpoints set. The response above could have told 244 # us about 2 breakpoints, but we want to make sure we don't have the 245 # third one still set in the target 246 response = self.dap_server.request_testGetTargetBreakpoints() 247 if response: 248 breakpoints = response["body"]["breakpoints"] 249 self.assertEqual( 250 len(breakpoints), 251 len(lines), 252 "expect %u source breakpoints" % (len(lines)), 253 ) 254 for breakpoint in breakpoints: 255 line = breakpoint["line"] 256 self.assertIn(line, lines, "line expected in lines array") 257 self.assertTrue( 258 breakpoint["verified"], "expect breakpoint still verified" 259 ) 260 261 @skipIfWindows 262 def test_clear_breakpoints_unset_breakpoints(self): 263 """Test clearing breakpoints like test_set_and_clear, but clear 264 breakpoints by omitting the breakpoints array instead of sending an 265 empty one.""" 266 lines = [ 267 line_number("main.cpp", "break 12"), 268 line_number("main.cpp", "break 13"), 269 ] 270 271 # Visual Studio Code Debug Adaptors have no way to specify the file 272 # without launching or attaching to a process, so we must start a 273 # process in order to be able to set breakpoints. 274 program = self.getBuildArtifact("a.out") 275 self.build_and_launch(program) 276 277 # Set one breakpoint and verify that it got set correctly. 278 response = self.dap_server.request_setBreakpoints(self.main_path, lines) 279 line_to_id = {} 280 breakpoints = response["body"]["breakpoints"] 281 self.assertEqual( 282 len(breakpoints), len(lines), "expect %u source breakpoints" % (len(lines)) 283 ) 284 for breakpoint, index in zip(breakpoints, range(len(lines))): 285 line = breakpoint["line"] 286 self.assertTrue(line, lines[index]) 287 # Store the "id" of the breakpoint that was set for later 288 line_to_id[line] = breakpoint["id"] 289 self.assertIn(line, lines, "line expected in lines array") 290 self.assertTrue(breakpoint["verified"], "expect breakpoint verified") 291 292 # Now clear all breakpoints for the source file by not setting the 293 # lines array. 294 lines = None 295 response = self.dap_server.request_setBreakpoints(self.main_path, lines) 296 breakpoints = response["body"]["breakpoints"] 297 self.assertEqual(len(breakpoints), 0, "expect no source breakpoints") 298 299 # Verify with the target that all breakpoints have been cleared. 300 response = self.dap_server.request_testGetTargetBreakpoints() 301 breakpoints = response["body"]["breakpoints"] 302 self.assertEqual(len(breakpoints), 0, "expect no source breakpoints") 303 304 @skipIfWindows 305 def test_functionality(self): 306 """Tests hitting breakpoints and the functionality of a single 307 breakpoint, like 'conditions' and 'hitCondition' settings.""" 308 loop_line = line_number("main.cpp", "// break loop") 309 310 program = self.getBuildArtifact("a.out") 311 self.build_and_launch(program) 312 # Set a breakpoint at the loop line with no condition and no 313 # hitCondition 314 breakpoint_ids = self.set_source_breakpoints(self.main_path, [loop_line]) 315 self.assertEqual(len(breakpoint_ids), 1, "expect one breakpoint") 316 self.dap_server.request_continue() 317 318 # Verify we hit the breakpoint we just set 319 self.verify_breakpoint_hit(breakpoint_ids) 320 321 # Make sure i is zero at first breakpoint 322 i = int(self.dap_server.get_local_variable_value("i")) 323 self.assertEqual(i, 0, "i != 0 after hitting breakpoint") 324 325 # Update the condition on our breakpoint 326 new_breakpoint_ids = self.set_source_breakpoints( 327 self.main_path, [loop_line], [{"condition": "i==4"}] 328 ) 329 self.assertEqual( 330 breakpoint_ids, 331 new_breakpoint_ids, 332 "existing breakpoint should have its condition " "updated", 333 ) 334 335 self.continue_to_breakpoints(breakpoint_ids) 336 i = int(self.dap_server.get_local_variable_value("i")) 337 self.assertEqual(i, 4, "i != 4 showing conditional works") 338 339 new_breakpoint_ids = self.set_source_breakpoints( 340 self.main_path, [loop_line], [{"hitCondition": "2"}] 341 ) 342 343 self.assertEqual( 344 breakpoint_ids, 345 new_breakpoint_ids, 346 "existing breakpoint should have its condition " "updated", 347 ) 348 349 # Continue with a hitCondition of 2 and expect it to skip 1 value 350 self.continue_to_breakpoints(breakpoint_ids) 351 i = int(self.dap_server.get_local_variable_value("i")) 352 self.assertEqual(i, 6, "i != 6 showing hitCondition works") 353 354 # continue after hitting our hitCondition and make sure it only goes 355 # up by 1 356 self.continue_to_breakpoints(breakpoint_ids) 357 i = int(self.dap_server.get_local_variable_value("i")) 358 self.assertEqual(i, 7, "i != 7 showing post hitCondition hits every time") 359