DavidSpickett created this revision.
Herald added subscribers: mgrang, kristof.beyls.
DavidSpickett requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This is to be used when you want to know what subranges
of a larger range have memory tagging.

Like MakeTaggedRange but memory without tags is skipped
and you get a list of ranges back.

Will be used later by DumpDataExtractor to show memory tags.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D112824

Files:
  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

Index: lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
===================================================================
--- lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
+++ lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
@@ -247,6 +247,101 @@
   ASSERT_EQ(*got, expected_range);
 }
 
+TEST(MemoryTagManagerAArch64MTETest, MakeTaggedRanges) {
+  MemoryTagManagerAArch64MTE manager;
+  MemoryRegionInfos memory_regions;
+
+  // Note that MakeTaggedRanges takes start/end address.
+  // Whereas TagRanges and regions take start address and size.
+
+  // Range must not be inverted
+  // (this is the only error situation)
+  ASSERT_THAT_EXPECTED(
+      manager.MakeTaggedRange(1, 0, memory_regions),
+      llvm::FailedWithMessage(
+          "End address (0x0) must be greater than the start address (0x1)"));
+
+  // No regions means no tagged regions, no ranges returned.
+  llvm::Expected<std::vector<MemoryTagManager::TagRange>> got =
+      manager.MakeTaggedRanges(0, 0x10, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{});
+
+  // Cover whole range, untagged. No ranges returned.
+  memory_regions.push_back(MakeRegionInfo(0, 0x20, false));
+  got = manager.MakeTaggedRanges(0, 0x20, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{});
+
+  // Make the region tagged and it'll be the one range returned.
+  memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes);
+  got = manager.MakeTaggedRanges(0, 0x20, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+                      MemoryTagManager::TagRange(0, 0x20)});
+
+  // This region will be trimmed if it's larger than the whole range.
+  memory_regions.clear();
+  memory_regions.push_back(MakeRegionInfo(0, 0x40, true));
+  got = manager.MakeTaggedRanges(0x10, 0x30, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+                      MemoryTagManager::TagRange(0x10, 0x20)});
+
+  memory_regions.clear();
+
+  // Only start of range is tagged, only that is returned.
+  // Start the region just before the requested range to check
+  // we limit the result to the requested range.
+  memory_regions.push_back(MakeRegionInfo(0, 0x20, true));
+  got = manager.MakeTaggedRanges(0x10, 0x100, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+                      MemoryTagManager::TagRange(0x10, 0x10)});
+
+  // Add a tagged region at the end, now we get both
+  // and the middle is untagged.
+  // The range added here is deliberately over the end of the
+  // requested range to show that we trim the end.
+  memory_regions.push_back(MakeRegionInfo(0xE0, 0x40, true));
+  got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+
+  std::vector<MemoryTagManager::TagRange> expected{
+      MemoryTagManager::TagRange(0x10, 0x10),
+      MemoryTagManager::TagRange(0xE0, 0x30)};
+  ASSERT_EQ(*got, expected);
+
+  // Now add a middle tagged region.
+  memory_regions.push_back(MakeRegionInfo(0x90, 0x20, true));
+  // MakeTaggedRanges will sort the regions it is given, so the output
+  // is always in ascending address order. So this goes in the middle
+  // of expected.
+  expected.insert(std::next(expected.begin()),
+                  MemoryTagManager::TagRange(0x90, 0x20));
+  got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, expected);
+
+  // Then if we add untagged regions in between the tagged,
+  // the output should stay the same.
+  memory_regions.push_back(MakeRegionInfo(0x20, 0x30, false));
+  memory_regions.push_back(MakeRegionInfo(0xC0, 0x10, false));
+  got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, expected);
+
+  // Finally check that we handle only having the end of the range.
+  memory_regions.clear();
+  expected.clear();
+
+  memory_regions.push_back(MakeRegionInfo(0x100, 0x10, true));
+  expected.push_back(MemoryTagManager::TagRange(0x100, 0x10));
+  got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_EQ(*got, expected);
+}
+
 TEST(MemoryTagManagerAArch64MTETest, RemoveNonAddressBits) {
   MemoryTagManagerAArch64MTE manager;
 
Index: lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
===================================================================
--- lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
+++ lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
@@ -36,6 +36,10 @@
       lldb::addr_t addr, lldb::addr_t end_addr,
       const lldb_private::MemoryRegionInfos &memory_regions) const override;
 
+  llvm::Expected<std::vector<TagRange>> MakeTaggedRanges(
+      lldb::addr_t addr, lldb::addr_t end_addr,
+      lldb_private::MemoryRegionInfos memory_regions) const override;
+
   llvm::Expected<std::vector<lldb::addr_t>>
   UnpackTagsData(const std::vector<uint8_t> &tags,
                  size_t granules = 0) const override;
Index: lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
===================================================================
--- lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
+++ lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "MemoryTagManagerAArch64MTE.h"
+#include "llvm/Support/Error.h"
 
 using namespace lldb_private;
 
@@ -66,6 +67,15 @@
   return TagRange(new_start, new_len);
 }
 
