[Lldb-commits] [lldb] r349182 - Cache memory regions in ProcessMinidump and use the linux maps as the source of the information if available

Greg Clayton via lldb-commits lldb-commits at lists.llvm.org
Fri Dec 14 11:36:01 PST 2018


Author: gclayton
Date: Fri Dec 14 11:36:01 2018
New Revision: 349182

URL: http://llvm.org/viewvc/llvm-project?rev=349182&view=rev
Log:
Cache memory regions in ProcessMinidump and use the linux maps as the source of the information if available

Breakpad creates minidump files that sometimes have:
- linux maps textual content
- no MemoryInfoList

Right now unless the file has a MemoryInfoList we get no region information.

This patch:

- reads and caches the memory region info one time and sorts it for easy subsequent access
- get the region info from the best source in this order:
  - linux maps info (if available)
  - MemoryInfoList (if available)
  - MemoryList or Memory64List
- returns memory region info for the gaps between regions (before the first and after the last)

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


Added:
    lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.cpp
    lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.h
    lldb/trunk/unittests/Process/minidump/Inputs/regions-linux-map.dmp   (with props)
    lldb/trunk/unittests/Process/minidump/Inputs/regions-memlist.dmp   (with props)
    lldb/trunk/unittests/Process/minidump/Inputs/regions-memlist64.dmp   (with props)
Modified:
    lldb/trunk/include/lldb/Target/MemoryRegionInfo.h
    lldb/trunk/lldb.xcodeproj/project.pbxproj
    lldb/trunk/source/Plugins/Process/Utility/CMakeLists.txt
    lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp
    lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h
    lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h
    lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.cpp
    lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp

Modified: lldb/trunk/include/lldb/Target/MemoryRegionInfo.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/MemoryRegionInfo.h?rev=349182&r1=349181&r2=349182&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/MemoryRegionInfo.h (original)
+++ lldb/trunk/include/lldb/Target/MemoryRegionInfo.h Fri Dec 14 11:36:01 2018
@@ -109,6 +109,20 @@ protected:
   OptionalBool m_flash;
   lldb::offset_t m_blocksize;
 };
+  
+inline bool operator<(const MemoryRegionInfo &lhs,
+                      const MemoryRegionInfo &rhs) {
+  return lhs.GetRange() < rhs.GetRange();
+}
+
+inline bool operator<(const MemoryRegionInfo &lhs, lldb::addr_t rhs) {
+  return lhs.GetRange().GetRangeBase() < rhs;
+}
+
+inline bool operator<(lldb::addr_t lhs, const MemoryRegionInfo &rhs) {
+  return lhs < rhs.GetRange().GetRangeBase();
+}
+
 }
 
 namespace llvm {

Modified: lldb/trunk/lldb.xcodeproj/project.pbxproj
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lldb.xcodeproj/project.pbxproj?rev=349182&r1=349181&r2=349182&view=diff
==============================================================================
--- lldb/trunk/lldb.xcodeproj/project.pbxproj (original)
+++ lldb/trunk/lldb.xcodeproj/project.pbxproj Fri Dec 14 11:36:01 2018
@@ -402,6 +402,8 @@
 		4CDB8D6D1DBA91B6006C5B13 /* LibStdcppUniquePointer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CDB8D671DBA91A6006C5B13 /* LibStdcppUniquePointer.cpp */; };
 		268900DA13353E6F00698AC0 /* LineEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1910F1B8EC00F91463 /* LineEntry.cpp */; };
 		268900DB13353E6F00698AC0 /* LineTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F1A10F1B8EC00F91463 /* LineTable.cpp */; };
