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