Skip to content

Commit e29d4eb

Browse files
authored
[lldb-dap] Correct lldb-dap seq handling. (llvm#164306)
We've been treating the `seq` attribute incorrectly in lldb-dap. Previously, we always had `seq=0` for events and responses. We only filled in the `seq` field on reverse requests. However, looking at the spec and other DAP implementations, we are supposed to fill in the `seq` field for each request we send to the DAP client. I've updated our usage to fill in `seq` in `DAP::Send` so that all events/requests/responses have a properly filled `seq` value.
1 parent c89eb13 commit e29d4eb

File tree

7 files changed

+105
-75
lines changed

7 files changed

+105
-75
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import signal
1212
import sys
1313
import threading
14+
import warnings
1415
import time
1516
from typing import (
1617
Any,
@@ -383,6 +384,10 @@ def _process_recv_packets(self) -> None:
383384
"""Process received packets, updating the session state."""
384385
with self._recv_condition:
385386
for packet in self._recv_packets:
387+
if packet and ("seq" not in packet or packet["seq"] == 0):
388+
warnings.warn(
389+
f"received a malformed packet, expected 'seq != 0' for {packet!r}"
390+
)
386391
# Handle events that may modify any stateful properties of
387392
# the DAP session.
388393
if packet and packet["type"] == "event":
@@ -576,6 +581,7 @@ def wait_for_stopped(self) -> Optional[List[Event]]:
576581
# If we exited, then we are done
577582
if stopped_event["event"] == "exited":
578583
break
584+
579585
# Otherwise we stopped and there might be one or more 'stopped'
580586
# events for each thread that stopped with a reason, so keep
581587
# checking for more 'stopped' events and return all of them

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -266,40 +266,49 @@ void DAP::SendJSON(const llvm::json::Value &json) {
266266
Send(message);
267267
}
268268

269-
void DAP::Send(const Message &message) {
270-
if (const protocol::Event *event = std::get_if<protocol::Event>(&message)) {
269+
Id DAP::Send(const Message &message) {
270+
std::lock_guard<std::mutex> guard(call_mutex);
271+
Message msg = std::visit(
272+
[this](auto &&msg) -> Message {
273+
if (msg.seq == kCalculateSeq)
274+
msg.seq = seq++;
275+
return msg;
276+
},
277+
Message(message));
278+
279+
if (const protocol::Event *event = std::get_if<protocol::Event>(&msg)) {
271280
if (llvm::Error err = transport.Send(*event))
272281
DAP_LOG_ERROR(log, std::move(err), "({0}) sending event failed",
273282
m_client_name);
274-
return;
283+
return event->seq;
275284
}
276285

277-
if (const Request *req = std::get_if<Request>(&message)) {
286+
if (const Request *req = std::get_if<Request>(&msg)) {
278287
if (llvm::Error err = transport.Send(*req))
279288
DAP_LOG_ERROR(log, std::move(err), "({0}) sending request failed",
280289
m_client_name);
281-
return;
290+
return req->seq;
282291
}
283292

284-
if (const Response *resp = std::get_if<Response>(&message)) {
293+
if (const Response *resp = std::get_if<Response>(&msg)) {
285294
// FIXME: After all the requests have migrated from LegacyRequestHandler >
286295
// RequestHandler<> this should be handled in RequestHandler<>::operator().
287296
// If the debugger was interrupted, convert this response into a
288297
// 'cancelled' response because we might have a partial result.
289-
llvm::Error err =
290-
(debugger.InterruptRequested())
291-
? transport.Send({/*request_seq=*/resp->request_seq,
292-
/*command=*/resp->command,
293-
/*success=*/false,
294-
/*message=*/eResponseMessageCancelled,
295-
/*body=*/std::nullopt})
296-
: transport.Send(*resp);
297-
if (err) {
298+
llvm::Error err = (debugger.InterruptRequested())
299+
? transport.Send({
300+
/*request_seq=*/resp->request_seq,
301+
/*command=*/resp->command,
302+
/*success=*/false,
303+
/*message=*/eResponseMessageCancelled,
304+
/*body=*/std::nullopt,
305+
/*seq=*/resp->seq,
306+
})
307+
: transport.Send(*resp);
308+
if (err)
298309
DAP_LOG_ERROR(log, std::move(err), "({0}) sending response failed",
299310
m_client_name);
300-
return;
301-
}
302-
return;
311+
return resp->seq;
303312
}
304313

305314
llvm_unreachable("Unexpected message type");

lldb/tools/lldb-dap/DAP.h

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ struct DAP final : public DAPTransport::MessageHandler {
136136
/// unless we send a "thread" event to indicate the thread exited.
137137
llvm::DenseSet<lldb::tid_t> thread_ids;
138138

139-
uint32_t reverse_request_seq = 0;
139+
protocol::Id seq = 0;
140140
std::mutex call_mutex;
141141
llvm::SmallDenseMap<int64_t, std::unique_ptr<ResponseHandler>>
142142
inflight_reverse_requests;
@@ -220,8 +220,8 @@ struct DAP final : public DAPTransport::MessageHandler {
220220
/// Serialize the JSON value into a string and send the JSON packet to the
221221
/// "out" stream.
222222
void SendJSON(const llvm::json::Value &json);
223-
/// Send the given message to the client
224-
void Send(const protocol::Message &message);
223+
/// Send the given message to the client.
224+
protocol::Id Send(const protocol::Message &message);
225225

226226
void SendOutput(OutputType o, const llvm::StringRef output);
227227

@@ -353,19 +353,13 @@ struct DAP final : public DAPTransport::MessageHandler {
353353
template <typename Handler>
354354
void SendReverseRequest(llvm::StringRef command,
355355
llvm::json::Value arguments) {
356-
int64_t id;
357-
{
358-
std::lock_guard<std::mutex> locker(call_mutex);
359-
id = ++reverse_request_seq;
360-
inflight_reverse_requests[id] = std::make_unique<Handler>(command, id);
361-
}
362-
363-
SendJSON(llvm::json::Object{
364-
{"type", "request"},
365-
{"seq", id},
366-
{"command", command},
367-
{"arguments", std::move(arguments)},
356+
protocol::Id id = Send(protocol::Request{
357+
command.str(),
358+
std::move(arguments),
368359
});
360+
361+
std::lock_guard<std::mutex> locker(call_mutex);
362+
inflight_reverse_requests[id] = std::make_unique<Handler>(command, id);
369363
}
370364

371365
/// The set of capabilities supported by this adapter.

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,12 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
157157
void BaseRequestHandler::Run(const Request &request) {
158158
// If this request was cancelled, send a cancelled response.
159159
if (dap.IsCancelled(request)) {
160-
Response cancelled{/*request_seq=*/request.seq,
161-
/*command=*/request.command,
162-
/*success=*/false,
163-
/*message=*/eResponseMessageCancelled,
164-
/*body=*/std::nullopt};
160+
Response cancelled{
161+
/*request_seq=*/request.seq,
162+
/*command=*/request.command,
163+
/*success=*/false,
164+
/*message=*/eResponseMessageCancelled,
165+
};
165166
dap.Send(cancelled);
166167
return;
167168
}

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "DAP.h"
1111
#include "ExceptionBreakpoint.h"
1212
#include "LLDBUtils.h"
13+
#include "Protocol/ProtocolBase.h"
1314
#include "ProtocolUtils.h"
1415
#include "lldb/API/SBAddress.h"
1516
#include "lldb/API/SBCompileUnit.h"
@@ -284,7 +285,7 @@ void FillResponse(const llvm::json::Object &request,
284285
// Fill in all of the needed response fields to a "request" and set "success"
285286
// to true by default.
286287
response.try_emplace("type", "response");
287-
response.try_emplace("seq", (int64_t)0);
288+
response.try_emplace("seq", protocol::kCalculateSeq);
288289
EmplaceSafeString(response, "command",
289290
GetString(request, "command").value_or(""));
290291
const uint64_t seq = GetInteger<uint64_t>(request, "seq").value_or(0);
@@ -417,7 +418,7 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
417418
// }
418419
llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
419420
llvm::json::Object event;
420-
event.try_emplace("seq", 0);
421+
event.try_emplace("seq", protocol::kCalculateSeq);
421422
event.try_emplace("type", "event");
422423
EmplaceSafeString(event, "event", event_name);
423424
return event;

lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ bool fromJSON(const json::Value &Params, MessageType &M, json::Path P) {
5858
}
5959

6060
json::Value toJSON(const Request &R) {
61+
assert(R.seq != kCalculateSeq && "invalid seq");
62+
6163
json::Object Result{
6264
{"type", "request"},
6365
{"seq", R.seq},
@@ -103,8 +105,10 @@ bool operator==(const Request &a, const Request &b) {
103105
}
104106

105107
json::Value toJSON(const Response &R) {
108+
assert(R.seq != kCalculateSeq && "invalid seq");
109+
106110
json::Object Result{{"type", "response"},
107-
{"seq", 0},
111+
{"seq", R.seq},
108112
{"command", R.command},
109113
{"request_seq", R.request_seq},
110114
{"success", R.success}};
@@ -132,8 +136,9 @@ json::Value toJSON(const Response &R) {
132136
return std::move(Result);
133137
}
134138

135-
bool fromJSON(json::Value const &Params,
136-
std::variant<ResponseMessage, std::string> &M, json::Path P) {
139+
static bool fromJSON(json::Value const &Params,
140+
std::variant<ResponseMessage, std::string> &M,
141+
json::Path P) {
137142
auto rawMessage = Params.getAsString();
138143
if (!rawMessage) {
139144
P.report("expected a string");
@@ -157,8 +162,7 @@ bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
157162
return false;
158163

159164
MessageType type;
160-
int64_t seq;
161-
if (!O.map("type", type) || !O.map("seq", seq) ||
165+
if (!O.map("type", type) || !O.map("seq", R.seq) ||
162166
!O.map("command", R.command) || !O.map("request_seq", R.request_seq))
163167
return false;
164168

@@ -168,12 +172,7 @@ bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
168172
}
169173

170174
if (R.command.empty()) {
171-
P.field("command").report("expected to not be ''");
172-
return false;
173-
}
174-
175-
if (R.request_seq == 0) {
176-
P.field("request_seq").report("expected to not be '0'");
175+
P.field("command").report("expected to not be empty");
177176
return false;
178177
}
179178

@@ -217,9 +216,11 @@ bool fromJSON(json::Value const &Params, ErrorMessage &EM, json::Path P) {
217216
}
218217

219218
json::Value toJSON(const Event &E) {
219+
assert(E.seq != kCalculateSeq && "invalid seq");
220+
220221
json::Object Result{
221222
{"type", "event"},
222-
{"seq", 0},
223+
{"seq", E.seq},
223224
{"event", E.event},
224225
};
225226

@@ -235,22 +236,16 @@ bool fromJSON(json::Value const &Params, Event &E, json::Path P) {
235236
return false;
236237

237238
MessageType type;
238-
int64_t seq;
239-
if (!O.map("type", type) || !O.map("seq", seq) || !O.map("event", E.event))
239+
if (!O.map("type", type) || !O.map("seq", E.seq) || !O.map("event", E.event))
240240
return false;
241241

242242
if (type != eMessageTypeEvent) {
243243
P.field("type").report("expected to be 'event'");
244244
return false;
245245
}
246246

247-
if (seq != 0) {
248-
P.field("seq").report("expected to be '0'");
249-
return false;
250-
}
251-
252247
if (E.event.empty()) {
253-
P.field("event").report("expected to not be ''");
248+
P.field("event").report("expected to not be empty");
254249
return false;
255250
}
256251

lldb/tools/lldb-dap/Protocol/ProtocolBase.h

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,32 @@ namespace lldb_dap::protocol {
3030

3131
// MARK: Base Protocol
3232

33+
/// Message unique identifier type.
3334
using Id = int64_t;
3435

36+
/// A unique identifier that indicates the `seq` field should be calculated by
37+
/// the current session.
38+
static constexpr Id kCalculateSeq = INT64_MAX;
39+
3540
/// A client or debug adapter initiated request.
3641
struct Request {
37-
/// Sequence number of the message (also known as message ID). The `seq` for
38-
/// the first message sent by a client or debug adapter is 1, and for each
39-
/// subsequent message is 1 greater than the previous message sent by that
40-
/// actor. `seq` can be used to order requests, responses, and events, and to
41-
/// associate requests with their corresponding responses. For protocol
42-
/// messages of type `request` the sequence number can be used to cancel the
43-
/// request.
44-
Id seq;
45-
4642
/// The command to execute.
4743
std::string command;
4844

4945
/// Object containing arguments for the command.
5046
///
5147
/// Request handlers are expected to validate the arguments, which is handled
5248
/// by `RequestHandler`.
53-
std::optional<llvm::json::Value> arguments;
49+
std::optional<llvm::json::Value> arguments = std::nullopt;
50+
51+
/// Sequence number of the message (also known as message ID). The `seq` for
52+
/// the first message sent by a client or debug adapter is 1, and for each
53+
/// subsequent message is 1 greater than the previous message sent by that
54+
/// actor. `seq` can be used to order requests, responses, and events, and to
55+
/// associate requests with their corresponding responses. For protocol
56+
/// messages of type `request` the sequence number can be used to cancel the
57+
/// request.
58+
Id seq = kCalculateSeq;
5459
};
5560
llvm::json::Value toJSON(const Request &);
5661
bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path);
@@ -62,7 +67,16 @@ struct Event {
6267
std::string event;
6368

6469
/// Event-specific information.
65-
std::optional<llvm::json::Value> body;
70+
std::optional<llvm::json::Value> body = std::nullopt;
71+
72+
/// Sequence number of the message (also known as message ID). The `seq` for
73+
/// the first message sent by a client or debug adapter is 1, and for each
74+
/// subsequent message is 1 greater than the previous message sent by that
75+
/// actor. `seq` can be used to order requests, responses, and events, and to
76+
/// associate requests with their corresponding responses. For protocol
77+
/// messages of type `request` the sequence number can be used to cancel the
78+
/// request.
79+
Id seq = kCalculateSeq;
6680
};
6781
llvm::json::Value toJSON(const Event &);
6882
bool fromJSON(const llvm::json::Value &, Event &, llvm::json::Path);
@@ -78,7 +92,7 @@ enum ResponseMessage : unsigned {
7892
/// Response for a request.
7993
struct Response {
8094
/// Sequence number of the corresponding request.
81-
Id request_seq;
95+
Id request_seq = 0;
8296

8397
/// The command requested.
8498
std::string command;
@@ -87,21 +101,31 @@ struct Response {
87101
/// attribute may contain the result of the request. If the value is false,
88102
/// the attribute `message` contains the error in short form and the `body`
89103
/// may contain additional information (see `ErrorMessage`).
90-
bool success;
104+
bool success = false;
91105

92106
// FIXME: Migrate usage of fallback string to ErrorMessage
93107

94108
/// Contains the raw error in short form if `success` is false. This raw error
95109
/// might be interpreted by the client and is not shown in the UI. Some
96110
/// predefined values exist.
97-
std::optional<std::variant<ResponseMessage, std::string>> message;
111+
std::optional<std::variant<ResponseMessage, std::string>> message =
112+
std::nullopt;
98113

99114
/// Contains request result if success is true and error details if success is
100115
/// false.
101116
///
102117
/// Request handlers are expected to build an appropriate body, see
103118
/// `RequestHandler`.
104-
std::optional<llvm::json::Value> body;
119+
std::optional<llvm::json::Value> body = std::nullopt;
120+
121+
/// Sequence number of the message (also known as message ID). The `seq` for
122+
/// the first message sent by a client or debug adapter is 1, and for each
123+
/// subsequent message is 1 greater than the previous message sent by that
124+
/// actor. `seq` can be used to order requests, responses, and events, and to
125+
/// associate requests with their corresponding responses. For protocol
126+
/// messages of type `request` the sequence number can be used to cancel the
127+
/// request.
128+
Id seq = kCalculateSeq;
105129
};
106130
bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path);
107131
llvm::json::Value toJSON(const Response &);

0 commit comments

Comments
 (0)