Cache memory regions in ProcessMinidump and use the linux maps as the source of the information if available

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)

Differential Revision: https://reviews.llvm.org/D55522

llvm-svn: 349182
This commit is contained in:
Greg Clayton 2018-12-14 19:36:01 +00:00
parent cac3d118ae
commit 026e1bf56a
13 changed files with 455 additions and 105 deletions

View File

@ -109,6 +109,20 @@ protected:
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 {

View File

@ -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 = "<group>"; };
26BC7F1A10F1B8EC00F91463 /* LineTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LineTable.cpp; path = source/Symbol/LineTable.cpp; sourceTree = "<group>"; };
26BC7C5C10F1B6E900F91463 /* LineTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LineTable.h; path = include/lldb/Symbol/LineTable.h; sourceTree = "<group>"; };
2647B64221C43BB000A81D15 /* LinuxProcMaps.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinuxProcMaps.cpp; path = Utility/LinuxProcMaps.cpp; sourceTree = "<group>"; };
2647B64121C43BAF00A81D15 /* LinuxProcMaps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinuxProcMaps.h; path = Utility/LinuxProcMaps.h; sourceTree = "<group>"; };
23059A0519532B96007B8189 /* LinuxSignals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LinuxSignals.cpp; path = Utility/LinuxSignals.cpp; sourceTree = "<group>"; };
23059A0619532B96007B8189 /* LinuxSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LinuxSignals.h; path = Utility/LinuxSignals.h; sourceTree = "<group>"; };
2647B63D21C436BC00A81D15 /* Listener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Listener.cpp; path = source/Utility/Listener.cpp; sourceTree = "<group>"; };
@ -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 */,

View File

@ -5,6 +5,7 @@ add_lldb_library(lldbPluginProcessUtility PLUGIN
HistoryThread.cpp
HistoryUnwind.cpp
InferiorCallPOSIX.cpp
LinuxProcMaps.cpp
LinuxSignals.cpp
MipsLinuxSignals.cpp
NativeRegisterContextRegisterInfo.cpp

View File

@ -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;
}
}

View File

@ -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 <functional>
namespace lldb_private {
typedef std::function<bool(const lldb_private::MemoryRegionInfo &,
const lldb_private::Status &)> LinuxMapCallback;
void ParseLinuxMapRegions(llvm::StringRef linux_map,
LinuxMapCallback const &callback);
} // namespace lldb_private
#endif // liblldb_LinuxProcMaps_H_

View File

@ -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 <algorithm>
#include <map>
#include <vector>
#include <utility>
using namespace lldb_private;
using namespace minidump;
@ -410,72 +412,147 @@ llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
return range->range_ref.slice(offset, overlap);
}
llvm::Optional<MemoryRegionInfo>
MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
MemoryRegionInfo info;
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryInfoList);
static bool
CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser,
std::vector<MemoryRegionInfo> &regions) {
auto data = parser.GetStream(MinidumpStreamType::LinuxMaps);
if (data.empty())
return llvm::None;
return false;
ParseLinuxMapRegions(llvm::toStringRef(data),
[&](const lldb_private::MemoryRegionInfo &region,
const lldb_private::Status &status) -> bool {
if (status.Success())
regions.push_back(region);
return true;
});
return !regions.empty();
}
std::vector<const MinidumpMemoryInfo *> mem_info_list =
MinidumpMemoryInfo::ParseMemoryInfoList(data);
static bool
CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser,
std::vector<MemoryRegionInfo> &regions) {
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;
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<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 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;
}
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();
}
// 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);
static bool
CreateRegionsCacheFromMemoryList(MinidumpParser &parser,
std::vector<MemoryRegionInfo> &regions) {
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();
}
// 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;
static bool
CreateRegionsCacheFromMemory64List(MinidumpParser &parser,
std::vector<MemoryRegionInfo> &regions) {
llvm::ArrayRef<uint8_t> data =
parser.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;
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;
}
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());
}
return FindMemoryRegion(load_addr);
}
Status MinidumpParser::Initialize() {

View File

@ -1,5 +1,4 @@
//===-- MinidumpParser.h -----------------------------------------*- C++
//-*-===//
//===-- MinidumpParser.h -----------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
@ -86,7 +85,7 @@ public:
llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size);
llvm::Optional<MemoryRegionInfo> 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 @@ public:
private:
MinidumpParser(const lldb::DataBufferSP &data_buf_sp);
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;
bool m_parsed_regions = false;
};
} // end namespace minidump

View File

@ -256,25 +256,6 @@ struct MinidumpMemoryInfoListHeader {
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<const MinidumpMemoryInfo *>
ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data);
};
static_assert(sizeof(MinidumpMemoryInfo) == 48,
"sizeof MinidumpMemoryInfo is not correct!");
enum class MinidumpMemoryInfoState : uint32_t {
MemCommit = 0x1000,
MemFree = 0x10000,
@ -310,6 +291,45 @@ enum class MinidumpMemoryProtectionContants : uint32_t {
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ PageTargetsInvalid)
};
// 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<const MinidumpMemoryInfo *>
ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data);
bool isReadable() const {
const auto mask = MinidumpMemoryProtectionContants::PageNoAccess;
return (static_cast<uint32_t>(mask) & protect) == 0;
}
bool isWritable() const {
const auto mask = MinidumpMemoryProtectionContants::PageWritable;
return (static_cast<uint32_t>(mask) & protect) != 0;
}
bool isExecutable() const {
const auto mask = MinidumpMemoryProtectionContants::PageExecutable;
return (static_cast<uint32_t>(mask) & protect) != 0;
}
bool isMapped() const {
return state != static_cast<uint32_t>(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 {

View File

@ -284,14 +284,8 @@ ArchSpec ProcessMinidump::GetArchitecture() {
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(); }

View File

@ -300,29 +300,120 @@ TEST_F(MinidumpParserTest, FindMemoryRangeWithFullMemoryMinidump) {
EXPECT_FALSE(parser->FindMemoryRange(0x7ffe0000 + 4096).hasValue());
}
void check_region_info(std::unique_ptr<MinidumpParser> &parser,
const uint64_t addr, MemoryRegionInfo::OptionalBool read,
MemoryRegionInfo::OptionalBool write,
MemoryRegionInfo::OptionalBool exec) {
void check_region(std::unique_ptr<MinidumpParser> &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<MinidumpParser> &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(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_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 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/<pid>/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 @@ TEST_F(MinidumpParserTest, MinidumpModuleOrder) {
ASSERT_TRUE((bool)name);
EXPECT_EQ(std::string("/tmp/b"), *name);
}