+		2647B64421C43BB000A81D15 /* LinuxProcMaps.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2647B64221C43BB000A81D15 /* LinuxProcMaps.cpp */; };
+		2647B64321C43BB000A81D15 /* LinuxProcMaps.h in Headers */ = {isa = PBXBuildFile; fileRef = 2647B64121C43BAF00A81D15 /* LinuxProcMaps.h */; };
 		23059A0719532B96007B8189 /* LinuxSignals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23059A0519532B96007B8189 /* LinuxSignals.cpp */; };
 		2647B63E21C436BD00A81D15 /* Listener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2647B63D21C436BC00A81D15 /* Listener.cpp */; };
 		2647B63821C4369500A81D15 /* Listener.h in Headers */ = {isa = PBXBuildFile; fileRef = 2647B63721C4369500A81D15 /* Listener.h */; };
@@ -2050,6 +2052,8 @@
 		26BC7C5B10F1B6E900F91463 /* LineEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LineEntry.h; path = include/lldb/Symbol/LineEntry.h; sourceTree = "<group>"; };
 		26BC7F1A10F1B8EC00F91463 /* LineTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LineTable.cpp; path = source/Symbol/LineTable.cpp; sourceTree = "<group>"; };
 		26BC7C5C10F1B6E900F91463 /* LineTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LineTable.h; path = include/lldb/Symbol/LineTable.h; sourceTree = "<group>"; };
+		2647B64221C43BB000A81D15 /* LinuxProcMaps.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinuxProcMaps.cpp; path = Utility/LinuxProcMaps.cpp; sourceTree = "<group>"; };
+		2647B64121C43BAF00A81D15 /* LinuxProcMaps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinuxProcMaps.h; path = Utility/LinuxProcMaps.h; sourceTree = "<group>"; };
 		23059A0519532B96007B8189 /* LinuxSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinuxSignals.cpp; path = Utility/LinuxSignals.cpp; sourceTree = "<group>"; };
 		23059A0619532B96007B8189 /* LinuxSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinuxSignals.h; path = Utility/LinuxSignals.h; sourceTree = "<group>"; };
 		2647B63D21C436BC00A81D15 /* Listener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Listener.cpp; path = source/Utility/Listener.cpp; sourceTree = "<group>"; };
@@ -4948,6 +4952,8 @@
 				B28058A0139988B0002D96D0 /* InferiorCallPOSIX.cpp */,
 				B28058A2139988C6002D96D0 /* InferiorCallPOSIX.h */,
 				B2D3033612EFA5C500F84EB3 /* InstructionUtils.h */,
+				2647B64221C43BB000A81D15 /* LinuxProcMaps.cpp */,
+				2647B64121C43BAF00A81D15 /* LinuxProcMaps.h */,
 				23059A0519532B96007B8189 /* LinuxSignals.cpp */,
 				23059A0619532B96007B8189 /* LinuxSignals.h */,
 				23173F8B192BA93F005C708F /* lldb-x86-register-enums.h */,
