1""" 2Test lldb-vscode setBreakpoints request 3""" 4 5 6import unittest2 7import vscode 8from lldbsuite.test.decorators import * 9from lldbsuite.test.lldbtest import * 10from lldbsuite.test import lldbutil 11import lldbvscode_testcase 12import time 13import os 14 15 16class TestVSCode_launch(lldbvscode_testcase.VSCodeTestCaseBase): 17 18 @skipIfWindows 19 @skipIfDarwin # Flaky 20 @skipIfRemote 21 def test_default(self): 22 ''' 23 Tests the default launch of a simple program. No arguments, 24 environment, or anything else is specified. 25 ''' 26 program = self.getBuildArtifact("a.out") 27 self.build_and_launch(program) 28 self.continue_to_exit() 29 # Now get the STDOUT and verify our program argument is correct 30 output = self.get_stdout() 31 self.assertTrue(output and len(output) > 0, 32 "expect program output") 33 lines = output.splitlines() 34 self.assertIn(program, lines[0], 35 "make sure program path is in first argument") 36 37 @skipIfWindows 38 @skipIfRemote 39 def test_termination(self): 40 ''' 41 Tests the correct termination of lldb-vscode upon a 'disconnect' 42 request. 43 ''' 44 self.create_debug_adaptor() 45 # The underlying lldb-vscode process must be alive 46 self.assertEqual(self.vscode.process.poll(), None) 47 48 # The lldb-vscode process should finish even though 49 # we didn't close the communication socket explicitly 50 self.vscode.request_disconnect() 51 52 # Wait until the underlying lldb-vscode process dies. 53 # We need to do this because the popen.wait function in python2.7 54 # doesn't have a timeout argument. 55 for _ in range(10): 56 time.sleep(1) 57 if self.vscode.process.poll() is not None: 58 break 59 # Check the return code 60 self.assertEqual(self.vscode.process.poll(), 0) 61 62 @skipIfWindows 63 @skipIfRemote 64 def test_stopOnEntry(self): 65 ''' 66 Tests the default launch of a simple program that stops at the 67 entry point instead of continuing. 68 ''' 69 program = self.getBuildArtifact("a.out") 70 self.build_and_launch(program, stopOnEntry=True) 71 self.set_function_breakpoints(['main']) 72 stopped_events = self.continue_to_next_stop() 73 for stopped_event in stopped_events: 74 if 'body' in stopped_event: 75 body = stopped_event['body'] 76 if 'reason' in body: 77 reason = body['reason'] 78 self.assertTrue( 79 reason != 'breakpoint', 80 'verify stop isn\'t "main" breakpoint') 81 82 @skipIfWindows 83 @skipIfRemote 84 def test_cwd(self): 85 ''' 86 Tests the default launch of a simple program with a current working 87 directory. 88 ''' 89 program = self.getBuildArtifact("a.out") 90 program_parent_dir = os.path.realpath( 91 os.path.dirname(os.path.dirname(program))) 92 self.build_and_launch(program, 93 cwd=program_parent_dir) 94 self.continue_to_exit() 95 # Now get the STDOUT and verify our program argument is correct 96 output = self.get_stdout() 97 self.assertTrue(output and len(output) > 0, 98 "expect program output") 99 lines = output.splitlines() 100 found = False 101 for line in lines: 102 if line.startswith('cwd = \"'): 103 quote_path = '"%s"' % (program_parent_dir) 104 found = True 105 self.assertIn(quote_path, line, 106 "working directory '%s' not in '%s'" % ( 107 program_parent_dir, line)) 108 self.assertTrue(found, "verified program working directory") 109 110 @skipIfWindows 111 @skipIfRemote 112 def test_debuggerRoot(self): 113 ''' 114 Tests the "debuggerRoot" will change the working directory of 115 the lldb-vscode debug adaptor. 116 ''' 117 program = self.getBuildArtifact("a.out") 118 program_parent_dir = os.path.realpath( 119 os.path.dirname(os.path.dirname(program))) 120 commands = ['platform shell echo cwd = $PWD'] 121 self.build_and_launch(program, 122 debuggerRoot=program_parent_dir, 123 initCommands=commands) 124 output = self.get_console() 125 self.assertTrue(output and len(output) > 0, 126 "expect console output") 127 lines = output.splitlines() 128 prefix = 'cwd = ' 129 found = False 130 for line in lines: 131 if line.startswith(prefix): 132 found = True 133 self.assertEquals(program_parent_dir, line[len(prefix):], 134 "lldb-vscode working dir '%s' == '%s'" % ( 135 program_parent_dir, line[6:])) 136 self.assertTrue(found, "verified lldb-vscode working directory") 137 self.continue_to_exit() 138 139 @skipIfWindows 140 @skipIfRemote 141 def test_sourcePath(self): 142 ''' 143 Tests the "sourcePath" will set the target.source-map. 144 ''' 145 program = self.getBuildArtifact("a.out") 146 program_dir = os.path.dirname(program) 147 self.build_and_launch(program, 148 sourcePath=program_dir) 149 output = self.get_console() 150 self.assertTrue(output and len(output) > 0, 151 "expect console output") 152 lines = output.splitlines() 153 prefix = '(lldb) settings set target.source-map "." ' 154 found = False 155 for line in lines: 156 if line.startswith(prefix): 157 found = True 158 quoted_path = '"%s"' % (program_dir) 159 self.assertEquals(quoted_path, line[len(prefix):], 160 "lldb-vscode working dir %s == %s" % ( 161 quoted_path, line[6:])) 162 self.assertTrue(found, 'found "sourcePath" in console output') 163 self.continue_to_exit() 164 165 @skipIfWindows 166 @skipIfRemote 167 def test_disableSTDIO(self): 168 ''' 169 Tests the default launch of a simple program with STDIO disabled. 170 ''' 171 program = self.getBuildArtifact("a.out") 172 self.build_and_launch(program, 173 disableSTDIO=True) 174 self.continue_to_exit() 175 # Now get the STDOUT and verify our program argument is correct 176 output = self.get_stdout() 177 self.assertEquals(output, None, 178 "expect no program output") 179 180 @skipIfWindows 181 @skipIfLinux # shell argument expansion doesn't seem to work on Linux 182 @expectedFailureAll(oslist=["freebsd", "netbsd"], 183 bugnumber="llvm.org/pr48349") 184 @skipIfRemote 185 def test_shellExpandArguments_enabled(self): 186 ''' 187 Tests the default launch of a simple program with shell expansion 188 enabled. 189 ''' 190 program = self.getBuildArtifact("a.out") 191 program_dir = os.path.dirname(program) 192 glob = os.path.join(program_dir, '*.out') 193 self.build_and_launch(program, args=[glob], shellExpandArguments=True) 194 self.continue_to_exit() 195 # Now get the STDOUT and verify our program argument is correct 196 output = self.get_stdout() 197 self.assertTrue(output and len(output) > 0, 198 "expect no program output") 199 lines = output.splitlines() 200 for line in lines: 201 quote_path = '"%s"' % (program) 202 if line.startswith("arg[1] ="): 203 self.assertIn(quote_path, line, 204 'verify "%s" expanded to "%s"' % ( 205 glob, program)) 206 207 @skipIfWindows 208 @skipIfRemote 209 def test_shellExpandArguments_disabled(self): 210 ''' 211 Tests the default launch of a simple program with shell expansion 212 disabled. 213 ''' 214 program = self.getBuildArtifact("a.out") 215 program_dir = os.path.dirname(program) 216 glob = os.path.join(program_dir, '*.out') 217 self.build_and_launch(program, 218 args=[glob], 219 shellExpandArguments=False) 220 self.continue_to_exit() 221 # Now get the STDOUT and verify our program argument is correct 222 output = self.get_stdout() 223 self.assertTrue(output and len(output) > 0, 224 "expect no program output") 225 lines = output.splitlines() 226 for line in lines: 227 quote_path = '"%s"' % (glob) 228 if line.startswith("arg[1] ="): 229 self.assertIn(quote_path, line, 230 'verify "%s" stayed to "%s"' % ( 231 glob, glob)) 232 233 @skipIfWindows 234 @skipIfRemote 235 def test_args(self): 236 ''' 237 Tests launch of a simple program with arguments 238 ''' 239 program = self.getBuildArtifact("a.out") 240 args = ["one", "with space", "'with single quotes'", 241 '"with double quotes"'] 242 self.build_and_launch(program, 243 args=args) 244 self.continue_to_exit() 245 246 # Now get the STDOUT and verify our arguments got passed correctly 247 output = self.get_stdout() 248 self.assertTrue(output and len(output) > 0, 249 "expect program output") 250 lines = output.splitlines() 251 # Skip the first argument that contains the program name 252 lines.pop(0) 253 # Make sure arguments we specified are correct 254 for (i, arg) in enumerate(args): 255 quoted_arg = '"%s"' % (arg) 256 self.assertIn(quoted_arg, lines[i], 257 'arg[%i] "%s" not in "%s"' % (i+1, quoted_arg, lines[i])) 258 259 @skipIfWindows 260 @skipIfRemote 261 def test_environment(self): 262 ''' 263 Tests launch of a simple program with environment variables 264 ''' 265 program = self.getBuildArtifact("a.out") 266 env = ["NO_VALUE", "WITH_VALUE=BAR", "EMPTY_VALUE=", 267 "SPACE=Hello World"] 268 self.build_and_launch(program, 269 env=env) 270 self.continue_to_exit() 271 272 # Now get the STDOUT and verify our arguments got passed correctly 273 output = self.get_stdout() 274 self.assertTrue(output and len(output) > 0, 275 "expect program output") 276 lines = output.splitlines() 277 # Skip the all arguments so we have only environment vars left 278 while len(lines) and lines[0].startswith("arg["): 279 lines.pop(0) 280 # Make sure each environment variable in "env" is actually set in the 281 # program environment that was printed to STDOUT 282 for var in env: 283 found = False 284 for program_var in lines: 285 if var in program_var: 286 found = True 287 break 288 self.assertTrue(found, 289 '"%s" must exist in program environment (%s)' % ( 290 var, lines)) 291 292 @skipIfWindows 293 @skipIfRemote 294 @skipIf(archs=["arm", "aarch64"]) # failed run https://lab.llvm.org/buildbot/#/builders/96/builds/6933 295 def test_commands(self): 296 ''' 297 Tests the "initCommands", "preRunCommands", "stopCommands", 298 "terminateCommands" and "exitCommands" that can be passed during 299 launch. 300 301 "initCommands" are a list of LLDB commands that get executed 302 before the targt is created. 303 "preRunCommands" are a list of LLDB commands that get executed 304 after the target has been created and before the launch. 305 "stopCommands" are a list of LLDB commands that get executed each 306 time the program stops. 307 "exitCommands" are a list of LLDB commands that get executed when 308 the process exits 309 "terminateCommands" are a list of LLDB commands that get executed when 310 the debugger session terminates. 311 ''' 312 program = self.getBuildArtifact("a.out") 313 initCommands = ['target list', 'platform list'] 314 preRunCommands = ['image list a.out', 'image dump sections a.out'] 315 postRunCommands = ['help trace', 'help process trace'] 316 stopCommands = ['frame variable', 'bt'] 317 exitCommands = ['expr 2+3', 'expr 3+4'] 318 terminateCommands = ['expr 4+2'] 319 self.build_and_launch(program, 320 initCommands=initCommands, 321 preRunCommands=preRunCommands, 322 postRunCommands=postRunCommands, 323 stopCommands=stopCommands, 324 exitCommands=exitCommands, 325 terminateCommands=terminateCommands) 326 327 # Get output from the console. This should contain both the 328 # "initCommands" and the "preRunCommands". 329 output = self.get_console() 330 # Verify all "initCommands" were found in console output 331 self.verify_commands('initCommands', output, initCommands) 332 # Verify all "preRunCommands" were found in console output 333 self.verify_commands('preRunCommands', output, preRunCommands) 334 # Verify all "postRunCommands" were found in console output 335 self.verify_commands('postRunCommands', output, postRunCommands) 336 337 source = 'main.c' 338 first_line = line_number(source, '// breakpoint 1') 339 second_line = line_number(source, '// breakpoint 2') 340 lines = [first_line, second_line] 341 342 # Set 2 breakpoints so we can verify that "stopCommands" get run as the 343 # breakpoints get hit 344 breakpoint_ids = self.set_source_breakpoints(source, lines) 345 self.assertEquals(len(breakpoint_ids), len(lines), 346 "expect correct number of breakpoints") 347 348 # Continue after launch and hit the first breakpoint. 349 # Get output from the console. This should contain both the 350 # "stopCommands" that were run after the first breakpoint was hit 351 self.continue_to_breakpoints(breakpoint_ids) 352 output = self.get_console(timeout=1.0) 353 self.verify_commands('stopCommands', output, stopCommands) 354 355 # Continue again and hit the second breakpoint. 356 # Get output from the console. This should contain both the 357 # "stopCommands" that were run after the second breakpoint was hit 358 self.continue_to_breakpoints(breakpoint_ids) 359 output = self.get_console(timeout=1.0) 360 self.verify_commands('stopCommands', output, stopCommands) 361 362 # Continue until the program exits 363 self.continue_to_exit() 364 # Get output from the console. This should contain both the 365 # "exitCommands" that were run after the second breakpoint was hit 366 # and the "terminateCommands" due to the debugging session ending 367 output = self.collect_console(duration=1.0) 368 self.verify_commands('exitCommands', output, exitCommands) 369 self.verify_commands('terminateCommands', output, terminateCommands) 370 371 @skipIfWindows 372 @skipIfRemote 373 def test_extra_launch_commands(self): 374 ''' 375 Tests the "launchCommands" with extra launching settings 376 ''' 377 self.build_and_create_debug_adaptor() 378 program = self.getBuildArtifact("a.out") 379 380 source = 'main.c' 381 first_line = line_number(source, '// breakpoint 1') 382 second_line = line_number(source, '// breakpoint 2') 383 # Set target binary and 2 breakpoints 384 # then we can varify the "launchCommands" get run 385 # also we can verify that "stopCommands" get run as the 386 # breakpoints get hit 387 launchCommands = [ 388 'target create "%s"' % (program), 389 'breakpoint s -f main.c -l %d' % first_line, 390 'breakpoint s -f main.c -l %d' % second_line, 391 'process launch --stop-at-entry' 392 ] 393 394 initCommands = ['target list', 'platform list'] 395 preRunCommands = ['image list a.out', 'image dump sections a.out'] 396 stopCommands = ['frame variable', 'bt'] 397 exitCommands = ['expr 2+3', 'expr 3+4'] 398 self.launch(program, 399 initCommands=initCommands, 400 preRunCommands=preRunCommands, 401 stopCommands=stopCommands, 402 exitCommands=exitCommands, 403 launchCommands=launchCommands) 404 405 # Get output from the console. This should contain both the 406 # "initCommands" and the "preRunCommands". 407 output = self.get_console() 408 # Verify all "initCommands" were found in console output 409 self.verify_commands('initCommands', output, initCommands) 410 # Verify all "preRunCommands" were found in console output 411 self.verify_commands('preRunCommands', output, preRunCommands) 412 413 # Verify all "launchCommands" were founc in console output 414 # After execution, program should launch 415 self.verify_commands('launchCommands', output, launchCommands) 416 # Verify the "stopCommands" here 417 self.continue_to_next_stop() 418 output = self.get_console(timeout=1.0) 419 self.verify_commands('stopCommands', output, stopCommands) 420 421 # Continue and hit the second breakpoint. 422 # Get output from the console. This should contain both the 423 # "stopCommands" that were run after the first breakpoint was hit 424 self.continue_to_next_stop() 425 output = self.get_console(timeout=1.0) 426 self.verify_commands('stopCommands', output, stopCommands) 427 428 # Continue until the program exits 429 self.continue_to_exit() 430 # Get output from the console. This should contain both the 431 # "exitCommands" that were run after the second breakpoint was hit 432 output = self.get_console(timeout=1.0) 433 self.verify_commands('exitCommands', output, exitCommands) 434 435 @skipIfWindows 436 @skipIfNetBSD # Hangs on NetBSD as well 437 @skipIfDarwin 438 @skipIf(archs=["arm", "aarch64"]) # Example of a flaky run http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/5540/steps/test/logs/stdio 439 def test_terminate_commands(self): 440 ''' 441 Tests that the "terminateCommands", that can be passed during 442 launch, are run when the debugger is disconnected. 443 ''' 444 self.build_and_create_debug_adaptor() 445 program = self.getBuildArtifact("a.out") 446 447 terminateCommands = ['expr 4+2'] 448 self.launch(program=program, 449 terminateCommands=terminateCommands) 450 self.get_console() 451 # Once it's disconnected the console should contain the 452 # "terminateCommands" 453 self.vscode.request_disconnect(terminateDebuggee=True) 454 output = self.collect_console(duration=1.0) 455 self.verify_commands('terminateCommands', output, terminateCommands) 456