Skip to content

Commit 1041d54

Browse files
authored
[lldb-dap] Updating the 'next' request handler use well structured types (llvm#136642)
This updates the 'next' request to use well structured types. While working on this I also simplified the 'RequestHandler' implementation to better handle void responses by allowing requests to return a 'llvm::Error' instead of an 'llvm::Expected<std::monostate>'. This makes it easier to write and understand request handles that have simple ack responses.
1 parent 6dbc01e commit 1041d54

12 files changed

+151
-107
lines changed

lldb/tools/lldb-dap/DAP.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "lldb/Utility/Status.h"
2929
#include "lldb/lldb-defines.h"
3030
#include "lldb/lldb-enumerations.h"
31+
#include "lldb/lldb-types.h"
3132
#include "llvm/ADT/ArrayRef.h"
3233
#include "llvm/ADT/STLExtras.h"
3334
#include "llvm/ADT/ScopeExit.h"
@@ -499,6 +500,10 @@ ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
499500
return exc_bp;
500501
}
501502

503+
lldb::SBThread DAP::GetLLDBThread(lldb::tid_t tid) {
504+
return target.GetProcess().GetThreadByID(tid);
505+
}
506+
502507
lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
503508
auto tid = GetInteger<int64_t>(arguments, "threadId")
504509
.value_or(LLDB_INVALID_THREAD_ID);

lldb/tools/lldb-dap/DAP.h

+1
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ struct DAP {
266266

267267
ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread);
268268

269+
lldb::SBThread GetLLDBThread(lldb::tid_t id);
269270
lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments);
270271

271272
lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);

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

+3-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include "Protocol/ProtocolRequests.h"
1111
#include "llvm/Support/Error.h"
1212

13-
using namespace lldb_dap;
13+
using namespace llvm;
1414
using namespace lldb_dap::protocol;
1515

