Skip to content

Conversation

felipepiovezan
Copy link
Contributor

This commit implements, in debugserver, the packet as discussed in the RFC 1.

@felipepiovezan felipepiovezan marked this pull request as ready for review October 9, 2025 17:49
@llvmbot llvmbot added the lldb label Oct 9, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 9, 2025

@llvm/pr-subscribers-lldb

Author: Felipe de Azevedo Piovezan (felipepiovezan)

Changes

This commit implements, in debugserver, the packet as discussed in the RFC 1.


Full diff: https://github.com/llvm/llvm-project/pull/162670.diff

5 Files Affected:

  • (added) lldb/test/API/macosx/debugserver-multimemread/Makefile (+3)
  • (added) lldb/test/API/macosx/debugserver-multimemread/TestDebugserverMultiMemRead.py (+72)
  • (added) lldb/test/API/macosx/debugserver-multimemread/main.c (+10)
  • (modified) lldb/tools/debugserver/source/RNBRemote.cpp (+140)
  • (modified) lldb/tools/debugserver/source/RNBRemote.h (+2)
diff --git a/lldb/test/API/macosx/debugserver-multimemread/Makefile b/lldb/test/API/macosx/debugserver-multimemread/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/macosx/debugserver-multimemread/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/macosx/debugserver-multimemread/TestDebugserverMultiMemRead.py b/lldb/test/API/macosx/debugserver-multimemread/TestDebugserverMultiMemRead.py
new file mode 100644
index 0000000000000..5c0c7a34e18bd
--- /dev/null
+++ b/lldb/test/API/macosx/debugserver-multimemread/TestDebugserverMultiMemRead.py
@@ -0,0 +1,72 @@
+"""
+Tests the exit code/description coming from the debugserver.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+@skipUnlessDarwin
+@skipIfOutOfTreeDebugserver
+class TestCase(TestBase):
+    def send_process_packet(self, packet_str):
+        self.runCmd(f"proc plugin packet send {packet_str}", check=False)
+        # The output is of the form:
+        #  packet: <packet_str>
+        #  response: <response>
+        reply = self.res.GetOutput().split("\n")
+        reply[0] = reply[0].strip()
+        reply[1] = reply[1].strip()
+
+        self.assertTrue(reply[0].startswith("packet: "), reply[0])
+        self.assertTrue(reply[1].startswith("response: "))
+        return reply[1][len("response: ") :]
+
+    def test_packets(self):
+        self.build()
+        source_file = lldb.SBFileSpec("main.c")
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, "break here", source_file
+        )
+
+        reply = self.send_process_packet("qSupported")
+        self.assertIn("MultiMemRead+", reply)
+
+        mem_address_var = thread.frames[0].FindVariable("memory")
+        self.assertTrue(mem_address_var)
+        mem_address = int(mem_address_var.GetValue(), 16)
+
+        reply = self.send_process_packet("MultiMemRead:")
+        self.assertEqual(reply, "E03")
+        reply = self.send_process_packet("MultiMemRead:ranges:")
+        self.assertEqual(reply, "E03")
+        reply = self.send_process_packet("MultiMemRead:ranges:10")
+        self.assertEqual(reply, "E03")
+        reply = self.send_process_packet("MultiMemRead:ranges:10,")
+        self.assertEqual(reply, "E03")
+        reply = self.send_process_packet("MultiMemRead:ranges:10,2")
+        self.assertEqual(reply, "E03")
+        reply = self.send_process_packet("MultiMemRead:ranges:10,2,")
+        self.assertEqual(reply, "E03")
+        reply = self.send_process_packet("MultiMemRead:ranges:10,2,;")
+        self.assertEqual(reply, "0;")  # Debugserver is permissive with trailing commas.
+        reply = self.send_process_packet("MultiMemRead:ranges:10,2;")
+        self.assertEqual(reply, "0;")
+        reply = self.send_process_packet(f"MultiMemRead:ranges:{mem_address:x},0;")
+        self.assertEqual(reply, "0;")
+        reply = self.send_process_packet(
+            f"MultiMemRead:ranges:{mem_address:x},0;options:;"
+        )
+        self.assertEqual(reply, "0;")
+        reply = self.send_process_packet(f"MultiMemRead:ranges:{mem_address:x},2;")
+        self.assertEqual(reply, "2;ab")
+        reply = self.send_process_packet(
+            f"MultiMemRead:ranges:{mem_address:x},2,{mem_address+2:x},4;"
+        )
+        self.assertEqual(reply, "2,4;abcdef")
+        reply = self.send_process_packet(
+            f"MultiMemRead:ranges:{mem_address:x},2,{mem_address+2:x},4,{mem_address+6:x},8;"
+        )
+        self.assertEqual(reply, "2,4,8;abcdefghijklmn")
diff --git a/lldb/test/API/macosx/debugserver-multimemread/main.c b/lldb/test/API/macosx/debugserver-multimemread/main.c
new file mode 100644
index 0000000000000..1a756a3c4cbac
--- /dev/null
+++ b/lldb/test/API/macosx/debugserver-multimemread/main.c
@@ -0,0 +1,10 @@
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char **argv) {
+  char *memory = malloc(1024);
+  memset(memory, '-', 1024);
+  for (int i = 0; i < 50; i++)
+    memory[i] = 'a' + i;
+  return 0; // break here
+}
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index 434e9cfa40fb4..dfb0443fdd3dd 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -246,6 +246,11 @@ void RNBRemote::CreatePacketTable() {
                      "Read memory"));
   t.push_back(Packet(read_register, &RNBRemote::HandlePacket_p, NULL, "p",
                      "Read one register"));
+  // Careful: this *must* come before the `M` packet, as debugserver matches
+  // packet prefixes against known packet names. Inverting the order would match
+  // `MultiMemRead` as an `M` packet.
+  t.push_back(Packet(multi_mem_read, &RNBRemote::HandlePacket_MultiMemRead,
+                     NULL, "MultiMemRead", "Read multiple memory addresses"));
   t.push_back(Packet(write_memory, &RNBRemote::HandlePacket_M, NULL, "M",
                      "Write memory"));
   t.push_back(Packet(write_register, &RNBRemote::HandlePacket_P, NULL, "P",
@@ -3160,6 +3165,140 @@ rnb_err_t RNBRemote::HandlePacket_m(const char *p) {
   return SendPacket(ostrm.str());
 }
 
+/// Returns true if `str` starts with `prefix`.
+static bool starts_with(std::string_view str, std::string_view prefix) {
+  return str.size() >= prefix.size() &&
+         str.compare(0, prefix.size(), prefix) == 0;
+}
+
+/// Attempts to parse a prefix of `number_str` as a number of type `T`. If
+/// successful, the number is returned and the prefix is dropped from
+/// `number_str`.
+template <typename T>
+static std::optional<T> extract_number(std::string_view &number_str) {
+  static_assert(std::is_integral<T>());
+  char *str_end = nullptr;
+  errno = 0;
+  nub_addr_t number = strtoull(number_str.data(), &str_end, 16);
+  if (errno != 0)
+    return std::nullopt;
+  assert(str_end);
+  number_str.remove_prefix(str_end - number_str.data());
+  return number;
+}
+
+/// Splits `list_str` into multiple string_views separated by `,`.
+static std::vector<std::string_view>
+parse_comma_separated_list(std::string_view list_str) {
+  std::vector<std::string_view> list;
+  while (!list_str.empty()) {
+    auto pos = list_str.find(',');
+    list.push_back(list_str.substr(0, pos));
+    if (pos == list_str.npos)
+      break;
+    list_str.remove_prefix(pos + 1);
+  }
+  return list;
+}
+
+rnb_err_t RNBRemote::HandlePacket_MultiMemRead(const char *p) {
+  const std::string_view packet_name("MultiMemRead:");
+  std::string_view packet(p);
+
+  if (!starts_with(packet, packet_name))
+    return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+                                  "Invalid MultiMemRead packet prefix");
+
+  packet.remove_prefix(packet_name.size());
+
+  const std::string_view ranges_prefix("ranges:");
+  if (!starts_with(packet, ranges_prefix))
+    return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+                                  "Missing 'ranges' in MultiMemRead packet");
+  packet.remove_prefix(ranges_prefix.size());
+
+  std::vector<std::pair<nub_addr_t, std::size_t>> ranges;
+  std::size_t total_length = 0;
+
+  // Ranges should have the form: <addr>,<size>[,<addr>,<size>]*;
+  auto end_of_ranges_pos = packet.find(';');
+  if (end_of_ranges_pos == packet.npos)
+    return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+                                  "MultiMemRead missing end of ranges marker");
+
+  std::vector<std::string_view> numbers_list =
+      parse_comma_separated_list(packet.substr(0, end_of_ranges_pos));
+  packet.remove_prefix(end_of_ranges_pos + 1);
+
+  // Ranges are pairs, so the number of elements must be even.
+  if (numbers_list.size() % 2 == 1)
+    return HandlePacket_ILLFORMED(
+        __FILE__, __LINE__, p,
+        "MultiMemRead has an odd number of numbers for the ranges");
+
+  for (unsigned idx = 0; idx < numbers_list.size(); idx += 2) {
+    std::optional<nub_addr_t> maybe_addr =
+        extract_number<nub_addr_t>(numbers_list[idx]);
+    std::optional<size_t> maybe_length =
+        extract_number<size_t>(numbers_list[idx + 1]);
+    if (!maybe_addr || !maybe_length)
+      return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+                                    "Invalid MultiMemRead range");
+    // A sanity check that the packet requested is not too large or a negative
+    // number.
+    if (*maybe_length > 4 * 1024 * 1024)
+      return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+                                    "MultiMemRead length is too large");
+
+    ranges.emplace_back(*maybe_addr, *maybe_length);
+    total_length += *maybe_length;
+  }
+
+  if (ranges.empty())
+    return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+                                  "MultiMemRead has an empty range list");
+
+  // If 'options:' is present, ensure it's empty.
+  const std::string_view options_prefix("options:");
+  if (starts_with(packet, options_prefix)) {
+    packet.remove_prefix(options_prefix.size());
+    if (packet.empty())
+      return HandlePacket_ILLFORMED(
+          __FILE__, __LINE__, packet.data(),
+          "Too short 'options' in MultiMemRead packet");
+    if (packet[0] != ';')
+      return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+                                    "Unimplemented 'options' in MultiMemRead");
+    packet.remove_prefix(1);
+  }
+
+  if (!packet.empty())
+    return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+                                  "Invalid MultiMemRead address_range");
+
+  std::vector<std::vector<uint8_t>> buffers;
+  buffers.reserve(ranges.size());
+  for (auto [base_addr, length] : ranges) {
+    buffers.emplace_back(length, '\0');
+    nub_size_t bytes_read = DNBProcessMemoryRead(m_ctx.ProcessID(), base_addr,
+                                                 length, buffers.back().data());
+    buffers.back().resize(bytes_read);
+  }
+
+  std::ostringstream reply_stream;
+  bool first = true;
+  for (const std::vector<uint8_t> &buffer : buffers) {
+    reply_stream << (first ? "" : ",") << std::hex << buffer.size();
+    first = false;
+  }
+  reply_stream << ';';
+
+  for (const std::vector<uint8_t> &buffer : buffers)
+    binary_encode_data_vector(reply_stream, buffer);
+
+  return SendPacket(reply_stream.str());
+}
+
 // Read memory, sent it up as binary data.
 // Usage:  xADDR,LEN
 // ADDR and LEN are both base 16.
@@ -3525,6 +3664,7 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
   if (supports_memory_tagging())
     reply << "memory-tagging+;";
 
+  reply << "MultiMemRead+;";
   return SendPacket(reply.str().c_str());
 }
 
diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h
index cf1c978afcd23..b32c00adc8b23 100644
--- a/lldb/tools/debugserver/source/RNBRemote.h
+++ b/lldb/tools/debugserver/source/RNBRemote.h
@@ -136,6 +136,7 @@ class RNBRemote {
     query_transfer,                     // 'qXfer:'
     json_query_dyld_process_state,      // 'jGetDyldProcessState'
     enable_error_strings,               // 'QEnableErrorStrings'
+    multi_mem_read,                     // 'MultiMemRead'
     unknown_type
   };
   // clang-format on
@@ -216,6 +217,7 @@ class RNBRemote {
   rnb_err_t HandlePacket_last_signal(const char *p);
   rnb_err_t HandlePacket_m(const char *p);
   rnb_err_t HandlePacket_M(const char *p);
+  rnb_err_t HandlePacket_MultiMemRead(const char *p);
   rnb_err_t HandlePacket_x(const char *p);
   rnb_err_t HandlePacket_X(const char *p);
   rnb_err_t HandlePacket_z(const char *p);

Comment on lines 3170 to 3171
return str.size() >= prefix.size() &&
str.compare(0, prefix.size(), prefix) == 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return str.size() >= prefix.size() &&
str.compare(0, prefix.size(), prefix) == 0;
return str.substr(0, prefix.size()) == prefix;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will throw if prefix.size() > str.size()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I like the idea of using == to simplify it, I will make some tweaks to the suggested code

Copy link
Contributor

@kastiglione kastiglione Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will throw if prefix.size() > str.size()

oh from my read of the docs it doesn't

Copy link
Contributor Author

@felipepiovezan felipepiovezan Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we looking at the same place?

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, my suggested code has pos = 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhhhh you're totally right. My bad!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk if you can use StringRef here but it's very stringview like and it has a starts_with.

Maybe not because you don't want to link llvm libraries.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugserver doesn't link to llvm or lldb, today. it does include a socket header from the lldb sources, but everything in there is defined in the header. it does allow us to build debugserver entirely separately from the rest of llvm/lldb, handy when cross compiling to a different target quickly, via its Xcode project file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... it hurts a lot knowing all the StringRef(ArrayRef) methods that I can't use! :((

static_assert(std::is_integral<T>());
char *str_end = nullptr;
errno = 0;
nub_addr_t number = strtoull(number_str.data(), &str_end, 16);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
nub_addr_t number = strtoull(number_str.data(), &str_end, 16);
T number = strtoull(number_str.data(), &str_end, 16);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am tempted to get rid of the template and make this a uint64_t function only, as this is using that strtoull function.

/// successful, the number is returned and the prefix is dropped from
/// `number_str`.
template <typename T>
static std::optional<T> extract_number(std::string_view &number_str) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to put this next to decode_uint64() in the source file, in case some future person is inspired to update the caller to that old function to use this one. The other ~37 strtoull's in this file parsing base16 strings do them all separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, also moved the other helper functions to the top of this file, in case we want to re-use them later.

packet.remove_prefix(packet_name.size());

const std::string_view ranges_prefix("ranges:");
if (!starts_with(packet, ranges_prefix))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're doing key-value pairs, I wouldn't hardcode that this key name must be first in the packet. It's an extra thing to handle in the future when we have additional keys "old debugservers need this key to come first".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by removing the options field.


// If 'options:' is present, ensure it's empty.
const std::string_view options_prefix("options:");
if (starts_with(packet, options_prefix)) {
Copy link
Collaborator

@jasonmolenda jasonmolenda Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hardcodes that options key must come after ranges key. I'd find these from the start of the original packet string. Why Look for an options key and require that it be empty? This means a future lldb can't use this key for anything.

std::vector<std::vector<uint8_t>> buffers;
buffers.reserve(ranges.size());
for (auto [base_addr, length] : ranges) {
buffers.emplace_back(length, '\0');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's such a minor style nit I hesitate to say it, but '\0' is the NUL character and is the marker of the end of a c-string. This is a vector<uint8_t> and I'd use a 0 here; there's no string stuff involved.

@jasonmolenda
Copy link
Collaborator

Looks very good, I had a couple of small suggestions but thumbs-up from me.

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great that you added tests.

The parsing is a bit "my kingdom for a StringRef" but I assume you have your reasons to avoid it in debugserver.

reply[0] = reply[0].strip()
reply[1] = reply[1].strip()

self.assertTrue(reply[0].startswith("packet: "), reply[0])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this one have reply[0] in it, for the assert failure message? Add a "expected bla got: " if that's the case, on the response as well.

Copy link
Contributor Author

@felipepiovezan felipepiovezan Oct 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a bit of an oopsie on my part. Will remove from both.

Comment on lines 20 to 21
reply[0] = reply[0].strip()
reply[1] = reply[1].strip()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could name these packet and response. Signals what you expect to find in there.


mem_address_var = thread.frames[0].FindVariable("memory")
self.assertTrue(mem_address_var)
mem_address = int(mem_address_var.GetValue(), 16)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have a GetAsWhatever method on SBValue use that. Same thing but seems more "proper" to use that.

self.assertTrue(mem_address_var)
mem_address = int(mem_address_var.GetValue(), 16)

reply = self.send_process_packet("MultiMemRead:")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add one without the colon, just "MultiMemRead". Might hit a generic "oh no no ;" handler that already existed but that's fine to check anyway.

self.assertEqual(reply, "E03")
reply = self.send_process_packet("MultiMemRead:ranges:10")
self.assertEqual(reply, "E03")
reply = self.send_process_packet("MultiMemRead:ranges:10,")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comments to say why each one is invalid.

self.assertEqual(reply, "E03")
reply = self.send_process_packet("MultiMemRead:ranges:10,2,;")
self.assertEqual(reply, "0;") # Debugserver is permissive with trailing commas.
reply = self.send_process_packet("MultiMemRead:ranges:10,2;")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for 3 numbers. address, size, address - no size. It's an error I assume.

reply = self.send_process_packet(f"MultiMemRead:ranges:{mem_address:x},0;")
self.assertEqual(reply, "0;")
reply = self.send_process_packet(
f"MultiMemRead:ranges:{mem_address:x},0;options:;"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we keep the options, if, reverse the order of keys as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed options!

reply = self.send_process_packet(
f"MultiMemRead:ranges:{mem_address:x},2,{mem_address+2:x},4,{mem_address+6:x},8;"
)
self.assertEqual(reply, "2,4,8;abcdefghijklmn")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tests for zero length reply in the middle and end, you have one for the start earlier.

Also check a zero length returns 0 bytes for a mapped and unmapped location.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added zero length reads at the middle end using mapped addresses.

The unmapped addresses were already covered, I believe, but I also added a ranges:0,0; to simulate what a client may do when checking for MultiMemRead support.

memset(memory, '-', 1024);
for (int i = 0; i < 50; i++)
memory[i] = 'a' + i;
return 0; // break here
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is the a-z gives you a pattern to look for, and if we ever see the dashes, we know it didn't read correctly?

(and you didn't calloc it because all 0s could be the result of us forgetting to actually read anything)

In which case would it be a bit more rigorous to offset the a-z a bit from the start of the region? Now if you have an overread, we see dashes, but if we underread, then we get random bytes. The test would still fail but it would be random what wrong bytes we saw.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points, added some ---s at the start of the memory region as well

Comment on lines 3170 to 3171
return str.size() >= prefix.size() &&
str.compare(0, prefix.size(), prefix) == 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk if you can use StringRef here but it's very stringview like and it has a starts_with.

Maybe not because you don't want to link llvm libraries.

@@ -0,0 +1,72 @@
"""
Tests the exit code/description coming from the debugserver.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"...when handling MultiMemRead packets."

@felipepiovezan felipepiovezan force-pushed the felipe/multimem_debugserver_impl branch from dee9744 to 55c8f39 Compare October 12, 2025 15:14
@felipepiovezan
Copy link
Contributor Author

felipepiovezan commented Oct 12, 2025

Addressed review comments, also had to rebase due to a conflict

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants