Skip to content

Commit e733348

Browse files
committed
[lldb-dap] Change the launch sequence
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 asynchronous, including when we have attach or launch commands. That removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125
1 parent 8d02529 commit e733348

9 files changed

+163
-126
lines changed

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode,
8484
: log(log), transport(transport), broadcaster("lldb-dap"),
8585
exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
8686
stop_at_entry(false), is_attach(false),
87-
restarting_process_id(LLDB_INVALID_PROCESS_ID),
88-
configuration_done_sent(false), waiting_for_run_in_terminal(false),
87+
restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false),
88+
waiting_for_run_in_terminal(false),
8989
progress_event_reporter(
9090
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
9191
reverse_request_seq(0), repl_mode(default_repl_mode) {
@@ -893,10 +893,23 @@ llvm::Error DAP::Loop() {
893893
return errWrapper;
894894
}
895895

896+
// The launch sequence is special and we need to carefully handle
897+
// packets in the right order. The launch and attach requests cannot
898+
// be answered until we've gotten the confgigurationDone request. We
899+
// can't answer the threads request until we've successfully launched
900+
// or attached.
901+
bool is_part_of_launch_sequence = false;
902+
896903
if (const protocol::Request *req =
897-
std::get_if<protocol::Request>(&*next);
898-
req && req->command == "disconnect") {
899-
disconnecting = true;
904+
std::get_if<protocol::Request>(&*next)) {
905+
if (req->command == "disconnect")
906+
disconnecting = true;
907+
if (!configuration_done) {
908+
if (req->command == "launch" || req->command == "attach")
909+
is_part_of_launch_sequence = true;
910+
if (req->command == "threads" && !m_launch_sequence_queue.empty())
911+
is_part_of_launch_sequence = true;
912+
}
900913
}
901914

902915
const std::optional<CancelArguments> cancel_args =
@@ -924,7 +937,10 @@ llvm::Error DAP::Loop() {
924937

925938
{
926939
std::lock_guard<std::mutex> guard(m_queue_mutex);
927-
m_queue.push_back(std::move(*next));
940+
if (is_part_of_launch_sequence)
941+
m_launch_sequence_queue.push_back(std::move(*next));
942+
else
943+
m_queue.push_back(std::move(*next));
928944
}
929945
m_queue_cv.notify_one();
930946
}
@@ -938,15 +954,30 @@ llvm::Error DAP::Loop() {
938954
StopEventHandlers();
939955
});
940956

941-
while (!disconnecting || !m_queue.empty()) {
957+
while (true) {
942958
std::unique_lock<std::mutex> lock(m_queue_mutex);
943-
m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); });
959+
m_queue_cv.wait(lock, [&] {
960+
return disconnecting || !m_queue.empty() ||
961+
(!m_launch_sequence_queue.empty() && configuration_done);
962+
});
944963

945-
if (m_queue.empty())
964+
if (disconnecting)
946965
break;
947966

948-
Message next = m_queue.front();
949-
m_queue.pop_front();
967+
bool is_launch_seqeuence =
968+
!m_launch_sequence_queue.empty() && configuration_done;
969+
970+
auto &active_queue =
971+
is_launch_seqeuence ? m_launch_sequence_queue : m_queue;
972+
973+
assert(!active_queue.empty() &&
974+
"shouldn't have gotten past the wait with an empty queue");
975+
976+
Message next = active_queue.front();
977+
active_queue.pop_front();
978+
979+
// Unlock while we're processing the event.
980+
lock.unlock();
950981

