Skip to content

Commit 51aa6a4

Browse files
authored
[lldb-dap] Use protocol types for ReadMemory request (#144552)
Read memory from process instead of target.
1 parent c4d7ea8 commit 51aa6a4

File tree

6 files changed

+152
-123
lines changed

6 files changed

+152
-123
lines changed

lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,17 @@ def test_readMemory(self):
111111
# VS Code sends those in order to check if a `memoryReference` can actually be dereferenced.
112112
mem = self.dap_server.request_readMemory(memref, 0, 0)
113113
self.assertEqual(mem["success"], True)
114-
self.assertEqual(mem["body"]["data"], "")
114+
self.assertNotIn(
115+
"data", mem["body"], f"expects no data key in response: {mem!r}"
116+
)
117+
118+
# Reads at offset 0x0 return unreadable bytes
119+
bytes_to_read = 6
120+
mem = self.dap_server.request_readMemory("0x0", 0, bytes_to_read)
121+
self.assertEqual(mem["body"]["unreadableBytes"], bytes_to_read)
122+
123+
# Reads with invalid address fails.
124+
mem = self.dap_server.request_readMemory("-3204", 0, 10)
125+
self.assertFalse(mem["success"], "expect fail on reading memory.")
115126

116-
# Reads at offset 0x0 fail
117-
mem = self.dap_server.request_readMemory("0x0", 0, 6)
118-
self.assertEqual(mem["success"], False)
127+
self.continue_to_exit()

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

Lines changed: 27 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -7,136 +7,47 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "DAP.h"
10-
#include "EventHelper.h"
1110
#include "JSONUtils.h"
1211
#include "RequestHandler.h"
1312
#include "llvm/ADT/StringExtras.h"
14-
#include "llvm/Support/Base64.h"
1513

1614
namespace lldb_dap {
1715

18-
// "ReadMemoryRequest": {
19-
// "allOf": [ { "$ref": "#/definitions/Request" }, {
20-
// "type": "object",
21-
// "description": "Reads bytes from memory at the provided location. Clients
22-
// should only call this request if the corresponding
23-
// capability `supportsReadMemoryRequest` is true.",
24-
// "properties": {
25-
// "command": {
26-
// "type": "string",
27-
// "enum": [ "readMemory" ]
28-
// },
29-
// "arguments": {
30-
// "$ref": "#/definitions/ReadMemoryArguments"
31-
// }
32-
// },
33-
// "required": [ "command", "arguments" ]
34-
// }]
35-
// },
36-
// "ReadMemoryArguments": {
37-
// "type": "object",
38-
// "description": "Arguments for `readMemory` request.",
39-
// "properties": {
40-
// "memoryReference": {
41-
// "type": "string",
42-
// "description": "Memory reference to the base location from which data
43-
// should be read."
44-
// },
45-
// "offset": {
46-
// "type": "integer",
47-
// "description": "Offset (in bytes) to be applied to the reference
48-
// location before reading data. Can be negative."
49-
// },
50-
// "count": {
51-
// "type": "integer",
52-
// "description": "Number of bytes to read at the specified location and
53-
// offset."
54-
// }
55-
// },
56-
// "required": [ "memoryReference", "count" ]
57-
// },
58-
// "ReadMemoryResponse": {
59-
// "allOf": [ { "$ref": "#/definitions/Response" }, {
60-
// "type": "object",
61-
// "description": "Response to `readMemory` request.",
62-
// "properties": {
63-
// "body": {
64-
// "type": "object",
65-
// "properties": {
66-
// "address": {
67-
// "type": "string",
68-
// "description": "The address of the first byte of data returned.
69-
// Treated as a hex value if prefixed with `0x`, or
70-
// as a decimal value otherwise."
71-
// },
72-
// "unreadableBytes": {
73-
// "type": "integer",
74-
// "description": "The number of unreadable bytes encountered after
75-
// the last successfully read byte.\nThis can be
76-
// used to determine the number of bytes that should
77-
// be skipped before a subsequent
78-
// `readMemory` request succeeds."
79-
// },
80-
// "data": {
81-
// "type": "string",
82-
// "description": "The bytes read from memory, encoded using base64.
83-
// If the decoded length of `data` is less than the
84-
// requested `count` in the original `readMemory`
85-
// request, and `unreadableBytes` is zero or
86-
// omitted, then the client should assume it's
87-
// reached the end of readable memory."
88-
// }
89-
// },
90-
// "required": [ "address" ]
91-
// }
92-
// }
93-
// }]
94-
// },
95-
void ReadMemoryRequestHandler::operator()(
96-
const llvm::json::Object &request) const {
97-
llvm::json::Object response;
98-
FillResponse(request, response);
99-
auto *arguments = request.getObject("arguments");
16+
// Reads bytes from memory at the provided location.
17+
//
18+
// Clients should only call this request if the corresponding capability
19+
// `supportsReadMemoryRequest` is true
20+
llvm::Expected<protocol::ReadMemoryResponseBody>
21+
ReadMemoryRequestHandler::Run(const protocol::ReadMemoryArguments &args) const {
22+
const lldb::addr_t raw_address = args.memoryReference + args.offset;
10023

101-
llvm::StringRef memoryReference =
102-
GetString(arguments, "memoryReference").value_or("");
103-
auto addr_opt = DecodeMemoryReference(memoryReference);
104-
if (!addr_opt.has_value()) {
105-
response["success"] = false;
106-
response["message"] =
107-
"Malformed memory reference: " + memoryReference.str();
108-
dap.SendJSON(llvm::json::Value(std::move(response)));
109-
return;
110-
}
111-
lldb::addr_t addr_int = *addr_opt;
112-
addr_int += GetInteger<uint64_t>(arguments, "offset").value_or(0);
113-
const uint64_t count_requested =
114-
GetInteger<uint64_t>(arguments, "count").value_or(0);
24+
lldb::SBProcess process = dap.target.GetProcess();
25+
if (!lldb::SBDebugger::StateIsStoppedState(process.GetState()))
26+
return llvm::make_error<NotStoppedError>();
11527

28+
const uint64_t count_read = std::max<uint64_t>(args.count, 1);
11629
// We also need support reading 0 bytes
11730
// VS Code sends those requests to check if a `memoryReference`
11831
// can be dereferenced.
119-
const uint64_t count_read = std::max<uint64_t>(count_requested, 1);
120-
std::vector<uint8_t> buf;
121-
buf.resize(count_read);
32+
protocol::ReadMemoryResponseBody response;
33+
std::vector<std::byte> &buffer = response.data;
34+
buffer.resize(count_read);
35+
12236
lldb::SBError error;
123-
lldb::SBAddress addr{addr_int, dap.target};
124-
size_t count_result =
125-
dap.target.ReadMemory(addr, buf.data(), count_read, error);
126-
if (count_result == 0) {
127-
response["success"] = false;
128-
EmplaceSafeString(response, "message", error.GetCString());
129-
dap.SendJSON(llvm::json::Value(std::move(response)));
130-
return;
37+
const size_t memory_count = dap.target.GetProcess().ReadMemory(
38+
raw_address, buffer.data(), buffer.size(), error);
39+
40+
response.address = "0x" + llvm::utohexstr(raw_address);
41+
42+
// reading memory may fail for multiple reasons. memory not readable,
43+
// reading out of memory range and gaps in memory. return from
44+
// the last readable byte.
45+
if (error.Fail() && (memory_count < count_read)) {
46+
response.unreadableBytes = count_read - memory_count;
13147
}
132-
buf.resize(std::min<size_t>(count_result, count_requested));
13348

134-
llvm::json::Object body;
135-
std::string formatted_addr = "0x" + llvm::utohexstr(addr_int);
136-
body.try_emplace("address", formatted_addr);
137-
body.try_emplace("data", llvm::encodeBase64(buf));
138-
response.try_emplace("body", std::move(body));
139-
dap.SendJSON(llvm::json::Value(std::move(response)));
49+
buffer.resize(std::min<size_t>(memory_count, args.count));
50+
return response;
14051
}
14152

14253
} // namespace lldb_dap

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,14 +564,17 @@ class DisassembleRequestHandler final
564564
Run(const protocol::DisassembleArguments &args) const override;
565565
};
566566

567-
class ReadMemoryRequestHandler : public LegacyRequestHandler {
567+
class ReadMemoryRequestHandler final
568+
: public RequestHandler<protocol::ReadMemoryArguments,
569+
llvm::Expected<protocol::ReadMemoryResponseBody>> {
568570
public:
569-
using LegacyRequestHandler::LegacyRequestHandler;
571+
using RequestHandler::RequestHandler;
570572
static llvm::StringLiteral GetCommand() { return "readMemory"; }
571573
FeatureSet GetSupportedFeatures() const override {
572574
return {protocol::eAdapterFeatureReadMemoryRequest};
573575
}
574-
void operator()(const llvm::json::Object &request) const override;
576+
llvm::Expected<protocol::ReadMemoryResponseBody>
577+
Run(const protocol::ReadMemoryArguments &args) const override;
575578
};
576579

577580
class CancelRequestHandler : public RequestHandler<protocol::CancelArguments,

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

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

99
#include "Protocol/ProtocolRequests.h"
10+
#include "JSONUtils.h"
1011
#include "llvm/ADT/DenseMap.h"
1112
#include "llvm/ADT/StringMap.h"
1213
#include "llvm/ADT/StringRef.h"
14+
#include "llvm/Support/Base64.h"
1315
#include "llvm/Support/JSON.h"
1416
#include <utility>
1517

@@ -480,4 +482,39 @@ json::Value toJSON(const DisassembleResponseBody &DRB) {
480482
return json::Object{{"instructions", DRB.instructions}};
481483
}
482484

485+
bool fromJSON(const json::Value &Params, ReadMemoryArguments &RMA,
486+
json::Path P) {
487+
json::ObjectMapper O(Params, P);
488+
489+
const json::Object *rma_obj = Params.getAsObject();
490+
constexpr llvm::StringRef ref_key = "memoryReference";
491+
const std::optional<llvm::StringRef> memory_ref = rma_obj->getString(ref_key);
492+
if (!memory_ref) {
493+
P.field(ref_key).report("missing value");
494+
return false;
495+
}
496+
497+
const std::optional<lldb::addr_t> addr_opt =
498+
DecodeMemoryReference(*memory_ref);
499+
if (!addr_opt) {
500+
P.field(ref_key).report("Malformed memory reference");
501+
return false;
502+
}
503+
504+
RMA.memoryReference = *addr_opt;
505+
506+
return O && O.map("count", RMA.count) && O.mapOptional("offset", RMA.offset);
507+
}
508+
509+
json::Value toJSON(const ReadMemoryResponseBody &RMR) {
510+
json::Object result{{"address", RMR.address}};
511+
512+
if (RMR.unreadableBytes != 0)
513+
result.insert({"unreadableBytes", RMR.unreadableBytes});
514+
if (!RMR.data.empty())
515+
result.insert({"data", llvm::encodeBase64(RMR.data)});
516+
517+
return result;
518+
}
519+
483520
} // namespace lldb_dap::protocol

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,42 @@ bool fromJSON(const llvm::json::Value &, DisassembleResponseBody &,
839839
llvm::json::Path);
840840
llvm::json::Value toJSON(const DisassembleResponseBody &);
841841

842+
/// Arguments for `readMemory` request.
843+
struct ReadMemoryArguments {
844+
/// Memory reference to the base location from which data should be read.
845+
lldb::addr_t memoryReference;
846+
847+
/// Offset (in bytes) to be applied to the reference location before reading
848+
/// data. Can be negative.
849+
int64_t offset = 0;
850+
851+
/// Number of bytes to read at the specified location and offset.
852+
uint64_t count;
853+
};
854+
bool fromJSON(const llvm::json::Value &, ReadMemoryArguments &,
855+
llvm::json::Path);
856+
857+
/// Response to `readMemory` request.
858+
struct ReadMemoryResponseBody {
859+
/// The address of the first byte of data returned.
860+
/// Treated as a hex value if prefixed with `0x`, or as a decimal value
861+
/// otherwise.
862+
std::string address;
863+
864+
/// The number of unreadable bytes encountered after the last successfully
865+
/// read byte.
866+
/// This can be used to determine the number of bytes that should be skipped
867+
/// before a subsequent `readMemory` request succeeds.
868+
uint64_t unreadableBytes = 0;
869+
870+
/// The bytes read from memory, encoded using base64. If the decoded length
871+
/// of `data` is less than the requested `count` in the original `readMemory`
872+
/// request, and `unreadableBytes` is zero or omitted, then the client should
873+
/// assume it's reached the end of readable memory.
874+
std::vector<std::byte> data;
875+
};
876+
llvm::json::Value toJSON(const ReadMemoryResponseBody &);
877+
842878
} // namespace lldb_dap::protocol
843879

844880
#endif

lldb/unittests/DAP/ProtocolTypesTest.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,3 +765,36 @@ TEST(ProtocolTypesTest, StepInTarget) {
765765
EXPECT_EQ(target.endLine, deserialized_target->endLine);
766766
EXPECT_EQ(target.endColumn, deserialized_target->endColumn);
767767
}
768+
769+
TEST(ProtocolTypesTest, ReadMemoryArguments) {
770+
ReadMemoryArguments args;
771+
args.count = 20;
772+
args.memoryReference = 43962;
773+
args.offset = 0;
774+
775+
llvm::Expected<ReadMemoryArguments> expected =
776+
parse<ReadMemoryArguments>(R"({"memoryReference":"-4000", "count": 20})");
777+
ASSERT_THAT_EXPECTED(expected, llvm::Failed());
778+
expected = parse<ReadMemoryArguments>(
779+
R"({"memoryReference":"0xabba", "count": 20})");
780+
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
781+
782+
EXPECT_EQ(args.count, expected->count);
783+
EXPECT_EQ(args.memoryReference, expected->memoryReference);
784+
EXPECT_EQ(args.offset, expected->offset);
785+
}
786+
787+
TEST(ProtocolTypesTest, ReadMemoryResponseBody) {
788+
ReadMemoryResponseBody response;
789+
response.address = "0xdeadbeef";
790+
const std::string data_str = "hello world!";
791+
std::transform(data_str.begin(), data_str.end(),
792+
std::back_inserter(response.data),
793+
[](char letter) { return std::byte(letter); });
794+
response.unreadableBytes = 1;
795+
796+
Expected<Value> expected = json::parse(
797+
R"({ "address": "0xdeadbeef", "data": "aGVsbG8gd29ybGQh", "unreadableBytes": 1})");
798+
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
799+
EXPECT_EQ(pp(*expected), pp(response));
800+
}

0 commit comments

Comments
 (0)