blob: 1bd16f4819486d5784e0ee0e1208a4f35a93b2b3 [file] [log] [blame]
"""
Test lldb-vscode setBreakpoints request
"""
import unittest2
import vscode
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import lldbvscode_testcase
import time
import os
class TestVSCode_launch(lldbvscode_testcase.VSCodeTestCaseBase):
mydir = TestBase.compute_mydir(__file__)
@skipIfWindows
@skipIfDarwin # Flaky
@skipIfRemote
def test_default(self):
'''
Tests the default launch of a simple program. No arguments,
environment, or anything else is specified.
'''
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
self.continue_to_exit()
# Now get the STDOUT and verify our program argument is correct
output = self.get_stdout()
self.assertTrue(output and len(output) > 0,
"expect program output")
lines = output.splitlines()
self.assertIn(program, lines[0],
"make sure program path is in first argument")
@skipIfWindows
@skipIfRemote
def test_termination(self):
'''
Tests the correct termination of lldb-vscode upon a 'disconnect'
request.
'''
self.create_debug_adaptor()
# The underlying lldb-vscode process must be alive
self.assertEqual(self.vscode.process.poll(), None)
# The lldb-vscode process should finish even though
# we didn't close the communication socket explicitly
self.vscode.request_disconnect()
# Wait until the underlying lldb-vscode process dies.
# We need to do this because the popen.wait function in python2.7
# doesn't have a timeout argument.
for _ in range(10):
time.sleep(1)
if self.vscode.process.poll() is not None:
break
# Check the return code
self.assertEqual(self.vscode.process.poll(), 0)
@skipIfWindows
@skipIfRemote
def test_stopOnEntry(self):
'''
Tests the default launch of a simple program that stops at the
entry point instead of continuing.
'''
program = self.getBuildArtifact("a.out")
self.build_and_launch(program, stopOnEntry=True)
self.set_function_breakpoints(['main'])
stopped_events = self.continue_to_next_stop()
for stopped_event in stopped_events:
if 'body' in stopped_event:
body = stopped_event['body']
if 'reason' in body:
reason = body['reason']
self.assertTrue(
reason != 'breakpoint',
'verify stop isn\'t "main" breakpoint')
@skipIfWindows
@skipIfRemote
def test_cwd(self):
'''
Tests the default launch of a simple program with a current working
directory.
'''
program = self.getBuildArtifact("a.out")
program_parent_dir = os.path.realpath(
os.path.dirname(os.path.dirname(program)))
self.build_and_launch(program,
cwd=program_parent_dir)
self.continue_to_exit()
# Now get the STDOUT and verify our program argument is correct
output = self.get_stdout()
self.assertTrue(output and len(output) > 0,
"expect program output")
lines = output.splitlines()
found = False
for line in lines:
if line.startswith('cwd = \"'):
quote_path = '"%s"' % (program_parent_dir)
found = True
self.assertIn(quote_path, line,
"working directory '%s' not in '%s'" % (
program_parent_dir, line))
self.assertTrue(found, "verified program working directory")
@skipIfWindows
@skipIfRemote
def test_debuggerRoot(self):
'''
Tests the "debuggerRoot" will change the working directory of
the lldb-vscode debug adaptor.
'''
program = self.getBuildArtifact("a.out")
program_parent_dir = os.path.realpath(
os.path.dirname(os.path.dirname(program)))
commands = ['platform shell echo cwd = $PWD']
self.build_and_launch(program,
debuggerRoot=program_parent_dir,
initCommands=commands)
output = self.get_console()
self.assertTrue(output and len(output) > 0,
"expect console output")
lines = output.splitlines()
prefix = 'cwd = '
found = False
for line in lines:
if line.startswith(prefix):
found = True
self.assertEquals(program_parent_dir, line[len(prefix):],
"lldb-vscode working dir '%s' == '%s'" % (
program_parent_dir, line[6:]))
self.assertTrue(found, "verified lldb-vscode working directory")
self.continue_to_exit()
@skipIfWindows
@skipIfRemote
def test_sourcePath(self):
'''
Tests the "sourcePath" will set the target.source-map.
'''
program = self.getBuildArtifact("a.out")
program_dir = os.path.dirname(program)
self.build_and_launch(program,
sourcePath=program_dir)
output = self.get_console()
self.assertTrue(output and len(output) > 0,
"expect console output")
lines = output.splitlines()
prefix = '(lldb) settings set target.source-map "." '
found = False
for line in lines:
if line.startswith(prefix):
found = True
quoted_path = '"%s"' % (program_dir)
self.assertEquals(quoted_path, line[len(prefix):],
"lldb-vscode working dir %s == %s" % (
quoted_path, line[6:]))
self.assertTrue(found, 'found "sourcePath" in console output')
self.continue_to_exit()
@skipIfWindows
@skipIfRemote
def test_disableSTDIO(self):
'''
Tests the default launch of a simple program with STDIO disabled.
'''
program = self.getBuildArtifact("a.out")
self.build_and_launch(program,
disableSTDIO=True)
self.continue_to_exit()
# Now get the STDOUT and verify our program argument is correct
output = self.get_stdout()
self.assertEquals(output, None,
"expect no program output")
@skipIfWindows
@skipIfLinux # shell argument expansion doesn't seem to work on Linux
@expectedFailureAll(oslist=["freebsd", "netbsd"],
bugnumber="llvm.org/pr48349")
@skipIfRemote
def test_shellExpandArguments_enabled(self):
'''
Tests the default launch of a simple program with shell expansion
enabled.
'''
program = self.getBuildArtifact("a.out")
program_dir = os.path.dirname(program)
glob = os.path.join(program_dir, '*.out')
self.build_and_launch(program, args=[glob], shellExpandArguments=True)
self.continue_to_exit()
# Now get the STDOUT and verify our program argument is correct
output = self.get_stdout()
self.assertTrue(output and len(output) > 0,
"expect no program output")
lines = output.splitlines()
for line in lines:
quote_path = '"%s"' % (program)
if line.startswith("arg[1] ="):
self.assertIn(quote_path, line,
'verify "%s" expanded to "%s"' % (
glob, program))
@skipIfWindows
@skipIfRemote
def test_shellExpandArguments_disabled(self):
'''
Tests the default launch of a simple program with shell expansion
disabled.
'''
program = self.getBuildArtifact("a.out")
program_dir = os.path.dirname(program)
glob = os.path.join(program_dir, '*.out')
self.build_and_launch(program,
args=[glob],
shellExpandArguments=False)
self.continue_to_exit()
# Now get the STDOUT and verify our program argument is correct
output = self.get_stdout()
self.assertTrue(output and len(output) > 0,
"expect no program output")
lines = output.splitlines()
for line in lines:
quote_path = '"%s"' % (glob)
if line.startswith("arg[1] ="):
self.assertIn(quote_path, line,
'verify "%s" stayed to "%s"' % (
glob, glob))
@skipIfWindows
@skipIfRemote
def test_args(self):
'''
Tests launch of a simple program with arguments
'''
program = self.getBuildArtifact("a.out")
args = ["one", "with space", "'with single quotes'",
'"with double quotes"']
self.build_and_launch(program,
args=args)
self.continue_to_exit()
# Now get the STDOUT and verify our arguments got passed correctly
output = self.get_stdout()
self.assertTrue(output and len(output) > 0,
"expect program output")
lines = output.splitlines()
# Skip the first argument that contains the program name
lines.pop(0)
# Make sure arguments we specified are correct
for (i, arg) in enumerate(args):
quoted_arg = '"%s"' % (arg)
self.assertIn(quoted_arg, lines[i],
'arg[%i] "%s" not in "%s"' % (i+1, quoted_arg, lines[i]))
@skipIfWindows
@skipIfRemote
def test_environment(self):
'''
Tests launch of a simple program with environment variables
'''
program = self.getBuildArtifact("a.out")
env = ["NO_VALUE", "WITH_VALUE=BAR", "EMPTY_VALUE=",
"SPACE=Hello World"]
self.build_and_launch(program,
env=env)
self.continue_to_exit()
# Now get the STDOUT and verify our arguments got passed correctly
output = self.get_stdout()
self.assertTrue(output and len(output) > 0,
"expect program output")
lines = output.splitlines()
# Skip the all arguments so we have only environment vars left
while len(lines) and lines[0].startswith("arg["):
lines.pop(0)
# Make sure each environment variable in "env" is actually set in the
# program environment that was printed to STDOUT
for var in env:
found = False
for program_var in lines:
if var in program_var:
found = True
break
self.assertTrue(found,
'"%s" must exist in program environment (%s)' % (
var, lines))
@skipIfWindows
@skipIfRemote
def test_commands(self):
'''
Tests the "initCommands", "preRunCommands", "stopCommands",
"terminateCommands" and "exitCommands" that can be passed during
launch.
"initCommands" are a list of LLDB commands that get executed
before the targt is created.
"preRunCommands" are a list of LLDB commands that get executed
after the target has been created and before the launch.
"stopCommands" are a list of LLDB commands that get executed each
time the program stops.
"exitCommands" are a list of LLDB commands that get executed when
the process exits
"terminateCommands" are a list of LLDB commands that get executed when
the debugger session terminates.
'''
program = self.getBuildArtifact("a.out")
initCommands = ['target list', 'platform list']
preRunCommands = ['image list a.out', 'image dump sections a.out']
stopCommands = ['frame variable', 'bt']
exitCommands = ['expr 2+3', 'expr 3+4']
terminateCommands = ['expr 4+2']
self.build_and_launch(program,
initCommands=initCommands,
preRunCommands=preRunCommands,
stopCommands=stopCommands,
exitCommands=exitCommands,
terminateCommands=terminateCommands)
# Get output from the console. This should contain both the
# "initCommands" and the "preRunCommands".
output = self.get_console()
# Verify all "initCommands" were found in console output
self.verify_commands('initCommands', output, initCommands)
# Verify all "preRunCommands" were found in console output
self.verify_commands('preRunCommands', output, preRunCommands)
source = 'main.c'
first_line = line_number(source, '// breakpoint 1')
second_line = line_number(source, '// breakpoint 2')
lines = [first_line, second_line]
# Set 2 breakpoints so we can verify that "stopCommands" get run as the
# breakpoints get hit
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEquals(len(breakpoint_ids), len(lines),
"expect correct number of breakpoints")
# Continue after launch and hit the first breakpoint.
# Get output from the console. This should contain both the
# "stopCommands" that were run after the first breakpoint was hit
self.continue_to_breakpoints(breakpoint_ids)
output = self.get_console(timeout=1.0)
self.verify_commands('stopCommands', output, stopCommands)
# Continue again and hit the second breakpoint.
# Get output from the console. This should contain both the
# "stopCommands" that were run after the second breakpoint was hit
self.continue_to_breakpoints(breakpoint_ids)
output = self.get_console(timeout=1.0)
self.verify_commands('stopCommands', output, stopCommands)
# Continue until the program exits
self.continue_to_exit()
# Get output from the console. This should contain both the
# "exitCommands" that were run after the second breakpoint was hit
# and the "terminateCommands" due to the debugging session ending
output = self.collect_console(duration=1.0)
self.verify_commands('exitCommands', output, exitCommands)
self.verify_commands('terminateCommands', output, terminateCommands)
@skipIfWindows
@skipIfRemote
def test_extra_launch_commands(self):
'''
Tests the "luanchCommands" with extra launching settings
'''
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
source = 'main.c'
first_line = line_number(source, '// breakpoint 1')
second_line = line_number(source, '// breakpoint 2')
# Set target binary and 2 breakpoints
# then we can varify the "launchCommands" get run
# also we can verify that "stopCommands" get run as the
# breakpoints get hit
launchCommands = [
'target create "%s"' % (program),
'br s -f main.c -l %d' % first_line,
'br s -f main.c -l %d' % second_line,
'process launch --stop-at-entry'
]
initCommands = ['target list', 'platform list']
preRunCommands = ['image list a.out', 'image dump sections a.out']
stopCommands = ['frame variable', 'bt']
exitCommands = ['expr 2+3', 'expr 3+4']
self.launch(program,
initCommands=initCommands,
preRunCommands=preRunCommands,
stopCommands=stopCommands,
exitCommands=exitCommands,
launchCommands=launchCommands)
# Get output from the console. This should contain both the
# "initCommands" and the "preRunCommands".
output = self.get_console()
# Verify all "initCommands" were found in console output
self.verify_commands('initCommands', output, initCommands)
# Verify all "preRunCommands" were found in console output
self.verify_commands('preRunCommands', output, preRunCommands)
# Verify all "launchCommands" were founc in console output
# After execution, program should launch
self.verify_commands('launchCommands', output, launchCommands)
# Verify the "stopCommands" here
self.continue_to_next_stop()
output = self.get_console(timeout=1.0)
self.verify_commands('stopCommands', output, stopCommands)
# Continue and hit the second breakpoint.
# Get output from the console. This should contain both the
# "stopCommands" that were run after the first breakpoint was hit
self.continue_to_next_stop()
output = self.get_console(timeout=1.0)
self.verify_commands('stopCommands', output, stopCommands)
# Continue until the program exits
self.continue_to_exit()
# Get output from the console. This should contain both the
# "exitCommands" that were run after the second breakpoint was hit
output = self.get_console(timeout=1.0)
self.verify_commands('exitCommands', output, exitCommands)
@skipIfWindows
@skipIfNetBSD # Hangs on NetBSD as well
@skipIfDarwin
@skipIf(archs=["arm", "aarch64"]) # Example of a flaky run http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/5540/steps/test/logs/stdio
def test_terminate_commands(self):
'''
Tests that the "terminateCommands", that can be passed during
launch, are run when the debugger is disconnected.
'''
self.build_and_create_debug_adaptor()
program = self.getBuildArtifact("a.out")
terminateCommands = ['expr 4+2']
self.launch(program=program,
terminateCommands=terminateCommands)
self.get_console()
# Once it's disconnected the console should contain the
# "terminateCommands"
self.vscode.request_disconnect(terminateDebuggee=True)
output = self.collect_console(duration=1.0)
self.verify_commands('terminateCommands', output, terminateCommands)
@skipIfWindows
@skipIfRemote
def test_progress_events(self):
'''
Tests the progress events to ensure we are receiving them.
'''
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
# Set a breakpoint at 'main'. This will cause all of the symbol tables
# for all modules in LLDB to be parsed and we should get a progress
# event for each shared library.
breakpoint_ids = self.set_function_breakpoints(['main'])
self.continue_to_breakpoints(breakpoint_ids)
# Make sure we at least got some progress events
self.assertTrue(len(self.vscode.progress_events) > 0)
# Track all 'progressStart' events by saving all 'progressId' values.
progressStart_ids = set()
# Track all 'progressEnd' events by saving all 'progressId' values.
progressEnd_ids = set()
# We will watch for events whose title starts with
# 'Parsing symbol table for ' and we will save the remainder of the
# line which will contain the shared library basename. Since we set a
# breakpoint by name for 'main', we will expect to see progress events
# for all shared libraries that say that the symbol table is being
# parsed.
symtab_progress_shlibs = set()
# Get a list of modules in the current target so we can verify that
# we do in fact get a progress event for each shared library.
target_shlibs = self.vscode.get_modules()
# Iterate over all progress events and save all start and end IDs, and
# remember any shared libraries that got symbol table parsing progress
# events.
for progress_event in self.vscode.progress_events:
event_type = progress_event['event']
if event_type == 'progressStart':
progressStart_ids.add(progress_event['body']['progressId'])
title = progress_event['body']['title']
if title.startswith('Parsing symbol table for '):
symtab_progress_shlibs.add(title[25:])
if event_type == 'progressEnd':
progressEnd_ids.add(progress_event['body']['progressId'])
# Make sure for each 'progressStart' event, we got a matching
# 'progressEnd' event.
self.assertTrue(progressStart_ids == progressEnd_ids,
('Make sure we got a "progressEnd" for each '
'"progressStart" event that we have.'))
ignored_libraries = {"[vdso]"}
# Verify we got a symbol table parsing progress event for each shared
# library in our target.
for target_shlib_basename in target_shlibs.keys():
if target_shlib_basename in ignored_libraries:
continue
self.assertIn(target_shlib_basename, symtab_progress_shlibs,
'Make sure we got a symbol table progress event for "%s"' % (target_shlib_basename))