Skip to content

Commit ffa98aa

Browse files
felipepiovezangithub-actions[bot]
authored andcommitted
Automerge: [lldb] Implement ProcessGDBRemote support for ReadMemoryRanges (#164311)
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 were implemented incorrectly, 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. Depends on llvm/llvm-project#163651
2 parents f9dd202 + 276bccd commit ffa98aa

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed

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

Lines changed: 103 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,108 @@ 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 ';' in: '{0}'",
2836+
response_str));
2837+
2838+
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;
2839+
2840+
// Sizes are separated by a `,`.
2841+
for (llvm::StringRef size_str : llvm::split(sizes_str, ',')) {
2842+
uint64_t read_size;
2843+
if (size_str.getAsInteger(16, read_size))
2844+
return llvm::createStringError(llvm::formatv(
2845+
"MultiMemRead response has invalid size string: {0}", size_str));
2846+
2847+
if (memory_data.size() < read_size)
2848+
return llvm::createStringError(
2849+
llvm::formatv("MultiMemRead response did not have enough data, "
2850+
"requested sizes: {0}",
2851+
sizes_str));
2852+
2853+
llvm::StringRef region_to_read = memory_data.take_front(read_size);
2854+
memory_data = memory_data.drop_front(read_size);
2855+
2856+
assert(buffer.size() >= read_size);
2857+
llvm::MutableArrayRef<uint8_t> region_to_write =
2858+
buffer.take_front(read_size);
2859+
buffer = buffer.drop_front(read_size);
2860+
2861+
memcpy(region_to_write.data(), region_to_read.data(), read_size);
2862+
read_results.push_back(region_to_write);
2863+
}
2864+
2865+
return read_results;
2866+
}
2867+
27652868
bool ProcessGDBRemote::SupportsMemoryTagging() {
27662869
return m_gdb_comm.GetMemoryTaggingSupported();
27672870
}

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

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

140+
/// Override of ReadMemoryRanges that uses MultiMemRead to optimize this
141+
/// operation.
142+
llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
143+
ReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
144+
llvm::MutableArrayRef<uint8_t> buf) override;
145+
146+
private:
147+
llvm::Expected<StringExtractorGDBRemote>
148+
SendMultiMemReadPacket(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges);
149+
150+
llvm::Expected<llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>>
151+
ParseMultiMemReadPacket(llvm::StringRef response_str,
152+
llvm::MutableArrayRef<uint8_t> buffer,
153+
unsigned expected_num_ranges);
154+
155+
public:
140156
Status
141157
WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override;
142158

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,30 @@ 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+
70+
# This test is only checking that the packet it used at all (and that
71+
# no errors are produced). It doesn't check that the packet is being
72+
# used to solve a problem in an optimal way.
73+
self.assertIn("MultiMemRead:", log_text)
74+
self.assertNotIn("MultiMemRead error", log_text)

0 commit comments

Comments
 (0)