951982
if (!HandleObject(next))
952983
return llvm::createStringError(llvm::inconvertibleErrorCode(),

lldb/tools/lldb-dap/DAP.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ struct DAP {
188188
// shutting down the entire adapter. When we're restarting, we keep the id of
189189
// the old process here so we can detect this case and keep running.
190190
lldb::pid_t restarting_process_id;
191-
bool configuration_done_sent;
191+
bool configuration_done;
192192
llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers;
193193
bool waiting_for_run_in_terminal;
194194
ProgressEventReporter progress_event_reporter;
@@ -417,8 +417,10 @@ struct DAP {
417417
lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); }
418418

419419
private:
420-
std::mutex m_queue_mutex;
420+
/// Queue for all incoming messages.
421421
std::deque<protocol::Message> m_queue;
422+
std::deque<protocol::Message> m_launch_sequence_queue;
423+
std::mutex m_queue_mutex;
422424
std::condition_variable m_queue_cv;
423425

424426
std::mutex m_cancelled_requests_mutex;

lldb/tools/lldb-dap/EventHelper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) {
222222

223223
// If the focus thread is not set then we haven't reported any thread status
224224
// to the client, so nothing to report.
225-
if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) {
225+
if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) {
226226
return;
227227
}
228228

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

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -137,55 +137,67 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
137137
dap.SendOutput(OutputType::Console,
138138
llvm::StringRef(attach_msg, attach_msg_len));
139139
}
140-
if (attachCommands.empty()) {
141-
// No "attachCommands", just attach normally.
142140

143-
// Disable async events so the attach will be successful when we return from
144-
// the launch call and the launch will happen synchronously
141+
{
142+
// Perform the launch in synchronous mode so that we don't have to worry
143+
// about process state changes during the launch.
145144
ScopeSyncMode scope_sync_mode(dap.debugger);
146-
147-
if (core_file.empty()) {
148-
if ((pid != LLDB_INVALID_PROCESS_ID) &&
149-
(gdb_remote_port != invalid_port)) {
150-
// If both pid and port numbers are specified.
151-
error.SetErrorString("The user can't specify both pid and port");
152-
} else if (gdb_remote_port != invalid_port) {
153-
// If port is specified and pid is not.
154-
lldb::SBListener listener = dap.debugger.GetListener();
155-
156-
// If the user hasn't provided the hostname property, default localhost
157-
// being used.
158-
std::string connect_url =
159-
llvm::formatv("connect://{0}:", gdb_remote_hostname);
160-
connect_url += std::to_string(gdb_remote_port);
161-
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
162-
error);
145+
if (attachCommands.empty()) {
146+
// No "attachCommands", just attach normally.
147+
if (core_file.empty()) {
148+
if ((pid != LLDB_INVALID_PROCESS_ID) &&
149+
(gdb_remote_port != invalid_port)) {
150+
// If both pid and port numbers are specified.
151+
error.SetErrorString("The user can't specify both pid and port");
152+
} else if (gdb_remote_port != invalid_port) {
153+
// If port is specified and pid is not.
154+
lldb::SBListener listener = dap.debugger.GetListener();
155+
156+
// If the user hasn't provided the hostname property, default
157+
// localhost being used.
158+
std::string connect_url =
159+
llvm::formatv("connect://{0}:", gdb_remote_hostname);
160+
connect_url += std::to_string(gdb_remote_port);
161+
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
162+
error);
163+
} else {
164+
// Attach by process name or id.
165+
dap.target.Attach(attach_info, error);
166+
}
163167
} else {
164-
// Attach by process name or id.
165-
dap.target.Attach(attach_info, error);
168+
dap.target.LoadCore(core_file.data(), error);
166169
}
167170
} else {
168-
dap.target.LoadCore(core_file.data(), error);
169-
}
170-
} 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;
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
173+
// there 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;
179+
}
180+
// The custom commands might have created a new target so we should use
181+
// the selected target after these commands are run.
182+
dap.target = dap.debugger.GetSelectedTarget();
179183
}
180-
// The custom commands might have created a new target so we should use the
181-
// selected target after these commands are run.
182-
dap.target = dap.debugger.GetSelectedTarget();
183-
184-
// Make sure the process is attached and stopped before proceeding as the
185-
// the launch commands are not run using the synchronous mode.
186-
error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds));
187184
}
188185

186+
// Make sure the process is attached and stopped.
187+
error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds));
188+
189+
// Clients can request a baseline of currently existing threads after
190+
// we acknowledge the configurationDone request.
191+
// Client requests the baseline of currently existing threads after
192+
// a successful or attach by sending a 'threads' request
193+
// right after receiving the configurationDone response.
194+
// Obtain the list of threads before we resume the process
195+
dap.initial_thread_list =
196+
GetThreads(dap.target.GetProcess(), dap.thread_format);
197+
198+
if (!dap.stop_at_entry)
199+
dap.target.GetProcess().Continue();
200+
189201
if (error.Success() && core_file.empty()) {
190202
auto attached_pid = dap.target.GetProcess().GetProcessID();
191203
if (attached_pid == LLDB_INVALID_PROCESS_ID) {
@@ -204,10 +216,8 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
204216
}
205217

206218
dap.SendJSON(llvm::json::Value(std::move(response)));
207-
if (error.Success()) {
219+
if (error.Success())
208220
SendProcessEvent(dap, Attach);
209-
dap.SendJSON(CreateEventObject("initialized"));
210-
}
211221
}
212222

