Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ def __init__(
self.events: List[Event] = []
self.progress_events: List[Event] = []
self.invalidated_event: Optional[Event] = None
self.memory_event: Optional[Event] = None
self.reverse_requests: List[Request] = []
self.module_events: List[Dict] = []
self.sequence: int = 1
Expand Down Expand Up @@ -443,6 +444,8 @@ def _handle_event(self, packet: Event) -> None:
self.capabilities.update(body["capabilities"])
elif event == "invalidated":
self.invalidated_event = packet
elif event == "memory":
self.memory_event = packet

def _handle_reverse_request(self, request: Request) -> None:
if request in self.reverse_requests:
Expand Down Expand Up @@ -1018,6 +1021,7 @@ def request_initialize(self, sourceInitFile=False):
"supportsStartDebuggingRequest": True,
"supportsProgressReporting": True,
"supportsInvalidatedEvent": True,
"supportsMemoryEvent": True,
"$__lldb_sourceInitFile": sourceInitFile,
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,14 @@ def verify_invalidated_event(self, expected_areas):
areas = event["body"].get("areas", [])
self.assertEqual(set(expected_areas), set(areas))

def verify_memory_event(self, memoryReference):
if memoryReference is None:
self.assertIsNone(self.dap_server.memory_event)
event = self.dap_server.memory_event
self.dap_server.memory_event = None
self.assertIsNotNone(event)
self.assertEqual(memoryReference, event["body"].get("memoryReference"))

def get_dict_value(self, d: dict, key_path: list[str]) -> Any:
"""Verify each key in the key_path array is in contained in each
dictionary within "d". Assert if any key isn't in the
Expand Down Expand Up @@ -364,6 +372,7 @@ def set_variable(self, varRef, name, value, id=None):
response = self.dap_server.request_setVariable(varRef, name, str(value), id=id)
if response["success"]:
self.verify_invalidated_event(["variables"])
self.verify_memory_event(response["body"].get("memoryReference"))
return response

def set_local(self, name, value, id=None):
Expand Down
11 changes: 11 additions & 0 deletions lldb/tools/lldb-dap/EventHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,15 @@ void SendInvalidatedEvent(
dap.Send(protocol::Event{"invalidated", std::move(body)});
}

void SendMemoryEvent(DAP &dap, lldb::SBValue variable) {
if (!dap.clientFeatures.contains(protocol::eClientFeatureMemoryEvent))
return;
protocol::MemoryEventBody body;
body.memoryReference = variable.GetLoadAddress();
body.count = variable.GetByteSize();
if (body.memoryReference == LLDB_INVALID_ADDRESS)
return;
dap.Send(protocol::Event{"memory", std::move(body)});
}

} // namespace lldb_dap
2 changes: 2 additions & 0 deletions lldb/tools/lldb-dap/EventHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process);
void SendInvalidatedEvent(
DAP &dap, llvm::ArrayRef<protocol::InvalidatedEventBody::Area> areas);

void SendMemoryEvent(DAP &dap, lldb::SBValue variable);

} // namespace lldb_dap

#endif
3 changes: 3 additions & 0 deletions lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ SetVariableRequestHandler::Run(const SetVariableArguments &args) const {
// (e.g. references) can be changed.
SendInvalidatedEvent(dap, {InvalidatedEventBody::eAreaVariables});

// Also send memory event to signal client that variable memory was changed.
SendMemoryEvent(dap, variable);

return body;
}

Expand Down
8 changes: 8 additions & 0 deletions lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "Protocol/ProtocolEvents.h"
#include "JSONUtils.h"
#include "llvm/Support/JSON.h"

using namespace llvm;
Expand Down Expand Up @@ -56,4 +57,11 @@ llvm::json::Value toJSON(const InvalidatedEventBody &IEB) {
return Result;
}

llvm::json::Value toJSON(const MemoryEventBody &MEB) {
return json::Object{
{"memoryReference", EncodeMemoryReference(MEB.memoryReference)},
{"offset", MEB.offset},
{"count", MEB.count}};
}

} // namespace lldb_dap::protocol
29 changes: 29 additions & 0 deletions lldb/tools/lldb-dap/Protocol/ProtocolEvents.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_EVENTS_H

#include "Protocol/ProtocolTypes.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-types.h"
#include "llvm/Support/JSON.h"
#include <cstdint>
Expand Down Expand Up @@ -88,6 +89,34 @@ struct InvalidatedEventBody {
llvm::json::Value toJSON(const InvalidatedEventBody::Area &);
llvm::json::Value toJSON(const InvalidatedEventBody &);

/// This event indicates that some memory range has been updated. It should only
/// be sent if the corresponding capability supportsMemoryEvent is true.
///
/// Clients typically react to the event by re-issuing a readMemory request if
/// they show the memory identified by the memoryReference and if the updated
/// memory range overlaps the displayed range. Clients should not make
/// assumptions how individual memory references relate to each other, so they
/// should not assume that they are part of a single continuous address range
/// and might overlap.
///
/// Debug adapters can use this event to indicate that the contents of a memory
/// range has changed due to some other request like setVariable or
/// setExpression. Debug adapters are not expected to emit this event for each
/// and every memory change of a running program, because that information is
/// typically not available from debuggers and it would flood clients with too
/// many events.
struct MemoryEventBody {
/// Memory reference of a memory range that has been updated.
lldb::addr_t memoryReference = LLDB_INVALID_ADDRESS;

/// Starting offset in bytes where memory has been updated. Can be negative.
int64_t offset = 0;

/// Number of bytes updated.
uint64_t count = 0;
};
llvm::json::Value toJSON(const MemoryEventBody &);

} // end namespace lldb_dap::protocol

#endif
13 changes: 13 additions & 0 deletions lldb/unittests/DAP/ProtocolTypesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1088,3 +1088,16 @@ TEST(ProtocolTypesTest, InvalidatedEventBody) {
})";
EXPECT_EQ(json, pp(body));
}

TEST(ProtocolTypesTest, MemoryEventBody) {
MemoryEventBody body;
body.memoryReference = 12345;
body.offset = 0;
body.count = 4;
StringRef json = R"({
"count": 4,
"memoryReference": "0x3039",
"offset": 0
})";
EXPECT_EQ(json, pp(body));
}
Loading