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.assertTrue(program in 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.assertTrue(quote_path in 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.assertTrue(quote_path in 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.assertTrue(quote_path in 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.assertTrue(quoted_arg in 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 def test_commands(self): 297 ''' 298 Tests the "initCommands", "preRunCommands", "stopCommands", 299 "terminateCommands" and "exitCommands" that can be passed during 300 launch. 301 302 "initCommands" are a list of LLDB commands that get executed 303 before the targt is created. 304 "preRunCommands" are a list of LLDB commands that get executed 305 after the target has been created and before the launch. 306 "stopCommands" are a list of LLDB commands that get executed each 307 time the program stops. 308 "exitCommands" are a list of LLDB commands that get executed when 309 the process exits 310 "terminateCommands" are a list of LLDB commands that get executed when 311 the debugger session terminates. 312 ''' 313 program = self.getBuildArtifact("a.out") 314 initCommands = ['target list', 'platform list'] 315 preRunCommands = ['image list a.out', 'image dump sections a.out'] 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 stopCommands=stopCommands, 323 exitCommands=exitCommands, 324 terminateCommands=terminateCommands) 325 326 # Get output from the console. This should contain both the 327 # "initCommands" and the "preRunCommands". 328 output = self.get_console() 329 # Verify all "initCommands" were found in console output 330 self.verify_commands('initCommands', output, initCommands) 331 # Verify all "preRunCommands" were found in console output 332 self.verify_commands('preRunCommands', output, preRunCommands) 333 334 source = 'main.c' 335 first_line = line_number(source, '// breakpoint 1') 336 second_line = line_number(source, '// breakpoint 2') 337 lines = [first_line, second_line] 338 339 # Set 2 breakpoints so we can verify that "stopCommands" get run as the 340 # breakpoints get hit 341 breakpoint_ids = self.set_source_breakpoints(source, lines) 342 self.assertEquals(len(breakpoint_ids), len(lines), 343 "expect correct number of breakpoints") 344 345 # Continue after launch and hit the first breakpoint. 346 # Get output from the console. This should contain both the 347 # "stopCommands" that were run after the first breakpoint was hit 348 self.continue_to_breakpoints(breakpoint_ids) 349 output = self.get_console(timeout=1.0) 350 self.verify_commands('stopCommands', output, stopCommands) 351 352 # Continue again and hit the second breakpoint. 353 # Get output from the console. This should contain both the 354 # "stopCommands" that were run after the second breakpoint was hit 355 self.continue_to_breakpoints(breakpoint_ids) 356 output = self.get_console(timeout=1.0) 357 self.verify_commands('stopCommands', output, stopCommands) 358 359 # Continue until the program exits 360 self.continue_to_exit() 361 # Get output from the console. This should contain both the 362 # "exitCommands" that were run after the second breakpoint was hit 363 # and the "terminateCommands" due to the debugging session ending 364 output = self.collect_console(duration=1.0) 365 self.verify_commands('exitCommands', output, exitCommands) 366 self.verify_commands('terminateCommands', output, terminateCommands) 367 368 @skipIfWindows 369 @skipIfRemote 370 def test_extra_launch_commands(self): 371 ''' 372 Tests the "luanchCommands" with extra launching settings 373 ''' 374 self.build_and_create_debug_adaptor() 375 program = self.getBuildArtifact("a.out") 376 377 source = 'main.c' 378 first_line = line_number(source, '// breakpoint 1') 379 second_line = line_number(source, '// breakpoint 2') 380 # Set target binary and 2 breakpoints 381 # then we can varify the "launchCommands" get run 382 # also we can verify that "stopCommands" get run as the 383 # breakpoints get hit 384 launchCommands = [ 385 'target create "%s"' % (program), 386 'br s -f main.c -l %d' % first_line, 387 'br s -f main.c -l %d' % second_line, 388 'process launch --stop-at-entry' 389 ] 390 391 initCommands = ['target list', 'platform list'] 392 preRunCommands = ['image list a.out', 'image dump sections a.out'] 393 stopCommands = ['frame variable', 'bt'] 394 exitCommands = ['expr 2+3', 'expr 3+4'] 395 self.launch(program, 396 initCommands=initCommands, 397 preRunCommands=preRunCommands, 398 stopCommands=stopCommands, 399 exitCommands=exitCommands, 400 launchCommands=launchCommands) 401 402 # Get output from the console. This should contain both the 403 # "initCommands" and the "preRunCommands". 404 output = self.get_console() 405 # Verify all "initCommands" were found in console output 406 self.verify_commands('initCommands', output, initCommands) 407 # Verify all "preRunCommands" were found in console output 408 self.verify_commands('preRunCommands', output, preRunCommands) 409 410 # Verify all "launchCommands" were founc in console output 411 # After execution, program should launch 412 self.verify_commands('launchCommands', output, launchCommands) 413 # Verify the "stopCommands" here 414 self.continue_to_next_stop() 415 output = self.get_console(timeout=1.0) 416 self.verify_commands('stopCommands', output, stopCommands) 417 418 # Continue and hit the second breakpoint. 419 # Get output from the console. This should contain both the 420 # "stopCommands" that were run after the first breakpoint was hit 421 self.continue_to_next_stop() 422 output = self.get_console(timeout=1.0) 423 self.verify_commands('stopCommands', output, stopCommands) 424 425 # Continue until the program exits 426 self.continue_to_exit() 427 # Get output from the console. This should contain both the 428 # "exitCommands" that were run after the second breakpoint was hit 429 output = self.get_console(timeout=1.0) 430 self.verify_commands('exitCommands', output, exitCommands) 431 432 @skipIfWindows 433 @skipIfNetBSD # Hangs on NetBSD as well 434 @skipIfDarwin 435 @skipIf(archs=["arm", "aarch64"]) # Example of a flaky run http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/5540/steps/test/logs/stdio 436 def test_terminate_commands(self): 437 ''' 438 Tests that the "terminateCommands", that can be passed during 439 launch, are run when the debugger is disconnected. 440 ''' 441 self.build_and_create_debug_adaptor() 442 program = self.getBuildArtifact("a.out") 443 444 terminateCommands = ['expr 4+2'] 445 self.launch(program=program, 446 terminateCommands=terminateCommands) 447 self.get_console() 448 # Once it's disconnected the console should contain the 449 # "terminateCommands" 450 self.vscode.request_disconnect(terminateDebuggee=True) 451 output = self.collect_console(duration=1.0) 452 self.verify_commands('terminateCommands', output, terminateCommands) 453