[Lldb-commits] [lldb] 5d34362 - [lldb][AArch64] Add MTE memory tag reading to lldb

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Thu Jun 24 09:17:17 PDT 2021


Author: David Spickett
Date: 2021-06-24T17:17:10+01:00
New Revision: 5d3436200147a999670f754288a03c4ac5a15aeb

URL: https://github.com/llvm/llvm-project/commit/5d3436200147a999670f754288a03c4ac5a15aeb
DIFF: https://github.com/llvm/llvm-project/commit/5d3436200147a999670f754288a03c4ac5a15aeb.diff

LOG: [lldb][AArch64] Add MTE memory tag reading to lldb

This adds GDB client support for the qMemTags packet
which reads memory tags. Following the design
which was recently committed to GDB.

https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html#General-Query-Packets
(look for qMemTags)

lldb commands will use the new Process methods
GetMemoryTagManager and ReadMemoryTags.

The former takes a range and checks that:
* The current process architecture has an architecture plugin
* That plugin provides a MemoryTagManager
* That the range of memory requested lies in a tagged range
  (it will expand it to granules for you)

If all that was true you get a MemoryTagManager you
can give to ReadMemoryTags.

This two step process is done to allow commands to get the
tag manager without having to read tags as well. For example
you might just want to remove a logical tag, or error early
if a range with tagged addresses is inverted.

Note that getting a MemoryTagManager doesn't mean that the process
or a specific memory range is tagged. Those are seperate checks.
Having a tag manager just means this architecture *could* have
a tagging feature enabled.

An architecture plugin has been added for AArch64 which
will return a MemoryTagManagerAArch64MTE, which was added in a
previous patch.

Reviewed By: omjavaid

Differential Revision: https://reviews.llvm.org/D95602

Added: 
    lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp
    lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.h
    lldb/source/Plugins/Architecture/AArch64/CMakeLists.txt

Modified: 
    lldb/include/lldb/Core/Architecture.h
    lldb/include/lldb/Target/Process.h
    lldb/source/Plugins/Architecture/CMakeLists.txt
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
    lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
    lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
    lldb/source/Target/Process.cpp
    lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Core/Architecture.h b/lldb/include/lldb/Core/Architecture.h
index 2ea8bd31ebf41..b68bf27ae0df8 100644
--- a/lldb/include/lldb/Core/Architecture.h
+++ b/lldb/include/lldb/Core/Architecture.h
@@ -10,6 +10,7 @@
 #define LLDB_CORE_ARCHITECTURE_H
 
 #include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/MemoryTagManager.h"
 
 namespace lldb_private {
 
@@ -97,6 +98,17 @@ class Architecture : public PluginInterface {
                                                Target &target) const {
     return addr;
   }
+
+  // Returns a pointer to an object that can manage memory tags for this
+  // Architecture E.g. masking out tags, unpacking tag streams etc. Returns
+  // nullptr if the architecture does not have a memory tagging extension.
+  //
+  // The return pointer being valid does not mean that the current process has
+  // memory tagging enabled, just that a tagging technology exists for this
+  // architecture.
+  virtual const MemoryTagManager *GetMemoryTagManager() const {
+    return nullptr;
+  }
 };
 
 } // namespace lldb_private

diff  --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index c849bd7766018..ba51056b1c9e9 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -34,6 +34,7 @@
 #include "lldb/Target/ExecutionContextScope.h"
 #include "lldb/Target/InstrumentationRuntime.h"
 #include "lldb/Target/Memory.h"
+#include "lldb/Target/MemoryTagManager.h"
 #include "lldb/Target/QueueList.h"
 #include "lldb/Target/ThreadList.h"
 #include "lldb/Target/ThreadPlanStack.h"
@@ -1709,6 +1710,44 @@ class Process : public std::enable_shared_from_this<Process>,
   lldb::addr_t CallocateMemory(size_t size, uint32_t permissions,
                                Status &error);
 
