Skip to content

Commit 730fdeb

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 received 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. - Add synchronization for ignoring the first stop event. I originally wanted to do all the launching and attaching in asynchronous mode, but that's a little bit trickier than expected. On macOS, I'm getting an additional stop when we hit the dyld breakpoint. This might be something I come back to, but for now I'm keeping the old behavior with the proposed synchronization from #137920. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125
1 parent 8d02529 commit 730fdeb

9 files changed

+106
-51
lines changed

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 26 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) {
@@ -892,11 +892,14 @@ llvm::Error DAP::Loop() {
892892
errWrapper.SetErrorString(llvm::toString(std::move(err)).c_str());
893893
return errWrapper;
894894
}
895+
bool is_launch_attach = false;
895896

896897
if (const protocol::Request *req =
897-
std::get_if<protocol::Request>(&*next);
898-
req && req->command == "disconnect") {
899-
disconnecting = true;
898+
std::get_if<protocol::Request>(&*next)) {
899+
if (req->command == "disconnect")
900+
disconnecting = true;
901+
if (req->command == "launch" || req->command == "attach")
902+
is_launch_attach = true;
900903
}
901904

902905
const std::optional<CancelArguments> cancel_args =
@@ -924,7 +927,10 @@ llvm::Error DAP::Loop() {
924927

925928
{
926929
std::lock_guard<std::mutex> guard(m_queue_mutex);
927-
m_queue.push_back(std::move(*next));
930+
if (is_launch_attach)
931+
m_launch_attach_queue.push_back(std::move(*next));
932+
else
933+
m_queue.push_back(std::move(*next));
928934
}
929935
m_queue_cv.notify_one();
930936
}
@@ -938,15 +944,24 @@ llvm::Error DAP::Loop() {
938944
StopEventHandlers();
939945
});
940946

941-
while (!disconnecting || !m_queue.empty()) {
947+
while (true) {
942948
std::unique_lock<std::mutex> lock(m_queue_mutex);
943-
m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); });
949+
m_queue_cv.wait(lock, [&] {
950+
return disconnecting || !m_queue.empty() ||
951+
(configuration_done && !m_launch_attach_queue.empty());
952+
});
944953

945-
if (m_queue.empty())
954+
if (disconnecting)
946955
break;
947956

948-
Message next = m_queue.front();
949-
m_queue.pop_front();
957+
auto &active_queue = configuration_done && !m_launch_attach_queue.empty()
958+
? m_launch_attach_queue
959+
: m_queue;
960+
961+
assert(!active_queue.empty());
962+
963+
Message next = active_queue.front();
964+
active_queue.pop_front();
950965

951966
if (!HandleObject(next))
952967
return llvm::createStringError(llvm::inconvertibleErrorCode(),

lldb/tools/lldb-dap/DAP.h

Lines changed: 12 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;
@@ -214,6 +214,13 @@ struct DAP {
214214
/// The initial thread list upon attaching.
215215
std::optional<llvm::json::Array> initial_thread_list;
216216

217+
/// Synchronization of stop events between the event and request thread.
218+
/// @{
219+
std::mutex ignore_stop_mutex;
220+
std::condition_variable ignore_stop_cv;
221+
bool ignore_next_stop;
222+
/// @}
223+
217224
/// Creates a new DAP sessions.
218225
///
219226
/// \param[in] log
@@ -417,8 +424,11 @@ struct DAP {
417424
lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); }
418425

419426
private:
420-
std::mutex m_queue_mutex;
427+
/// Queue for all incoming messages.
421428
std::deque<protocol::Message> m_queue;
429+
/// Dedicated queue for launching and attaching.
430+
std::deque<protocol::Message> m_launch_attach_queue;
431+
std::mutex m_queue_mutex;
422432
std::condition_variable m_queue_cv;
423433

424434
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: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
128128
return;
129129
}
130130

131+
std::unique_lock<std::mutex> lock(dap.ignore_stop_mutex);
132+
dap.ignore_next_stop = !dap.stop_at_entry;
133+
131134
if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) &&
132135
wait_for) {
133136
char attach_msg[256];
@@ -181,9 +184,24 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
181184
// selected target after these commands are run.
182185
dap.target = dap.debugger.GetSelectedTarget();
183186

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));
187+
}
188+
189+
// Notify the event thread that it should ignore the next stop event.
190+
lock.unlock();
191+
dap.ignore_stop_cv.notify_one();
192+
193+
// Make sure the process is attached and stopped before proceeding.
194+
error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds));
195+
196+
// FIXME: Unify this between launch & attach.
197+
if (dap.stop_at_entry) {
198+
// Client requests the baseline of currently existing threads after
199+
// a successful launch or attach by sending a 'threads' request
200+
// right after receiving the configurationDone response.
201+
// Obtain the list of threads before we resume the process
202+
dap.initial_thread_list =
203+
GetThreads(dap.target.GetProcess(), dap.thread_format);
204+
dap.target.GetProcess().Continue();
187205
}
188206

