[lldb-dap] Change the launch sequence (#138219) (reland)
This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125
This commit is contained in:
parent
7313c3b1f1
commit
aeeb9a3c09
@ -132,7 +132,6 @@ class DebugCommunication(object):
|
||||
self.exit_status = None
|
||||
self.initialize_body = None
|
||||
self.thread_stop_reasons = {}
|
||||
self.breakpoint_events = []
|
||||
self.progress_events = []
|
||||
self.reverse_requests = []
|
||||
self.module_events = []
|
||||
@ -244,13 +243,6 @@ class DebugCommunication(object):
|
||||
self._process_stopped()
|
||||
tid = body["threadId"]
|
||||
self.thread_stop_reasons[tid] = body
|
||||
elif event == "breakpoint":
|
||||
# Breakpoint events come in when a breakpoint has locations
|
||||
# added or removed. Keep track of them so we can look for them
|
||||
# in tests.
|
||||
self.breakpoint_events.append(packet)
|
||||
# no need to add 'breakpoint' event packets to our packets list
|
||||
return keepGoing
|
||||
elif event.startswith("progress"):
|
||||
# Progress events come in as 'progressStart', 'progressUpdate',
|
||||
# and 'progressEnd' events. Keep these around in case test
|
||||
@ -412,6 +404,15 @@ class DebugCommunication(object):
|
||||
self.threads = []
|
||||
return stopped_events
|
||||
|
||||
def wait_for_breakpoint_events(self, timeout=None):
|
||||
breakpoint_events = []
|
||||
while True:
|
||||
event = self.wait_for_event("breakpoint", timeout=timeout)
|
||||
if not event:
|
||||
break
|
||||
breakpoint_events.append(event)
|
||||
return breakpoint_events
|
||||
|
||||
def wait_for_exited(self):
|
||||
event_dict = self.wait_for_event("exited")
|
||||
if event_dict is None:
|
||||
@ -591,6 +592,7 @@ class DebugCommunication(object):
|
||||
attachCommands=None,
|
||||
terminateCommands=None,
|
||||
coreFile=None,
|
||||
stopOnAttach=True,
|
||||
postRunCommands=None,
|
||||
sourceMap=None,
|
||||
gdbRemotePort=None,
|
||||
@ -620,6 +622,8 @@ class DebugCommunication(object):
|
||||
args_dict["attachCommands"] = attachCommands
|
||||
if coreFile:
|
||||
args_dict["coreFile"] = coreFile
|
||||
if stopOnAttach:
|
||||
args_dict["stopOnEntry"] = stopOnAttach
|
||||
if postRunCommands:
|
||||
args_dict["postRunCommands"] = postRunCommands
|
||||
if sourceMap:
|
||||
@ -632,7 +636,7 @@ class DebugCommunication(object):
|
||||
response = self.send_recv(command_dict)
|
||||
|
||||
if response["success"]:
|
||||
self.wait_for_events(["process", "initialized"])
|
||||
self.wait_for_event("process")
|
||||
return response
|
||||
|
||||
def request_breakpointLocations(
|
||||
@ -666,10 +670,6 @@ class DebugCommunication(object):
|
||||
response = self.send_recv(command_dict)
|
||||
if response:
|
||||
self.configuration_done_sent = True
|
||||
# Client requests the baseline of currently existing threads after
|
||||
# a successful launch or attach.
|
||||
# Kick off the threads request that follows
|
||||
self.request_threads()
|
||||
return response
|
||||
|
||||
def _process_stopped(self):
|
||||
@ -887,7 +887,7 @@ class DebugCommunication(object):
|
||||
response = self.send_recv(command_dict)
|
||||
|
||||
if response["success"]:
|
||||
self.wait_for_events(["process", "initialized"])
|
||||
self.wait_for_event("process")
|
||||
return response
|
||||
|
||||
def request_next(self, threadId, granularity="statement"):
|
||||
@ -1325,6 +1325,26 @@ def attach_options_specified(options):
|
||||
|
||||
def run_vscode(dbg, args, options):
|
||||
dbg.request_initialize(options.sourceInitFile)
|
||||
|
||||
if options.sourceBreakpoints:
|
||||
source_to_lines = {}
|
||||
for file_line in options.sourceBreakpoints:
|
||||
(path, line) = file_line.split(":")
|
||||
if len(path) == 0 or len(line) == 0:
|
||||
print('error: invalid source with line "%s"' % (file_line))
|
||||
|
||||
else:
|
||||
if path in source_to_lines:
|
||||
source_to_lines[path].append(int(line))
|
||||
else:
|
||||
source_to_lines[path] = [int(line)]
|
||||
for source in source_to_lines:
|
||||
dbg.request_setBreakpoints(source, source_to_lines[source])
|
||||
if options.funcBreakpoints:
|
||||
dbg.request_setFunctionBreakpoints(options.funcBreakpoints)
|
||||
|
||||
dbg.request_configurationDone()
|
||||
|
||||
if attach_options_specified(options):
|
||||
response = dbg.request_attach(
|
||||
program=options.program,
|
||||
@ -1353,23 +1373,6 @@ def run_vscode(dbg, args, options):
|
||||
)
|
||||
|
||||
if response["success"]:
|
||||
if options.sourceBreakpoints:
|
||||
source_to_lines = {}
|
||||
for file_line in options.sourceBreakpoints:
|
||||
(path, line) = file_line.split(":")
|
||||
if len(path) == 0 or len(line) == 0:
|
||||
print('error: invalid source with line "%s"' % (file_line))
|
||||
|
||||
else:
|
||||
if path in source_to_lines:
|
||||
source_to_lines[path].append(int(line))
|
||||
else:
|
||||
source_to_lines[path] = [int(line)]
|
||||
for source in source_to_lines:
|
||||
dbg.request_setBreakpoints(source, source_to_lines[source])
|
||||
if options.funcBreakpoints:
|
||||
dbg.request_setFunctionBreakpoints(options.funcBreakpoints)
|
||||
dbg.request_configurationDone()
|
||||
dbg.wait_for_stopped()
|
||||
else:
|
||||
if "message" in response:
|
||||
|
@ -340,6 +340,7 @@ class DAPTestCaseBase(TestBase):
|
||||
exitCommands=None,
|
||||
attachCommands=None,
|
||||
coreFile=None,
|
||||
stopOnAttach=True,
|
||||
disconnectAutomatically=True,
|
||||
terminateCommands=None,
|
||||
postRunCommands=None,
|
||||
@ -348,6 +349,8 @@ class DAPTestCaseBase(TestBase):
|
||||
expectFailure=False,
|
||||
gdbRemotePort=None,
|
||||
gdbRemoteHostname=None,
|
||||
sourceBreakpoints=None,
|
||||
functionBreakpoints=None,
|
||||
):
|
||||
"""Build the default Makefile target, create the DAP debug adapter,
|
||||
and attach to the process.
|
||||
@ -364,6 +367,28 @@ class DAPTestCaseBase(TestBase):
|
||||
self.addTearDownHook(cleanup)
|
||||
# Initialize and launch the program
|
||||
self.dap_server.request_initialize(sourceInitFile)
|
||||
self.dap_server.wait_for_event("initialized")
|
||||
|
||||
# Set source breakpoints as part of the launch sequence.
|
||||
if sourceBreakpoints:
|
||||
for source_path, lines in sourceBreakpoints:
|
||||
response = self.dap_server.request_setBreakpoints(source_path, lines)
|
||||
self.assertTrue(
|
||||
response["success"],
|
||||
"setBreakpoints failed (%s)" % (response),
|
||||
)
|
||||
|
||||
# Set function breakpoints as part of the launch sequence.
|
||||
if functionBreakpoints:
|
||||
response = self.dap_server.request_setFunctionBreakpoints(
|
||||
functionBreakpoints
|
||||
)
|
||||
self.assertTrue(
|
||||
response["success"],
|
||||
"setFunctionBreakpoint failed (%s)" % (response),
|
||||
)
|
||||
|
||||
self.dap_server.request_configurationDone()
|
||||
response = self.dap_server.request_attach(
|
||||
program=program,
|
||||
pid=pid,
|
||||
@ -376,6 +401,7 @@ class DAPTestCaseBase(TestBase):
|
||||
attachCommands=attachCommands,
|
||||
terminateCommands=terminateCommands,
|
||||
coreFile=coreFile,
|
||||
stopOnAttach=stopOnAttach,
|
||||
postRunCommands=postRunCommands,
|
||||
sourceMap=sourceMap,
|
||||
gdbRemotePort=gdbRemotePort,
|
||||
@ -419,6 +445,8 @@ class DAPTestCaseBase(TestBase):
|
||||
commandEscapePrefix=None,
|
||||
customFrameFormat=None,
|
||||
customThreadFormat=None,
|
||||
sourceBreakpoints=None,
|
||||
functionBreakpoints=None,
|
||||
):
|
||||
"""Sending launch request to dap"""
|
||||
|
||||
@ -434,6 +462,29 @@ class DAPTestCaseBase(TestBase):
|
||||
|
||||
# Initialize and launch the program
|
||||
self.dap_server.request_initialize(sourceInitFile)
|
||||
self.dap_server.wait_for_event("initialized")
|
||||
|
||||
# Set source breakpoints as part of the launch sequence.
|
||||
if sourceBreakpoints:
|
||||
for source_path, lines in sourceBreakpoints:
|
||||
response = self.dap_server.request_setBreakpoints(source_path, lines)
|
||||
self.assertTrue(
|
||||
response["success"],
|
||||
"setBreakpoints failed (%s)" % (response),
|
||||
)
|
||||
|
||||
# Set function breakpoints as part of the launch sequence.
|
||||
if functionBreakpoints:
|
||||
response = self.dap_server.request_setFunctionBreakpoints(
|
||||
functionBreakpoints
|
||||
)
|
||||
self.assertTrue(
|
||||
response["success"],
|
||||
"setFunctionBreakpoint failed (%s)" % (response),
|
||||
)
|
||||
|
||||
self.dap_server.request_configurationDone()
|
||||
|
||||
response = self.dap_server.request_launch(
|
||||
program,
|
||||
args=args,
|
||||
@ -504,6 +555,8 @@ class DAPTestCaseBase(TestBase):
|
||||
customThreadFormat=None,
|
||||
launchCommands=None,
|
||||
expectFailure=False,
|
||||
sourceBreakpoints=None,
|
||||
functionBreakpoints=None,
|
||||
):
|
||||
"""Build the default Makefile target, create the DAP debug adapter,
|
||||
and launch the process.
|
||||
@ -540,6 +593,8 @@ class DAPTestCaseBase(TestBase):
|
||||
customThreadFormat=customThreadFormat,
|
||||
launchCommands=launchCommands,
|
||||
expectFailure=expectFailure,
|
||||
sourceBreakpoints=sourceBreakpoints,
|
||||
functionBreakpoints=functionBreakpoints,
|
||||
)
|
||||
|
||||
def getBuiltinDebugServerTool(self):
|
||||
|
@ -27,6 +27,8 @@ def spawn_and_wait(program, delay):
|
||||
@skip
|
||||
class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
|
||||
def set_and_hit_breakpoint(self, continueToExit=True):
|
||||
self.dap_server.wait_for_stopped()
|
||||
|
||||
source = "main.c"
|
||||
breakpoint1_line = line_number(source, "// breakpoint 1")
|
||||
lines = [breakpoint1_line]
|
||||
|
@ -18,17 +18,17 @@ import sys
|
||||
import socket
|
||||
|
||||
|
||||
@skip
|
||||
class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
|
||||
default_timeout = 20
|
||||
|
||||
def set_and_hit_breakpoint(self, continueToExit=True):
|
||||
self.dap_server.wait_for_stopped()
|
||||
|
||||
source = "main.c"
|
||||
main_source_path = os.path.join(os.getcwd(), source)
|
||||
breakpoint1_line = line_number(main_source_path, "// breakpoint 1")
|
||||
breakpoint1_line = line_number(source, "// breakpoint 1")
|
||||
lines = [breakpoint1_line]
|
||||
# Set breakpoint in the thread function so we can step the threads
|
||||
breakpoint_ids = self.set_source_breakpoints(main_source_path, lines)
|
||||
breakpoint_ids = self.set_source_breakpoints(source, lines)
|
||||
self.assertEqual(
|
||||
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
|
||||
)
|
||||
|
@ -81,52 +81,27 @@ class TestDAP_breakpointEvents(lldbdap_testcase.DAPTestCaseBase):
|
||||
breakpoint["verified"], "expect foo breakpoint to not be verified"
|
||||
)
|
||||
|
||||
# Get the stop at the entry point
|
||||
self.continue_to_next_stop()
|
||||
# Make sure we're stopped.
|
||||
self.dap_server.wait_for_stopped()
|
||||
|
||||
# We are now stopped at the entry point to the program. Shared
|
||||
# libraries are not loaded yet (at least on macOS they aren't) and only
|
||||
# the breakpoint in the main executable should be resolved.
|
||||
self.assertEqual(len(self.dap_server.breakpoint_events), 1)
|
||||
event = self.dap_server.breakpoint_events[0]
|
||||
body = event["body"]
|
||||
self.assertEqual(
|
||||
body["reason"], "changed", "breakpoint event should say changed"
|
||||
)
|
||||
breakpoint = body["breakpoint"]
|
||||
self.assertEqual(breakpoint["id"], main_bp_id)
|
||||
self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved")
|
||||
|
||||
# Clear the list of breakpoint events so we don't see this one again.
|
||||
self.dap_server.breakpoint_events.clear()
|
||||
# Flush the breakpoint events.
|
||||
self.dap_server.wait_for_breakpoint_events(timeout=5)
|
||||
|
||||
# Continue to the breakpoint
|
||||
self.continue_to_breakpoints(dap_breakpoint_ids)
|
||||
|
||||
# When the process launches, we first expect to see both the main and
|
||||
# foo breakpoint as unresolved.
|
||||
for event in self.dap_server.breakpoint_events[:2]:
|
||||
body = event["body"]
|
||||
self.assertEqual(
|
||||
body["reason"], "changed", "breakpoint event should say changed"
|
||||
)
|
||||
breakpoint = body["breakpoint"]
|
||||
self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids)
|
||||
self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved")
|
||||
verified_breakpoint_ids = []
|
||||
unverified_breakpoint_ids = []
|
||||
for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5):
|
||||
breakpoint = breakpoint_event["body"]["breakpoint"]
|
||||
id = breakpoint["id"]
|
||||
if breakpoint["verified"]:
|
||||
verified_breakpoint_ids.append(id)
|
||||
else:
|
||||
unverified_breakpoint_ids.append(id)
|
||||
|
||||
# Then, once the dynamic loader has given us a load address, they
|
||||
# should show up as resolved again.
|
||||
for event in self.dap_server.breakpoint_events[3:]:
|
||||
body = event["body"]
|
||||
self.assertEqual(
|
||||
body["reason"], "changed", "breakpoint event should say changed"
|
||||
)
|
||||
breakpoint = body["breakpoint"]
|
||||
self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids)
|
||||
self.assertTrue(breakpoint["verified"], "breakpoint should be resolved")
|
||||
self.assertNotIn(
|
||||
"source",
|
||||
breakpoint,
|
||||
"breakpoint event should not return a source object",
|
||||
)
|
||||
self.assertIn("line", breakpoint, "breakpoint event should have line")
|
||||
self.assertIn(main_bp_id, unverified_breakpoint_ids)
|
||||
self.assertIn(foo_bp_id, unverified_breakpoint_ids)
|
||||
|
||||
self.assertIn(main_bp_id, verified_breakpoint_ids)
|
||||
self.assertIn(foo_bp_id, verified_breakpoint_ids)
|
||||
|
@ -2,7 +2,6 @@
|
||||
Test lldb-dap completions request
|
||||
"""
|
||||
|
||||
|
||||
import lldbdap_testcase
|
||||
import dap_server
|
||||
from lldbsuite.test import lldbutil
|
||||
@ -32,6 +31,7 @@ variable_var_completion = {
|
||||
variable_var1_completion = {"text": "var1", "label": "var1 -- int &"}
|
||||
variable_var2_completion = {"text": "var2", "label": "var2 -- int &"}
|
||||
|
||||
|
||||
# Older version of libcxx produce slightly different typename strings for
|
||||
# templates like vector.
|
||||
@skipIf(compiler="clang", compiler_version=["<", "16.0"])
|
||||
@ -43,16 +43,22 @@ class TestDAP_completions(lldbdap_testcase.DAPTestCaseBase):
|
||||
for not_expected_item in not_expected_list:
|
||||
self.assertNotIn(not_expected_item, actual_list)
|
||||
|
||||
|
||||
def setup_debugee(self):
|
||||
def setup_debugee(self, stopOnEntry=False):
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
|
||||
source = "main.cpp"
|
||||
breakpoint1_line = line_number(source, "// breakpoint 1")
|
||||
breakpoint2_line = line_number(source, "// breakpoint 2")
|
||||
|
||||
self.set_source_breakpoints(source, [breakpoint1_line, breakpoint2_line])
|
||||
self.build_and_launch(
|
||||
program,
|
||||
stopOnEntry=stopOnEntry,
|
||||
sourceBreakpoints=[
|
||||
(
|
||||
source,
|
||||
[
|
||||
line_number(source, "// breakpoint 1"),
|
||||
line_number(source, "// breakpoint 2"),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def test_command_completions(self):
|
||||
"""
|
||||
@ -235,7 +241,7 @@ class TestDAP_completions(lldbdap_testcase.DAPTestCaseBase):
|
||||
"""
|
||||
Tests completion requests in "repl-mode=auto"
|
||||
"""
|
||||
self.setup_debugee()
|
||||
self.setup_debugee(stopOnEntry=True)
|
||||
|
||||
res = self.dap_server.request_evaluate(
|
||||
"`lldb-dap repl-mode auto", context="repl"
|
||||
|
@ -19,6 +19,7 @@ def get_subprocess(root_process, process_name):
|
||||
|
||||
self.assertTrue(False, "No subprocess with name %s found" % process_name)
|
||||
|
||||
|
||||
class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
|
||||
def check_lldb_command(
|
||||
self, lldb_command, contains_string, assert_msg, command_escape_prefix="`"
|
||||
@ -52,7 +53,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
|
||||
character.
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
self.build_and_launch(program, stopOnEntry=True)
|
||||
source = "main.cpp"
|
||||
breakpoint1_line = line_number(source, "// breakpoint 1")
|
||||
lines = [breakpoint1_line]
|
||||
@ -81,7 +82,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
|
||||
|
||||
def test_custom_escape_prefix(self):
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program, commandEscapePrefix="::")
|
||||
self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="::")
|
||||
source = "main.cpp"
|
||||
breakpoint1_line = line_number(source, "// breakpoint 1")
|
||||
breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
|
||||
@ -96,7 +97,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
|
||||
|
||||
def test_empty_escape_prefix(self):
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program, commandEscapePrefix="")
|
||||
self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="")
|
||||
source = "main.cpp"
|
||||
breakpoint1_line = line_number(source, "// breakpoint 1")
|
||||
breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
|
||||
@ -113,7 +114,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
|
||||
def test_exit_status_message_sigterm(self):
|
||||
source = "main.cpp"
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program, commandEscapePrefix="")
|
||||
self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="")
|
||||
breakpoint1_line = line_number(source, "// breakpoint 1")
|
||||
breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line])
|
||||
self.continue_to_breakpoints(breakpoint_ids)
|
||||
@ -167,7 +168,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
|
||||
|
||||
def test_diagnositcs(self):
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
self.build_and_launch(program, stopOnEntry=True)
|
||||
|
||||
core = self.getBuildArtifact("minidump.core")
|
||||
self.yaml2obj("minidump.yaml", core)
|
||||
|
@ -16,7 +16,9 @@ class TestDAP_redirection_to_console(lldbdap_testcase.DAPTestCaseBase):
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(
|
||||
program, lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""}
|
||||
program,
|
||||
stopOnEntry=True,
|
||||
lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""},
|
||||
)
|
||||
|
||||
source = "main.cpp"
|
||||
|
@ -31,7 +31,7 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
||||
created.
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program, disconnectAutomatically=False)
|
||||
self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False)
|
||||
|
||||
# We set a breakpoint right before the side effect file is created
|
||||
self.set_source_breakpoints(
|
||||
@ -39,7 +39,11 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
||||
)
|
||||
self.continue_to_next_stop()
|
||||
|
||||
# verify we haven't produced the side effect file yet
|
||||
self.assertFalse(os.path.exists(program + ".side_effect"))
|
||||
|
||||
self.dap_server.request_disconnect()
|
||||
|
||||
# verify we didn't produce the side effect file
|
||||
time.sleep(1)
|
||||
self.assertFalse(os.path.exists(program + ".side_effect"))
|
||||
|
@ -10,6 +10,7 @@ from lldbsuite.test import lldbutil
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
|
||||
|
||||
# DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660.
|
||||
@skip
|
||||
class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase):
|
||||
@ -42,7 +43,9 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase):
|
||||
self.context = context
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(
|
||||
program, enableAutoVariableSummaries=enableAutoVariableSummaries
|
||||
program,
|
||||
enableAutoVariableSummaries=enableAutoVariableSummaries,
|
||||
stopOnEntry=True,
|
||||
)
|
||||
source = "main.cpp"
|
||||
self.set_source_breakpoints(
|
||||
|
@ -2,7 +2,6 @@
|
||||
Test exception behavior in DAP with signal.
|
||||
"""
|
||||
|
||||
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
import lldbdap_testcase
|
||||
@ -17,7 +16,7 @@ class TestDAP_exception(lldbdap_testcase.DAPTestCaseBase):
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
self.dap_server.request_continue()
|
||||
|
||||
self.assertTrue(self.verify_stop_exception_info("signal SIGABRT"))
|
||||
exceptionInfo = self.get_exceptionInfo()
|
||||
self.assertEqual(exceptionInfo["breakMode"], "always")
|
||||
|
@ -15,6 +15,7 @@ import re
|
||||
# Despite the test program printing correctly. See
|
||||
# https://github.com/llvm/llvm-project/issues/137599.
|
||||
|
||||
|
||||
class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
||||
@skipIfWindows
|
||||
def test_default(self):
|
||||
@ -88,8 +89,8 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program, stopOnEntry=True)
|
||||
self.set_function_breakpoints(["main"])
|
||||
stopped_events = self.continue_to_next_stop()
|
||||
|
||||
stopped_events = self.dap_server.wait_for_stopped()
|
||||
for stopped_event in stopped_events:
|
||||
if "body" in stopped_event:
|
||||
body = stopped_event["body"]
|
||||
@ -357,6 +358,7 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
||||
terminateCommands = ["expr 4+2"]
|
||||
self.build_and_launch(
|
||||
program,
|
||||
stopOnEntry=True,
|
||||
initCommands=initCommands,
|
||||
preRunCommands=preRunCommands,
|
||||
postRunCommands=postRunCommands,
|
||||
@ -530,6 +532,7 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
||||
terminateCommands = ["expr 4+2"]
|
||||
self.launch(
|
||||
program=program,
|
||||
stopOnEntry=True,
|
||||
terminateCommands=terminateCommands,
|
||||
disconnectAutomatically=False,
|
||||
)
|
||||
|
@ -50,7 +50,7 @@ class TestDAP_progress(lldbdap_testcase.DAPTestCaseBase):
|
||||
@skipIfWindows
|
||||
def test(self):
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
self.build_and_launch(program, stopOnEntry=True)
|
||||
progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py")
|
||||
self.dap_server.request_evaluate(
|
||||
f"`command script import {progress_emitter}", context="repl"
|
||||
|
@ -20,7 +20,7 @@ class TestDAP_repl_mode_detection(lldbdap_testcase.DAPTestCaseBase):
|
||||
|
||||
def test_completions(self):
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
self.build_and_launch(program, stopOnEntry=True)
|
||||
|
||||
source = "main.cpp"
|
||||
breakpoint1_line = line_number(source, "// breakpoint 1")
|
||||
|
@ -22,7 +22,6 @@ class TestDAP_restart(lldbdap_testcase.DAPTestCaseBase):
|
||||
[bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B])
|
||||
|
||||
# Verify we hit A, then B.
|
||||
self.dap_server.request_configurationDone()
|
||||
self.verify_breakpoint_hit([bp_A])
|
||||
self.dap_server.request_continue()
|
||||
self.verify_breakpoint_hit([bp_B])
|
||||
|
@ -74,7 +74,6 @@ class TestDAP_restart_runInTerminal(lldbdap_testcase.DAPTestCaseBase):
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program, runInTerminal=True, stopOnEntry=True)
|
||||
[bp_main] = self.set_function_breakpoints(["main"])
|
||||
self.dap_server.request_configurationDone()
|
||||
|
||||
# When using stopOnEntry, configurationDone doesn't result in a running
|
||||
# process, we should immediately get a stopped event instead.
|
||||
|
@ -16,12 +16,14 @@ class TestDAP_sendEvent(lldbdap_testcase.DAPTestCaseBase):
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
source = "main.c"
|
||||
breakpoint_line = line_number(source, "// breakpoint")
|
||||
custom_event_body = {
|
||||
"key": 321,
|
||||
"arr": [True],
|
||||
}
|
||||
self.build_and_launch(
|
||||
program,
|
||||
sourceBreakpoints=[(source, [breakpoint_line])],
|
||||
stopCommands=[
|
||||
"lldb-dap send-event my-custom-event-no-body",
|
||||
"lldb-dap send-event my-custom-event '{}'".format(
|
||||
@ -30,11 +32,6 @@ class TestDAP_sendEvent(lldbdap_testcase.DAPTestCaseBase):
|
||||
],
|
||||
)
|
||||
|
||||
breakpoint_line = line_number(source, "// breakpoint")
|
||||
|
||||
self.set_source_breakpoints(source, [breakpoint_line])
|
||||
self.continue_to_next_stop()
|
||||
|
||||
custom_event = self.dap_server.wait_for_event(
|
||||
filter=["my-custom-event-no-body"]
|
||||
)
|
||||
|
@ -61,7 +61,7 @@ class TestDAP_stackTrace(lldbdap_testcase.DAPTestCaseBase):
|
||||
Tests the 'stackTrace' packet and all its variants.
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
self.build_and_launch(program, stopOnEntry=True)
|
||||
source = "main.c"
|
||||
self.source_path = os.path.join(os.getcwd(), source)
|
||||
self.recurse_end = line_number(source, "recurse end")
|
||||
|
@ -37,7 +37,7 @@ class TestDAP_stackTraceMissingSourcePath(lldbdap_testcase.DAPTestCaseBase):
|
||||
breakpoint_line = line_number(other_source_file, "// Break here")
|
||||
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program, commandEscapePrefix="")
|
||||
self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="")
|
||||
|
||||
breakpoint_ids = self.set_source_breakpoints(
|
||||
other_source_file, [breakpoint_line]
|
||||
|
@ -2,7 +2,6 @@
|
||||
Test lldb-dap start-debugging reverse requests.
|
||||
"""
|
||||
|
||||
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
import lldbdap_testcase
|
||||
@ -16,7 +15,7 @@ class TestDAP_startDebugging(lldbdap_testcase.DAPTestCaseBase):
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
source = "main.c"
|
||||
self.build_and_launch(program)
|
||||
self.build_and_launch(program, stopOnEntry=True)
|
||||
|
||||
breakpoint_line = line_number(source, "// breakpoint")
|
||||
|
||||
|
@ -19,7 +19,7 @@ class TestDAP_stop_hooks(lldbdap_testcase.DAPTestCaseBase):
|
||||
self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands)
|
||||
|
||||
# The first stop is on entry.
|
||||
self.continue_to_next_stop()
|
||||
self.dap_server.wait_for_stopped()
|
||||
|
||||
breakpoint_ids = self.set_function_breakpoints(["main"])
|
||||
# This request hangs if the race happens, because, in that case, the
|
||||
|
@ -13,13 +13,13 @@ class TestDAP_variables_children(lldbdap_testcase.DAPTestCaseBase):
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(
|
||||
program,
|
||||
stopOnEntry=True,
|
||||
preRunCommands=[
|
||||
"command script import '%s'" % self.getSourcePath("formatter.py")
|
||||
],
|
||||
)
|
||||
source = "main.cpp"
|
||||
breakpoint1_line = line_number(source, "// break here")
|
||||
lines = [breakpoint1_line]
|
||||
|
||||
breakpoint_ids = self.set_source_breakpoints(
|
||||
source, [line_number(source, "// break here")]
|
||||
@ -47,7 +47,7 @@ class TestDAP_variables_children(lldbdap_testcase.DAPTestCaseBase):
|
||||
Test the stepping out of a function with return value show the children correctly
|
||||
"""
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
self.build_and_launch(program, stopOnEntry=True)
|
||||
|
||||
function_name = "test_return_variable_with_children"
|
||||
breakpoint_ids = self.set_function_breakpoints([function_name])
|
||||
|
@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode,
|
||||
: log(log), transport(transport), broadcaster("lldb-dap"),
|
||||
exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
|
||||
stop_at_entry(false), is_attach(false),
|
||||
restarting_process_id(LLDB_INVALID_PROCESS_ID),
|
||||
configuration_done_sent(false), waiting_for_run_in_terminal(false),
|
||||
restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false),
|
||||
waiting_for_run_in_terminal(false),
|
||||
progress_event_reporter(
|
||||
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
|
||||
reverse_request_seq(0), repl_mode(default_repl_mode) {
|
||||
@ -893,10 +893,19 @@ llvm::Error DAP::Loop() {
|
||||
return errWrapper;
|
||||
}
|
||||
|
||||
// The launch sequence is special and we need to carefully handle
|
||||
// packets in the right order. Until we've handled configurationDone,
|
||||
bool add_to_pending_queue = false;
|
||||
|
||||
if (const protocol::Request *req =
|
||||
std::get_if<protocol::Request>(&*next);
|
||||
req && req->command == "disconnect") {
|
||||
disconnecting = true;
|
||||
std::get_if<protocol::Request>(&*next)) {
|
||||
llvm::StringRef command = req->command;
|
||||
if (command == "disconnect")
|
||||
disconnecting = true;
|
||||
if (!configuration_done)
|
||||
add_to_pending_queue =
|
||||
command != "initialize" && command != "configurationDone" &&
|
||||
command != "disconnect" && !command.ends_with("Breakpoints");
|
||||
}
|
||||
|
||||
const std::optional<CancelArguments> cancel_args =
|
||||
@ -924,7 +933,8 @@ llvm::Error DAP::Loop() {
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_queue_mutex);
|
||||
m_queue.push_back(std::move(*next));
|
||||
auto &queue = add_to_pending_queue ? m_pending_queue : m_queue;
|
||||
queue.push_back(std::move(*next));
|
||||
}
|
||||
m_queue_cv.notify_one();
|
||||
}
|
||||
@ -938,16 +948,19 @@ llvm::Error DAP::Loop() {
|
||||
StopEventHandlers();
|
||||
});
|
||||
|
||||
while (!disconnecting || !m_queue.empty()) {
|
||||
while (true) {
|
||||
std::unique_lock<std::mutex> lock(m_queue_mutex);
|
||||
m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); });
|
||||
|
||||
if (m_queue.empty())
|
||||
if (disconnecting && m_queue.empty())
|
||||
break;
|
||||
|
||||
Message next = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
|
||||
// Unlock while we're processing the event.
|
||||
lock.unlock();
|
||||
|
||||
if (!HandleObject(next))
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"unhandled packet");
|
||||
@ -1219,6 +1232,16 @@ void DAP::SetConfiguration(const protocol::Configuration &config,
|
||||
SetThreadFormat(*configuration.customThreadFormat);
|
||||
}
|
||||
|
||||
void DAP::SetConfigurationDone() {
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_queue_mutex);
|
||||
std::copy(m_pending_queue.begin(), m_pending_queue.end(),
|
||||
std::front_inserter(m_queue));
|
||||
configuration_done = true;
|
||||
}
|
||||
m_queue_cv.notify_all();
|
||||
}
|
||||
|
||||
void DAP::SetFrameFormat(llvm::StringRef format) {
|
||||
if (format.empty())
|
||||
return;
|
||||
|
@ -188,7 +188,7 @@ struct DAP {
|
||||
// shutting down the entire adapter. When we're restarting, we keep the id of
|
||||
// the old process here so we can detect this case and keep running.
|
||||
lldb::pid_t restarting_process_id;
|
||||
bool configuration_done_sent;
|
||||
bool configuration_done;
|
||||
llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers;
|
||||
bool waiting_for_run_in_terminal;
|
||||
ProgressEventReporter progress_event_reporter;
|
||||
@ -251,6 +251,8 @@ struct DAP {
|
||||
/// Configures the debug adapter for launching/attaching.
|
||||
void SetConfiguration(const protocol::Configuration &confing, bool is_attach);
|
||||
|
||||
void SetConfigurationDone();
|
||||
|
||||
/// Configure source maps based on the current `DAPConfiguration`.
|
||||
void ConfigureSourceMaps();
|
||||
|
||||
@ -417,8 +419,10 @@ struct DAP {
|
||||
lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); }
|
||||
|
||||
private:
|
||||
std::mutex m_queue_mutex;
|
||||
/// Queue for all incoming messages.
|
||||
std::deque<protocol::Message> m_queue;
|
||||
std::deque<protocol::Message> m_pending_queue;
|
||||
std::mutex m_queue_mutex;
|
||||
std::condition_variable m_queue_cv;
|
||||
|
||||
std::mutex m_cancelled_requests_mutex;
|
||||
|
@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) {
|
||||
|
||||
// If the focus thread is not set then we haven't reported any thread status
|
||||
// to the client, so nothing to report.
|
||||
if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) {
|
||||
if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -133,61 +133,70 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
||||
dap.SendOutput(OutputType::Console,
|
||||
llvm::StringRef(attach_msg, attach_msg_len));
|
||||
}
|
||||
if (attachCommands.empty()) {
|
||||
// No "attachCommands", just attach normally.
|
||||
|
||||
// Disable async events so the attach will be successful when we return from
|
||||
// the launch call and the launch will happen synchronously
|
||||
{
|
||||
// Perform the launch in synchronous mode so that we don't have to worry
|
||||
// about process state changes during the launch.
|
||||
ScopeSyncMode scope_sync_mode(dap.debugger);
|
||||
if (attachCommands.empty()) {
|
||||
// No "attachCommands", just attach normally.
|
||||
if (core_file.empty()) {
|
||||
if ((pid != LLDB_INVALID_PROCESS_ID) &&
|
||||
(gdb_remote_port != invalid_port)) {
|
||||
// If both pid and port numbers are specified.
|
||||
error.SetErrorString("The user can't specify both pid and port");
|
||||
} else if (gdb_remote_port != invalid_port) {
|
||||
// If port is specified and pid is not.
|
||||
lldb::SBListener listener = dap.debugger.GetListener();
|
||||
|
||||
if (core_file.empty()) {
|
||||
if ((pid != LLDB_INVALID_PROCESS_ID) &&
|
||||
(gdb_remote_port != invalid_port)) {
|
||||
// If both pid and port numbers are specified.
|
||||
error.SetErrorString("The user can't specify both pid and port");
|
||||
} else if (gdb_remote_port != invalid_port) {
|
||||
// If port is specified and pid is not.
|
||||
lldb::SBListener listener = dap.debugger.GetListener();
|
||||
|
||||
// If the user hasn't provided the hostname property, default localhost
|
||||
// being used.
|
||||
std::string connect_url =
|
||||
llvm::formatv("connect://{0}:", gdb_remote_hostname);
|
||||
connect_url += std::to_string(gdb_remote_port);
|
||||
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
|
||||
error);
|
||||
// If the user hasn't provided the hostname property, default
|
||||
// localhost being used.
|
||||
std::string connect_url =
|
||||
llvm::formatv("connect://{0}:", gdb_remote_hostname);
|
||||
connect_url += std::to_string(gdb_remote_port);
|
||||
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
|
||||
error);
|
||||
} else {
|
||||
// Attach by pid or process name.
|
||||
lldb::SBAttachInfo attach_info;
|
||||
if (pid != LLDB_INVALID_PROCESS_ID)
|
||||
attach_info.SetProcessID(pid);
|
||||
else if (dap.configuration.program.has_value())
|
||||
attach_info.SetExecutable(dap.configuration.program->data());
|
||||
attach_info.SetWaitForLaunch(wait_for, false /*async*/);
|
||||
dap.target.Attach(attach_info, error);
|
||||
}
|
||||
} else {
|
||||
// Attach by pid or process name.
|
||||
lldb::SBAttachInfo attach_info;
|
||||
if (pid != LLDB_INVALID_PROCESS_ID)
|
||||
attach_info.SetProcessID(pid);
|
||||
else if (dap.configuration.program.has_value())
|
||||
attach_info.SetExecutable(dap.configuration.program->data());
|
||||
attach_info.SetWaitForLaunch(wait_for, false /*async*/);
|
||||
dap.target.Attach(attach_info, error);
|
||||
dap.target.LoadCore(core_file.data(), error);
|
||||
}
|
||||
} else {
|
||||
dap.target.LoadCore(core_file.data(), error);
|
||||
// We have "attachCommands" that are a set of commands that are expected
|
||||
// to execute the commands after which a process should be created. If
|
||||
// there is no valid process after running these commands, we have failed.
|
||||
if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
|
||||
response["success"] = false;
|
||||
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
return;
|
||||
}
|
||||
// The custom commands might have created a new target so we should use
|
||||
// the selected target after these commands are run.
|
||||
dap.target = dap.debugger.GetSelectedTarget();
|
||||
}
|
||||
} else {
|
||||
// We have "attachCommands" that are a set of commands that are expected
|
||||
// to execute the commands after which a process should be created. If there
|
||||
// is no valid process after running these commands, we have failed.
|
||||
if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
|
||||
response["success"] = false;
|
||||
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
return;
|
||||
}
|
||||
// The custom commands might have created a new target so we should use the
|
||||
// selected target after these commands are run.
|
||||
dap.target = dap.debugger.GetSelectedTarget();
|
||||
|
||||
// Make sure the process is attached and stopped before proceeding as the
|
||||
// the launch commands are not run using the synchronous mode.
|
||||
error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds));
|
||||
}
|
||||
|
||||
// Make sure the process is attached and stopped.
|
||||
error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds));
|
||||
|
||||
// Clients can request a baseline of currently existing threads after
|
||||
// we acknowledge the configurationDone request.
|
||||
// Client requests the baseline of currently existing threads after
|
||||
// a successful or attach by sending a 'threads' request
|
||||
// right after receiving the configurationDone response.
|
||||
// Obtain the list of threads before we resume the process
|
||||
dap.initial_thread_list =
|
||||
GetThreads(dap.target.GetProcess(), dap.thread_format);
|
||||
|
||||
if (error.Success() && core_file.empty()) {
|
||||
auto attached_pid = dap.target.GetProcess().GetProcessID();
|
||||
if (attached_pid == LLDB_INVALID_PROCESS_ID) {
|
||||
@ -206,9 +215,17 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
||||
}
|
||||
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
|
||||
// FIXME: Move this into PostRun.
|
||||
if (error.Success()) {
|
||||
SendProcessEvent(dap, Attach);
|
||||
dap.SendJSON(CreateEventObject("initialized"));
|
||||
if (dap.target.GetProcess().IsValid()) {
|
||||
SendProcessEvent(dap, Attach);
|
||||
|
||||
if (dap.stop_at_entry)
|
||||
SendThreadStoppedEvent(dap);
|
||||
else
|
||||
dap.target.GetProcess().Continue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,21 +47,11 @@ namespace lldb_dap {
|
||||
|
||||
void ConfigurationDoneRequestHandler::operator()(
|
||||
const llvm::json::Object &request) const {
|
||||
dap.SetConfigurationDone();
|
||||
|
||||
llvm::json::Object response;
|
||||
FillResponse(request, response);
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
dap.configuration_done_sent = true;
|
||||
if (dap.stop_at_entry)
|
||||
SendThreadStoppedEvent(dap);
|
||||
else {
|
||||
// Client requests the baseline of currently existing threads after
|
||||
// a successful launch or attach by sending a 'threads' request
|
||||
// right after receiving the configurationDone response.
|
||||
// Obtain the list of threads before we resume the process
|
||||
dap.initial_thread_list =
|
||||
GetThreads(dap.target.GetProcess(), dap.thread_format);
|
||||
dap.target.GetProcess().Continue();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) {
|
||||
lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
|
||||
if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
|
||||
auto state = lldb::SBProcess::GetStateFromEvent(event);
|
||||
|
||||
DAP_LOG(dap.log, "State = {0}", state);
|
||||
switch (state) {
|
||||
case lldb::eStateConnected:
|
||||
case lldb::eStateDetached:
|
||||
case lldb::eStateInvalid:
|
||||
// Not a state event
|
||||
break;
|
||||
case lldb::eStateUnloaded:
|
||||
break;
|
||||
case lldb::eStateConnected:
|
||||
break;
|
||||
case lldb::eStateAttaching:
|
||||
break;
|
||||
case lldb::eStateLaunching:
|
||||
break;
|
||||
case lldb::eStateStepping:
|
||||
break;
|
||||
case lldb::eStateCrashed:
|
||||
break;
|
||||
case lldb::eStateDetached:
|
||||
break;
|
||||
case lldb::eStateSuspended:
|
||||
break;
|
||||
case lldb::eStateLaunching:
|
||||
case lldb::eStateStopped:
|
||||
// We launch and attach in synchronous mode then the first stop
|
||||
// event will not be delivered. If we use "launchCommands" during a
|
||||
// launch or "attachCommands" during an attach we might some process
|
||||
// stop events which we do not want to send an event for. We will
|
||||
// manually send a stopped event in request_configurationDone(...)
|
||||
// so don't send any before then.
|
||||
if (dap.configuration_done_sent) {
|
||||
// Only report a stopped event if the process was not
|
||||
// automatically restarted.
|
||||
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
|
||||
SendStdOutStdErr(dap, process);
|
||||
SendThreadStoppedEvent(dap);
|
||||
}
|
||||
case lldb::eStateSuspended:
|
||||
// Only report a stopped event if the process was not
|
||||
// automatically restarted.
|
||||
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
|
||||
SendStdOutStdErr(dap, process);
|
||||
SendThreadStoppedEvent(dap);
|
||||
}
|
||||
break;
|
||||
case lldb::eStateRunning:
|
||||
case lldb::eStateStepping:
|
||||
dap.WillContinue();
|
||||
SendContinuedEvent(dap);
|
||||
break;
|
||||
@ -284,6 +269,7 @@ llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run(
|
||||
// Do not source init files until in/out/err are configured.
|
||||
dap.debugger = lldb::SBDebugger::Create(false);
|
||||
dap.debugger.SetInputFile(dap.in);
|
||||
dap.target = dap.debugger.GetDummyTarget();
|
||||
|
||||
llvm::Expected<int> out_fd = dap.out.GetWriteFileDescriptor();
|
||||
if (!out_fd)
|
||||
@ -338,4 +324,8 @@ llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run(
|
||||
return dap.GetCapabilities();
|
||||
}
|
||||
|
||||
void InitializeRequestHandler::PostRun() const {
|
||||
dap.SendJSON(CreateEventObject("initialized"));
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -71,9 +71,12 @@ void LaunchRequestHandler::PostRun() const {
|
||||
if (dap.target.GetProcess().IsValid()) {
|
||||
// Attach happens when launching with runInTerminal.
|
||||
SendProcessEvent(dap, dap.is_attach ? Attach : Launch);
|
||||
}
|
||||
|
||||
dap.SendJSON(CreateEventObject("initialized"));
|
||||
if (dap.stop_at_entry)
|
||||
SendThreadStoppedEvent(dap);
|
||||
else
|
||||
dap.target.GetProcess().Continue();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "Handler/RequestHandler.h"
|
||||
#include "DAP.h"
|
||||
#include "EventHelper.h"
|
||||
#include "Handler/ResponseHandler.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "LLDBUtils.h"
|
||||
@ -162,7 +163,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
|
||||
dap.target.GetProcess().Continue();
|
||||
|
||||
// Now that the actual target is just starting (i.e. exec was just invoked),
|
||||
// we return the debugger to its async state.
|
||||
// we return the debugger to its sync state.
|
||||
scope_sync_mode.reset();
|
||||
|
||||
// If sending the notification failed, the launcher should be dead by now and
|
||||
@ -238,35 +239,47 @@ llvm::Error BaseRequestHandler::LaunchProcess(
|
||||
launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
|
||||
lldb::eLaunchFlagStopAtEntry);
|
||||
|
||||
if (arguments.runInTerminal) {
|
||||
if (llvm::Error err = RunInTerminal(dap, arguments))
|
||||
return err;
|
||||
} else if (launchCommands.empty()) {
|
||||
lldb::SBError error;
|
||||
// Disable async events so the launch will be successful when we return from
|
||||
// the launch call and the launch will happen synchronously
|
||||
{
|
||||
// Perform the launch in synchronous mode so that we don't have to worry
|
||||
// about process state changes during the launch.
|
||||
ScopeSyncMode scope_sync_mode(dap.debugger);
|
||||
dap.target.Launch(launch_info, error);
|
||||
if (error.Fail())
|
||||
return llvm::make_error<DAPError>(error.GetCString());
|
||||
} else {
|
||||
// Set the launch info so that run commands can access the configured
|
||||
// launch details.
|
||||
dap.target.SetLaunchInfo(launch_info);
|
||||
if (llvm::Error err = dap.RunLaunchCommands(launchCommands))
|
||||
return err;
|
||||
|
||||
// The custom commands might have created a new target so we should use the
|
||||
// selected target after these commands are run.
|
||||
dap.target = dap.debugger.GetSelectedTarget();
|
||||
// Make sure the process is launched and stopped at the entry point before
|
||||
// proceeding as the launch commands are not run using the synchronous
|
||||
// mode.
|
||||
lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout);
|
||||
if (error.Fail())
|
||||
return llvm::make_error<DAPError>(error.GetCString());
|
||||
if (arguments.runInTerminal) {
|
||||
if (llvm::Error err = RunInTerminal(dap, arguments))
|
||||
return err;
|
||||
} else if (launchCommands.empty()) {
|
||||
lldb::SBError error;
|
||||
dap.target.Launch(launch_info, error);
|
||||
if (error.Fail())
|
||||
return llvm::make_error<DAPError>(error.GetCString());
|
||||
} else {
|
||||
// Set the launch info so that run commands can access the configured
|
||||
// launch details.
|
||||
dap.target.SetLaunchInfo(launch_info);
|
||||
if (llvm::Error err = dap.RunLaunchCommands(launchCommands))
|
||||
return err;
|
||||
|
||||
// The custom commands might have created a new target so we should use
|
||||
// the selected target after these commands are run.
|
||||
dap.target = dap.debugger.GetSelectedTarget();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the process is launched and stopped at the entry point before
|
||||
// proceeding.
|
||||
lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout);
|
||||
if (error.Fail())
|
||||
return llvm::make_error<DAPError>(error.GetCString());
|
||||
|
||||
// Clients can request a baseline of currently existing threads after
|
||||
// we acknowledge the configurationDone request.
|
||||
// Client requests the baseline of currently existing threads after
|
||||
// a successful or attach by sending a 'threads' request
|
||||
// right after receiving the configurationDone response.
|
||||
// Obtain the list of threads before we resume the process
|
||||
dap.initial_thread_list =
|
||||
GetThreads(dap.target.GetProcess(), dap.thread_format);
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
|
@ -282,6 +282,7 @@ public:
|
||||
static llvm::StringLiteral GetCommand() { return "initialize"; }
|
||||
llvm::Expected<protocol::InitializeResponseBody>
|
||||
Run(const protocol::InitializeRequestArguments &args) const override;
|
||||
void PostRun() const override;
|
||||
};
|
||||
|
||||
class LaunchRequestHandler
|
||||
|
Loading…
x
Reference in New Issue
Block a user