Index: include/lldb/Target/MemoryRegionInfo.h =================================================================== --- include/lldb/Target/MemoryRegionInfo.h +++ include/lldb/Target/MemoryRegionInfo.h @@ -109,8 +109,22 @@ 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 { template <> struct format_provider { 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,19 @@ 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; + bool m_parsed_regions = false; }; } // 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,187 @@ 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; + auto text = llvm::toStringRef(data); + llvm::StringRef line; + constexpr auto yes = MemoryRegionInfo::eYes; + constexpr 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; + return false; + + constexpr auto yes = MemoryRegionInfo::eYes; + constexpr auto no = MemoryRegionInfo::eNo; - const auto yes = MemoryRegionInfo::eYes; - const auto no = MemoryRegionInfo::eNo; - - const MinidumpMemoryInfo *next_entry = nullptr; + m_regions.reserve(mem_info_list.size()); for (const auto &entry : mem_info_list) { - const auto head = entry->base_address; - const auto tail = head + entry->region_size; + 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); + m_regions.push_back(region); + } + return !m_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); +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; + + m_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.SetMapped(MemoryRegionInfo::eYes); + m_regions.push_back(region); + } + m_regions.shrink_to_fit(); + return !m_regions.empty(); +} - const uint32_t PageNoAccess = - static_cast(MinidumpMemoryProtectionContants::PageNoAccess); - info.SetReadable((entry->protect & PageNoAccess) == 0 ? yes : no); +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; + + m_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.SetMapped(MemoryRegionInfo::eYes); + m_regions.push_back(region); + } + m_regions.shrink_to_fit(); + return !m_regions.empty(); +} - 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 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; - } +llvm::Optional +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; + if (pos != begin) { + --pos; + if (pos->GetRange().Contains(load_addr)) + return *pos; } + + MemoryRegionInfo region; + if (pos == begin) + region.GetRange().SetRangeBase(0); + else { + auto prev = pos - 1; + 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); +llvm::Optional +MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) { + // See if we have cached our memory regions yet? + if (m_parsed_regions) + 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 + m_parsed_regions = true; + if (!CreateRegionsCacheFromLinuxMaps()) + if (!CreateRegionsCacheFromMemoryInfoList()) + if (!CreateRegionsCacheFromMemoryList()) + CreateRegionsCacheFromMemory64List(); + std::sort(m_regions.begin(), m_regions.end()); + return FindMemoryRegion(load_addr); } Status MinidumpParser::Initialize() { Index: source/Plugins/Process/minidump/MinidumpTypes.h =================================================================== --- source/Plugins/Process/minidump/MinidumpTypes.h +++ source/Plugins/Process/minidump/MinidumpTypes.h @@ -256,25 +256,6 @@ 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 - ParseMemoryInfoList(llvm::ArrayRef &data); -}; -static_assert(sizeof(MinidumpMemoryInfo) == 48, - "sizeof MinidumpMemoryInfo is not correct!"); - enum class MinidumpMemoryInfoState : uint32_t { MemCommit = 0x1000, MemFree = 0x10000, @@ -311,6 +292,45 @@ }; // 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 + ParseMemoryInfoList(llvm::ArrayRef &data); + + bool isReadable() const { + const auto mask = MinidumpMemoryProtectionContants::PageNoAccess; + return (static_cast(mask) & protect) == 0; + } + + bool isWritable() const { + const auto mask = MinidumpMemoryProtectionContants::PageWritable; + return (static_cast(mask) & protect) != 0; + } + + bool isExecutable() const { + const auto mask = MinidumpMemoryProtectionContants::PageExecutable; + return (static_cast(mask) & protect) != 0; + } + + bool isMapped() const { + return state != static_cast(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;