+static llvm::Error make_invalid_range_err(lldb::addr_t addr,
+                                          lldb::addr_t end_addr) {
+  return llvm::createStringError(
+      llvm::inconvertibleErrorCode(),
+      "End address (0x%" PRIx64
+      ") must be greater than the start address (0x%" PRIx64 ")",
+      end_addr, addr);
+}
+
 llvm::Expected<MemoryTagManager::TagRange>
 MemoryTagManagerAArch64MTE::MakeTaggedRange(
     lldb::addr_t addr, lldb::addr_t end_addr,
@@ -74,13 +84,8 @@
   // We must remove tags here otherwise an address with a higher
   // tag value will always be > the other.
   ptrdiff_t len = 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);
-  }
+  if (len <= 0)
+    return make_invalid_range_err(addr, end_addr);
 
   // Region addresses will not have memory tags. So when searching
   // we must use an untagged address.
@@ -123,6 +128,73 @@
   return tag_range;
 }
 
+llvm::Expected<std::vector<MemoryTagManager::TagRange>>
+MemoryTagManagerAArch64MTE::MakeTaggedRanges(
+    lldb::addr_t addr, lldb::addr_t end_addr,
+    lldb_private::MemoryRegionInfos memory_regions) const {
+  // First check that the range is not inverted.
+  // We must remove tags here otherwise an address with a higher
+  // tag value will always be > the other.
+  ptrdiff_t len = AddressDiff(end_addr, addr);
+  if (len <= 0)
+    return make_invalid_range_err(addr, end_addr);
+
+  std::vector<MemoryTagManager::TagRange> tagged_ranges;
+  // No memory regions means no tagged memory at all
+  if (memory_regions.empty())
+    return tagged_ranges;
+
+  // For the logic to work regions must be in ascending order.
+  // We're going to assume that there are no overlapping regions
+  // and that each region is granule aligned already.
+  // Regions are most likely multiples of page size and granules
+  // are some smaller division of that.
+  std::sort(memory_regions.begin(), memory_regions.end(),
+            [](const MemoryRegionInfo &lhs, const MemoryRegionInfo &rhs) {
+              return lhs.GetRange().GetRangeBase() <
+                     rhs.GetRange().GetRangeBase();
+            });
+
+  // Region addresses will not have memory tags so when searching
+  // we must use an untagged address.
+  MemoryRegionInfo::RangeType range(RemoveNonAddressBits(addr), len);
+  range = ExpandToGranule(range);
+
+  MemoryRegionInfos::const_iterator region = memory_regions.begin();
+  MemoryRegionInfos::const_iterator end_region = memory_regions.end();
+
+  // While there are regions to check and the range has non zero length
+  for (; region != end_region && range.IsValid(); ++region) {
+    // If the region doesn't overlap the range at all, ignore it.
+    if (!region->GetRange().DoesIntersect(range))
+      continue;
+
+    // If it's tagged record this sub-range.
+    // (assuming that it's already granule aligned)
+    if (region->GetMemoryTagged()) {
+      // The region found may extend outside the requested range.
+      // For example the first region might start before the range.
+      // We must only add what covers the requested range.
+      lldb::addr_t start =
+          std::max(range.GetRangeBase(), region->GetRange().GetRangeBase());
+      lldb::addr_t end =
+          std::min(range.GetRangeEnd(), region->GetRange().GetRangeEnd());
+      tagged_ranges.push_back(MemoryTagManager::TagRange(start, end - start));
+    }
+
+    // Move the range up to start at the end of the region.
+    lldb::addr_t old_end = range.GetRangeEnd();
+    // This "slides" the range so it moves the end as well.
+    range.SetRangeBase(region->GetRange().GetRangeEnd());
+    // So we set the end back to the original end address after sliding it up.
+    range.SetRangeEnd(old_end);
+    // (if the above were to try to set end < begin the range will just be set
+    // to 0 size)
+  }
+
+  return tagged_ranges;
+}
+
 llvm::Expected<std::vector<lldb::addr_t>>
 MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags,
                                            size_t granules /*=0*/) const {
Index: lldb/include/lldb/Target/MemoryTagManager.h
===================================================================
--- lldb/include/lldb/Target/MemoryTagManager.h
+++ lldb/include/lldb/Target/MemoryTagManager.h
@@ -72,6 +72,20 @@
       lldb::addr_t addr, lldb::addr_t end_addr,
       const lldb_private::MemoryRegionInfos &memory_regions) const = 0;
 
+  // Given a range addr to end_addr, check that end_addr >= addr.
+  // If it is not, return an error saying so.
+  // Otherwise, granule align it and return a set of ranges representing
+  // subsections of the aligned range that have memory tagging enabled.
+  //
+  // Basically a sparse version of MakeTaggedRange. Use this when you
+  // want to know which parts of a larger range have memory tagging.
+  //
+  // Tags in the input addresses are ignored and not present
+  // in the returned range.
+  virtual llvm::Expected<std::vector<TagRange>>
+  MakeTaggedRanges(lldb::addr_t addr, lldb::addr_t end_addr,
+                   lldb_private::MemoryRegionInfos memory_regions) const = 0;
+
   // Return the type value to use in GDB protocol qMemTags packets to read
   // allocation tags. This is named "Allocation" specifically because the spec
   // allows for logical tags to be read the same way, though we do not use that.
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to