@@ -6995,6 +7001,7 @@
 			files = (
 				AF8AD6381BEC28C400150209 /* PlatformRemoteAppleTV.h in Headers */,
 				26EFB61C1BFE8D3E00544801 /* PlatformNetBSD.h in Headers */,
+				2647B64321C43BB000A81D15 /* LinuxProcMaps.h in Headers */,
 				AF3A4AD31EA05C4700B5DEB4 /* PlatformRemoteDarwinDevice.h in Headers */,
 				AF9113FE1FBE78EA004320CD /* RegisterContextPOSIXCore_ppc64le.h in Headers */,
 				AF33B4BF1C1FA441001B28D9 /* NetBSDSignals.h in Headers */,
@@ -7966,6 +7973,7 @@
 				AF2BA6EC1A707E3400C5248A /* UriParser.cpp in Sources */,
 				2689006D13353E0E00698AC0 /* IRExecutionUnit.cpp in Sources */,
 				304B2E461CAAA57B007829FE /* ClangUtil.cpp in Sources */,
+				2647B64421C43BB000A81D15 /* LinuxProcMaps.cpp in Sources */,
 				2689006E13353E1A00698AC0 /* File.cpp in Sources */,
 				2689006F13353E1A00698AC0 /* FileSpec.cpp in Sources */,
 				AF6CA6661FBBAF28005A0DC3 /* ArchSpec.cpp in Sources */,

Modified: lldb/trunk/source/Plugins/Process/Utility/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Utility/CMakeLists.txt?rev=349182&r1=349181&r2=349182&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Utility/CMakeLists.txt (original)
+++ lldb/trunk/source/Plugins/Process/Utility/CMakeLists.txt Fri Dec 14 11:36:01 2018
@@ -5,6 +5,7 @@ add_lldb_library(lldbPluginProcessUtilit
   HistoryThread.cpp
   HistoryUnwind.cpp
   InferiorCallPOSIX.cpp
+  LinuxProcMaps.cpp
   LinuxSignals.cpp
   MipsLinuxSignals.cpp
   NativeRegisterContextRegisterInfo.cpp

Added: lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.cpp?rev=349182&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.cpp (added)
+++ lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.cpp Fri Dec 14 11:36:01 2018
@@ -0,0 +1,113 @@
+//===-- LinuxProcMaps.cpp ---------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LinuxProcMaps.h"
+#include "llvm/ADT/StringRef.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StringExtractor.h"
+
+using namespace lldb_private;
+
+static Status
+ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
+                                      MemoryRegionInfo &memory_region_info) {
+  memory_region_info.Clear();
+  
+  StringExtractor line_extractor(maps_line);
+  
+  // Format: {address_start_hex}-{address_end_hex} perms offset  dev   inode
+  // pathname perms: rwxp   (letter is present if set, '-' if not, final
+  // character is p=private, s=shared).
+  
+  // Parse out the starting address
+  lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
+  
+  // Parse out hyphen separating start and end address from range.
+  if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
+    return Status(
+        "malformed /proc/{pid}/maps entry, missing dash between address range");
+  
+  // Parse out the ending address
+  lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
+  
+  // Parse out the space after the address.
+  if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
+    return Status(
+        "malformed /proc/{pid}/maps entry, missing space after range");
+  
+  // Save the range.
+  memory_region_info.GetRange().SetRangeBase(start_address);
+  memory_region_info.GetRange().SetRangeEnd(end_address);
+  
+  // Any memory region in /proc/{pid}/maps is by definition mapped into the
+  // process.
+  memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+  
+  // Parse out each permission entry.
+  if (line_extractor.GetBytesLeft() < 4)
+    return Status("malformed /proc/{pid}/maps entry, missing some portion of "
+                  "permissions");
+  
+  // Handle read permission.
+  const char read_perm_char = line_extractor.GetChar();
+  if (read_perm_char == 'r')
+    memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
+  else if (read_perm_char == '-')
+    memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+  else
+    return Status("unexpected /proc/{pid}/maps read permission char");
+  
+  // Handle write permission.
+  const char write_perm_char = line_extractor.GetChar();
+  if (write_perm_char == 'w')
+    memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
+  else if (write_perm_char == '-')
+    memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+  else
+    return Status("unexpected /proc/{pid}/maps write permission char");
+  
+  // Handle execute permission.
+  const char exec_perm_char = line_extractor.GetChar();
+  if (exec_perm_char == 'x')
+    memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
+  else if (exec_perm_char == '-')
+    memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+  else
+    return Status("unexpected /proc/{pid}/maps exec permission char");
+  
+  line_extractor.GetChar();              // Read the private bit
+  line_extractor.SkipSpaces();           // Skip the separator
+  line_extractor.GetHexMaxU64(false, 0); // Read the offset
+  line_extractor.GetHexMaxU64(false, 0); // Read the major device number
+  line_extractor.GetChar();              // Read the device id separator
+  line_extractor.GetHexMaxU64(false, 0); // Read the major device number
+  line_extractor.SkipSpaces();           // Skip the separator
+  line_extractor.GetU64(0, 10);          // Read the inode number
+  
+  line_extractor.SkipSpaces();
+  const char *name = line_extractor.Peek();
+  if (name)
+    memory_region_info.SetName(name);
+  
+  return Status();
+}
+
+void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
+                                        LinuxMapCallback const &callback) {
+  llvm::StringRef lines(linux_map);
+  llvm::StringRef line;
+  while (!lines.empty()) {
+    std::tie(line, lines) = lines.split('\n');
+    MemoryRegionInfo region;
+    Status error = ParseMemoryRegionInfoFromProcMapsLine(line, region);
+    if (!callback(region, error))
+      break;
+  }
+}

Added: lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.h?rev=349182&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.h (added)
+++ lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.h Fri Dec 14 11:36:01 2018
@@ -0,0 +1,28 @@
+//===-- LinuxProcMaps.h -----------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_LinuxProcMaps_H_
+#define liblldb_LinuxProcMaps_H_
+
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+#include <functional>
+
+
+namespace lldb_private {
+
+typedef std::function<bool(const lldb_private::MemoryRegionInfo &,
+                           const lldb_private::Status &)> LinuxMapCallback;
+
+void ParseLinuxMapRegions(llvm::StringRef linux_map,
+                          LinuxMapCallback const &callback);
+
+} // namespace lldb_private
+
+#endif // liblldb_LinuxProcMaps_H_

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=349182&r1=349181&r2=349182&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp (original)
+++ lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp Fri Dec 14 11:36:01 2018
@@ -13,12 +13,14 @@
 
 #include "lldb/Target/MemoryRegionInfo.h"
 #include "lldb/Utility/LLDBAssert.h"
+#include "Plugins/Process/Utility/LinuxProcMaps.h"
 
 // C includes
 // C++ includes
 #include <algorithm>
 #include <map>
 #include <vector>
+#include <utility>
 
 using namespace lldb_private;
 using namespace minidump;
@@ -410,72 +412,147 @@ llvm::ArrayRef<uint8_t> MinidumpParser::
   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);
+static bool
+CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser,
+                                std::vector<MemoryRegionInfo> &regions) {
+  auto data = parser.GetStream(MinidumpStreamType::LinuxMaps);
   if (data.empty())
-    return llvm::None;
+    return false;
+  ParseLinuxMapRegions(llvm::toStringRef(data),
+                       [&](const lldb_private::MemoryRegionInfo &region,
+                           const lldb_private::Status &status) -> bool {
+    if (status.Success())
+      regions.push_back(region);
+    return true;
+  });
+  return !regions.empty();
+}
 
-  std::vector<const MinidumpMemoryInfo *> mem_info_list =
-      MinidumpMemoryInfo::ParseMemoryInfoList(data);
+static bool
+CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser,
+                                     std::vector<MemoryRegionInfo> &regions) {
+  auto data = parser.GetStream(MinidumpStreamType::MemoryInfoList);
+  if (data.empty())
+    return false;
+  auto mem_info_list = MinidumpMemoryInfo::ParseMemoryInfoList(data);
   if (mem_info_list.empty())
-    return llvm::None;
+    return false;
+  constexpr auto yes = MemoryRegionInfo::eYes;
+  constexpr auto no = MemoryRegionInfo::eNo;
+  regions.reserve(mem_info_list.size());
+  for (const auto &entry : mem_info_list) {
+    MemoryRegionInfo region;
+    region.GetRange().SetRangeBase(entry->base_address);
+    region.GetRange().SetByteSize(entry->region_size);
+    region.SetReadable(entry->isReadable() ? yes : no);
+    region.SetWritable(entry->isWritable() ? yes : no);
+    region.SetExecutable(entry->isExecutable() ? yes : no);
+    region.SetMapped(entry->isMapped() ? yes : no);
+    regions.push_back(region);
+  }
+  return !regions.empty();
+}
 
-  const auto yes = MemoryRegionInfo::eYes;
-  const auto no = MemoryRegionInfo::eNo;
+static bool
+CreateRegionsCacheFromMemoryList(MinidumpParser &parser,
+                                 std::vector<MemoryRegionInfo> &regions) {
+  auto data = parser.GetStream(MinidumpStreamType::MemoryList);
+  if (data.empty())
+    return false;
+  auto memory_list = MinidumpMemoryDescriptor::ParseMemoryList(data);
+  if (memory_list.empty())
+    return false;
+  regions.reserve(memory_list.size());
+  for (const auto &memory_desc : memory_list) {
+    if (memory_desc.memory.data_size == 0)
+      continue;
+    MemoryRegionInfo region;
+    region.GetRange().SetRangeBase(memory_desc.start_of_memory_range);
+    region.GetRange().SetByteSize(memory_desc.memory.data_size);
+    region.SetReadable(MemoryRegionInfo::eYes);
+    region.SetMapped(MemoryRegionInfo::eYes);
+    regions.push_back(region);
+  }
+  regions.shrink_to_fit();
+  return !regions.empty();
+}
 
-  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;
+static bool
+CreateRegionsCacheFromMemory64List(MinidumpParser &parser,
+                                   std::vector<MemoryRegionInfo> &regions) {
+  llvm::ArrayRef<uint8_t> data =
+      parser.GetStream(MinidumpStreamType::Memory64List);
+  if (data.empty())
+    return false;
+  llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
+  uint64_t base_rva;
+  std::tie(memory64_list, base_rva) =
+      MinidumpMemoryDescriptor64::ParseMemory64List(data);
+  
+  if (memory64_list.empty())
+    return false;
+    
+  regions.reserve(memory64_list.size());
+  for (const auto &memory_desc : memory64_list) {
+    if (memory_desc.data_size == 0)
+      continue;
+    MemoryRegionInfo region;
+    region.GetRange().SetRangeBase(memory_desc.start_of_memory_range);
+    region.GetRange().SetByteSize(memory_desc.data_size);
+    region.SetReadable(MemoryRegionInfo::eYes);
+    region.SetMapped(MemoryRegionInfo::eYes);
+    regions.push_back(region);
+  }
+  regions.shrink_to_fit();
+  return !regions.empty();
+}
 
-    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;
-    }
+MemoryRegionInfo
+MinidumpParser::FindMemoryRegion(lldb::addr_t load_addr) const {
+  auto begin = m_regions.begin();
+  auto end = m_regions.end();
+  auto pos = std::lower_bound(begin, end, load_addr);
+  if (pos != end && pos->GetRange().Contains(load_addr))
+    return *pos;
+  
+  MemoryRegionInfo region;
+  if (pos == begin)
+    region.GetRange().SetRangeBase(0);
+  else {
+    auto prev = pos - 1;
+    if (prev->GetRange().Contains(load_addr))
+      return *prev;
+    region.GetRange().SetRangeBase(prev->GetRange().GetRangeEnd());
   }
+  if (pos == end)
+    region.GetRange().SetRangeEnd(UINT64_MAX);
+  else
+    region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase());
+  region.SetReadable(MemoryRegionInfo::eNo);
+  region.SetWritable(MemoryRegionInfo::eNo);
+  region.SetExecutable(MemoryRegionInfo::eNo);
+  region.SetMapped(MemoryRegionInfo::eNo);
+  return region;
+}
 