+  /// If the address range given is in a memory tagged range and this
+  /// architecture and process supports memory tagging, return a tag
+  /// manager that can be used to maniupulate those memory tags.
+  /// Tags present in the addresses given are ignored.
+  ///
+  /// \param[in] addr
+  ///     Start of memory range.
+  ///
+  /// \param[in] end_addr
+  ///     End of the memory range. Where end is one beyond the last byte to be
+  ///     included.
+  ///
+  /// \return
+  ///     Either a valid pointer to a tag manager or an error describing why one
+  ///     could not be provided.
+  llvm::Expected<const MemoryTagManager *>
+  GetMemoryTagManager(lldb::addr_t addr, lldb::addr_t end_addr);
+
+  /// Expands the range addr to addr+len to align with granule boundaries and
+  /// then calls DoReadMemoryTags to do the target specific operations.
+  /// Tags are returned unpacked so can be used without conversion.
+  ///
+  /// \param[in] tag_manager
+  ///     The tag manager to get memory tagging information from.
+  ///
+  /// \param[in] addr
+  ///     Start of memory range to read tags for.
+  ///
+  /// \param[in] len
+  ///     Length of memory range to read tags for (in bytes).
+  ///
+  /// \return
+  ///     Either the unpacked tags or an error describing a failure to read
+  ///     or unpack them.
+  llvm::Expected<std::vector<lldb::addr_t>>
+  ReadMemoryTags(const MemoryTagManager *tag_manager, lldb::addr_t addr,
+                 size_t len);
+
   /// Resolve dynamically loaded indirect functions.
   ///
   /// \param[in] address
@@ -2728,6 +2767,29 @@ void PruneThreadPlans();
   ///     false otherwise.
   virtual bool SupportsMemoryTagging() { return false; }
 
+  /// Does the final operation to read memory tags. E.g. sending a GDB packet.
+  /// It assumes that ReadMemoryTags has checked that memory tagging is enabled
+  /// and has expanded the memory range as needed.
+  ///
+  /// \param[in] addr
+  ///    Start of address range to read memory tags for.
+  ///
+  /// \param[in] len
+  ///    Length of the memory range to read tags for (in bytes).
+  ///
+  /// \param[in] type
+  ///    Type of tags to read (get this from a MemoryTagManager)
+  ///
+  /// \return
+  ///     The packed tag data received from the remote or an error
+  ///     if the read failed.
+  virtual llvm::Expected<std::vector<uint8_t>>
+  DoReadMemoryTags(lldb::addr_t addr, size_t len, int32_t type) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "%s does not support reading memory tags",
+                                   GetPluginName().GetCString());
+  }
+
   // Type definitions
   typedef std::map<lldb::LanguageType, lldb::LanguageRuntimeSP>
       LanguageRuntimeCollection;

diff  --git a/lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp b/lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp
new file mode 100644
index 0000000000000..9994cc293d6aa
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp
@@ -0,0 +1,45 @@
+//===-- ArchitectureAArch64.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Architecture/AArch64/ArchitectureAArch64.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Utility/ArchSpec.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+LLDB_PLUGIN_DEFINE(ArchitectureAArch64)
+
+ConstString ArchitectureAArch64::GetPluginNameStatic() {
+  return ConstString("aarch64");
+}
+
+void ArchitectureAArch64::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                "AArch64-specific algorithms",
+                                &ArchitectureAArch64::Create);
+}
+
+void ArchitectureAArch64::Terminate() {
+  PluginManager::UnregisterPlugin(&ArchitectureAArch64::Create);
+}
+
+std::unique_ptr<Architecture>
+ArchitectureAArch64::Create(const ArchSpec &arch) {
+  auto machine = arch.GetMachine();
+  if (machine != llvm::Triple::aarch64 && machine != llvm::Triple::aarch64_be &&
+      machine != llvm::Triple::aarch64_32) {
+    return nullptr;
+  }
+  return std::unique_ptr<Architecture>(new ArchitectureAArch64());
+}
+
+ConstString ArchitectureAArch64::GetPluginName() {
+  return GetPluginNameStatic();
+}
+uint32_t ArchitectureAArch64::GetPluginVersion() { return 1; }

