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 GetMemory(lldb::addr_t addr, size_t size); - llvm::Optional GetMemoryRegionInfo(lldb::addr_t); + llvm::Optional 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 + FindMemoryRegion(lldb::addr_t load_addr) const; + private: lldb::DataBufferSP m_data_sp; llvm::DenseMap m_directory_map; ArchSpec m_arch; + std::vector 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 #include #include +#include using namespace lldb_private; using namespace minidump; @@ -401,72 +402,207 @@ return range->range_ref.slice(offset, overlap); } -llvm::Optional -MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) { - MemoryRegionInfo info; +bool MinidumpParser::CreateRegionsCacheFromLinuxMaps() { + llvm::ArrayRef 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 data = GetStream(MinidumpStreamType::MemoryInfoList); if (data.empty()) - return llvm::None; - + return false; std::vector 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(MinidumpMemoryProtectionContants::PageNoAccess); - info.SetReadable((entry->protect & PageNoAccess) == 0 ? yes : no); - const uint32_t PageWritable = static_cast(MinidumpMemoryProtectionContants::PageWritable); - info.SetWritable((entry->protect & PageWritable) != 0 ? yes : no); - - const uint32_t PageExecutable = static_cast( - MinidumpMemoryProtectionContants::PageExecutable); - info.SetExecutable((entry->protect & PageExecutable) != 0 ? yes : no); - + const uint32_t PageExecutable = + static_cast(MinidumpMemoryProtectionContants::PageExecutable); const uint32_t MemFree = static_cast(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 data = GetStream(MinidumpStreamType::MemoryList); + + if (data.empty()) + return false; + + llvm::ArrayRef 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 data = GetStream(MinidumpStreamType::Memory64List); + + if (data.empty()) + return false; + + llvm::ArrayRef 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 +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 +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() {