[Lldb-commits] [lldb] r284593 - Minidump plugin: functions parsing memory structures and filtering module list

Dimitar Vlahovski via lldb-commits lldb-commits at lists.llvm.org
Wed Oct 19 07:14:18 PDT 2016


Author: dvlahovski
Date: Wed Oct 19 09:14:18 2016
New Revision: 284593

URL: http://llvm.org/viewvc/llvm-project?rev=284593&view=rev
Log:
Minidump plugin: functions parsing memory structures and filtering module list

Summary:
Now the Minidump parser can parse the:
1) MemoryInfoList - containing region info about memory ranges (readable,
writable, executable)
2) Memory64List - this is the stuct used when the Minidump is a
full-memory one.
3) Adding filtering of the module list (shared libraries list) - there
can be mutliple records in the module list under the same name but with
different load address (e.g. when the binary has non contigious
sections). FilterModuleList eliminates the duplicated modules, leaving
the one with the lowest load addr.

Added unit tests for everything.

Reviewers: labath, zturner

Subscribers: beanz, mgorny, modocache, lldb-commits, amccarth

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


Added:
    lldb/trunk/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp
      - copied unchanged from r284590, lldb/trunk/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz_wow64.dmp
    lldb/trunk/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp   (with props)
Modified:
    lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp
    lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h
    lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.cpp
    lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h
    lldb/trunk/unittests/Process/minidump/CMakeLists.txt
    lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp

Modified: lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp?rev=284593&r1=284592&r2=284593&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp (original)
+++ lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp Wed Oct 19 09:14:18 2016
@@ -11,8 +11,11 @@
 #include "MinidumpParser.h"
 
 // Other libraries and framework includes
+#include "lldb/Target/MemoryRegionInfo.h"
+
 // C includes
 // C++ includes
+#include <map>
 
 using namespace lldb_private;
 using namespace minidump;
@@ -100,6 +103,14 @@ llvm::ArrayRef<MinidumpThread> MinidumpP
   return MinidumpThread::ParseThreadList(data);
 }
 
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContext(const MinidumpThread &td) {
+  if (td.thread_context.rva + td.thread_context.data_size > GetData().size())
+    return llvm::None;
+
+  return GetData().slice(td.thread_context.rva, td.thread_context.data_size);
+}
+
 const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() {
   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo);
 
@@ -216,6 +227,42 @@ llvm::ArrayRef<MinidumpModule> MinidumpP
   return MinidumpModule::ParseModuleList(data);
 }
 
+std::vector<const MinidumpModule *> MinidumpParser::GetFilteredModuleList() {
+  llvm::ArrayRef<MinidumpModule> modules = GetModuleList();
+  // mapping module_name to pair(load_address, pointer to module struct in
+  // memory)
+  llvm::StringMap<std::pair<uint64_t, const MinidumpModule *>> lowest_addr;
+
+  std::vector<const MinidumpModule *> filtered_modules;
+
+  llvm::Optional<std::string> name;
+  std::string module_name;
+
+  for (const auto &module : modules) {
+    name = GetMinidumpString(module.module_name_rva);
+
+    if (!name)
+      continue;
+
+    module_name = name.getValue();
+
+    auto iter = lowest_addr.end();
+    bool exists;
+    std::tie(iter, exists) = lowest_addr.try_emplace(
+        module_name, std::make_pair(module.base_of_image, &module));
+
+    if (exists && module.base_of_image < iter->second.first)
+      iter->second = std::make_pair(module.base_of_image, &module);
+  }
+
+  filtered_modules.reserve(lowest_addr.size());
+  for (const auto &module : lowest_addr) {
+    filtered_modules.push_back(module.second.second);
+  }
+
+  return filtered_modules;
+}
+
 const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception);
 
@@ -224,3 +271,156 @@ const MinidumpExceptionStream *MinidumpP
 
   return MinidumpExceptionStream::Parse(data);
 }