189207
if (error.Success() && core_file.empty()) {
@@ -204,10 +222,8 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
204222
}
205223

206224
dap.SendJSON(llvm::json::Value(std::move(response)));
207-
if (error.Success()) {
225+
if (error.Success())
208226
SendProcessEvent(dap, Attach);
209-
dap.SendJSON(CreateEventObject("initialized"));
210-
}
211227
}
212228

213229
} // 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: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -161,20 +161,22 @@ static void EventThreadFunction(DAP &dap) {
161161
case lldb::eStateSuspended:
162162
break;
163163
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);
164+
{
165+
std::unique_lock<std::mutex> lock(dap.ignore_stop_mutex);
166+
if (dap.ignore_next_stop) {
167+
DAP_LOG(dap.log, "Ignoring process stop event");
175168
SendThreadStoppedEvent(dap);
169+
dap.ignore_next_stop = false;
170+
continue;
176171
}
177172
}
173+
174+
// Only report a stopped event if the process was not
175+
// automatically restarted.
176+
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
177+
SendStdOutStdErr(dap, process);
178+
SendThreadStoppedEvent(dap);
179+
}
178180
break;
179181
case lldb::eStateRunning:
180182
dap.WillContinue();
@@ -284,6 +286,7 @@ llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run(
284286
// Do not source init files until in/out/err are configured.
285287
dap.debugger = lldb::SBDebugger::Create(false);
286288
dap.debugger.SetInputFile(dap.in);
289+
dap.target = dap.debugger.GetDummyTarget();
287290

288291
llvm::Expected<int> out_fd = dap.out.GetWriteFileDescriptor();
289292
if (!out_fd)
@@ -338,4 +341,8 @@ llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run(
338341
return dap.GetCapabilities();
339342
}
340343

344+
void InitializeRequestHandler::PostRun() const {
345+
dap.SendJSON(CreateEventObject("initialized"));
346+
}
347+
341348
} // 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

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,9 @@ llvm::Error BaseRequestHandler::LaunchProcess(
238238
launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
239239
lldb::eLaunchFlagStopAtEntry);
240240

241+
std::unique_lock<std::mutex> lock(dap.ignore_stop_mutex);
242+
dap.ignore_next_stop = !dap.stop_at_entry;
243+
241244
if (arguments.runInTerminal) {
242245
if (llvm::Error err = RunInTerminal(dap, arguments))
243246
return err;
@@ -259,12 +262,27 @@ llvm::Error BaseRequestHandler::LaunchProcess(
259262
// The custom commands might have created a new target so we should use the
260263
// selected target after these commands are run.
261264
dap.target = dap.debugger.GetSelectedTarget();
262-
// Make sure the process is launched and stopped at the entry point before
263-
// proceeding as the launch commands are not run using the synchronous
264-
// mode.
265-
lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout);
266-
if (error.Fail())
267-
return llvm::make_error<DAPError>(error.GetCString());
265+
}
266+
267+
// Notify the event thread that it should ignore the next stop event.
268+
lock.unlock();
269+
dap.ignore_stop_cv.notify_one();
270+
271+
// Make sure the process is launched and stopped at the entry point before
272+
// proceeding.
273+
lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout);
274+
if (error.Fail())
275+
return llvm::make_error<DAPError>(error.GetCString());
276+
277+
// FIXME: Unify this between launch & attach.
278+
if (!dap.stop_at_entry) {
279+
// Client requests the baseline of currently existing threads after
280+
// a successful launch or attach by sending a 'threads' request
281+
// right after receiving the configurationDone response.
282+
// Obtain the list of threads before we resume the process
283+
dap.initial_thread_list =
284+
GetThreads(dap.target.GetProcess(), dap.thread_format);
285+
dap.target.GetProcess().Continue();
268286
}
269287

270288
return llvm::Error::success();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ class InitializeRequestHandler
282282
static llvm::StringLiteral GetCommand() { return "initialize"; }
283283
llvm::Expected<protocol::InitializeResponseBody>
284284
Run(const protocol::InitializeRequestArguments &args) const override;
285+
void PostRun() const override;
285286
};
286287

287288
class LaunchRequestHandler

0 commit comments

Comments
 (0)