Index: lldb/trunk/include/lldb/Target/MemoryRegionInfo.h =================================================================== --- lldb/trunk/include/lldb/Target/MemoryRegionInfo.h +++ lldb/trunk/include/lldb/Target/MemoryRegionInfo.h @@ -109,6 +109,20 @@ 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 { Index: lldb/trunk/lldb.xcodeproj/project.pbxproj =================================================================== --- lldb/trunk/lldb.xcodeproj/project.pbxproj +++ lldb/trunk/lldb.xcodeproj/project.pbxproj @@ -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 = ""; }; 26BC7F1A10F1B8EC00F91463 /* LineTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LineTable.cpp; path = source/Symbol/LineTable.cpp; sourceTree = ""; }; 26BC7C5C10F1B6E900F91463 /* LineTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LineTable.h; path = include/lldb/Symbol/LineTable.h; sourceTree = ""; }; + 2647B64221C43BB000A81D15 /* LinuxProcMaps.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinuxProcMaps.cpp; path = Utility/LinuxProcMaps.cpp; sourceTree = ""; }; + 2647B64121C43BAF00A81D15 /* LinuxProcMaps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinuxProcMaps.h; path = Utility/LinuxProcMaps.h; sourceTree = ""; }; 23059A0519532B96007B8189 /* LinuxSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinuxSignals.cpp; path = Utility/LinuxSignals.cpp; sourceTree = ""; }; 23059A0619532B96007B8189 /* LinuxSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinuxSignals.h; path = Utility/LinuxSignals.h; sourceTree = ""; }; 2647B63D21C436BC00A81D15 /* Listener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Listener.cpp; path = source/Utility/Listener.cpp; sourceTree = ""; }; @@ -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 */, Index: lldb/trunk/source/Plugins/Process/Utility/CMakeLists.txt =================================================================== --- lldb/trunk/source/Plugins/Process/Utility/CMakeLists.txt +++ lldb/trunk/source/Plugins/Process/Utility/CMakeLists.txt @@ -5,6 +5,7 @@ HistoryThread.cpp HistoryUnwind.cpp InferiorCallPOSIX.cpp + LinuxProcMaps.cpp LinuxSignals.cpp MipsLinuxSignals.cpp NativeRegisterContextRegisterInfo.cpp Index: lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.h =================================================================== --- lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.h +++ lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.h @@ -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 + + +namespace lldb_private { + +typedef std::function LinuxMapCallback; + +void ParseLinuxMapRegions(llvm::StringRef linux_map, + LinuxMapCallback const &callback); + +} // namespace lldb_private + +#endif // liblldb_LinuxProcMaps_H_ Index: lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.cpp +++ lldb/trunk/source/Plugins/Process/Utility/LinuxProcMaps.cpp @@ -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; + } +} Index: lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h +++ lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h @@ -1,5 +1,4 @@ -//===-- MinidumpParser.h -----------------------------------------*- C++ -//-*-===// +//===-- MinidumpParser.h -----------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // @@ -86,7 +85,7 @@ llvm::ArrayRef GetMemory(lldb::addr_t addr, size_t size); - llvm::Optional 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 @@ private: MinidumpParser(const lldb::DataBufferSP &data_buf_sp); + MemoryRegionInfo 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: lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp +++ lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -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 #include #include +#include using namespace lldb_private; using namespace minidump; @@ -410,72 +412,147 @@ return range->range_ref.slice(offset, overlap); } -llvm::Optional -MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) { - MemoryRegionInfo info; - llvm::ArrayRef data = GetStream(MinidumpStreamType::MemoryInfoList); +static bool +CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser, + std::vector ®ions) { + auto data = parser.GetStream(MinidumpStreamType::LinuxMaps); if (data.empty()) - return llvm::None; + return false; + ParseLinuxMapRegions(llvm::toStringRef(data), + [&](const lldb_private::MemoryRegionInfo ®ion, + const lldb_private::Status &status) -> bool { + if (status.Success()) + regions.push_back(region); + return true; + }); + return !regions.empty(); +} - std::vector mem_info_list = - MinidumpMemoryInfo::ParseMemoryInfoList(data); +static bool +CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser, + std::vector ®ions) { + 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; - - const auto yes = MemoryRegionInfo::eYes; - const auto no = MemoryRegionInfo::eNo; - - const MinidumpMemoryInfo *next_entry = nullptr; + 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) { - 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); + regions.push_back(region); + } + return !regions.empty(); +} + +static bool +CreateRegionsCacheFromMemoryList(MinidumpParser &parser, + std::vector ®ions) { + 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(); +} + +static bool +CreateRegionsCacheFromMemory64List(MinidumpParser &parser, + std::vector ®ions) { + llvm::ArrayRef data = + parser.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; + + 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(); +} + +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; +} - 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(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 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; - } +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()); } - - // 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; + return FindMemoryRegion(load_addr); } Status MinidumpParser::Initialize() { Index: lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/MinidumpTypes.h +++ lldb/trunk/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; Index: lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.cpp +++ lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -284,14 +284,8 @@ 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(); } Index: lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp =================================================================== --- lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp +++ lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp @@ -300,29 +300,120 @@ EXPECT_FALSE(parser->FindMemoryRange(0x7ffe0000 + 4096).hasValue()); } -void check_region_info(std::unique_ptr &parser, - const uint64_t addr, MemoryRegionInfo::OptionalBool read, - MemoryRegionInfo::OptionalBool write, - MemoryRegionInfo::OptionalBool exec) { +void check_region(std::unique_ptr &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 &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//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 @@ ASSERT_TRUE((bool)name); EXPECT_EQ(std::string("/tmp/b"), *name); } +