+
+llvm::Optional<minidump::Range>
+MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryList);
+  llvm::ArrayRef<uint8_t> data64 = GetStream(MinidumpStreamType::Memory64List);
+
+  if (data.empty() && data64.empty())
+    return llvm::None;
+
+  if (!data.empty()) {
+    llvm::ArrayRef<MinidumpMemoryDescriptor> memory_list =
+        MinidumpMemoryDescriptor::ParseMemoryList(data);
+
+    if (memory_list.empty())
+      return llvm::None;
+
+    for (const auto &memory_desc : memory_list) {
+      const MinidumpLocationDescriptor &loc_desc = memory_desc.memory;
+      const lldb::addr_t range_start = memory_desc.start_of_memory_range;
+      const size_t range_size = loc_desc.data_size;
+
+      if (loc_desc.rva + loc_desc.data_size > GetData().size())
+        return llvm::None;
+
+      if (range_start <= addr && addr < range_start + range_size) {
+        return minidump::Range(range_start,
+                               GetData().slice(loc_desc.rva, range_size));
+      }
+    }
+  }
+
+  // Some Minidumps have a Memory64ListStream that captures all the heap
+  // memory (full-memory Minidumps).  We can't exactly use the same loop as
+  // above, because the Minidump uses slightly different data structures to
+  // describe those
+
+  if (!data64.empty()) {
+    llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
+    uint64_t base_rva;
+    std::tie(memory64_list, base_rva) =
+        MinidumpMemoryDescriptor64::ParseMemory64List(data64);
+
+    if (memory64_list.empty())
+      return llvm::None;
+
+    for (const auto &memory_desc64 : memory64_list) {
+      const lldb::addr_t range_start = memory_desc64.start_of_memory_range;
+      const size_t range_size = memory_desc64.data_size;
+
+      if (base_rva + range_size > GetData().size())
+        return llvm::None;
+
+      if (range_start <= addr && addr < range_start + range_size) {
+        return minidump::Range(range_start,
+                               GetData().slice(base_rva, range_size));
+      }
+      base_rva += range_size;
+    }
+  }
+
+  return llvm::None;
+}
+
+llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
+                                                  size_t size) {
+  // I don't have a sense of how frequently this is called or how many memory
+  // ranges a Minidump typically has, so I'm not sure if searching for the
+  // appropriate range linearly each time is stupid.  Perhaps we should build
+  // an index for faster lookups.
+  llvm::Optional<minidump::Range> range = FindMemoryRange(addr);
+  if (!range)
+    return {};
+
+  // There's at least some overlap between the beginning of the desired range
+  // (addr) and the current range.  Figure out where the overlap begins and
+  // how much overlap there is.
+
+  const size_t offset = addr - range->start;
+
+  if (addr < range->start || offset >= range->range_ref.size())
+    return {};
+
+  const size_t overlap = std::min(size, range->range_ref.size() - offset);
+  return range->range_ref.slice(offset, overlap);
+}
+
+llvm::Optional<MemoryRegionInfo>
+MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
+  MemoryRegionInfo info;
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryInfoList);
+  if (data.empty())
+    return llvm::None;
+
+  std::vector<const MinidumpMemoryInfo *> mem_info_list =
+      MinidumpMemoryInfo::ParseMemoryInfoList(data);
+  if (mem_info_list.empty())
+    return llvm::None;
+
+  const auto yes = MemoryRegionInfo::eYes;
+  const auto no = MemoryRegionInfo::eNo;
+
+  const MinidumpMemoryInfo *next_entry = nullptr;
+  for (const auto &entry : mem_info_list) {
+    const auto head = entry->base_address;
+    const auto tail = head + entry->region_size;
+
+    if (head <= load_addr && load_addr < tail) {
+      info.GetRange().SetRangeBase(
+          (entry->state != uint32_t(MinidumpMemoryInfoState::MemFree))
+              ? head
+              : load_addr);
+      info.GetRange().SetRangeEnd(tail);
+
+      const uint32_t PageNoAccess =
+          static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageNoAccess);
+      info.SetReadable((entry->protect & PageNoAccess) == 0 ? yes : no);
+
+      const uint32_t PageWritable =
+          static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageWritable);
+      info.SetWritable((entry->protect & PageWritable) != 0 ? yes : no);
+
+      const uint32_t PageExecutable = static_cast<uint32_t>(
+          MinidumpMemoryProtectionContants::PageExecutable);
+      info.SetExecutable((entry->protect & PageExecutable) != 0 ? yes : no);
+
+      const uint32_t MemFree =
+          static_cast<uint32_t>(MinidumpMemoryInfoState::MemFree);
+      info.SetMapped((entry->state != MemFree) ? yes : no);
+
+      return info;
+    } else if (head > load_addr &&
+               (next_entry == nullptr || head < next_entry->base_address)) {
+      // In case there is no region containing load_addr keep track of the
+      // nearest region after load_addr so we can return the distance to it.
+      next_entry = entry;
+    }
+  }
+
+  // No containing region found. Create an unmapped region that extends to the
+  // next region or LLDB_INVALID_ADDRESS
+  info.GetRange().SetRangeBase(load_addr);
+  info.GetRange().SetRangeEnd((next_entry != nullptr) ? next_entry->base_address
+                                                      : LLDB_INVALID_ADDRESS);
+  info.SetReadable(no);
+  info.SetWritable(no);
+  info.SetExecutable(no);
+  info.SetMapped(no);
+
+  // Note that the memory info list doesn't seem to contain ranges in kernel
+  // space, so if you're walking a stack that has kernel frames, the stack may
+  // appear truncated.
+  return info;
+}

