Skip to content

Commit 1bdae98

Browse files
committed
[lldb-dap] Fix raciness in launch and attach tests #137920
1 parent 1495d13 commit 1bdae98

File tree

10 files changed

+88
-29
lines changed

10 files changed

+88
-29
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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ 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),
89+
first_stop_state(FirstStopState::NoStopEvent),
90+
waiting_for_run_in_terminal(false),
8991
progress_event_reporter(
9092
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
9193
reverse_request_seq(0), repl_mode(default_repl_mode) {

lldb/tools/lldb-dap/DAP.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,17 @@ 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+
193+
enum class FirstStopState {
194+
NoStopEvent,
195+
PendingStopEvent,
196+
IgnoredStopEvent,
197+
};
198+
std::mutex first_stop_mutex;
199+
std::condition_variable first_stop_cv;
200+
FirstStopState first_stop_state;
201+
202+
bool ignore_next_stop;
192203
llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers;
193204
bool waiting_for_run_in_terminal;
194205
ProgressEventReporter progress_event_reporter;

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,15 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
158158
std::string connect_url =
159159
llvm::formatv("connect://{0}:", gdb_remote_hostname);
160160
connect_url += std::to_string(gdb_remote_port);
161-
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
162-
error);
161+
// Connect remote will generate a stopped event even in synchronous
162+
// mode.
163+
{
164+
std::lock_guard<std::mutex> guard(dap.first_stop_mutex);
165+
dap.first_stop_state = DAP::FirstStopState::PendingStopEvent;
166+
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
167+
error);
168+
}
169+
dap.first_stop_cv.notify_one();
163170
} else {
164171
// Attach by process name or id.
165172
dap.target.Attach(attach_info, error);
@@ -168,15 +175,21 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
168175
dap.target.LoadCore(core_file.data(), error);
169176
}
170177
} else {
171-
// We have "attachCommands" that are a set of commands that are expected
172-
// to execute the commands after which a process should be created. If there
173-
// is no valid process after running these commands, we have failed.
174-
if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
175-
response["success"] = false;
176-
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
177-
dap.SendJSON(llvm::json::Value(std::move(response)));
178-
return;
178+
{
179+
std::lock_guard<std::mutex> guard(dap.first_stop_mutex);
180+
dap.first_stop_state = DAP::FirstStopState::PendingStopEvent;
181+
// We have "attachCommands" that are a set of commands that are expected
182+
// to execute the commands after which a process should be created. If
183+
// there is no valid process after running these commands, we have failed.
184+
if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
185+
response["success"] = false;
186+
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
187+
dap.SendJSON(llvm::json::Value(std::move(response)));
188+
return;
189+
}
179190
}
191+
dap.first_stop_cv.notify_one();
192+
180193
// The custom commands might have created a new target so we should use the
181194
// selected target after these commands are run.
182195
dap.target = dap.debugger.GetSelectedTarget();

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,24 @@ void ConfigurationDoneRequestHandler::operator()(
5151
FillResponse(request, response);
5252
dap.SendJSON(llvm::json::Value(std::move(response)));
5353
dap.configuration_done_sent = true;
54+
55+
{
56+
// Temporary unlock the API mutex to avoid a deadlock between the API mutex
57+
// and the first stop mutex.
58+
lock.unlock();
59+
60+
// Block until we have either ignored the fist stop event or we didn't
61+
// generate one because we attached or launched in synchronous mode.
62+
std::unique_lock<std::mutex> stop_lock(dap.first_stop_mutex);
63+
dap.first_stop_cv.wait(stop_lock, [&] {
64+
return dap.first_stop_state == DAP::FirstStopState::NoStopEvent ||
65+
dap.first_stop_state == DAP::FirstStopState::IgnoredStopEvent;
66+
});
67+
68+
// Relock the API mutex.
69+
lock.lock();
70+
}
71+
5472
if (dap.stop_at_entry)
5573
SendThreadStoppedEvent(dap);
5674
else {

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

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,23 @@ 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) {
171-
// Only report a stopped event if the process was not
172-
// automatically restarted.
173-
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
174-
SendStdOutStdErr(dap, process);
175-
SendThreadStoppedEvent(dap);
170+
{
171+
std::unique_lock<std::mutex> lock(dap.first_stop_mutex);
172+
if (dap.first_stop_state ==
173+
DAP::FirstStopState::PendingStopEvent) {
174+
dap.first_stop_state = DAP::FirstStopState::IgnoredStopEvent;
175+
lock.unlock();
176+
dap.first_stop_cv.notify_one();
177+
continue;
176178
}
177179
}
180+
181+
// Only report a stopped event if the process was not
182+
// automatically restarted.
183+
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
184+
SendStdOutStdErr(dap, process);
185+
SendThreadStoppedEvent(dap);
186+
}
178187
break;
179188
case lldb::eStateRunning:
180189
dap.WillContinue();

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,12 @@ void BaseRequestHandler::Run(const Request &request) {
188188
return;
189189
}
190190

191-
lldb::SBMutex lock = dap.GetAPIMutex();
192-
std::lock_guard<lldb::SBMutex> guard(lock);
193-
194191
// FIXME: After all the requests have migrated from LegacyRequestHandler >
195192
// RequestHandler<> we should be able to move this into
196193
// RequestHandler<>::operator().
194+
lock.lock();
197195
operator()(request);
196+
lock.unlock();
198197

199198
// FIXME: After all the requests have migrated from LegacyRequestHandler >
200199
// RequestHandler<> we should be able to check `debugger.InterruptRequest` and
@@ -250,11 +249,17 @@ llvm::Error BaseRequestHandler::LaunchProcess(
250249
if (error.Fail())
251250
return llvm::make_error<DAPError>(error.GetCString());
252251
} else {
253-
// Set the launch info so that run commands can access the configured
254-
// launch details.
255-
dap.target.SetLaunchInfo(launch_info);
256-
if (llvm::Error err = dap.RunLaunchCommands(launchCommands))
257-
return err;
252+
{
253+
std::lock_guard<std::mutex> guard(dap.first_stop_mutex);
254+
dap.first_stop_state = DAP::FirstStopState::PendingStopEvent;
255+
256+
// Set the launch info so that run commands can access the configured
257+
// launch details.
258+
dap.target.SetLaunchInfo(launch_info);
259+
if (llvm::Error err = dap.RunLaunchCommands(launchCommands))
260+
return err;
261+
}
262+
dap.first_stop_cv.notify_all();
258263

259264
// The custom commands might have created a new target so we should use the
260265
// selected target after these commands are run.

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ struct DAP;
3737
/// the RequestHandler template subclass instead.
3838
class BaseRequestHandler {
3939
public:
40-
BaseRequestHandler(DAP &dap) : dap(dap) {}
40+
BaseRequestHandler(DAP &dap)
41+
: dap(dap), mutex(dap.GetAPIMutex()), lock(mutex, std::defer_lock) {}
4142

4243
/// BaseRequestHandler are not copyable.
4344
/// @{
@@ -80,6 +81,8 @@ class BaseRequestHandler {
8081
/// @}
8182

8283
DAP &dap;
84+
lldb::SBMutex mutex;
85+
mutable std::unique_lock<lldb::SBMutex> lock;
8386
};
8487

8588
/// FIXME: Migrate callers to typed RequestHandler for improved type handling.

0 commit comments

Comments
 (0)