clayborg created this revision.
clayborg added reviewers: labath, markmentovai, zturner, tatyana-krasnukha.
Herald added a subscriber: mgrang.

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)

This patch is a different patch that would replace: 
https://reviews.llvm.org/D55472

If we decide to go with this patch, then I will add tests.


https://reviews.llvm.org/D55522

Files:
  source/Plugins/Process/minidump/MinidumpParser.cpp
  source/Plugins/Process/minidump/MinidumpParser.h

Index: source/Plugins/Process/minidump/MinidumpParser.h
===================================================================
--- source/Plugins/Process/minidump/MinidumpParser.h
+++ source/Plugins/Process/minidump/MinidumpParser.h
@@ -86,7 +86,7 @@
 
   llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size);
 
-  llvm::Optional<MemoryRegionInfo> GetMemoryRegionInfo(lldb::addr_t);
+  llvm::Optional<MemoryRegionInfo> GetMemoryRegionInfo(lldb::addr_t load_addr);
 
   // Perform consistency checks and initialize internal data structures
   Status Initialize();
@@ -94,10 +94,18 @@
 private:
   MinidumpParser(const lldb::DataBufferSP &data_buf_sp);
 
+  bool CreateRegionsCacheFromLinuxMaps();
+  bool CreateRegionsCacheFromMemoryInfoList();
+  bool CreateRegionsCacheFromMemoryList();
+  bool CreateRegionsCacheFromMemory64List();
+  llvm::Optional<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;
 };
 
 } // end namespace minidump
Index: source/Plugins/Process/minidump/MinidumpParser.cpp
===================================================================
--- source/Plugins/Process/minidump/MinidumpParser.cpp
+++ source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <map>
 #include <vector>
+#include <utility>
 
 using namespace lldb_private;
 using namespace minidump;
@@ -401,72 +402,207 @@
   return range->range_ref.slice(offset, overlap);
 }
 
-llvm::Optional<MemoryRegionInfo>
-MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
-  MemoryRegionInfo info;
+bool MinidumpParser::CreateRegionsCacheFromLinuxMaps() {
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::LinuxMaps);
+  if (data.empty())
+    return false;
+  llvm::StringRef text((const char *)data.data(), data.size());
+  llvm::StringRef line;
+  constexpr const auto yes = MemoryRegionInfo::eYes;
+  constexpr const auto no = MemoryRegionInfo::eNo;
+  while (!text.empty()) {
+    std::tie(line, text) = text.split('\n');
+    // Parse the linux maps line. Example line is:
+    // 400b3000-400b5000 r-xp 00000000 b3:17 159        /system/bin/app_process
+    uint64_t start_addr, end_addr, offset;
+    uint32_t device_major, device_minor, inode;
+    if (line.consumeInteger(16, start_addr))
+      continue;
+    if (!line.consume_front("-"))
+      continue;
+    if (line.consumeInteger(16, end_addr))
+      continue;
+    line = line.ltrim();
+    llvm::StringRef permissions = line.substr(0, 4);
+    line = line.drop_front(4);
+    line = line.ltrim();
+    if (line.consumeInteger(16, offset))
+      continue;
+    line = line.ltrim();
+    if (line.consumeInteger(16, device_major))
+      continue;
+    if (!line.consume_front(":"))
+      continue;
+    if (line.consumeInteger(16, device_minor))
+      continue;
+    line = line.ltrim();
+    if (line.consumeInteger(16, inode))
+      continue;
+    line = line.ltrim();
+    llvm::StringRef pathname = line;
+    MemoryRegionInfo region;
+    region.GetRange().SetRangeBase(start_addr);
+    region.GetRange().SetRangeEnd(end_addr);
+    region.SetName(pathname.str().c_str());
+    region.SetReadable(permissions[0] == 'r' ? yes : no);
+    region.SetWritable(permissions[1] == 'w' ? yes : no);
+    region.SetExecutable(permissions[2] == 'x' ? yes : no);
+    region.SetMapped(yes);
+    m_regions.push_back(region);
+  }
+  return !m_regions.empty();
+}
+
+bool MinidumpParser::CreateRegionsCacheFromMemoryInfoList() {
   llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryInfoList);
   if (data.empty())
