DavidSpickett updated this revision to Diff 446099.
DavidSpickett added a comment.

rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129487/new/

https://reviews.llvm.org/D129487

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
@@ -80,6 +80,72 @@
   ASSERT_THAT(expected, testing::ContainerEq(*packed));
 }
 
+TEST(MemoryTagManagerAArch64MTETest, UnpackTagsFromCoreFileSegment) {
+  MemoryTagManagerAArch64MTE manager;
+  // This is our fake segment data where tags are compressed as 2 4 bit tags
+  // per byte.
+  std::vector<uint8_t> tags_data;
+  MemoryTagManager::CoreReaderFn reader =
+      [&tags_data](lldb::offset_t offset, size_t length, void *dst) {
+        std::memcpy(dst, tags_data.data() + offset, length);
+        return length;
+      };
+
+  // Zero length is ok.
+  std::vector<lldb::addr_t> tags =
+      manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 0, 0);
+  ASSERT_EQ(tags.size(), (size_t)0);
+
+  // In the simplest case we read 2 tags which are in the same byte.
+  tags_data.push_back(0x21);
+  // The least significant bits are the first tag in memory.
+  std::vector<lldb::addr_t> expected{1, 2};
+  tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 0, 32);
+  ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+  // If we read just one then it will have to trim off the second one.
+  expected = std::vector<lldb::addr_t>{1};
+  tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 0, 16);
+  ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+  // If we read the second tag only then the first one must be trimmed.
+  expected = std::vector<lldb::addr_t>{2};
+  tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 16, 16);
+  ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+  // This trimming logic applies if you read a larger set of tags.
+  tags_data = std::vector<uint8_t>{0x21, 0x43, 0x65, 0x87};
+
+  // Trailing tag should be trimmed.
+  expected = std::vector<lldb::addr_t>{1, 2, 3};
+  tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 0, 48);
+  ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+  // Leading tag should be trimmed.
+  expected = std::vector<lldb::addr_t>{2, 3, 4};
+  tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 16, 48);
+  ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+  // Leading and trailing trimmmed.
+  expected = std::vector<lldb::addr_t>{2, 3, 4, 5};
+  tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 16, 64);
+  ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+  // The address given is an offset into the whole file so the address requested
+  // from the reader should be beyond that.
+  tags_data = std::vector<uint8_t>{0xFF, 0xFF, 0x21, 0x43, 0x65, 0x87};
+  expected = std::vector<lldb::addr_t>{1, 2};
+  tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 2, 0, 32);
+  ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+  // addr is a virtual address that we expect to be >= the tag segment's
+  // starting virtual address. So again an offset must be made from the
+  // difference.
+  expected = std::vector<lldb::addr_t>{3, 4};
+  tags = manager.UnpackTagsFromCoreFileSegment(reader, 32, 2, 64, 32);
+  ASSERT_THAT(expected, testing::ContainerEq(tags));
+}
+
 TEST(MemoryTagManagerAArch64MTETest, GetLogicalTag) {
   MemoryTagManagerAArch64MTE manager;
 
Index: lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
===================================================================
--- lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
+++ lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
@@ -44,6 +44,12 @@
   UnpackTagsData(const std::vector<uint8_t> &tags,
                  size_t granules = 0) const override;
 
+  std::vector<lldb::addr_t>
+  UnpackTagsFromCoreFileSegment(CoreReaderFn reader,
+                                lldb::addr_t tag_segment_virtual_address,
+                                lldb::addr_t tag_segment_data_address,
+                                lldb::addr_t addr, size_t len) const override;
+
   llvm::Expected<std::vector<uint8_t>>
   PackTags(const std::vector<lldb::addr_t> &tags) const override;
 
Index: lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
===================================================================
--- lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
+++ lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
@@ -247,6 +247,70 @@
   return unpacked;
 }
 
