Skip to content

Commit a7ba9a5

Browse files
[lldb] Implement ProcessGDBRemote support for ReadMemoryRanges
This commit makes use of the newly created MultiMemRead packet to provide an efficient implementation of MultiMemRead inside ProcessGDBRemote. Testing is tricky, but it is accomplished two ways: 1. Some Objective-C tests would fail if this is implemented correctly, as there is already an in-tree use of the base class implementation of MultiMemRead, which is now getting replaced by the derived class. 2. One Objective-C test is modified so that we ensure the packet is being sent by looking at the packet logs. While not the most elegant solution, it is a strategy adopted in other tests as well. This gets around the fact that we cannot instantiate / unittest a mock ProcessGDBRemote.
1 parent f2cb997 commit a7ba9a5

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
#include "lldb/Host/Host.h"
8787
#include "lldb/Utility/StringExtractorGDBRemote.h"
8888

89+
#include "llvm/ADT/STLExtras.h"
8990
#include "llvm/ADT/ScopeExit.h"
9091
#include "llvm/ADT/StringMap.h"
9192
#include "llvm/ADT/StringSwitch.h"
@@ -2762,6 +2763,107 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
27622763
return 0;
27632764
}
27642765

2766+
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
2767+
ProcessGDBRemote::ReadMemoryRanges(
2768+
llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
2769+
llvm::MutableArrayRef<uint8_t> buffer) {
2770+
if (!m_gdb_comm.GetMultiMemReadSupported())
2771+
return Process::ReadMemoryRanges(ranges, buffer);
2772+
2773+
llvm::Expected<StringExtractorGDBRemote> response =
2774+
SendMultiMemReadPacket(ranges);
2775+
if (!response) {
2776+
LLDB_LOG_ERROR(GetLog(GDBRLog::Process), response.takeError(),
2777+
"MultiMemRead error response: {0}");
2778+
return Process::ReadMemoryRanges(ranges, buffer);
2779+
}
2780+
2781+
llvm::StringRef response_str = response->GetStringRef();
2782+
const unsigned expected_num_ranges = ranges.size();
2783+
llvm::Expected<llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>>
2784+
parsed_response =
2785+
ParseMultiMemReadPacket(response_str, buffer, expected_num_ranges);
2786+
if (!parsed_response) {
2787+
LLDB_LOG_ERROR(GetLog(GDBRLog::Process), parsed_response.takeError(),
2788+
"MultiMemRead error parsing response: {0}");
2789+
return Process::ReadMemoryRanges(ranges, buffer);
2790+
}
2791+
return std::move(*parsed_response);
2792+
}
2793+
2794+
llvm::Expected<StringExtractorGDBRemote>
2795+
ProcessGDBRemote::SendMultiMemReadPacket(
2796+
llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges) {
2797+
std::string packet_str;
2798+
llvm::raw_string_ostream stream(packet_str);
2799+
stream << "MultiMemRead:ranges:";
2800+
2801+
auto range_to_stream = [&](auto range) {
2802+
// the "-" marker omits the '0x' prefix.
2803+
stream << llvm::formatv("{0:x-},{1:x-}", range.base, range.size);
2804+
};
2805+
llvm::interleave(ranges, stream, range_to_stream, ",");
2806+
stream << ";";
2807+
2808+
StringExtractorGDBRemote response;
2809+
GDBRemoteCommunication::PacketResult packet_result =
2810+
m_gdb_comm.SendPacketAndWaitForResponse(packet_str.data(), response,
2811+
GetInterruptTimeout());
2812+
if (packet_result != GDBRemoteCommunication::PacketResult::Success)
2813+
return llvm::createStringError(
2814+
llvm::formatv("MultiMemRead failed to send packet: '{0}'", packet_str));
2815+
2816+
if (response.IsErrorResponse())
2817+
return llvm::createStringError(
2818+
llvm::formatv("MultiMemRead failed: '{0}'", response.GetStringRef()));
2819+
2820+
if (!response.IsNormalResponse())
2821+
return llvm::createStringError(llvm::formatv(
2822+
"MultiMemRead unexpected response: '{0}'", response.GetStringRef()));
2823+
2824+
return response;
2825+
}
2826+
2827+
llvm::Expected<llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>>
2828+
ProcessGDBRemote::ParseMultiMemReadPacket(llvm::StringRef response_str,
2829+
llvm::MutableArrayRef<uint8_t> buffer,
2830+
unsigned expected_num_ranges) {
2831+
// The sizes and the data are separated by a `;`.
2832+
auto [sizes_str, memory_data] = response_str.split(';');
2833+
if (sizes_str.size() == response_str.size())
2834+
return llvm::createStringError(llvm::formatv(
2835+
"MultiMemRead response missing field separator: '{0}'", response_str));
2836+
2837+
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;
2838+
2839+
// Sizes are separated by a `,`.
2840+
for (llvm::StringRef size_str : llvm::split(sizes_str, ',')) {
2841+
uint64_t read_size;
2842+
if (size_str.getAsInteger(16, read_size))
2843+
return llvm::createStringError(llvm::formatv(
2844+
"MultiMemRead response has invalid size string: {0}", size_str));
2845+
2846+
if (memory_data.size() < read_size)
2847+
return llvm::createStringError(
2848+
llvm::formatv("MultiMemRead response did not have enough data, "
2849+
"requested sizes: {0}",
2850+
sizes_str));
2851+
2852+
llvm::StringRef region_to_read = memory_data.take_front(read_size);
2853+
memory_data = memory_data.drop_front(read_size);
2854+
2855+
assert(buffer.size() >= read_size);
2856+
llvm::MutableArrayRef<uint8_t> region_to_write =
2857+
buffer.take_front(read_size);
2858+
buffer = buffer.drop_front(read_size);
2859+
2860+
memcpy(region_to_write.data(), region_to_read.data(), read_size);
2861+
read_results.push_back(region_to_write);
2862+
}
2863+
2864+
return read_results;
2865+
}
2866+
27652867
bool ProcessGDBRemote::SupportsMemoryTagging() {
27662868
return m_gdb_comm.GetMemoryTaggingSupported();
27672869
}

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,20 @@ class ProcessGDBRemote : public Process,
137137
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
138138
Status &error) override;
139139

