[Lldb-commits] [lldb] adee89f - [lldb][AArch64] Add tag packing and repetition memory tag manager

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Fri Jul 16 06:21:44 PDT 2021


Author: David Spickett
Date: 2021-07-16T14:21:38+01:00
New Revision: adee89f8bcd11529bc9e046493b71fbe4b49dcd1

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

LOG: [lldb][AArch64] Add tag packing and repetition memory tag manager

PackTags is used by to compress tags to go in the QMemTags packet
and be passed to ptrace when writing memory tags.

The behaviour of RepeatTagsForRange matches that described for QMemTags
in the GDB documentation:
https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html#General-Query-Packets

In addition, unpacking tags with number of tags 0 now means
do not check that number of tags matches the range.
This will be used by lldb-server to unpack tags before repeating
them to fill the requested range.

Reviewed By: omjavaid

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

Added: 
    

Modified: 
    lldb/include/lldb/Target/MemoryTagManager.h
    lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
    lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
    lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/MemoryTagManager.h b/lldb/include/lldb/Target/MemoryTagManager.h
index 65abfcd76382..a5e0deba14a9 100644
--- a/lldb/include/lldb/Target/MemoryTagManager.h
+++ b/lldb/include/lldb/Target/MemoryTagManager.h
@@ -87,11 +87,45 @@ class MemoryTagManager {
   virtual size_t GetTagSizeInBytes() const = 0;
 
   // Unpack tags from their stored format (e.g. gdb qMemTags data) into seperate
-  // tags. Checks that each tag is within the expected value range and that the
-  // number of tags found matches the number of granules we originally asked
-  // for.
+  // tags.
+  //
+  // Checks that each tag is within the expected value range and if granules is
+  // set to non-zero, that the number of tags found matches the number of
+  // granules we expected to cover.
+  virtual llvm::Expected<std::vector<lldb::addr_t>>
+  UnpackTagsData(const std::vector<uint8_t> &tags,
+                 size_t granules = 0) const = 0;
+
+  // Pack uncompressed tags into their storage format (e.g. for gdb QMemTags).
+  // Checks that each tag is within the expected value range.
+  // We do not check the number of tags or range they apply to because
+  // it is up to the remote to repeat them as needed.
+  virtual llvm::Expected<std::vector<uint8_t>>
+  PackTags(const std::vector<lldb::addr_t> &tags) const = 0;
+
+  // Take a set of tags and repeat them as much as needed to cover the given
+  // range. We assume that this range has been previously expanded/aligned to
+  // granules. (this method is used by lldb-server to implement QMemTags
+  // packet handling)
+  //
+  // If the range is empty, zero tags are returned.
+  // If the range is not empty and...
+  //   * there are no tags, an error is returned.
+  //   * there are fewer tags than granules, the tags are repeated to fill the
+  //     range.
+  //   * there are more tags than granules, only the tags required to cover
+  //     the range are returned.
+  //
+  // When repeating tags it will not always return a multiple of the original
+  // list. For example if your range is 3 granules and your tags are 1 and 2.
+  // You will get tags 1, 2 and 1 returned. Rather than getting 1, 2, 1, 2,
+  // which would be one too many tags for the range.
+  //
+  // A single tag will just be repeated as you'd expected. Tag 1 over 3 granules
+  // would return 1, 1, 1.
   virtual llvm::Expected<std::vector<lldb::addr_t>>
-  UnpackTagsData(const std::vector<uint8_t> &tags, size_t granules) const = 0;
+  RepeatTagsForRange(const std::vector<lldb::addr_t> &tags,
+                     TagRange range) const = 0;
 
   virtual ~MemoryTagManager() {}
 };