+std::vector<lldb::addr_t>
+MemoryTagManagerAArch64MTE::UnpackTagsFromCoreFileSegment(
+    CoreReaderFn reader, lldb::addr_t tag_segment_virtual_address,
+    lldb::addr_t tag_segment_data_address, lldb::addr_t addr,
+    size_t len) const {
+  // We can assume by now that addr and len have been granule aligned by a tag
+  // manager. However because we have 2 tags per byte we need to round the range
+  // up again to align to 2 granule boundaries.
+  const size_t granule = GetGranuleSize();
+  const size_t two_granules = granule * 2;
+  lldb::addr_t aligned_addr = addr;
+  size_t aligned_len = len;
+
+  // First align the start address down.
+  if (aligned_addr % two_granules) {
+    assert(aligned_addr % two_granules == granule);
+    aligned_addr -= granule;
+    aligned_len += granule;
+  }
+
+  // Then align the length up.
+  bool aligned_length_up = false;
+  if (aligned_len % two_granules) {
+    assert(aligned_len % two_granules == granule);
+    aligned_len += granule;
+    aligned_length_up = true;
+  }
+
+  // ProcessElfCore should have validated this when it found the segment.
+  assert(aligned_addr >= tag_segment_virtual_address);
+
+  // By now we know that aligned_addr is aligned to a 2 granule boundary.
+  const size_t offset_granules =
+      (aligned_addr - tag_segment_virtual_address) / granule;
+  // 2 tags per byte.
+  const size_t file_offset_in_bytes = offset_granules / 2;
+
+  // By now we know that aligned_len is at least 2 granules.
+  const size_t tag_bytes_to_read = aligned_len / granule / 2;
+  std::vector<uint8_t> tag_data(tag_bytes_to_read);
+  const size_t bytes_copied =
+      reader(tag_segment_data_address + file_offset_in_bytes, tag_bytes_to_read,
+             tag_data.data());
+  assert(bytes_copied == tag_bytes_to_read);
+
+  std::vector<lldb::addr_t> tags;
+  tags.reserve(2 * tag_data.size());
+  // No need to check the range of the tag value here as each occupies only 4
+  // bits.
+  for (auto tag_byte : tag_data) {
+    tags.push_back(tag_byte & 0xf);
+    tags.push_back(tag_byte >> 4);
+  }
+
+  // If we aligned the address down, don't return the extra first tag.
+  if (addr != aligned_addr)
+    tags.erase(tags.begin());
+  // If we aligned the length up, don't return the extra last tag.
+  if (aligned_length_up)
+    tags.pop_back();
+
+  return tags;
+}
+
 llvm::Expected<std::vector<uint8_t>> MemoryTagManagerAArch64MTE::PackTags(
     const std::vector<lldb::addr_t> &tags) const {
   std::vector<uint8_t> packed;
Index: lldb/include/lldb/Target/MemoryTagManager.h
===================================================================
--- lldb/include/lldb/Target/MemoryTagManager.h
+++ lldb/include/lldb/Target/MemoryTagManager.h
@@ -113,6 +113,21 @@
   UnpackTagsData(const std::vector<uint8_t> &tags,
                  size_t granules = 0) const = 0;
 
+  // Unpack tags from a corefile segment containing compressed tags
+  // (compression that may be different from the one used for GDB transport).
+  //
+  // This method asumes that:
+  // * addr and len have been granule aligned by a tag manager
+  // * addr >= tag_segment_virtual_address
+  //
+  // 'reader' will always be a wrapper around a CoreFile in real use
+  // but allows testing without having to mock a CoreFile.
+  typedef std::function<size_t(lldb::offset_t, size_t, void *)> CoreReaderFn;
+  std::vector<lldb::addr_t> virtual UnpackTagsFromCoreFileSegment(
+      CoreReaderFn reader, lldb::addr_t tag_segment_virtual_address,
+      lldb::addr_t tag_segment_data_address, lldb::addr_t addr,
+      size_t len) 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
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to