-  // 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;
+MemoryRegionInfo
+MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
+  if (!m_parsed_regions) {
+    m_parsed_regions = true;
+    // We haven't cached our memory regions yet we will create the region cache
+    // once. We create the region cache using the best source. We start with
+    // the linux maps since they are the most complete and have names for the
+    // regions. Next we try the MemoryInfoList since it has
+    // read/write/execute/map data, and then fall back to the MemoryList and
+    // Memory64List to just get a list of the memory that is mapped in this
+    // core file
+    if (!CreateRegionsCacheFromLinuxMaps(*this, m_regions))
+      if (!CreateRegionsCacheFromMemoryInfoList(*this, m_regions))
+        if (!CreateRegionsCacheFromMemoryList(*this, m_regions))
+          CreateRegionsCacheFromMemory64List(*this, m_regions);
+    std::sort(m_regions.begin(), m_regions.end());
+  }
+  return FindMemoryRegion(load_addr);
 }
 
 Status MinidumpParser::Initialize() {

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=349182&r1=349181&r2=349182&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h (original)
+++ lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h Fri Dec 14 11:36:01 2018
@@ -1,5 +1,4 @@
-//===-- MinidumpParser.h -----------------------------------------*- C++
-//-*-===//
+//===-- MinidumpParser.h -----------------------------------------*- C++-*-===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -86,7 +85,7 @@ public:
 
   llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size);
 
-  llvm::Optional<MemoryRegionInfo> GetMemoryRegionInfo(lldb::addr_t);
+  MemoryRegionInfo GetMemoryRegionInfo(lldb::addr_t load_addr);
 
   // Perform consistency checks and initialize internal data structures
   Status Initialize();
@@ -94,10 +93,14 @@ public:
 private:
   MinidumpParser(const lldb::DataBufferSP &data_buf_sp);
 
+  MemoryRegionInfo FindMemoryRegion(lldb::addr_t load_addr) const;
+
 private:
   lldb::DataBufferSP m_data_sp;
   llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> m_directory_map;
   ArchSpec m_arch;
+  std::vector<MemoryRegionInfo> m_regions;
+  bool m_parsed_regions = false;
 };
 
 } // end namespace minidump

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=349182&r1=349181&r2=349182&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h (original)
+++ lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h Fri Dec 14 11:36:01 2018
@@ -256,25 +256,6 @@ struct MinidumpMemoryInfoListHeader {
 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,
@@ -311,6 +292,45 @@ enum class MinidumpMemoryProtectionConta
 };
 
 // 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);
+
+  bool isReadable() const {
+    const auto mask = MinidumpMemoryProtectionContants::PageNoAccess;
+    return (static_cast<uint32_t>(mask) & protect) == 0;
+  }
+
+  bool isWritable() const {
+    const auto mask = MinidumpMemoryProtectionContants::PageWritable;
+    return (static_cast<uint32_t>(mask) & protect) != 0;
+  }
+
+  bool isExecutable() const {
+    const auto mask = MinidumpMemoryProtectionContants::PageExecutable;
+    return (static_cast<uint32_t>(mask) & protect) != 0;
+  }
+  
+  bool isMapped() const {
+    return state != static_cast<uint32_t>(MinidumpMemoryInfoState::MemFree);
+  }
+};
+
+static_assert(sizeof(MinidumpMemoryInfo) == 48,
+              "sizeof MinidumpMemoryInfo is not correct!");
+
+// 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/source/Plugins/Process/minidump/ProcessMinidump.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.cpp?rev=349182&r1=349181&r2=349182&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.cpp (original)
+++ lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.cpp Fri Dec 14 11:36:01 2018
@@ -284,14 +284,8 @@ ArchSpec ProcessMinidump::GetArchitectur
 
 Status ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr,
                                             MemoryRegionInfo &range_info) {
-  Status error;
-  auto info = m_minidump_parser.GetMemoryRegionInfo(load_addr);
-  if (!info) {
-    error.SetErrorString("No valid MemoryRegionInfo found!");
-    return error;
-  }
-  range_info = info.getValue();
-  return error;
+  range_info = m_minidump_parser.GetMemoryRegionInfo(load_addr);
+  return Status();
 }
 
 void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); }

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

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

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

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

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

