[Lldb-commits] [lldb] [lldb-dap] Change the launch sequence (reland) (PR #138981)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Wed May 7 16:18:11 PDT 2025
https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/138981
>From 8c7a01465891c83f7cc99ee95b0852c56e0f150f Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Tue, 6 May 2025 15:58:44 -0700
Subject: [PATCH 1/2] [lldb-dap] Change the launch sequence (#138219)
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
---
.../test/tools/lldb-dap/dap_server.py | 65 +++++-----
.../test/tools/lldb-dap/lldbdap_testcase.py | 7 ++
.../tools/lldb-dap/attach/TestDAP_attach.py | 2 +
.../attach/TestDAP_attachByPortNum.py | 8 +-
.../TestDAP_breakpointEvents.py | 61 +++-------
.../completions/TestDAP_completions.py | 6 +-
.../tools/lldb-dap/console/TestDAP_console.py | 2 +-
.../lldb-dap/disconnect/TestDAP_disconnect.py | 6 +-
.../lldb-dap/evaluate/TestDAP_evaluate.py | 5 +-
.../tools/lldb-dap/launch/TestDAP_launch.py | 4 +-
.../lldb-dap/progress/TestDAP_Progress.py | 2 +-
.../repl-mode/TestDAP_repl_mode_detection.py | 2 +-
.../tools/lldb-dap/restart/TestDAP_restart.py | 1 -
.../restart/TestDAP_restart_runInTerminal.py | 1 -
.../lldb-dap/stop-hooks/TestDAP_stop_hooks.py | 2 +-
lldb/tools/lldb-dap/DAP.cpp | 39 ++++--
lldb/tools/lldb-dap/DAP.h | 8 +-
lldb/tools/lldb-dap/EventHelper.cpp | 2 +-
.../lldb-dap/Handler/AttachRequestHandler.cpp | 115 ++++++++++--------
.../ConfigurationDoneRequestHandler.cpp | 14 +--
.../Handler/InitializeRequestHandler.cpp | 44 +++----
.../lldb-dap/Handler/LaunchRequestHandler.cpp | 7 +-
.../tools/lldb-dap/Handler/RequestHandler.cpp | 67 ++++++----
lldb/tools/lldb-dap/Handler/RequestHandler.h | 1 +
24 files changed, 252 insertions(+), 219 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 6d9ab770684f1..e10342b72f4f0 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -132,7 +132,6 @@ def __init__(self, recv, send, init_commands, log_file=None):
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 @@ def handle_recv_packet(self, packet):
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 @@ def wait_for_stopped(self, timeout=None):
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 @@ def request_attach(
attachCommands=None,
terminateCommands=None,
coreFile=None,
+ stopOnAttach=True,
postRunCommands=None,
sourceMap=None,
gdbRemotePort=None,
@@ -620,6 +622,8 @@ def request_attach(
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 @@ def request_attach(
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 @@ def request_configurationDone(self):
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 @@ def request_launch(
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:
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 2c14bb35162b5..958c7268c0c72 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -340,6 +340,7 @@ def attach(
exitCommands=None,
attachCommands=None,
coreFile=None,
+ stopOnAttach=True,
disconnectAutomatically=True,
terminateCommands=None,
postRunCommands=None,
@@ -364,6 +365,8 @@ def cleanup():
self.addTearDownHook(cleanup)
# Initialize and launch the program
self.dap_server.request_initialize(sourceInitFile)
+ self.dap_server.wait_for_event("initialized")
+ self.dap_server.request_configurationDone()
response = self.dap_server.request_attach(
program=program,
pid=pid,
@@ -376,6 +379,7 @@ def cleanup():
attachCommands=attachCommands,
terminateCommands=terminateCommands,
coreFile=coreFile,
+ stopOnAttach=stopOnAttach,
postRunCommands=postRunCommands,
sourceMap=sourceMap,
gdbRemotePort=gdbRemotePort,
@@ -434,6 +438,9 @@ def cleanup():
# Initialize and launch the program
self.dap_server.request_initialize(sourceInitFile)
+ self.dap_server.wait_for_event("initialized")
+ self.dap_server.request_configurationDone()
+
response = self.dap_server.request_launch(
program,
args=args,
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
index f48d5a7db3c50..741c011a3d692 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -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]
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
index 7f93b9f2a3a22..7250e67ebcd8c 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py
@@ -18,17 +18,17 @@
import socket
- at 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"
)
diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py
index e5590e1b332a0..8581f10cef22a 100644
--- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py
+++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py
@@ -81,52 +81,27 @@ def test_breakpoint_events(self):
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)
diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
index 210e591bff426..455ac84168baf 100644
--- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
+++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
@@ -44,9 +44,9 @@ def verify_completions(self, actual_list, expected_list, 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)
+ self.build_and_launch(program, stopOnEntry=stopOnEntry)
source = "main.cpp"
breakpoint1_line = line_number(source, "// breakpoint 1")
@@ -235,7 +235,7 @@ def test_auto_completions(self):
"""
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"
diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
index b07c4f871d73b..65a1bc04c7cd7 100644
--- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
+++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
@@ -167,7 +167,7 @@ def test_exit_status_message_ok(self):
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)
diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py
index 0cb792d662a80..09e3f62f0eead 100644
--- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py
+++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py
@@ -31,7 +31,7 @@ def test_launch(self):
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 @@ def test_launch(self):
)
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"))
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index d97fda730c46a..19b682dfcd22d 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -10,6 +10,7 @@
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 @@ def run_test_evaluate_expressions(
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(
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
index 931456299e03e..604a41678500c 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -88,8 +88,8 @@ def test_stopOnEntry(self):
"""
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"]
diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
index fee63655de0da..0f94b50c31fba 100755
--- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
+++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
@@ -50,7 +50,7 @@ def verify_progress_events(
@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"
diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py
index c6f59949d668e..81edcdf4bd0f9 100644
--- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py
+++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py
@@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex):
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")
diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py
index 36fa0bd40183f..5f95c7bfb1556 100644
--- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py
+++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py
@@ -22,7 +22,6 @@ def test_basic_functionality(self):
[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])
diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py
index a94c9860c1508..eed769a5a0cc6 100644
--- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py
+++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py
@@ -74,7 +74,6 @@ def test_stopOnEntry(self):
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.
diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py
index 70c11a63a79f7..7e28a5af4331c 100644
--- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py
+++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py
@@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self):
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
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 4b631484c9fab..62c60cc3a9b3b 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -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;
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 88eedb0860cf1..b581ae759b1bc 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -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;
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
index 2c659f39f4b66..ed2d8700c26b0 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -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;
}
diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
index 7a0f091128e4a..5dc9c3f9772e3 100644
--- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
@@ -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 (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 (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 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);
- }
- } 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;
+ // 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();
}
- // 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();
+ }
}
}
diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
index f39bbdefdbb95..802c28d7b8904 100644
--- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
@@ -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
diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
index ce34c52bcc334..aa947d3cb5ab9 100644
--- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -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
diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
index 3e4532e754ec6..7e0e76935dd02 100644
--- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp
@@ -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
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 7a75cd93abc19..282c5f4ab15a5 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -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();
}
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 37cc902e1c98e..9e9cfb13d77b8 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -282,6 +282,7 @@ class InitializeRequestHandler
static llvm::StringLiteral GetCommand() { return "initialize"; }
llvm::Expected<protocol::InitializeResponseBody>
Run(const protocol::InitializeRequestArguments &args) const override;
+ void PostRun() const override;
};
class LaunchRequestHandler
>From 1ac9193a54cd4c98c875bc079154453af1a47850 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Wed, 7 May 2025 15:26:01 -0700
Subject: [PATCH 2/2] Update more tests
---
.../test/tools/lldb-dap/lldbdap_testcase.py | 48 +++++++++++++++++++
.../completions/TestDAP_completions.py | 22 +++++----
.../tools/lldb-dap/console/TestDAP_console.py | 9 ++--
.../console/TestDAP_redirection_to_console.py | 4 +-
.../lldb-dap/exception/TestDAP_exception.py | 3 +-
.../tools/lldb-dap/launch/TestDAP_launch.py | 3 ++
.../lldb-dap/send-event/TestDAP_sendEvent.py | 7 +--
.../lldb-dap/stackTrace/TestDAP_stackTrace.py | 2 +-
.../TestDAP_stackTraceDisassemblyDisplay.py | 2 +-
.../startDebugging/TestDAP_startDebugging.py | 3 +-
.../children/TestDAP_variables_children.py | 4 +-
11 files changed, 81 insertions(+), 26 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 958c7268c0c72..c5a7eb76a58c7 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -349,6 +349,8 @@ def attach(
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.
@@ -366,6 +368,26 @@ def 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,
@@ -423,6 +445,8 @@ def launch(
commandEscapePrefix=None,
customFrameFormat=None,
customThreadFormat=None,
+ sourceBreakpoints=None,
+ functionBreakpoints=None,
):
"""Sending launch request to dap"""
@@ -439,6 +463,26 @@ def 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_launch(
@@ -511,6 +555,8 @@ def build_and_launch(
customThreadFormat=None,
launchCommands=None,
expectFailure=False,
+ sourceBreakpoints=None,
+ functionBreakpoints=None,
):
"""Build the default Makefile target, create the DAP debug adapter,
and launch the process.
@@ -547,6 +593,8 @@ def build_and_launch(
customThreadFormat=customThreadFormat,
launchCommands=launchCommands,
expectFailure=expectFailure,
+ sourceBreakpoints=sourceBreakpoints,
+ functionBreakpoints=functionBreakpoints,
)
def getBuiltinDebugServerTool(self):
diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
index 455ac84168baf..a94288c7a669e 100644
--- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
+++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
@@ -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_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 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]):
for not_expected_item in not_expected_list:
self.assertNotIn(not_expected_item, actual_list)
-
def setup_debugee(self, stopOnEntry=False):
program = self.getBuildArtifact("a.out")
- self.build_and_launch(program, stopOnEntry=stopOnEntry)
-
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):
"""
diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
index 65a1bc04c7cd7..8642e317f9b3a 100644
--- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
+++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
@@ -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 @@ def test_scopes_variables_setVariable_evaluate(self):
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 @@ def test_scopes_variables_setVariable_evaluate(self):
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 @@ def test_custom_escape_prefix(self):
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 @@ def test_empty_escape_prefix(self):
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)
diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py
index e367c327d4295..23500bd6fe586 100644
--- a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py
+++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py
@@ -16,7 +16,9 @@ def test(self):
"""
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"
diff --git a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py
index 39d73737b7e8c..ec7387dabb0c2 100644
--- a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py
+++ b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py
@@ -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 @@ def test_stopped_description(self):
"""
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")
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
index 604a41678500c..e8e9181f8da8d 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -15,6 +15,7 @@
# 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):
@@ -357,6 +358,7 @@ def test_commands(self):
terminateCommands = ["expr 4+2"]
self.build_and_launch(
program,
+ stopOnEntry=True,
initCommands=initCommands,
preRunCommands=preRunCommands,
postRunCommands=postRunCommands,
@@ -530,6 +532,7 @@ def test_terminate_commands(self):
terminateCommands = ["expr 4+2"]
self.launch(
program=program,
+ stopOnEntry=True,
terminateCommands=terminateCommands,
disconnectAutomatically=False,
)
diff --git a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py
index ce262be161861..64cec70aa923b 100644
--- a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py
+++ b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py
@@ -16,12 +16,14 @@ def test_send_event(self):
"""
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 @@ def test_send_event(self):
],
)
- 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"]
)
diff --git a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py
index 4e2a76cf76980..edf4adae14a3b 100644
--- a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py
+++ b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py
@@ -61,7 +61,7 @@ def test_stackTrace(self):
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")
diff --git a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py
index 08c225b3cada4..963d711978534 100644
--- a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py
+++ b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py
@@ -37,7 +37,7 @@ def build_and_run_until_breakpoint(self):
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]
diff --git a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
index fd452d91e472b..e37cd36d7f283 100644
--- a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
+++ b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
@@ -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 @@ def test_startDebugging(self):
"""
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")
diff --git a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py
index a9371e5c5fe68..eb09649f387d7 100644
--- a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py
+++ b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py
@@ -13,13 +13,13 @@ def test_get_num_children(self):
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 @@ def test_return_variable_with_children(self):
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])
More information about the lldb-commits
mailing list