Modified: lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h?rev=284593&r1=284592&r2=284593&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h (original)
+++ lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h Wed Oct 19 09:14:18 2016
@@ -34,6 +34,16 @@ namespace lldb_private {
 
 namespace minidump {
 
+// Describes a range of memory captured in the Minidump
+struct Range {
+  lldb::addr_t start; // virtual address of the beginning of the range
+  // range_ref - absolute pointer to the first byte of the range and size
+  llvm::ArrayRef<uint8_t> range_ref;
+
+  Range(lldb::addr_t start, llvm::ArrayRef<uint8_t> range_ref)
+      : start(start), range_ref(range_ref) {}
+};
+
 class MinidumpParser {
 public:
   static llvm::Optional<MinidumpParser>
@@ -47,6 +57,8 @@ public:
 
   llvm::ArrayRef<MinidumpThread> GetThreads();
 
+  llvm::ArrayRef<uint8_t> GetThreadContext(const MinidumpThread &td);
+
   const MinidumpSystemInfo *GetSystemInfo();
 
   ArchSpec GetArchitecture();
@@ -59,8 +71,20 @@ public:
 
   llvm::ArrayRef<MinidumpModule> GetModuleList();
 
+  // There are cases in which there is more than one record in the ModuleList
+  // for the same module name.(e.g. when the binary has non contiguous segments)
+  // So this function returns a filtered module list - if it finds records that
+  // have the same name, it keeps the copy with the lowest load address.
+  std::vector<const MinidumpModule *> GetFilteredModuleList();
+
   const MinidumpExceptionStream *GetExceptionStream();
 
+  llvm::Optional<Range> FindMemoryRange(lldb::addr_t addr);
+
+  llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size);
+
+  llvm::Optional<MemoryRegionInfo> GetMemoryRegionInfo(lldb::addr_t);
+
 private:
   lldb::DataBufferSP m_data_sp;
   const MinidumpHeader *m_header;

Modified: lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.cpp?rev=284593&r1=284592&r2=284593&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.cpp (original)
+++ lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.cpp Wed Oct 19 09:14:18 2016
@@ -176,3 +176,60 @@ MinidumpExceptionStream::Parse(llvm::Arr
 
   return exception_stream;
 }
+
+llvm::ArrayRef<MinidumpMemoryDescriptor>
+MinidumpMemoryDescriptor::ParseMemoryList(llvm::ArrayRef<uint8_t> &data) {
+  const llvm::support::ulittle32_t *mem_ranges_count;
+  Error error = consumeObject(data, mem_ranges_count);
+  if (error.Fail() ||
+      *mem_ranges_count * sizeof(MinidumpMemoryDescriptor) > data.size())
+    return {};
+
+  return llvm::makeArrayRef(
+      reinterpret_cast<const MinidumpMemoryDescriptor *>(data.data()),
+      *mem_ranges_count);
+}
+
+std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t>
+MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) {
+  const llvm::support::ulittle64_t *mem_ranges_count;
+  Error error = consumeObject(data, mem_ranges_count);
+  if (error.Fail() ||
+      *mem_ranges_count * sizeof(MinidumpMemoryDescriptor64) > data.size())
+    return {};
+
+  const llvm::support::ulittle64_t *base_rva;
+  error = consumeObject(data, base_rva);
+  if (error.Fail())
+    return {};
+
+  return std::make_pair(
+      llvm::makeArrayRef(
+          reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()),
+          *mem_ranges_count),
+      *base_rva);
+}
+
+std::vector<const MinidumpMemoryInfo *>
+MinidumpMemoryInfo::ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data) {
+  const MinidumpMemoryInfoListHeader *header;
+  Error error = consumeObject(data, header);
+  if (error.Fail() ||
+      header->size_of_header < sizeof(MinidumpMemoryInfoListHeader) ||
+      header->size_of_entry < sizeof(MinidumpMemoryInfo))
+    return {};
+
+  data = data.drop_front(header->size_of_header -
+                         sizeof(MinidumpMemoryInfoListHeader));
+
+  if (header->size_of_entry * header->num_of_entries > data.size())
+    return {};
+
+  std::vector<const MinidumpMemoryInfo *> result;
+  for (uint64_t i = 0; i < header->num_of_entries; ++i) {
+    result.push_back(reinterpret_cast<const MinidumpMemoryInfo *>(
+        data.data() + i * header->size_of_entry));
+  }
+
+  return result;
+}

