Skip to content

Commit c744f61

Browse files
authored
[lldb-dap] Add memory event (#158437)
This patch adds support for the `memory` event from the [DAP](https://microsoft.github.io/debug-adapter-protocol/specification#Events_Memory). After this event is emitted, VS Code refetches the corresponding memory range and re-renders the memory view. I think this patch and [PR](#151884) can improve experience for users who use `setVariable` request.
1 parent 1a4685d commit c744f61

File tree

8 files changed

+79
-0
lines changed

8 files changed

+79
-0
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ def __init__(
216216
self.events: List[Event] = []
217217
self.progress_events: List[Event] = []
218218
self.invalidated_event: Optional[Event] = None
219+
self.memory_event: Optional[Event] = None
219220
self.reverse_requests: List[Request] = []
220221
self.module_events: List[Dict] = []
221222
self.sequence: int = 1
@@ -443,6 +444,8 @@ def _handle_event(self, packet: Event) -> None:
443444
self.capabilities.update(body["capabilities"])
444445
elif event == "invalidated":
445446
self.invalidated_event = packet
447+
elif event == "memory":
448+
self.memory_event = packet
446449

447450
def _handle_reverse_request(self, request: Request) -> None:
448451
if request in self.reverse_requests:
@@ -1018,6 +1021,7 @@ def request_initialize(self, sourceInitFile=False):
10181021
"supportsStartDebuggingRequest": True,
10191022
"supportsProgressReporting": True,
10201023
"supportsInvalidatedEvent": True,
1024+
"supportsMemoryEvent": True,
10211025
"$__lldb_sourceInitFile": sourceInitFile,
10221026
},
10231027
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,14 @@ def verify_invalidated_event(self, expected_areas):
248248
areas = event["body"].get("areas", [])
249249
self.assertEqual(set(expected_areas), set(areas))
250250

251+
def verify_memory_event(self, memoryReference):
252+
if memoryReference is None:
253+
self.assertIsNone(self.dap_server.memory_event)
254+
event = self.dap_server.memory_event
255+
self.dap_server.memory_event = None
256+
self.assertIsNotNone(event)
257+
self.assertEqual(memoryReference, event["body"].get("memoryReference"))
258+
251259
def get_dict_value(self, d: dict, key_path: list[str]) -> Any:
252260
"""Verify each key in the key_path array is in contained in each
253261
dictionary within "d". Assert if any key isn't in the
@@ -364,6 +372,7 @@ def set_variable(self, varRef, name, value, id=None):
364372
response = self.dap_server.request_setVariable(varRef, name, str(value), id=id)
365373
if response["success"]:
366374
self.verify_invalidated_event(["variables"])
375+
self.verify_memory_event(response["body"].get("memoryReference"))
367376
return response
368377

369378
def set_local(self, name, value, id=None):

lldb/tools/lldb-dap/EventHelper.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,4 +284,15 @@ void SendInvalidatedEvent(
284284
dap.Send(protocol::Event{"invalidated", std::move(body)});
285285
}
286286

287+
void SendMemoryEvent(DAP &dap, lldb::SBValue variable) {
288+
if (!dap.clientFeatures.contains(protocol::eClientFeatureMemoryEvent))
289+
return;
290+
protocol::MemoryEventBody body;
291+
body.memoryReference = variable.GetLoadAddress();
292+
body.count = variable.GetByteSize();
293+
if (body.memoryReference == LLDB_INVALID_ADDRESS)
294+
return;
295+
dap.Send(protocol::Event{"memory", std::move(body)});
296+
}
297+
287298
} // namespace lldb_dap

lldb/tools/lldb-dap/EventHelper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process);
3737
void SendInvalidatedEvent(
3838
DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas);
3939

40+
void SendMemoryEvent(DAP &dap, lldb::SBValue variable);
41+
4042
} // namespace lldb_dap
4143

4244
#endif

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ SetVariableRequestHandler::Run(const SetVariableArguments &args) const {
8282
// (e.g. references) can be changed.
8383
SendInvalidatedEvent(dap, {InvalidatedEventBody::eAreaVariables});
8484

85+
// Also send memory event to signal client that variable memory was changed.
86+
SendMemoryEvent(dap, variable);
87+
8588
return body;
8689
}
8790

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "Protocol/ProtocolEvents.h"
10+
#include "JSONUtils.h"
1011
#include "llvm/Support/JSON.h"
1112

1213
using namespace llvm;
@@ -56,4 +57,11 @@ llvm::json::Value toJSON(const InvalidatedEventBody &IEB) {
5657
return Result;
5758
}
5859

60+
llvm::json::Value toJSON(const MemoryEventBody &MEB) {
61+
return json::Object{
62+
{"memoryReference", EncodeMemoryReference(MEB.memoryReference)},
63+
{"offset", MEB.offset},
64+
{"count", MEB.count}};
65+
}
66+
5967
} // namespace lldb_dap::protocol

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_EVENTS_H
2222

2323
#include "Protocol/ProtocolTypes.h"
24+
#include "lldb/lldb-defines.h"
2425
#include "lldb/lldb-types.h"
2526
#include "llvm/Support/JSON.h"
2627
#include <cstdint>
@@ -88,6 +89,34 @@ struct InvalidatedEventBody {
8889
llvm::json::Value toJSON(const InvalidatedEventBody::Area &);
8990
llvm::json::Value toJSON(const InvalidatedEventBody &);
9091

92+
/// This event indicates that some memory range has been updated. It should only
93+
/// be sent if the corresponding capability supportsMemoryEvent is true.
94+
///
95+
/// Clients typically react to the event by re-issuing a readMemory request if
96+
/// they show the memory identified by the memoryReference and if the updated
97+
/// memory range overlaps the displayed range. Clients should not make
98+
/// assumptions how individual memory references relate to each other, so they
99+
/// should not assume that they are part of a single continuous address range
100+
/// and might overlap.
101+
///
102+
/// Debug adapters can use this event to indicate that the contents of a memory
103+
/// range has changed due to some other request like setVariable or
104+
/// setExpression. Debug adapters are not expected to emit this event for each
105+
/// and every memory change of a running program, because that information is
106+
/// typically not available from debuggers and it would flood clients with too
107+
/// many events.
108+
struct MemoryEventBody {
109+
/// Memory reference of a memory range that has been updated.
110+
lldb::addr_t memoryReference = LLDB_INVALID_ADDRESS;
111+
112+
/// Starting offset in bytes where memory has been updated. Can be negative.
113+
int64_t offset = 0;
114+
115+
/// Number of bytes updated.
116+
uint64_t count = 0;
117+
};
118+
llvm::json::Value toJSON(const MemoryEventBody &);
119+
91120
} // end namespace lldb_dap::protocol
92121

93122
#endif

lldb/unittests/DAP/ProtocolTypesTest.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,3 +1088,16 @@ TEST(ProtocolTypesTest, InvalidatedEventBody) {
10881088
})";
10891089
EXPECT_EQ(json, pp(body));
10901090
}
1091+
1092+
TEST(ProtocolTypesTest, MemoryEventBody) {
1093+
MemoryEventBody body;
1094+
body.memoryReference = 12345;
1095+
body.offset = 0;
1096+
body.count = 4;
1097+
StringRef json = R"({
1098+
"count": 4,
1099+
"memoryReference": "0x3039",
1100+
"offset": 0
1101+
})";
1102+
EXPECT_EQ(json, pp(body));
1103+
}

0 commit comments

Comments
 (0)