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