1616
namespace lldb_dap {
@@ -45,12 +45,11 @@ namespace lldb_dap {
4545
///
4646
/// A client cannot assume that progress just got cancelled after sending
4747
/// the `cancel` request.
48-
llvm::Expected<CancelResponseBody>
49-
CancelRequestHandler::Run(const CancelArguments &arguments) const {
48+
Error CancelRequestHandler::Run(const CancelArguments &arguments) const {
5049
// Cancel support is built into the DAP::Loop handler for detecting
5150
// cancellations of pending or inflight requests.
5251
dap.ClearCancelRequest(arguments);
53-
return CancelResponseBody();
52+
return Error::success();
5453
}
5554

5655
} // namespace lldb_dap

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ using namespace lldb_dap::protocol;
1818
namespace lldb_dap {
1919

2020
/// Disconnect request; value of command field is 'disconnect'.
21-
Expected<DisconnectResponse> DisconnectRequestHandler::Run(
21+
Error DisconnectRequestHandler::Run(
2222
const std::optional<DisconnectArguments> &arguments) const {
2323
bool terminateDebuggee = dap.is_attach ? false : true;
2424

@@ -28,6 +28,6 @@ Expected<DisconnectResponse> DisconnectRequestHandler::Run(
2828
if (Error error = dap.Disconnect(terminateDebuggee))
2929
return error;
3030

31-
return DisconnectResponse();
31+
return Error::success();
3232
}
3333
} // namespace lldb_dap

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

+25-60
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,37 @@
88

99
#include "DAP.h"
1010
#include "EventHelper.h"
11-
#include "JSONUtils.h"
11+
#include "Protocol/ProtocolTypes.h"
1212
#include "RequestHandler.h"
13+
#include "llvm/Support/Error.h"
14+
15+
using namespace llvm;
16+
using namespace lldb_dap::protocol;
1317

1418
namespace lldb_dap {
1519

16-
// "NextRequest": {
17-
// "allOf": [ { "$ref": "#/definitions/Request" }, {
18-
// "type": "object",
19-
// "description": "Next request; value of command field is 'next'. The
20-
// request starts the debuggee to run again for one step.
21-
// The debug adapter first sends the NextResponse and then
22-
// a StoppedEvent (event type 'step') after the step has
23-
// completed.",
24-
// "properties": {
25-
// "command": {
26-
// "type": "string",
27-
// "enum": [ "next" ]
28-
// },
29-
// "arguments": {
30-
// "$ref": "#/definitions/NextArguments"
31-
// }
32-
// },
33-
// "required": [ "command", "arguments" ]
34-
// }]
35-
// },
36-
// "NextArguments": {
37-
// "type": "object",
38-
// "description": "Arguments for 'next' request.",
39-
// "properties": {
40-
// "threadId": {
41-
// "type": "integer",
42-
// "description": "Execute 'next' for this thread."
43-
// },
44-
// "granularity": {
45-
// "$ref": "#/definitions/SteppingGranularity",
46-
// "description": "Stepping granularity. If no granularity is specified, a
47-
// granularity of `statement` is assumed."
48-
// }
49-
// },
50-
// "required": [ "threadId" ]
51-
// },
52-
// "NextResponse": {
53-
// "allOf": [ { "$ref": "#/definitions/Response" }, {
54-
// "type": "object",
55-
// "description": "Response to 'next' request. This is just an
56-
// acknowledgement, so no body field is required."
57-
// }]
58-
// }
59-
void NextRequestHandler::operator()(const llvm::json::Object &request) const {
60-
llvm::json::Object response;
61-
FillResponse(request, response);
62-
const auto *arguments = request.getObject("arguments");
63-
lldb::SBThread thread = dap.GetLLDBThread(*arguments);
64-
if (thread.IsValid()) {
65-
// Remember the thread ID that caused the resume so we can set the
66-
// "threadCausedFocus" boolean value in the "stopped" events.
67-
dap.focus_tid = thread.GetThreadID();
68-
if (HasInstructionGranularity(*arguments)) {
69-
thread.StepInstruction(/*step_over=*/true);
70-
} else {
71-
thread.StepOver();
72-
}
20+
/// The request executes one step (in the given granularity) for the specified
21+
/// thread and allows all other threads to run freely by resuming them. If the
22+
/// debug adapter supports single thread execution (see capability
23+
/// `supportsSingleThreadExecutionRequests`), setting the `singleThread`
24+
/// argument to true prevents other suspended threads from resuming. The debug
25+
/// adapter first sends the response and then a `stopped` event (with reason
26+
/// `step`) after the step has completed.
27+
Error NextRequestHandler::Run(const NextArguments &args) const {
28+
lldb::SBThread thread = dap.GetLLDBThread(args.threadId);
29+
if (!thread.IsValid())
30+
return make_error<DAPError>("invalid thread");
31+
32+
// Remember the thread ID that caused the resume so we can set the
33+
// "threadCausedFocus" boolean value in the "stopped" events.
34+
dap.focus_tid = thread.GetThreadID();
35+
if (args.granularity == eSteppingGranularityInstruction) {
36+
thread.StepInstruction(/*step_over=*/true);
7337
} else {
74-
response["success"] = llvm::json::Value(false);
38+
thread.StepOver();
7539
}
76-
dap.SendJSON(llvm::json::Value(std::move(response)));
40+
41+
return Error::success();
7742
}
7843

7944
} // namespace lldb_dap

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

+44-38
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ class LegacyRequestHandler : public BaseRequestHandler {
9494
/// Base class for handling DAP requests. Handlers should declare their
9595
/// arguments and response body types like:
9696
///
97-
/// class MyRequestHandler : public RequestHandler<Arguments, ResponseBody> {
97+
/// class MyRequestHandler : public RequestHandler<Arguments, Response> {
9898
/// ....
9999
/// };
100-
template <typename Args, typename Body>
100+
template <typename Args, typename Resp>
101101
class RequestHandler : public BaseRequestHandler {
102102
using BaseRequestHandler::BaseRequestHandler;
103103

@@ -128,41 +128,29 @@ class RequestHandler : public BaseRequestHandler {
128128
<< "': " << llvm::toString(root.getError()) << "\n";
129129
root.printErrorContext(*request.arguments, OS);
130130

131-
protocol::ErrorMessage error_message;
132-
error_message.format = parse_failure;
133-
134-
protocol::ErrorResponseBody body;
135-
body.error = error_message;
136-
137131
response.success = false;
138-
response.body = std::move(body);
132+
response.body = ToResponse(llvm::make_error<DAPError>(parse_failure));
139133

140134
dap.Send(response);
141135
return;
142136
}
143137

144-
llvm::Expected<Body> body = Run(arguments);
145-
if (auto Err = body.takeError()) {
146-
protocol::ErrorMessage error_message;
147-
error_message.sendTelemetry = false;
148-
if (llvm::Error unhandled = llvm::handleErrors(
149-
std::move(Err), [&](const DAPError &E) -> llvm::Error {
150-
error_message.format = E.getMessage();
151-
error_message.showUser = E.getShowUser();
152-
error_message.id = E.convertToErrorCode().value();
153-
error_message.url = E.getURL();
154-
error_message.urlLabel = E.getURLLabel();
155-
return llvm::Error::success();
156-
}))
157-
error_message.format = llvm::toString(std::move(unhandled));
158-
protocol::ErrorResponseBody body;
159-
body.error = error_message;
160-
response.success = false;
161-
response.body = std::move(body);
138+
if constexpr (std::is_same_v<Resp, llvm::Error>) {
139+
if (llvm::Error err = Run(arguments)) {
140+
response.success = false;
141+
response.body = ToResponse(std::move(err));
142+
} else {
143+
response.success = true;
144+
}
162145
} else {
163-
response.success = true;
164-
if constexpr (!std::is_same_v<Body, std::monostate>)
146+
Resp body = Run(arguments);
147+
if (llvm::Error err = body.takeError()) {
148+
response.success = false;
149+
response.body = ToResponse(std::move(err));
150+
} else {
151+
response.success = true;
165152
response.body = std::move(*body);
153+
}
166154
}
167155

168156
// Mark the request as 'cancelled' if the debugger was interrupted while
@@ -177,7 +165,25 @@ class RequestHandler : public BaseRequestHandler {
177165
dap.Send(response);
178166
};
179167

180-
virtual llvm::Expected<Body> Run(const Args &) const = 0;
168+
virtual Resp Run(const Args &) const = 0;
169+
170+
protocol::ErrorResponseBody ToResponse(llvm::Error err) const {
171+
protocol::ErrorMessage error_message;
172+
error_message.sendTelemetry = false;
173+
if (llvm::Error unhandled = llvm::handleErrors(
174+
std::move(err), [&](const DAPError &E) -> llvm::Error {
175+
error_message.format = E.getMessage();
176+
error_message.showUser = E.getShowUser();
177+
error_message.id = E.convertToErrorCode().value();
178+
error_message.url = E.getURL();
179+
error_message.urlLabel = E.getURLLabel();
180+
return llvm::Error::success();
181+
}))
182+
error_message.format = llvm::toString(std::move(unhandled));
183+
protocol::ErrorResponseBody body;
184+
body.error = error_message;
185+
return body;
186+
}
181187
};
182188

183189
class AttachRequestHandler : public LegacyRequestHandler {
@@ -233,7 +239,7 @@ class DisconnectRequestHandler
233239
FeatureSet GetSupportedFeatures() const override {
234240
return {protocol::eAdapterFeatureTerminateDebuggee};
235241
}
236-
llvm::Expected<protocol::DisconnectResponse>
242+
llvm::Error
237243
Run(const std::optional<protocol::DisconnectArguments> &args) const override;
238244
};
239245

@@ -259,7 +265,7 @@ class ExceptionInfoRequestHandler : public LegacyRequestHandler {
259265

260266
class InitializeRequestHandler
261267
: public RequestHandler<protocol::InitializeRequestArguments,
262-
protocol::InitializeResponseBody> {
268+
llvm::Expected<protocol::InitializeResponseBody>> {
263269
public:
264270
using RequestHandler::RequestHandler;
265271
static llvm::StringLiteral GetCommand() { return "initialize"; }
@@ -284,11 +290,12 @@ class RestartRequestHandler : public LegacyRequestHandler {
284290
void operator()(const llvm::json::Object &request) const override;
285291
};
286292

287-
class NextRequestHandler : public LegacyRequestHandler {
293+
class NextRequestHandler
294+
: public RequestHandler<protocol::NextArguments, protocol::NextResponse> {
288295
public:
289-
using LegacyRequestHandler::LegacyRequestHandler;
296+
using RequestHandler::RequestHandler;
290297
static llvm::StringLiteral GetCommand() { return "next"; }
291-
void operator()(const llvm::json::Object &request) const override;
298+
llvm::Error Run(const protocol::NextArguments &args) const override;
292299
};
293300

294301
class StepInRequestHandler : public LegacyRequestHandler {
@@ -418,7 +425,7 @@ class SetVariableRequestHandler : public LegacyRequestHandler {
418425

419426
class SourceRequestHandler
420427
: public RequestHandler<protocol::SourceArguments,
421-
protocol::SourceResponseBody> {
428+
llvm::Expected<protocol::SourceResponseBody>> {
422429
public:
423430
using RequestHandler::RequestHandler;
424431
static llvm::StringLiteral GetCommand() { return "source"; }
@@ -486,8 +493,7 @@ class CancelRequestHandler
486493
FeatureSet GetSupportedFeatures() const override {
487494
return {protocol::eAdapterFeatureCancelRequest};
488495
}
489-
llvm::Expected<protocol::CancelResponseBody>
490-
Run(const protocol::CancelArguments &args) const override;
496+
llvm::Error Run(const protocol::CancelArguments &args) const override;
491497
};
492498

493499
/// A request used in testing to get the details on all breakpoints that are

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ struct ErrorResponseBody {
149149
llvm::json::Value toJSON(const ErrorResponseBody &);
150150

151151
/// This is just an acknowledgement, so no body field is required.
152-
using VoidResponse = std::monostate;
152+
using VoidResponse = llvm::Error;
153153

154154
} // namespace lldb_dap::protocol
155155

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "Protocol/ProtocolRequests.h"
10-
#include "DAP.h"
1110
#include "llvm/ADT/StringMap.h"
1211
#include "llvm/ADT/StringRef.h"
1312
#include "llvm/Support/JSON.h"
@@ -114,4 +113,12 @@ json::Value toJSON(const SourceResponseBody &SA) {
114113
return std::move(Result);
115114
}
116115

116+
bool fromJSON(const llvm::json::Value &Params, NextArguments &NA,
117+
llvm::json::Path P) {
118+
json::ObjectMapper OM(Params, P);
119+
return OM && OM.map("threadId", NA.threadId) &&
120+
OM.mapOptional("singleThread", NA.singleThread) &&
121+
OM.mapOptional("granularity", NA.granularity);
122+
}
123+
117124
} // namespace lldb_dap::protocol

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

+20
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "Protocol/ProtocolBase.h"
2424
#include "Protocol/ProtocolTypes.h"
25+
#include "lldb/lldb-defines.h"
2526
#include "llvm/ADT/DenseSet.h"
2627
#include "llvm/Support/JSON.h"
2728
#include <cstdint>
@@ -236,6 +237,25 @@ struct SourceResponseBody {
236237
};
237238
llvm::json::Value toJSON(const SourceResponseBody &);
238239

240+
/// Arguments for `next` request.
241+
struct NextArguments {
242+
/// Specifies the thread for which to resume execution for one step (of the
243+
/// given granularity).
244+
uint64_t threadId = LLDB_INVALID_THREAD_ID;
245+
246+
/// If this flag is true, all other suspended threads are not resumed.
247+
bool singleThread = false;
248+
249+
/// Stepping granularity. If no granularity is specified, a granularity of
250+
/// `statement` is assumed.
251+
SteppingGranularity granularity = eSteppingGranularityStatement;
252+
};
253+
bool fromJSON(const llvm::json::Value &, NextArguments &, llvm::json::Path);
254+
255+
/// Response to `next` request. This is just an acknowledgement, so no
256+
/// body field is required.
257+
using NextResponse = VoidResponse;
258+
239259
} // namespace lldb_dap::protocol
240260

241261
#endif

0 commit comments

Comments
 (0)