213223
} // namespace lldb_dap

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

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,11 @@ namespace lldb_dap {
4747

4848
void ConfigurationDoneRequestHandler::operator()(
4949
const llvm::json::Object &request) const {
50+
dap.configuration_done = true;
51+
5052
llvm::json::Object response;
5153
FillResponse(request, response);
5254
dap.SendJSON(llvm::json::Value(std::move(response)));
53-
dap.configuration_done_sent = true;
54-
if (dap.stop_at_entry)
55-
SendThreadStoppedEvent(dap);
56-
else {
57-
// Client requests the baseline of currently existing threads after
58-
// a successful launch or attach by sending a 'threads' request
59-
// right after receiving the configurationDone response.
60-
// Obtain the list of threads before we resume the process
61-
dap.initial_thread_list =
62-
GetThreads(dap.target.GetProcess(), dap.thread_format);
63-
dap.target.GetProcess().Continue();
64-
}
6555
}
6656

6757
} // namespace lldb_dap

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

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) {
140140
lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
141141
if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
142142
auto state = lldb::SBProcess::GetStateFromEvent(event);
143+
144+
DAP_LOG(dap.log, "State = {0}", state);
143145
switch (state) {
146+
case lldb::eStateConnected:
147+
case lldb::eStateDetached:
144148
case lldb::eStateInvalid:
145-
// Not a state event
146-
break;
147149
case lldb::eStateUnloaded:
148150
break;
149-
case lldb::eStateConnected:
150-
break;
151151
case lldb::eStateAttaching:
152-
break;
153-
case lldb::eStateLaunching:
154-
break;
155-
case lldb::eStateStepping:
156-
break;
157152
case lldb::eStateCrashed:
158-
break;
159-
case lldb::eStateDetached:
160-
break;
161-
case lldb::eStateSuspended:
162-
break;
153+
case lldb::eStateLaunching:
163154
case lldb::eStateStopped:
164-
// We launch and attach in synchronous mode then the first stop
165-
// event will not be delivered. If we use "launchCommands" during a
166-
// launch or "attachCommands" during an attach we might some process
167-
// stop events which we do not want to send an event for. We will
168-
// manually send a stopped event in request_configurationDone(...)
169-
// 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);
176-
}
155+
case lldb::eStateSuspended:
156+
// Only report a stopped event if the process was not
157+
// automatically restarted.
158+
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
159+
SendStdOutStdErr(dap, process);
160+
SendThreadStoppedEvent(dap);
177161
}
178162
break;
179163
case lldb::eStateRunning:
164+
case lldb::eStateStepping:
180165
dap.WillContinue();
181166
SendContinuedEvent(dap);
182167
break;
@@ -284,6 +269,7 @@ llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run(
284269
// Do not source init files until in/out/err are configured.
285270
dap.debugger = lldb::SBDebugger::Create(false);
286271
dap.debugger.SetInputFile(dap.in);
272+
dap.target = dap.debugger.GetDummyTarget();
287273

288274
llvm::Expected<int> out_fd = dap.out.GetWriteFileDescriptor();
289275
if (!out_fd)
@@ -338,4 +324,8 @@ llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run(
338324
return dap.GetCapabilities();
339325
}
340326

327+
void InitializeRequestHandler::PostRun() const {
328+
dap.SendJSON(CreateEventObject("initialized"));
329+
}
330+
341331
} // namespace lldb_dap

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ void LaunchRequestHandler::PostRun() const {
7272
// Attach happens when launching with runInTerminal.
7373
SendProcessEvent(dap, dap.is_attach ? Attach : Launch);
7474
}
75-
76-
dap.SendJSON(CreateEventObject("initialized"));
7775
}
7876

7977
} // namespace lldb_dap

0 commit comments

Comments
 (0)