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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits