labath created this revision.
labath added reviewers: amccarth, jhenderson, clayborg.
Herald added a project: LLVM.

This patch adds the definitions of the constants and structures
necessary to interpret the MemoryInfoList minidump stream, as well as
the object::MinidumpFile interface to access the stream.

While the code is fairly simple, there is one important deviation from
the other minidump streams, which is worth calling out explicitly.
Unlike other "List" streams, the size of the records inside
MemoryInfoList stream is not known statically. Instead it is described
in the stream header. This makes it impossible to return
ArrayRef<MemoryInfo> from the accessor method, as it is done with other
streams. Instead, I create an iterator class, which can be parameterized
by the runtime size of the structure, and return
iterator_range<iterator> instead.


Repository:
  rL LLVM

https://reviews.llvm.org/D68210

Files:
  include/llvm/BinaryFormat/Minidump.h
  include/llvm/BinaryFormat/MinidumpConstants.def
  include/llvm/Object/Minidump.h
  lib/Object/Minidump.cpp
  unittests/Object/MinidumpTest.cpp

Index: unittests/Object/MinidumpTest.cpp
===================================================================
--- unittests/Object/MinidumpTest.cpp
+++ unittests/Object/MinidumpTest.cpp
@@ -511,3 +511,180 @@
     EXPECT_EQ(0x00090807u, MD.Memory.RVA);
   }
 }
+
+TEST(MinidumpFile, getMemoryInfoList) {
+  std::vector<uint8_t> OneEntry{
+      // Header
+      'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
+      1, 0, 0, 0,                           // NumberOfStreams,
+      32, 0, 0, 0,                          // StreamDirectoryRVA
+      0, 1, 2, 3, 4, 5, 6, 7,               // Checksum, TimeDateStamp
+      0, 0, 0, 0, 0, 0, 0, 0,               // Flags
+                                            // Stream Directory
+      16, 0, 0, 0, 64, 0, 0, 0,             // Type, DataSize,
+      44, 0, 0, 0,                          // RVA
+      // MemoryInfoListHeader
+      16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
+      1, 0, 0, 0, 0, 0, 0, 0,   // NumberOfEntries
+      // MemoryInfo
+      0, 1, 2, 3, 4, 5, 6, 7,   // BaseAddress
+      8, 9, 0, 1, 2, 3, 4, 5,   // AllocationBase
+      16, 0, 0, 0, 6, 7, 8, 9,  // AllocationProtect, Reserved0
+      0, 1, 2, 3, 4, 5, 6, 7,   // RegionSize
+      0, 16, 0, 0, 32, 0, 0, 0, // State, Protect
+      0, 0, 2, 0, 8, 9, 0, 1,   // Type, Reserved1
+  };
+
+  // Same as before, but the list header is larger.
+  std::vector<uint8_t> BiggerHeader{
+      // Header
+      'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
+      1, 0, 0, 0,                           // NumberOfStreams,
+      32, 0, 0, 0,                          // StreamDirectoryRVA
+      0, 1, 2, 3, 4, 5, 6, 7,               // Checksum, TimeDateStamp
+      0, 0, 0, 0, 0, 0, 0, 0,               // Flags
+                                            // Stream Directory
+      16, 0, 0, 0, 68, 0, 0, 0,             // Type, DataSize,
+      44, 0, 0, 0,                          // RVA
+      // MemoryInfoListHeader
+      20, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
+      1, 0, 0, 0, 0, 0, 0, 0,   // NumberOfEntries
+      0, 0, 0, 0,               // ???
+      // MemoryInfo
+      0, 1, 2, 3, 4, 5, 6, 7,   // BaseAddress
+      8, 9, 0, 1, 2, 3, 4, 5,   // AllocationBase
+      16, 0, 0, 0, 6, 7, 8, 9,  // AllocationProtect, Reserved0
+      0, 1, 2, 3, 4, 5, 6, 7,   // RegionSize
+      0, 16, 0, 0, 32, 0, 0, 0, // State, Protect
+      0, 0, 2, 0, 8, 9, 0, 1,   // Type, Reserved1
+  };
+
+  // Same as before, but the entry is larger.
+  std::vector<uint8_t> BiggerEntry{
+      // Header
+      'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
+      1, 0, 0, 0,                           // NumberOfStreams,
+      32, 0, 0, 0,                          // StreamDirectoryRVA
+      0, 1, 2, 3, 4, 5, 6, 7,               // Checksum, TimeDateStamp
+      0, 0, 0, 0, 0, 0, 0, 0,               // Flags
+                                            // Stream Directory
+      16, 0, 0, 0, 68, 0, 0, 0,             // Type, DataSize,
+      44, 0, 0, 0,                          // RVA
+      // MemoryInfoListHeader
+      16, 0, 0, 0, 52, 0, 0, 0, // SizeOfHeader, SizeOfEntry
+      1, 0, 0, 0, 0, 0, 0, 0,   // NumberOfEntries
+      // MemoryInfo
+      0, 1, 2, 3, 4, 5, 6, 7,   // BaseAddress
+      8, 9, 0, 1, 2, 3, 4, 5,   // AllocationBase
+      16, 0, 0, 0, 6, 7, 8, 9,  // AllocationProtect, Reserved0
+      0, 1, 2, 3, 4, 5, 6, 7,   // RegionSize
+      0, 16, 0, 0, 32, 0, 0, 0, // State, Protect
+      0, 0, 2, 0, 8, 9, 0, 1,   // Type, Reserved1
+      0, 0, 0, 0,               // ???
+  };
+
+  for (ArrayRef<uint8_t> Data : {OneEntry, BiggerHeader, BiggerEntry}) {
+    auto ExpectedFile = create(Data);
+    ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
+    const MinidumpFile &File = **ExpectedFile;
+    auto ExpectedInfo = File.getMemoryInfoList();
+    ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded());
+    ASSERT_EQ(1u, std::distance(ExpectedInfo->begin(), ExpectedInfo->end()));
+    const MemoryInfo &Info = *ExpectedInfo.get().begin();
+    EXPECT_EQ(0x0706050403020100u, Info.BaseAddress);
+    EXPECT_EQ(0x0504030201000908u, Info.AllocationBase);
+    EXPECT_EQ(MemoryProtection::Execute, Info.AllocationProtect);
+    EXPECT_EQ(0x09080706u, Info.Reserved0);
+    EXPECT_EQ(0x0706050403020100u, Info.RegionSize);
+    EXPECT_EQ(MemoryState::Commit, Info.State);
+    EXPECT_EQ(MemoryProtection::ExecuteRead, Info.Protect);
+    EXPECT_EQ(MemoryType::Private, Info.Type);
+    EXPECT_EQ(0x01000908u, Info.Reserved1);
+  }
+
+  // Header does not fit into the stream.
+  std::vector<uint8_t> HeaderTooBig{
+      // Header
+      'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
+      1, 0, 0, 0,                           // NumberOfStreams,
+      32, 0, 0, 0,                          // StreamDirectoryRVA
+      0, 1, 2, 3, 4, 5, 6, 7,               // Checksum, TimeDateStamp
+      0, 0, 0, 0, 0, 0, 0, 0,               // Flags
+                                            // Stream Directory
+      16, 0, 0, 0, 12, 0, 0, 0,             // Type, DataSize,
+      44, 0, 0, 0,                          // RVA
+      // MemoryInfoListHeader
+      16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
+      1, 0, 0, 0,               // ???
+  };
+  EXPECT_THAT_EXPECTED(cantFail(create(HeaderTooBig))->getMemoryInfoList(),
+                       Failed<BinaryError>());
+
+  std::vector<uint8_t> EntryTooBig{
+      // Header
+      'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
+      1, 0, 0, 0,                           // NumberOfStreams,
+      32, 0, 0, 0,                          // StreamDirectoryRVA
+      0, 1, 2, 3, 4, 5, 6, 7,               // Checksum, TimeDateStamp
+      0, 0, 0, 0, 0, 0, 0, 0,               // Flags
+                                            // Stream Directory
+      16, 0, 0, 0, 64, 0, 0, 0,             // Type, DataSize,
+      44, 0, 0, 0,                          // RVA
+      // MemoryInfoListHeader
+      16, 0, 0, 0, 52, 0, 0, 0, // SizeOfHeader, SizeOfEntry
+      1, 0, 0, 0, 0, 0, 0, 0,   // NumberOfEntries
+      // MemoryInfo
+      0, 1, 2, 3, 4, 5, 6, 7,   // BaseAddress
+      8, 9, 0, 1, 2, 3, 4, 5,   // AllocationBase
+      16, 0, 0, 0, 6, 7, 8, 9,  // AllocationProtect, Reserved0
+      0, 1, 2, 3, 4, 5, 6, 7,   // RegionSize
+      0, 16, 0, 0, 32, 0, 0, 0, // State, Protect
+      0, 0, 2, 0, 8, 9, 0, 1,   // Type, Reserved1
+  };
+  EXPECT_THAT_EXPECTED(cantFail(create(EntryTooBig))->getMemoryInfoList(),
+                       Failed<BinaryError>());
+
+  std::vector<uint8_t> ThreeEntries{
+      // Header
+      'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
+      1, 0, 0, 0,                           // NumberOfStreams,
+      32, 0, 0, 0,                          // StreamDirectoryRVA
+      0, 1, 2, 3, 4, 5, 6, 7,               // Checksum, TimeDateStamp
+      0, 0, 0, 0, 0, 0, 0, 0,               // Flags
+                                            // Stream Directory
+      16, 0, 0, 0, 160, 0, 0, 0,            // Type, DataSize,
+      44, 0, 0, 0,                          // RVA
+      // MemoryInfoListHeader
+      16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
+      3, 0, 0, 0, 0, 0, 0, 0,   // NumberOfEntries
+      // MemoryInfo
+      0, 1, 2, 3, 0, 0, 0, 0, // BaseAddress
+      0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase
+      0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0
+      0, 0, 0, 0, 0, 0, 0, 0, // RegionSize
+      0, 0, 0, 0, 0, 0, 0, 0, // State, Protect
+      0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1
+      0, 0, 4, 5, 6, 7, 0, 0, // BaseAddress
+      0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase
+      0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0
+      0, 0, 0, 0, 0, 0, 0, 0, // RegionSize
+      0, 0, 0, 0, 0, 0, 0, 0, // State, Protect
+      0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1
+      0, 0, 0, 8, 9, 0, 1, 0, // BaseAddress
+      0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase
+      0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0
+      0, 0, 0, 0, 0, 0, 0, 0, // RegionSize
+      0, 0, 0, 0, 0, 0, 0, 0, // State, Protect
+      0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1
+  };
+  auto ExpectedFile = create(ThreeEntries);
+  ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
+  auto ExpectedInfo = ExpectedFile.get()->getMemoryInfoList();
+  ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded());
+  EXPECT_THAT(to_vector<3>(map_range(*ExpectedInfo,
+                                     [](const MemoryInfo &Info) -> uint64_t {
+                                       return Info.BaseAddress;
+                                     })),
+              testing::ElementsAre(0x0000000003020100u, 0x0000070605040000u,
+                                   0x0001000908000000u));
+}
Index: lib/Object/Minidump.cpp
===================================================================
--- lib/Object/Minidump.cpp
+++ lib/Object/Minidump.cpp
@@ -53,6 +53,24 @@
   return Result;
 }
 
+Expected<iterator_range<MinidumpFile::MemoryInfoIterator>>
+MinidumpFile::getMemoryInfoList() const {
+  auto OptionalStream = getRawStream(StreamType::MemoryInfoList);
+  if (!OptionalStream)
+    return createError("No such stream");
+  auto ExpectedHeader =
+      getDataSliceAs<minidump::MemoryInfoListHeader>(*OptionalStream, 0, 1);
+  if (!ExpectedHeader)
+    return ExpectedHeader.takeError();
+  const minidump::MemoryInfoListHeader &H = ExpectedHeader.get()[0];
+  auto ExpectedData = getDataSlice(*OptionalStream, H.SizeOfHeader,
+                                   H.SizeOfEntry * H.NumberOfEntries);
+  if (!ExpectedData)
+    return ExpectedData.takeError();
+  return make_range(MemoryInfoIterator(*ExpectedData, H.SizeOfEntry),
+                    MemoryInfoIterator({}, H.SizeOfEntry));
+}
+
 template <typename T>
 Expected<ArrayRef<T>> MinidumpFile::getListStream(StreamType Stream) const {
   auto OptionalStream = getRawStream(Stream);
Index: include/llvm/Object/Minidump.h
===================================================================
--- include/llvm/Object/Minidump.h
+++ include/llvm/Object/Minidump.h
@@ -11,6 +11,7 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/iterator.h"
 #include "llvm/BinaryFormat/Minidump.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Support/Error.h"
@@ -80,16 +81,56 @@
     return getListStream<minidump::Thread>(minidump::StreamType::ThreadList);
   }
 
-  /// Returns the list of memory ranges embedded in the MemoryList stream. An
-  /// error is returned if the file does not contain this stream, or if the
-  /// stream is not large enough to contain the number of memory descriptors
-  /// declared in the stream header. The consistency of the MemoryDescriptor
-  /// entries themselves is not checked in any way.
+  /// Returns the list of descriptors embedded in the MemoryList stream. The
+  /// descriptors provide the content of interesting regions of memory at the
+  /// time the minidump was taken. An error is returned if the file does not
+  /// contain this stream, or if the stream is not large enough to contain the
+  /// number of memory descriptors declared in the stream header. The
+  /// consistency of the MemoryDescriptor entries themselves is not checked in
+  /// any way.
   Expected<ArrayRef<minidump::MemoryDescriptor>> getMemoryList() const {
     return getListStream<minidump::MemoryDescriptor>(
         minidump::StreamType::MemoryList);
   }
 
+  class MemoryInfoIterator
+      : public iterator_facade_base<MemoryInfoIterator,
+                                    std::forward_iterator_tag,
+                                    minidump::MemoryInfo> {
+  public:
+    MemoryInfoIterator(ArrayRef<uint8_t> Storage, size_t Stride)
+        : Storage(Storage), Stride(Stride) {
+      assert(Storage.size() % Stride == 0);
+    }
+
+    bool operator==(const MemoryInfoIterator &R) const {
+      return Storage.size() == R.Storage.size();
+    }
+
+    const minidump::MemoryInfo &operator*() const  {
+      assert(Storage.size() >= sizeof(minidump::MemoryInfo));
+      return *reinterpret_cast<const minidump::MemoryInfo *>(Storage.data());
+    }
+
+    MemoryInfoIterator &operator++() {
+      Storage = Storage.drop_front(Stride);
+      return *this;
+    }
+
+  private:
+    ArrayRef<uint8_t> Storage;
+    size_t Stride;
+  };
+
+  /// Returns the list of descriptors embedded in the MemoryInfoList stream. The
+  /// descriptors provide properties (e.g. permissions) of interesting regions
+  /// of memory at the time the minidump was taken. An error is returned if the
+  /// file does not contain this stream, or if the stream is not large enough to
+  /// contain the number of memory descriptors declared in the stream header.
+  /// The consistency of the MemoryInfoList entries themselves is not checked
+  /// in any way.
+  Expected<iterator_range<MemoryInfoIterator>> getMemoryInfoList() const;
+
 private:
   static Error createError(StringRef Str) {
     return make_error<GenericBinaryError>(Str, object_error::parse_failed);
Index: include/llvm/BinaryFormat/MinidumpConstants.def
===================================================================
--- include/llvm/BinaryFormat/MinidumpConstants.def
+++ include/llvm/BinaryFormat/MinidumpConstants.def
@@ -6,8 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-#if !(defined HANDLE_MDMP_STREAM_TYPE || defined HANDLE_MDMP_ARCH ||           \
-      defined HANDLE_MDMP_PLATFORM)
+#if !(defined(HANDLE_MDMP_STREAM_TYPE) || defined(HANDLE_MDMP_ARCH) ||         \
+      defined(HANDLE_MDMP_PLATFORM) || defined(HANDLE_MDMP_PROTECT) ||         \
+      defined(HANDLE_MDMP_MEMSTATE) || defined(HANDLE_MDMP_MEMTYPE))
 #error "Missing HANDLE_MDMP definition"
 #endif
 
@@ -23,6 +24,18 @@
 #define HANDLE_MDMP_PLATFORM(CODE, NAME)
 #endif
 
+#ifndef HANDLE_MDMP_PROTECT
+#define HANDLE_MDMP_PROTECT(CODE, NAME, NATIVENAME)
+#endif
+
+#ifndef HANDLE_MDMP_MEMSTATE
+#define HANDLE_MDMP_MEMSTATE(CODE, NAME, NATIVENAME)
+#endif
+
+#ifndef HANDLE_MDMP_MEMTYPE
+#define HANDLE_MDMP_MEMTYPE(CODE, NAME, NATIVENAME)
+#endif
+
 HANDLE_MDMP_STREAM_TYPE(0x0003, ThreadList)
 HANDLE_MDMP_STREAM_TYPE(0x0004, ModuleList)
 HANDLE_MDMP_STREAM_TYPE(0x0005, MemoryList)
@@ -102,6 +115,30 @@
 HANDLE_MDMP_PLATFORM(0x8204, PS3) // PS3
 HANDLE_MDMP_PLATFORM(0x8205, NaCl) // Native Client (NaCl)
 
+HANDLE_MDMP_PROTECT(0x01, NoAccess, PAGE_NO_ACCESS)
+HANDLE_MDMP_PROTECT(0x02, ReadOnly, PAGE_READ_ONLY)
+HANDLE_MDMP_PROTECT(0x04, ReadWrite, PAGE_READ_WRITE)
+HANDLE_MDMP_PROTECT(0x08, WriteCopy, PAGE_WRITE_COPY)
+HANDLE_MDMP_PROTECT(0x10, Execute, PAGE_EXECUTE)
+HANDLE_MDMP_PROTECT(0x20, ExecuteRead, PAGE_EXECUTE_READ)
+HANDLE_MDMP_PROTECT(0x40, ExecuteReadWrite, PAGE_EXECUTE_READ_WRITE)
+HANDLE_MDMP_PROTECT(0x80, ExeciteWriteCopy, PAGE_EXECUTE_WRITE_COPY)
+HANDLE_MDMP_PROTECT(0x100, Guard, PAGE_GUARD)
+HANDLE_MDMP_PROTECT(0x200, NoCache, PAGE_NOCACHE)
+HANDLE_MDMP_PROTECT(0x400, WriteCombine, PAGE_WRITECOMBINE)
+HANDLE_MDMP_PROTECT(0x40000000, TargetsInvalid, PAGE_TARGETS_INVALID)
+
+HANDLE_MDMP_MEMSTATE(0x01000, Commit, MEM_COMMIT)
+HANDLE_MDMP_MEMSTATE(0x02000, Reserve, MEM_RESERVE)
+HANDLE_MDMP_MEMSTATE(0x10000, Free, MEM_FREE)
+
+HANDLE_MDMP_MEMTYPE(0x0020000, Private, MEM_PRIVATE)
+HANDLE_MDMP_MEMTYPE(0x0040000, Mapped, MEM_MAPPED)
+HANDLE_MDMP_MEMTYPE(0x1000000, Image, MEM_IMAGE)
+
 #undef HANDLE_MDMP_STREAM_TYPE
 #undef HANDLE_MDMP_ARCH
 #undef HANDLE_MDMP_PLATFORM
+#undef HANDLE_MDMP_PROTECT
+#undef HANDLE_MDMP_MEMSTATE
+#undef HANDLE_MDMP_MEMTYPE
Index: include/llvm/BinaryFormat/Minidump.h
===================================================================
--- include/llvm/BinaryFormat/Minidump.h
+++ include/llvm/BinaryFormat/Minidump.h
@@ -18,6 +18,7 @@
 #ifndef LLVM_BINARYFORMAT_MINIDUMP_H
 #define LLVM_BINARYFORMAT_MINIDUMP_H
 
+#include "llvm/ADT/BitmaskEnum.h"
 #include "llvm/ADT/DenseMapInfo.h"
 #include "llvm/Support/Endian.h"
 
@@ -67,6 +68,42 @@
 };
 static_assert(sizeof(MemoryDescriptor) == 16, "");
 
+struct MemoryInfoListHeader {
+  support::ulittle32_t SizeOfHeader;
+  support::ulittle32_t SizeOfEntry;
+  support::ulittle64_t NumberOfEntries;
+};
+static_assert(sizeof(MemoryInfoListHeader) == 16, "");
+
+enum class MemoryProtection : uint32_t {
+#define HANDLE_MDMP_PROTECT(CODE, NAME, NATIVENAME) NAME = CODE,
+#include "llvm/BinaryFormat/MinidumpConstants.def"
+  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue = */ 0xffffffffu),
+};
+
+enum class MemoryState : uint32_t {
+#define HANDLE_MDMP_MEMSTATE(CODE, NAME, NATIVENAME) NAME = CODE,
+#include "llvm/BinaryFormat/MinidumpConstants.def"
+};
+
+enum class MemoryType : uint32_t {
+#define HANDLE_MDMP_MEMTYPE(CODE, NAME, NATIVENAME) NAME = CODE,
+#include "llvm/BinaryFormat/MinidumpConstants.def"
+};
+
+struct MemoryInfo {
+  support::ulittle64_t BaseAddress;
+  support::ulittle64_t AllocationBase;
+  support::little_t<MemoryProtection> AllocationProtect;
+  support::ulittle32_t Reserved0;
+  support::ulittle64_t RegionSize;
+  support::little_t<MemoryState> State;
+  support::little_t<MemoryProtection> Protect;
+  support::little_t<MemoryType> Type;
+  support::ulittle32_t Reserved1;
+};
+static_assert(sizeof(MemoryInfo) == 48, "");
+
 /// Specifies the location and type of a single stream in the minidump file. The
 /// minidump stream directory is an array of entries of this type, with its size
 /// given by Header.NumberOfStreams.
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to