Skip to content

Commit bedc08b

Browse files
committed
[lldb-dap] Fix raciness in launch and attach tests
We've gotten multiple reports of the launch and attach test being flaky, both in CI and locally when running the tests. I believe the flakiness is due to a race between the main thread and the event handler thread. Launching and attaching is done in synchronous mode so the corresponding requests return only after the respective operation has been completed. In synchronous mode, no stop event is emitted. When we have launch or attach commands, we cannot use synchronous mode. To hide the stop events in this case, the default event handler thread was ignoring stop events before the "configuration done" request was answered. The problem with that is that there's no guarantee that we have handled the stop event before we have handled the configuration done request. When looking at the logs, you can see that we're still in the process of sending module events (which I recently added) when we receive, and respond to the "configuration done" request, before it sees the launch or attach stop event. At that point `dap.configuration_done_sent` is true and the event is sent, which the test doesn't expect. This PR fixes the raciness by using an atomic flag to signal that the next stop event should be ignored. An alternative approach could be to stop trying to hide the initial stop event, and instead report it to the client unconditionally. Instead of ignoring the stop for the asynchronous case, we could send a stop event after we're done handling the synchronous case. Fixes #137660
1 parent f73db3d commit bedc08b

File tree

8 files changed

+10
-5
lines changed

8 files changed

+10
-5
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def verify_breakpoint_hit(self, breakpoint_ids):
103103
match_desc = "breakpoint %s." % (breakpoint_id)
104104
if match_desc in description:
105105
return
106-
self.assertTrue(False, "breakpoint not hit")
106+
self.assertTrue(False, f"breakpoint not hit: stopped_events={stopped_events}")
107107

108108
def verify_stop_exception_info(self, expected_description, timeout=timeoutval):
109109
"""Wait for the process we are debugging to stop, and verify the stop

lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def spawn_and_wait(program, delay):
2525
process.wait()
2626

2727

28-
@skipIf
2928
class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
3029
def set_and_hit_breakpoint(self, continueToExit=True):
3130
source = "main.c"

lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import socket
2020

2121

22-
@skip
2322
class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase):
2423
default_timeout = 20
2524

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode,
8585
exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
8686
stop_at_entry(false), is_attach(false),
8787
restarting_process_id(LLDB_INVALID_PROCESS_ID),
88-
configuration_done_sent(false), waiting_for_run_in_terminal(false),
88+
configuration_done_sent(false), ignore_next_stop(false),
89+
waiting_for_run_in_terminal(false),
8990
progress_event_reporter(
9091
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
9192
reverse_request_seq(0), repl_mode(default_repl_mode) {

lldb/tools/lldb-dap/DAP.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ struct DAP {
189189
// the old process here so we can detect this case and keep running.
190190
lldb::pid_t restarting_process_id;
191191
bool configuration_done_sent;
192+
std::atomic<bool> ignore_next_stop;
192193
llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers;
193194
bool waiting_for_run_in_terminal;
194195
ProgressEventReporter progress_event_reporter;

lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
155155
std::string connect_url =
156156
llvm::formatv("connect://{0}:", gdb_remote_hostname);
157157
connect_url += std::to_string(gdb_remote_port);
158+
// Connect remote will generate a stopped event even in synchronous
159+
// mode.
160+
dap.ignore_next_stop = true;
158161
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
159162
error);
160163
} else {
@@ -166,6 +169,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
166169
// Reenable async events
167170
dap.debugger.SetAsync(true);
168171
} else {
172+
dap.ignore_next_stop = true;
169173
// We have "attachCommands" that are a set of commands that are expected
170174
// to execute the commands after which a process should be created. If there
171175
// is no valid process after running these commands, we have failed.

lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ static void EventThreadFunction(DAP &dap) {
167167
// stop events which we do not want to send an event for. We will
168168
// manually send a stopped event in request_configurationDone(...)
169169
// so don't send any before then.
170-
if (dap.configuration_done_sent) {
170+
if (!dap.ignore_next_stop.exchange(false)) {
171171
// Only report a stopped event if the process was not
172172
// automatically restarted.
173173
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {

lldb/tools/lldb-dap/Handler/RequestHandler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ llvm::Error BaseRequestHandler::LaunchProcess(
250250
if (error.Fail())
251251
return llvm::make_error<DAPError>(error.GetCString());
252252
} else {
253+
dap.ignore_next_stop = true;
253254
// Set the launch info so that run commands can access the configured
254255
// launch details.
255256
dap.target.SetLaunchInfo(launch_info);

0 commit comments

Comments
 (0)