-    return llvm::None;
-
+    return false;
   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);
-
+    return false;
+  
+  constexpr const auto yes = MemoryRegionInfo::eYes;
+  constexpr const auto no = MemoryRegionInfo::eNo;
       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 PageExecutable =
+      static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageExecutable);
       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;
+  for (const auto &entry : mem_info_list) {
+    MemoryRegionInfo region;
+    region.GetRange().SetRangeBase(entry->base_address);
+    region.GetRange().SetByteSize(entry->region_size);
+    region.SetReadable((entry->protect & PageNoAccess) == 0 ? yes : no);
+    region.SetWritable((entry->protect & PageWritable) != 0 ? yes : no);
+    region.SetExecutable((entry->protect & PageExecutable) != 0 ? yes : no);
+    region.SetMapped((entry->state != MemFree) ? yes : no);
+    m_regions.push_back(region);
+  }
+  return !m_regions.empty();
+}
+
+bool MinidumpParser::CreateRegionsCacheFromMemoryList() {
+  llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryList);
+  
+  if (data.empty())
+    return false;
+  
+  llvm::ArrayRef<MinidumpMemoryDescriptor> memory_list =
+      MinidumpMemoryDescriptor::ParseMemoryList(data);
+    
+  if (memory_list.empty())
+    return false;
+    
+  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.SetMapped(MemoryRegionInfo::eYes);
+    m_regions.push_back(region);
+  }
+  return !m_regions.empty();
+}
+
+bool MinidumpParser::CreateRegionsCacheFromMemory64List() {
+  llvm::ArrayRef<uint8_t> data = 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;
+    
+  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.SetMapped(MemoryRegionInfo::eYes);
+    m_regions.push_back(region);
+  }
+  return !m_regions.empty();
+}
+
+llvm::Optional<MemoryRegionInfo>
+MinidumpParser::FindMemoryRegion(lldb::addr_t load_addr) const {
+  if (!m_regions.empty()) {
+    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;
+    } else if (pos != begin) {
+      --pos;
+      if (pos->GetRange().Contains(load_addr))
+        return *pos;
     }
+    
+    MemoryRegionInfo region;
+    if (pos == end) {
+      if (pos == begin)
+        return llvm::None;
+      auto prev = pos - 1;
+      // Address past the end of all our regions
+      region.GetRange().SetRangeBase(prev->GetRange().GetRangeEnd());
+      region.GetRange().SetRangeEnd(UINT64_MAX);
+    } else {
+      if (pos == begin) {
+        region.GetRange().SetRangeBase(0);
+      } else {
+        auto prev = pos - 1;
+        region.GetRange().SetRangeBase(prev->GetRange().GetRangeEnd());
+      }
+      region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase());
+    }
+    region.SetReadable(MemoryRegionInfo::eNo);
+    region.SetWritable(MemoryRegionInfo::eNo);
+    region.SetExecutable(MemoryRegionInfo::eNo);
+    region.SetMapped(MemoryRegionInfo::eNo);
+    return region;
   }
+  return llvm::None;
+}
 
-  // 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);
+llvm::Optional<MemoryRegionInfo>
+MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
+  // See if we have cached our memory regions yet?
+  if (!m_regions.empty())
+    return FindMemoryRegion(load_addr);
 
-  // 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;
+  // 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() ||
+      CreateRegionsCacheFromMemoryInfoList() ||
+      CreateRegionsCacheFromMemoryList() ||
+      CreateRegionsCacheFromMemory64List()) {
+    std::sort(m_regions.begin(), m_regions.end());
+    return FindMemoryRegion(load_addr);
+  }
+  // No source was able to create a memory region cache, so just make on entry
+  // that encompasses the entire address space and mark it as not mapped.
+  MemoryRegionInfo region;
+  region.GetRange().SetRangeBase(0);
+  region.GetRange().SetByteSize(UINT64_MAX);
+  region.SetMapped(MemoryRegionInfo::eNo);
+  m_regions.push_back(region);
+  return m_regions.back();
 }
 
 Status MinidumpParser::Initialize() {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to