Propchange: lldb/trunk/unittests/Process/minidump/Inputs/regions-memlist64.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=349182&r1=349181&r2=349182&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp (original)
+++ lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp Fri Dec 14 11:36:01 2018
@@ -300,29 +300,120 @@ TEST_F(MinidumpParserTest, FindMemoryRan
   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) {
+void check_region(std::unique_ptr<MinidumpParser> &parser,
+                  lldb::addr_t addr, lldb::addr_t start, lldb::addr_t end,
+                  MemoryRegionInfo::OptionalBool read,
+                  MemoryRegionInfo::OptionalBool write,
+                  MemoryRegionInfo::OptionalBool exec,
+                  MemoryRegionInfo::OptionalBool mapped,
+                  ConstString name = ConstString()) {
   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());
+  EXPECT_EQ(start, range_info.GetRange().GetRangeBase());
+  EXPECT_EQ(end, range_info.GetRange().GetRangeEnd());
+  EXPECT_EQ(read, range_info.GetReadable());
+  EXPECT_EQ(write, range_info.GetWritable());
+  EXPECT_EQ(exec, range_info.GetExecutable());
+  EXPECT_EQ(mapped, range_info.GetMapped());
+  EXPECT_EQ(name, range_info.GetName());
+}
+
+// Same as above function where addr == start
+void check_region(std::unique_ptr<MinidumpParser> &parser,
+                  lldb::addr_t start, lldb::addr_t end,
+                  MemoryRegionInfo::OptionalBool read,
+                  MemoryRegionInfo::OptionalBool write,
+                  MemoryRegionInfo::OptionalBool exec,
+                  MemoryRegionInfo::OptionalBool mapped,
+                  ConstString name = ConstString()) {
+  check_region(parser, start, start, end, read, write, exec, mapped, name);
 }
 
+
+constexpr auto yes = MemoryRegionInfo::eYes;
+constexpr auto no = MemoryRegionInfo::eNo;
+constexpr auto unknown = MemoryRegionInfo::eDontKnow;
+
 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);