diff  --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
index e44a10da73af..d74b66b58afc 100644
--- a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
+++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
@@ -125,14 +125,17 @@ MemoryTagManagerAArch64MTE::MakeTaggedRange(
 
 llvm::Expected<std::vector<lldb::addr_t>>
 MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags,
-                                           size_t granules) const {
-  size_t num_tags = tags.size() / GetTagSizeInBytes();
-  if (num_tags != granules) {
-    return llvm::createStringError(
-        llvm::inconvertibleErrorCode(),
-        "Packed tag data size does not match expected number of tags. "
-        "Expected %zu tag(s) for %zu granules, got %zu tag(s).",
-        granules, granules, num_tags);
+                                           size_t granules /*=0*/) const {
+  // 0 means don't check the number of tags before unpacking
+  if (granules) {
+    size_t num_tags = tags.size() / GetTagSizeInBytes();
+    if (num_tags != granules) {
+      return llvm::createStringError(
+          llvm::inconvertibleErrorCode(),
+          "Packed tag data size does not match expected number of tags. "
+          "Expected %zu tag(s) for %zu granule(s), got %zu tag(s).",
+          granules, granules, num_tags);
+    }
   }
 
   // (if bytes per tag was not 1, we would reconstruct them here)
@@ -152,3 +155,46 @@ MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags,
 
   return unpacked;
 }
+
+llvm::Expected<std::vector<uint8_t>> MemoryTagManagerAArch64MTE::PackTags(
+    const std::vector<lldb::addr_t> &tags) const {
+  std::vector<uint8_t> packed;
+  packed.reserve(tags.size() * GetTagSizeInBytes());
+
+  for (auto tag : tags) {
+    if (tag > MTE_TAG_MAX) {
+      return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                     "Found tag 0x%" PRIx64
+                                     " which is > max MTE tag value of 0x%x.",
+                                     tag, MTE_TAG_MAX);
+    }
+    packed.push_back(static_cast<uint8_t>(tag));
+  }
+
+  return packed;
+}
+
+llvm::Expected<std::vector<lldb::addr_t>>
+MemoryTagManagerAArch64MTE::RepeatTagsForRange(
+    const std::vector<lldb::addr_t> &tags, TagRange range) const {
+  std::vector<lldb::addr_t> new_tags;
+
+  // If the range is not empty
+  if (range.IsValid()) {
+    if (tags.empty()) {
+      return llvm::createStringError(
+          llvm::inconvertibleErrorCode(),
+          "Expected some tags to cover given range, got zero.");
+    }
+
+    // We assume that this range has already been expanded/aligned to granules
+    size_t granules = range.GetByteSize() / GetGranuleSize();
+    new_tags.reserve(granules);
+    for (size_t to_copy = 0; granules > 0; granules -= to_copy) {
+      to_copy = granules > tags.size() ? tags.size() : granules;
+      new_tags.insert(new_tags.end(), tags.begin(), tags.begin() + to_copy);
+    }
+  }
+
+  return new_tags;
+}

diff  --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
index 55d80c8ead53..d4e8249da93f 100644
--- a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
+++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
@@ -38,7 +38,14 @@ class MemoryTagManagerAArch64MTE : public MemoryTagManager {
 
   llvm::Expected<std::vector<lldb::addr_t>>
   UnpackTagsData(const std::vector<uint8_t> &tags,
-                 size_t granules) const override;
+                 size_t granules = 0) const override;
+
+  llvm::Expected<std::vector<uint8_t>>
+  PackTags(const std::vector<lldb::addr_t> &tags) const override;
+
+  llvm::Expected<std::vector<lldb::addr_t>>
+  RepeatTagsForRange(const std::vector<lldb::addr_t> &tags,
+                     TagRange range) const override;
 };
 
 } // namespace lldb_private

diff  --git a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
index eec952943d79..128dac7a21c3 100644
--- a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
+++ b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
@@ -21,7 +21,7 @@ TEST(MemoryTagManagerAArch64MTETest, UnpackTagsData) {
       manager.UnpackTagsData(input, 2),
       llvm::FailedWithMessage(
           "Packed tag data size does not match expected number of tags. "
-          "Expected 2 tag(s) for 2 granules, got 0 tag(s)."));
+          "Expected 2 tag(s) for 2 granule(s), got 0 tag(s)."));
 
   // This is out of the valid tag range
   input.push_back(0x1f);