diff  --git a/lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.h b/lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.h
new file mode 100644
index 0000000000000..775478cc9338c
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.h
@@ -0,0 +1,40 @@
+//===-- ArchitectureAArch64.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_ARCHITECTURE_AARCH64_ARCHITECTUREAARCH64_H
+#define LLDB_SOURCE_PLUGINS_ARCHITECTURE_AARCH64_ARCHITECTUREAARCH64_H
+
+#include "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h"
+#include "lldb/Core/Architecture.h"
+
+namespace lldb_private {
+
+class ArchitectureAArch64 : public Architecture {
+public:
+  static ConstString GetPluginNameStatic();
+  static void Initialize();
+  static void Terminate();
+
+  ConstString GetPluginName() override;
+  uint32_t GetPluginVersion() override;
+
+  void OverrideStopInfo(Thread &thread) const override{};
+
+  const MemoryTagManager *GetMemoryTagManager() const override {
+    return &m_memory_tag_manager;
+  }
+
+private:
+  static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+  ArchitectureAArch64() = default;
+  MemoryTagManagerAArch64MTE m_memory_tag_manager;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_ARCHITECTURE_AARCH64_ARCHITECTUREAARCH64_H

diff  --git a/lldb/source/Plugins/Architecture/AArch64/CMakeLists.txt b/lldb/source/Plugins/Architecture/AArch64/CMakeLists.txt
new file mode 100644
index 0000000000000..9bcf99318622d
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/AArch64/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_lldb_library(lldbPluginArchitectureAArch64 PLUGIN
+  ArchitectureAArch64.cpp
+
+  LINK_LIBS
+    lldbPluginProcessUtility
+    lldbCore
+    lldbTarget
+    lldbUtility
+  LINK_COMPONENTS
+    Support
+  )

diff  --git a/lldb/source/Plugins/Architecture/CMakeLists.txt b/lldb/source/Plugins/Architecture/CMakeLists.txt
index 14ad91644595a..9ed8edf70af3f 100644
--- a/lldb/source/Plugins/Architecture/CMakeLists.txt
+++ b/lldb/source/Plugins/Architecture/CMakeLists.txt
@@ -1,3 +1,4 @@
 add_subdirectory(Arm)
 add_subdirectory(Mips)
 add_subdirectory(PPC64)
+add_subdirectory(AArch64)

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index db50afcfa33da..a7fcf084fa60c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -586,6 +586,50 @@ bool GDBRemoteCommunicationClient::GetMemoryTaggingSupported() {
   return m_supports_memory_tagging == eLazyBoolYes;
 }
 
+DataBufferSP GDBRemoteCommunicationClient::ReadMemoryTags(lldb::addr_t addr,
+                                                          size_t len,
+                                                          int32_t type) {
+  StreamString packet;
+  packet.Printf("qMemTags:%lx,%lx:%x", addr, len, type);
+  StringExtractorGDBRemote response;
+
+  Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_MEMORY);
+
+  if (SendPacketAndWaitForResponse(packet.GetString(), response, false) !=
+          PacketResult::Success ||
+      !response.IsNormalResponse()) {
+    LLDB_LOGF(log, "GDBRemoteCommunicationClient::%s: qMemTags packet failed",
+              __FUNCTION__);
+    return nullptr;
+  }
+
+  // We are expecting
+  // m<hex encoded bytes>
+
+  if (response.GetChar() != 'm') {
+    LLDB_LOGF(log,
+              "GDBRemoteCommunicationClient::%s: qMemTags response did not "
+              "begin with \"m\"",
+              __FUNCTION__);
+    return nullptr;
+  }
+
+  size_t expected_bytes = response.GetBytesLeft() / 2;
+  DataBufferSP buffer_sp(new DataBufferHeap(expected_bytes, 0));
+  size_t got_bytes = response.GetHexBytesAvail(buffer_sp->GetData());
+  // Check both because in some situations chars are consumed even
+  // if the decoding fails.
+  if (response.GetBytesLeft() || (expected_bytes != got_bytes)) {
+    LLDB_LOGF(
+        log,
+        "GDBRemoteCommunicationClient::%s: Invalid data in qMemTags response",
+        __FUNCTION__);
+    return nullptr;
+  }
+
+  return buffer_sp;
+}
+
 bool GDBRemoteCommunicationClient::GetxPacketSupported() {
   if (m_supports_x == eLazyBoolCalculate) {
     StringExtractorGDBRemote response;

diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 599020e4e78fa..fa67a6c69a535 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -453,6 +453,9 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
 
   bool GetMemoryTaggingSupported();
 
+  lldb::DataBufferSP ReadMemoryTags(lldb::addr_t addr, size_t len,
+                                    int32_t type);
+
   /// Use qOffsets to query the offset used when relocating the target
   /// executable. If successful, the returned structure will contain at least
   /// one value in the offsets field.

diff  --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index bd6c548f708a9..b86e31c63e9fe 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2771,6 +2771,25 @@ bool ProcessGDBRemote::SupportsMemoryTagging() {
   return m_gdb_comm.GetMemoryTaggingSupported();
 }
 
+llvm::Expected<std::vector<uint8_t>>
+ProcessGDBRemote::DoReadMemoryTags(lldb::addr_t addr, size_t len,
+                                   int32_t type) {
+  // By this point ReadMemoryTags has validated that tagging is enabled
+  // for this target/process/address.
+  DataBufferSP buffer_sp = m_gdb_comm.ReadMemoryTags(addr, len, type);
+  if (!buffer_sp) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Error reading memory tags from remote");
+  }
+
+  // Return the raw tag data
+  llvm::ArrayRef<uint8_t> tag_data = buffer_sp->GetData();
+  std::vector<uint8_t> got;
+  got.reserve(tag_data.size());
+  std::copy(tag_data.begin(), tag_data.end(), std::back_inserter(got));
+  return got;
+}
+
 Status ProcessGDBRemote::WriteObjectFile(
     std::vector<ObjectFile::LoadableData> entries) {
   Status error;

diff  --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index b69016ab5ae6b..764d800731a04 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -408,6 +408,9 @@ class ProcessGDBRemote : public Process,
 
   bool HasErased(FlashRange range);
 
+  llvm::Expected<std::vector<uint8_t>>
+  DoReadMemoryTags(lldb::addr_t addr, size_t len, int32_t type) override;
+
 private:
   // For ProcessGDBRemote only
   std::string m_partial_profile_data;

diff  --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 1449501fbe911..f139479db13bd 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -6065,3 +6065,84 @@ bool Process::CallVoidArgVoidPtrReturn(const Address *address,
 
   return false;
 }
+
+llvm::Expected<const MemoryTagManager *>
+Process::GetMemoryTagManager(lldb::addr_t addr, lldb::addr_t end_addr) {
+  Architecture *arch = GetTarget().GetArchitecturePlugin();
+  const MemoryTagManager *tag_manager =
+      arch ? arch->GetMemoryTagManager() : nullptr;
+  if (!arch || !tag_manager) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "This architecture does not support memory tagging",
+        GetPluginName().GetCString());
+  }
+
+  if (!SupportsMemoryTagging()) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Process does not support memory tagging");
+  }
+
+  ptr
diff _t len = tag_manager->AddressDiff(end_addr, addr);
+  if (len <= 0) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "End address (0x%" PRIx64
+        ") must be greater than the start address (0x%" PRIx64 ")",
+        end_addr, addr);
+  }
+
+  // Region lookup is not address size aware so mask the address
+  MemoryRegionInfo::RangeType tag_range(tag_manager->RemoveNonAddressBits(addr),
+                                        len);
+  tag_range = tag_manager->ExpandToGranule(tag_range);
+
+  // Make a copy so we can use the original range in errors
+  MemoryRegionInfo::RangeType remaining_range(tag_range);
+
+  // While we haven't found a matching memory region for some of the range
+  while (remaining_range.IsValid()) {
+    MemoryRegionInfo region;
+    Status status = GetMemoryRegionInfo(remaining_range.GetRangeBase(), region);
+
+    if (status.Fail() || region.GetMemoryTagged() != MemoryRegionInfo::eYes) {
+      return llvm::createStringError(
+          llvm::inconvertibleErrorCode(),
+          "Address range 0x%lx:0x%lx is not in a memory tagged region",
+          tag_range.GetRangeBase(), tag_range.GetRangeEnd());
+    }
+
+    if (region.GetRange().GetRangeEnd() >= remaining_range.GetRangeEnd()) {
+      // We've found a region for the whole range or the last piece of a range
+      remaining_range.SetByteSize(0);
+    } else {
+      // We've found some part of the range, look for the rest
+      remaining_range.SetRangeBase(region.GetRange().GetRangeEnd());
+    }
+  }
+
+  return tag_manager;
+}
+
+llvm::Expected<std::vector<lldb::addr_t>>
+Process::ReadMemoryTags(const MemoryTagManager *tag_manager, lldb::addr_t addr,
+                        size_t len) {
+  if (!tag_manager) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "A memory tag manager is required for reading memory tags.");
+  }
+
+  MemoryTagManager::TagRange range(tag_manager->RemoveNonAddressBits(addr),
+                                   len);
+  range = tag_manager->ExpandToGranule(range);
+
+  llvm::Expected<std::vector<uint8_t>> tag_data =
+      DoReadMemoryTags(range.GetRangeBase(), range.GetByteSize(),
+                       tag_manager->GetAllocationTagType());
+  if (!tag_data)
+    return tag_data.takeError();
+
+  return tag_manager->UnpackTagsData(
+      *tag_data, range.GetByteSize() / tag_manager->GetGranuleSize());
+}