Modified: lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h?rev=284593&r1=284592&r2=284593&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h (original)
+++ lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h Wed Oct 19 09:14:18 2016
@@ -207,10 +207,23 @@ static_assert(sizeof(MinidumpLocationDes
 struct MinidumpMemoryDescriptor {
   llvm::support::ulittle64_t start_of_memory_range;
   MinidumpLocationDescriptor memory;
+
+  static llvm::ArrayRef<MinidumpMemoryDescriptor>
+  ParseMemoryList(llvm::ArrayRef<uint8_t> &data);
 };
 static_assert(sizeof(MinidumpMemoryDescriptor) == 16,
               "sizeof MinidumpMemoryDescriptor is not correct!");
 
+struct MinidumpMemoryDescriptor64 {
+  llvm::support::ulittle64_t start_of_memory_range;
+  llvm::support::ulittle64_t data_size;
+
+  static std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t>
+  ParseMemory64List(llvm::ArrayRef<uint8_t> &data);
+};
+static_assert(sizeof(MinidumpMemoryDescriptor64) == 16,
+              "sizeof MinidumpMemoryDescriptor64 is not correct!");
+
 // Reference:
 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680365.aspx
 struct MinidumpDirectory {
@@ -221,6 +234,70 @@ static_assert(sizeof(MinidumpDirectory)
               "sizeof MinidumpDirectory is not correct!");
 
 // Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680385(v=vs.85).aspx
+struct MinidumpMemoryInfoListHeader {
+  llvm::support::ulittle32_t size_of_header;
+  llvm::support::ulittle32_t size_of_entry;
+  llvm::support::ulittle64_t num_of_entries;
+};
+static_assert(sizeof(MinidumpMemoryInfoListHeader) == 16,
+              "sizeof MinidumpMemoryInfoListHeader is not correct!");
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680386(v=vs.85).aspx
+struct MinidumpMemoryInfo {
+  llvm::support::ulittle64_t base_address;
+  llvm::support::ulittle64_t allocation_base;
+  llvm::support::ulittle32_t allocation_protect;
+  llvm::support::ulittle32_t alignment1;
+  llvm::support::ulittle64_t region_size;
+  llvm::support::ulittle32_t state;
+  llvm::support::ulittle32_t protect;
+  llvm::support::ulittle32_t type;
+  llvm::support::ulittle32_t alignment2;
+
+  static std::vector<const MinidumpMemoryInfo *>
+  ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data);
+};
+static_assert(sizeof(MinidumpMemoryInfo) == 48,
+              "sizeof MinidumpMemoryInfo is not correct!");
+
+enum class MinidumpMemoryInfoState : uint32_t {
+  MemCommit = 0x1000,
+  MemFree = 0x10000,
+  MemReserve = 0x2000,
+  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemFree)
+};
+
+enum class MinidumpMemoryInfoType : uint32_t {
+  MemImage = 0x1000000,
+  MemMapped = 0x40000,
+  MemPrivate = 0x20000,
+  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemImage)
+};
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx
+enum class MinidumpMemoryProtectionContants : uint32_t {
+  PageExecute = 0x10,
+  PageExecuteRead = 0x20,
+  PageExecuteReadWrite = 0x40,
+  PageExecuteWriteCopy = 0x80,
+  PageNoAccess = 0x01,
+  PageReadOnly = 0x02,
+  PageReadWrite = 0x04,
+  PageWriteCopy = 0x08,
+  PageTargetsInvalid = 0x40000000,
+  PageTargetsNoUpdate = 0x40000000,
+
+  PageWritable = PageExecuteReadWrite | PageExecuteWriteCopy | PageReadWrite |
+                 PageWriteCopy,
+  PageExecutable = PageExecute | PageExecuteRead | PageExecuteReadWrite |
+                   PageExecuteWriteCopy,
+  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ PageTargetsInvalid)
+};
+
+// Reference:
 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680517(v=vs.85).aspx
 struct MinidumpThread {
   llvm::support::ulittle32_t thread_id;

Modified: lldb/trunk/unittests/Process/minidump/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/minidump/CMakeLists.txt?rev=284593&r1=284592&r2=284593&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/minidump/CMakeLists.txt (original)
+++ lldb/trunk/unittests/Process/minidump/CMakeLists.txt Wed Oct 19 09:14:18 2016
@@ -4,6 +4,8 @@ add_lldb_unittest(LLDBMinidumpTests
 
 set(test_inputs
    linux-x86_64.dmp
-   fizzbuzz_no_heap.dmp)
+   linux-x86_64_not_crashed.dmp
+   fizzbuzz_no_heap.dmp
+   fizzbuzz_wow64.dmp)
 
 add_unittest_inputs(LLDBMinidumpTests "${test_inputs}")

Added: lldb/trunk/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp?rev=284593&view=auto
==============================================================================
Binary file - no diff available.

Propchange: lldb/trunk/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Modified: lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp?rev=284593&r1=284592&r2=284593&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp (original)
+++ lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp Wed Oct 19 09:14:18 2016
@@ -19,6 +19,7 @@
 #include "lldb/Core/ArchSpec.h"
 #include "lldb/Core/DataExtractor.h"
 #include "lldb/Host/FileSpec.h"
+#include "lldb/Target/MemoryRegionInfo.h"
 
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/Optional.h"
@@ -68,7 +69,11 @@ TEST_F(MinidumpParserTest, GetThreads) {
   ASSERT_EQ(1UL, thread_list.size());
 
   const MinidumpThread thread = thread_list[0];
-  ASSERT_EQ(16001UL, thread.thread_id);
+
+  EXPECT_EQ(16001UL, thread.thread_id);
+
+  llvm::ArrayRef<uint8_t> context = parser->GetThreadContext(thread);
+  EXPECT_EQ(1232UL, context.size());
 }
 
 TEST_F(MinidumpParserTest, GetThreadsTruncatedFile) {
@@ -131,6 +136,28 @@ TEST_F(MinidumpParserTest, GetModuleList
   }
 }
 
+TEST_F(MinidumpParserTest, GetFilteredModuleList) {
+  SetUpData("linux-x86_64_not_crashed.dmp");
+  llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList();
+  std::vector<const MinidumpModule *> filtered_modules =
+      parser->GetFilteredModuleList();
+  EXPECT_EQ(10UL, modules.size());
+  EXPECT_EQ(9UL, filtered_modules.size());
+  // EXPECT_GT(modules.size(), filtered_modules.size());
+  bool found = false;
+  for (size_t i = 0; i < filtered_modules.size(); ++i) {
+    llvm::Optional<std::string> name =
+        parser->GetMinidumpString(filtered_modules[i]->module_name_rva);
+    ASSERT_TRUE(name.hasValue());
+    if (name.getValue() == "/tmp/test/linux-x86_64_not_crashed") {
+      ASSERT_FALSE(found) << "There should be only one module with this name "
+                             "in the filtered module list";
+      found = true;
+      ASSERT_EQ(0x400000UL, filtered_modules[i]->base_of_image);
+    }
+  }
+}
+
 TEST_F(MinidumpParserTest, GetExceptionStream) {
   SetUpData("linux-x86_64.dmp");
   const MinidumpExceptionStream *exception_stream =
@@ -139,6 +166,81 @@ TEST_F(MinidumpParserTest, GetExceptionS
   ASSERT_EQ(11UL, exception_stream->exception_record.exception_code);
 }
 
+void check_mem_range_exists(std::unique_ptr<MinidumpParser> &parser,
+                            const uint64_t range_start,
+                            const uint64_t range_size) {
+  llvm::Optional<minidump::Range> range = parser->FindMemoryRange(range_start);
+  ASSERT_TRUE(range.hasValue()) << "There is no range containing this address";
+  EXPECT_EQ(range_start, range->start);
+  EXPECT_EQ(range_start + range_size, range->start + range->range_ref.size());
+}
+
+TEST_F(MinidumpParserTest, FindMemoryRange) {
+  SetUpData("linux-x86_64.dmp");
+  // There are two memory ranges in the file (size is in bytes, decimal):
+  // 1) 0x401d46 256
+  // 2) 0x7ffceb34a000 12288
+  EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue());
+  EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue());
+
+  check_mem_range_exists(parser, 0x401d46, 256);
+  EXPECT_FALSE(parser->FindMemoryRange(0x401d46 + 256).hasValue());
+
+  check_mem_range_exists(parser, 0x7ffceb34a000, 12288);
+  EXPECT_FALSE(parser->FindMemoryRange(0x7ffceb34a000 + 12288).hasValue());
+}
+
+TEST_F(MinidumpParserTest, GetMemory) {
+  SetUpData("linux-x86_64.dmp");
+
+  EXPECT_EQ(128UL, parser->GetMemory(0x401d46, 128).size());
+  EXPECT_EQ(256UL, parser->GetMemory(0x401d46, 512).size());
+
+  EXPECT_EQ(12288UL, parser->GetMemory(0x7ffceb34a000, 12288).size());
+  EXPECT_EQ(1024UL, parser->GetMemory(0x7ffceb34a000, 1024).size());
+
+  EXPECT_TRUE(parser->GetMemory(0x500000, 512).empty());
+}
+
+TEST_F(MinidumpParserTest, FindMemoryRangeWithFullMemoryMinidump) {
+  SetUpData("fizzbuzz_wow64.dmp");
+
+  // There are a lot of ranges in the file, just testing with some of them
+  EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue());
+  EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue());
+  check_mem_range_exists(parser, 0x10000, 65536); // first range
+  check_mem_range_exists(parser, 0x40000, 4096);
+  EXPECT_FALSE(parser->FindMemoryRange(0x40000 + 4096).hasValue());
+  check_mem_range_exists(parser, 0x77c12000, 8192);
+  check_mem_range_exists(parser, 0x7ffe0000, 4096); // last range
+  EXPECT_FALSE(parser->FindMemoryRange(0x7ffe0000 + 4096).hasValue());
+}
+
+void check_region_info(std::unique_ptr<MinidumpParser> &parser,
+                       const uint64_t addr, MemoryRegionInfo::OptionalBool read,
+                       MemoryRegionInfo::OptionalBool write,
+                       MemoryRegionInfo::OptionalBool exec) {
+  auto range_info = parser->GetMemoryRegionInfo(addr);
+  ASSERT_TRUE(range_info.hasValue());
+  EXPECT_EQ(read, range_info->GetReadable());
+  EXPECT_EQ(write, range_info->GetWritable());
+  EXPECT_EQ(exec, range_info->GetExecutable());
+}
+
+TEST_F(MinidumpParserTest, GetMemoryRegionInfo) {
+  SetUpData("fizzbuzz_wow64.dmp");
+
+  const auto yes = MemoryRegionInfo::eYes;
+  const auto no = MemoryRegionInfo::eNo;
+
+  check_region_info(parser, 0x00000, no, no, no);
+  check_region_info(parser, 0x10000, yes, yes, no);
+  check_region_info(parser, 0x20000, yes, yes, no);
+  check_region_info(parser, 0x30000, yes, yes, no);
+  check_region_info(parser, 0x31000, no, no, no);
+  check_region_info(parser, 0x40000, yes, no, no);
+}
+
 // Windows Minidump tests
 // fizzbuzz_no_heap.dmp is copied from the WinMiniDump tests
 TEST_F(MinidumpParserTest, GetArchitectureWindows) {




More information about the lldb-commits mailing list