140+
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
141+
ReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> vm_addrs,
142+
llvm::MutableArrayRef<uint8_t> buf) override;
143+
144+
private:
145+
llvm::Expected<StringExtractorGDBRemote>
146+
SendMultiMemReadPacket(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges);
147+
148+
llvm::Expected<llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>>
149+
ParseMultiMemReadPacket(llvm::StringRef response_str,
150+
llvm::MutableArrayRef<uint8_t> buffer,
151+
unsigned expected_num_ranges);
152+
153+
public:
140154
Status
141155
WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override;
142156

lldb/test/API/lang/objc/foundation/TestObjCMethodsNSError.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,26 @@ def test_NSError_p(self):
4545
],
4646
)
4747
self.runCmd("process continue")
48+
49+
@skipIfOutOfTreeDebugserver
50+
def test_runtime_types_efficient_memreads(self):
51+
# Test that we use an efficient reading of memory when reading
52+
# Objective-C method descriptions.
53+
logfile = os.path.join(self.getBuildDir(), "log.txt")
54+
self.runCmd(f"log enable -f {logfile} gdb-remote packets process")
55+
self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets"))
56+
57+
self.build()
58+
self.target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
59+
self, "// Break here for NSString tests", lldb.SBFileSpec("main.m", False)
60+
)
61+
62+
self.runCmd(f"proc plugin packet send StartTesting", check=False)
63+
self.expect('expression str = [NSString stringWithCString: "new"]')
64+
self.runCmd(f"proc plugin packet send EndTesting", check=False)
65+
66+
self.assertTrue(os.path.exists(logfile))
67+
log_text = open(logfile).read()
68+
log_text = log_text.split("StartTesting", 1)[-1].split("EndTesting", 1)[0]
69+
self.assertIn("MultiMemRead:", log_text)
70+
self.assertNotIn("MultiMemRead error", log_text)

0 commit comments

Comments
 (0)