@@ -41,6 +41,43 @@ TEST(MemoryTagManagerAArch64MTETest, UnpackTagsData) {
       manager.UnpackTagsData(input, 2);
   ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
   ASSERT_THAT(expected, testing::ContainerEq(*got));
+
+  // Error for too much tag data
+  ASSERT_THAT_EXPECTED(
+      manager.UnpackTagsData(input, 1),
+      llvm::FailedWithMessage(
+          "Packed tag data size does not match expected number of tags. "
+          "Expected 1 tag(s) for 1 granule(s), got 2 tag(s)."));
+
+  // By default, we don't check number of tags
+  llvm::Expected<std::vector<lldb::addr_t>> got_zero =
+      manager.UnpackTagsData(input);
+  ASSERT_THAT_EXPECTED(got_zero, llvm::Succeeded());
+  ASSERT_THAT(expected, testing::ContainerEq(*got));
+
+  // Which is the same as granules=0
+  got_zero = manager.UnpackTagsData(input, 0);
+  ASSERT_THAT_EXPECTED(got_zero, llvm::Succeeded());
+  ASSERT_THAT(expected, testing::ContainerEq(*got));
+}
+
+TEST(MemoryTagManagerAArch64MTETest, PackTags) {
+  MemoryTagManagerAArch64MTE manager;
+
+  // Error for tag out of range
+  llvm::Expected<std::vector<uint8_t>> invalid_tag_err =
+      manager.PackTags({0x10});
+  ASSERT_THAT_EXPECTED(
+      invalid_tag_err,
+      llvm::FailedWithMessage(
+          "Found tag 0x10 which is > max MTE tag value of 0xf."));
+
+  // 0xf here is the max tag value that we can pack
+  std::vector<lldb::addr_t> tags{0, 1, 0xf};
+  std::vector<uint8_t> expected{0, 1, 0xf};
+  llvm::Expected<std::vector<uint8_t>> packed = manager.PackTags(tags);
+  ASSERT_THAT_EXPECTED(packed, llvm::Succeeded());
+  ASSERT_THAT(expected, testing::ContainerEq(*packed));
 }
 
 TEST(MemoryTagManagerAArch64MTETest, GetLogicalTag) {
@@ -233,3 +270,53 @@ TEST(MemoryTagManagerAArch64MTETest, AddressDiff) {
   ASSERT_EQ(-32, manager.AddressDiff(0x5511222233334400, 0x4411222233334420));
   ASSERT_EQ(65, manager.AddressDiff(0x9911222233334441, 0x6611222233334400));
 }
+
+// Helper to check that repeating "tags" over "range" gives you
+// "expected_tags".
+static void
+test_repeating_tags(const std::vector<lldb::addr_t> &tags,
+                    MemoryTagManagerAArch64MTE::TagRange range,
+                    const std::vector<lldb::addr_t> &expected_tags) {
+  MemoryTagManagerAArch64MTE manager;
+  llvm::Expected<std::vector<lldb::addr_t>> tags_or_err =
+      manager.RepeatTagsForRange(tags, range);
+  ASSERT_THAT_EXPECTED(tags_or_err, llvm::Succeeded());
+  ASSERT_THAT(expected_tags, testing::ContainerEq(*tags_or_err));
+}
+
+TEST(MemoryTagManagerAArch64MTETest, RepeatTagsForRange) {
+  MemoryTagManagerAArch64MTE manager;
+
+  // Must have some tags if your range is not empty
+  llvm::Expected<std::vector<lldb::addr_t>> no_tags_err =
+      manager.RepeatTagsForRange({},
+                                 MemoryTagManagerAArch64MTE::TagRange{0, 16});
+  ASSERT_THAT_EXPECTED(
+      no_tags_err, llvm::FailedWithMessage(
+                       "Expected some tags to cover given range, got zero."));
+
+  // If the range is empty, you get no tags back
+  test_repeating_tags({1, 2, 3}, MemoryTagManagerAArch64MTE::TagRange{0, 0},
+                      {});
+  // And you don't need tags for an empty range
+  test_repeating_tags({}, MemoryTagManagerAArch64MTE::TagRange{0, 0}, {});
+
+  // A single tag will just be multiplied as many times as needed
+  test_repeating_tags({5}, MemoryTagManagerAArch64MTE::TagRange{0, 16}, {5});
+  test_repeating_tags({6}, MemoryTagManagerAArch64MTE::TagRange{0, 32}, {6, 6});
+
+  // If you've got as many tags as granules, it's a roundtrip
+  test_repeating_tags({7, 8}, MemoryTagManagerAArch64MTE::TagRange{0, 32},
+                      {7, 8});
+
+  // If you've got fewer tags than granules, they repeat. Exactly or partially
+  // as needed.
+  test_repeating_tags({7, 8}, MemoryTagManagerAArch64MTE::TagRange{0, 64},
+                      {7, 8, 7, 8});
+  test_repeating_tags({7, 8}, MemoryTagManagerAArch64MTE::TagRange{0, 48},
+                      {7, 8, 7});
+
+  // If you've got more tags than granules you get back only those needed
+  test_repeating_tags({1, 2, 3, 4}, MemoryTagManagerAArch64MTE::TagRange{0, 32},
+                      {1, 2});
+}


        


More information about the lldb-commits mailing list