diff  --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
index cf5a1a5580067..b9fc107527a21 100644
--- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
+++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -465,3 +465,68 @@ TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) {
   EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=0x1234"));
   EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=12345678123456789"));
 }
+
+static void
+check_qmemtags(TestClient &client, MockServer &server, size_t read_len,
+               const char *packet, llvm::StringRef response,
+               llvm::Optional<std::vector<uint8_t>> expected_tag_data) {
+  const auto &ReadMemoryTags = [&](size_t len, const char *packet,
+                                   llvm::StringRef response) {
+    std::future<DataBufferSP> result = std::async(std::launch::async, [&] {
+      return client.ReadMemoryTags(0xDEF0, read_len, 1);
+    });
+
+    HandlePacket(server, packet, response);
+    return result.get();
+  };
+
+  auto result = ReadMemoryTags(0, packet, response);
+  if (expected_tag_data) {
+    ASSERT_TRUE(result);
+    llvm::ArrayRef<uint8_t> expected(*expected_tag_data);
+    llvm::ArrayRef<uint8_t> got = result->GetData();
+    ASSERT_THAT(expected, testing::ContainerEq(got));
+  } else {
+    ASSERT_FALSE(result);
+  }
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, ReadMemoryTags) {
+  // Zero length reads are valid
+  check_qmemtags(client, server, 0, "qMemTags:def0,0:1", "m",
+                 std::vector<uint8_t>{});
+
+  // The client layer does not check the length of the received data.
+  // All we need is the "m" and for the decode to use all of the chars
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m09",
+                 std::vector<uint8_t>{0x9});
+
+  // Zero length response is fine as long as the "m" is present
+  check_qmemtags(client, server, 0, "qMemTags:def0,0:1", "m",
+                 std::vector<uint8_t>{});
+
+  // Normal responses
+  check_qmemtags(client, server, 16, "qMemTags:def0,10:1", "m66",
+                 std::vector<uint8_t>{0x66});
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m0102",
+                 std::vector<uint8_t>{0x1, 0x2});
+
+  // Empty response is an error
+  check_qmemtags(client, server, 17, "qMemTags:def0,11:1", "", llvm::None);
+  // Usual error response
+  check_qmemtags(client, server, 17, "qMemTags:def0,11:1", "E01", llvm::None);
+  // Leading m missing
+  check_qmemtags(client, server, 17, "qMemTags:def0,11:1", "01", llvm::None);
+  // Anything other than m is an error
+  check_qmemtags(client, server, 17, "qMemTags:def0,11:1", "z01", llvm::None);
+  // Decoding tag data doesn't use all the chars in the packet
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m09zz", llvm::None);
+  // Data that is not hex bytes
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "mhello",
+                 llvm::None);
+  // Data is not a complete hex char
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m9", llvm::None);
+  // Data has a trailing hex char
+  check_qmemtags(client, server, 32, "qMemTags:def0,20:1", "m01020",
+                 llvm::None);
+}


        


More information about the lldb-commits mailing list