Skip to content

Commit 0824e3c

Browse files
committed
[lldb-dap] Creating a 'capabilities' event helper.
This adds a new 'CapabilitiesEventBody' type for having a well structured type for the event and updates the 'stepInTargets' and 'restart' request to dynamically set their capabilities. This also fixes the 'stepInTargets' test on non-x86 platforms.
1 parent 0519453 commit 0824e3c

File tree

11 files changed

+112
-30
lines changed

11 files changed

+112
-30
lines changed

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ def as_dict(self):
135135
return source_dict
136136

137137

138+
class NotSupportedError(Exception):
139+
"""Raised if a feature is not supported due to its capabilities."""
140+
141+
138142
class DebugCommunication(object):
139143
def __init__(
140144
self,
@@ -153,7 +157,7 @@ def __init__(
153157
self.recv_thread = threading.Thread(target=self._read_packet_thread)
154158
self.process_event_body = None
155159
self.exit_status: Optional[int] = None
156-
self.initialize_body = None
160+
self.capabilities: dict[str, Any] = {}
157161
self.progress_events: list[Event] = []
158162
self.reverse_requests = []
159163
self.sequence = 1
@@ -300,6 +304,9 @@ def _handle_recv_packet(self, packet: Optional[ProtocolMessage]) -> bool:
300304
elif event == "breakpoint":
301305
# Breakpoint events are sent when a breakpoint is resolved
302306
self._update_verified_breakpoints([body["breakpoint"]])
307+
elif event == "capabilities":
308+
# Update the capabilities with new ones from the event.
309+
self.capabilities.update(body["capabilities"])
303310

304311
elif packet_type == "response":
305312
if packet["command"] == "disconnect":
@@ -488,13 +495,11 @@ def wait_for_terminated(self, timeout: Optional[float] = None):
488495
raise ValueError("didn't get terminated event")
489496
return event_dict
490497

491-
def get_initialize_value(self, key):
498+
def get_capability(self, key, default=None):
492499
"""Get a value for the given key if it there is a key/value pair in
493-
the "initialize" request response body.
500+
the capabilities reported by the adapter.
494501
"""
495-
if self.initialize_body and key in self.initialize_body:
496-
return self.initialize_body[key]
497-
return None
502+
return self.capabilities.get(key, default)
498503

499504
def get_threads(self):
500505
if self.threads is None:
@@ -760,6 +765,10 @@ def request_continue(self, threadId=None, singleThread=False):
760765
return response
761766

762767
def request_restart(self, restartArguments=None):
768+
if self.exit_status is not None:
769+
raise ValueError("request_restart called after process exited")
770+
if not self.get_capability("supportsRestartRequest", False):
771+
raise NotSupportedError("supportsRestartRequest is not set")
763772
command_dict = {
764773
"command": "restart",
765774
"type": "request",
@@ -867,7 +876,7 @@ def request_initialize(self, sourceInitFile=False):
867876
response = self.send_recv(command_dict)
868877
if response:
869878
if "body" in response:
870-
self.initialize_body = response["body"]
879+
self.capabilities = response["body"]
871880
return response
872881

873882
def request_launch(
@@ -972,6 +981,8 @@ def request_stepIn(self, threadId, targetId, granularity="statement"):
972981
def request_stepInTargets(self, frameId):
973982
if self.exit_status is not None:
974983
raise ValueError("request_stepInTargets called after process exited")
984+
if not self.get_capability("supportsStepInTargetsRequest", False):
985+
raise NotSupportedError("supportsStepInTargetsRequest is not set")
975986
args_dict = {"frameId": frameId}
976987
command_dict = {
977988
"command": "stepInTargets",

lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ def test_version(self):
566566
)
567567
version_eval_output = version_eval_response["body"]["result"]
568568

569-
version_string = self.dap_server.get_initialize_value("$__lldb_version")
569+
version_string = self.dap_server.get_capability("$__lldb_version")
570570
self.assertEqual(
571571
version_eval_output.splitlines(),
572572
version_string.splitlines(),

lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def test_basic(self):
3030
self.assertEqual(
3131
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
3232
)
33+
# Target based capability 'supportsStepInTargetsRequest' is sent in
34+
# 'configurationDone' which is called prior to continue.
3335
self.continue_to_breakpoints(breakpoint_ids)
3436

3537
threads = self.dap_server.get_threads()

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ add_lldb_library(lldbDAP
6767
Handler/VariablesRequestHandler.cpp
6868

6969
Protocol/ProtocolBase.cpp
70+
Protocol/ProtocolEvents.cpp
7071
Protocol/ProtocolTypes.cpp
7172
Protocol/ProtocolRequests.cpp
7273

lldb/tools/lldb-dap/EventHelper.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "DAPLog.h"
1212
#include "JSONUtils.h"
1313
#include "LLDBUtils.h"
14+
#include "Protocol/ProtocolEvents.h"
15+
#include "Protocol/ProtocolTypes.h"
1416
#include "lldb/API/SBFileSpec.h"
1517

1618
#if defined(_WIN32)
@@ -33,6 +35,22 @@ static void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) {
3335
dap.SendJSON(llvm::json::Value(std::move(event)));
3436
}
3537

38+
void SendTargetBasedCapabilities(DAP &dap) {
39+
if (!dap.target.IsValid())
40+
return;
41+
42+
protocol::CapabilitiesEventBody body;
43+
44+
// We only support restarting launch requests not attach requests.
45+
if (dap.last_launch_request)
46+
body.capabilities.supportedFeatures.insert(
47+
protocol::eAdapterFeatureRestartRequest);
48+
49+
// Only notify the client if supportedFeatures changed.
50+
if (!body.capabilities.supportedFeatures.empty())
51+
dap.Send(protocol::Event{"capabilities", body});
52+
}
53+
3654
// "ProcessEvent": {
3755
// "allOf": [
3856
// { "$ref": "#/definitions/Event" },

lldb/tools/lldb-dap/EventHelper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ struct DAP;
1616

1717
enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
1818

19+
/// Update capabilities based on the configured target.
20+
void SendTargetBasedCapabilities(DAP &dap);
21+
1922
void SendProcessEvent(DAP &dap, LaunchMethod launch_method);
2023

2124
void SendThreadStoppedEvent(DAP &dap);

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,6 @@ class RestartRequestHandler : public LegacyRequestHandler {
334334
public:
335335
using LegacyRequestHandler::LegacyRequestHandler;
336336
static llvm::StringLiteral GetCommand() { return "restart"; }
337-
FeatureSet GetSupportedFeatures() const override {
338-
return {protocol::eAdapterFeatureRestartRequest};
339-
}
340337
void operator()(const llvm::json::Object &request) const override;
341338
};
342339

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

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,6 @@ void RestartRequestHandler::operator()(
6969
dap.SendJSON(llvm::json::Value(std::move(response)));
7070
return;
7171
}
72-
// Check if we were in a "launch" session or an "attach" session.
73-
//
74-
// Restarting is not well defined when we started the session by attaching to
75-
// an existing process, because we don't know how the process was started, so
76-
// we don't support it.
77-
//
78-
// Note that when using runInTerminal we're technically attached, but it's an
79-
// implementation detail. The adapter *did* launch the process in response to
80-
// a "launch" command, so we can still stop it and re-run it. This is why we
81-
// don't just check `dap.is_attach`.
82-
if (!dap.last_launch_request) {
83-
response["success"] = llvm::json::Value(false);
84-
EmplaceSafeString(response, "message",
85-
"Restarting an \"attach\" session is not supported.");
86-
dap.SendJSON(llvm::json::Value(std::move(response)));
87-
return;
88-
}
8972

9073
const llvm::json::Object *arguments = request.getObject("arguments");
9174
if (arguments) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
//
1818
//===----------------------------------------------------------------------===//
1919

20-
#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
21-
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_H
20+
#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_BASE_H
21+
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_BASE_H
2222

2323
#include "llvm/Support/JSON.h"
2424
#include <cstdint>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- ProtocolEvents.cpp
2+
//--------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "Protocol/ProtocolEvents.h"
11+
#include "llvm/Support/JSON.h"
12+
13+
using namespace llvm;
14+
15+
namespace lldb_dap::protocol {
16+
17+
json::Value toJSON(const CapabilitiesEventBody &CEB) {
18+
return json::Object{{"capabilities", CEB.capabilities}};
19+
}
20+
21+
} // namespace lldb_dap::protocol

0 commit comments

Comments
 (0)