+  check_region(parser, 0x00000000, 0x00010000, no, no, no, no);
+  check_region(parser, 0x00010000, 0x00020000, yes, yes, no, yes);
+  check_region(parser, 0x00020000, 0x00030000, yes, yes, no, yes);
+  check_region(parser, 0x00030000, 0x00031000, yes, yes, no, yes);
+  check_region(parser, 0x00031000, 0x00040000, no, no, no, no);
+  check_region(parser, 0x00040000, 0x00041000, yes, no, no, yes);
+
+  // Check addresses contained inside ranges
+  check_region(parser, 0x00000001, 0x00000000, 0x00010000, no, no, no, no);
+  check_region(parser, 0x0000ffff, 0x00000000, 0x00010000, no, no, no, no);
+  check_region(parser, 0x00010001, 0x00010000, 0x00020000, yes, yes, no, yes);
+  check_region(parser, 0x0001ffff, 0x00010000, 0x00020000, yes, yes, no, yes);
+
+  // Test that an address after the last entry maps to rest of the memory space
+  check_region(parser, 0x7fff0000, 0x7fff0000, UINT64_MAX, no, no, no, no);
+}
+
+TEST_F(MinidumpParserTest, GetMemoryRegionInfoFromMemoryList) {
+  SetUpData("regions-memlist.dmp");
+  // Test we can get memory regions from the MINIDUMP_MEMORY_LIST stream when
+  // we don't have a MemoryInfoListStream.
+
+  // Test addres before the first entry comes back with nothing mapped up
+  // to first valid region info
+  check_region(parser, 0x00000000, 0x00001000, no, no, no, no);
+  check_region(parser, 0x00001000, 0x00001010, yes, unknown, unknown, yes);
+  check_region(parser, 0x00001010, 0x00002000, no, no, no, no);
+  check_region(parser, 0x00002000, 0x00002020, yes, unknown, unknown, yes);
+  check_region(parser, 0x00002020, UINT64_MAX, no, no, no, no);
+}
+
+TEST_F(MinidumpParserTest, GetMemoryRegionInfoFromMemory64List) {
+  SetUpData("regions-memlist64.dmp");
+  // Test we can get memory regions from the MINIDUMP_MEMORY64_LIST stream when
+  // we don't have a MemoryInfoListStream.
+
+  // Test addres before the first entry comes back with nothing mapped up
+  // to first valid region info
+  check_region(parser, 0x00000000, 0x00001000, no, no, no, no);
+  check_region(parser, 0x00001000, 0x00001010, yes, unknown, unknown, yes);
+  check_region(parser, 0x00001010, 0x00002000, no, no, no, no);
+  check_region(parser, 0x00002000, 0x00002020, yes, unknown, unknown, yes);
+  check_region(parser, 0x00002020, UINT64_MAX, no, no, no, no);
+}
+
+TEST_F(MinidumpParserTest, GetMemoryRegionInfoLinuxMaps) {
+  SetUpData("regions-linux-map.dmp");
+  // Test we can get memory regions from the linux /proc/<pid>/maps stream when
+  // we don't have a MemoryInfoListStream.
+
+  // Test addres before the first entry comes back with nothing mapped up
+  // to first valid region info
+  ConstString a("/system/bin/app_process");
+  ConstString b("/system/bin/linker");
+  ConstString c("/system/lib/liblog.so");
+  ConstString d("/system/lib/libc.so");
+  ConstString n;
+  check_region(parser, 0x00000000, 0x400d9000, no , no , no , no , n);
+  check_region(parser, 0x400d9000, 0x400db000, yes, no , yes, yes, a);
+  check_region(parser, 0x400db000, 0x400dc000, yes, no , no , yes, a);
+  check_region(parser, 0x400dc000, 0x400dd000, yes, yes, no , yes, n);
+  check_region(parser, 0x400dd000, 0x400ec000, yes, no , yes, yes, b);
+  check_region(parser, 0x400ec000, 0x400ed000, yes, no , no , yes, n);
+  check_region(parser, 0x400ed000, 0x400ee000, yes, no , no , yes, b);
+  check_region(parser, 0x400ee000, 0x400ef000, yes, yes, no , yes, b);
+  check_region(parser, 0x400ef000, 0x400fb000, yes, yes, no , yes, n);
+  check_region(parser, 0x400fb000, 0x400fc000, yes, no , yes, yes, c);
+  check_region(parser, 0x400fc000, 0x400fd000, yes, yes, yes, yes, c);
+  check_region(parser, 0x400fd000, 0x400ff000, yes, no , yes, yes, c);
+  check_region(parser, 0x400ff000, 0x40100000, yes, no , no , yes, c);
+  check_region(parser, 0x40100000, 0x40101000, yes, yes, no , yes, c);
+  check_region(parser, 0x40101000, 0x40122000, yes, no , yes, yes, d);
+  check_region(parser, 0x40122000, 0x40123000, yes, yes, yes, yes, d);
+  check_region(parser, 0x40123000, 0x40167000, yes, no , yes, yes, d);
+  check_region(parser, 0x40167000, 0x40169000, yes, no , no , yes, d);
+  check_region(parser, 0x40169000, 0x4016b000, yes, yes, no , yes, d);
+  check_region(parser, 0x4016b000, 0x40176000, yes, yes, no , yes, n);
+  check_region(parser, 0x40176000, UINT64_MAX, no , no , no , no , n);
 }
 
 // Windows Minidump tests
@@ -571,3 +662,4 @@ TEST_F(MinidumpParserTest, MinidumpModul
   ASSERT_TRUE((bool)name);
   EXPECT_EQ(std::string("/tmp/b"), *name);
 }
+




More information about the lldb-commits mailing list