diff options
author | Dimitar Vlahovski <dvlahovski@google.com> | 2016-10-19 14:14:18 +0000 |
---|---|---|
committer | Dimitar Vlahovski <dvlahovski@google.com> | 2016-10-19 14:14:18 +0000 |
commit | b52206decce2fb92e132af8db02b66b5cee3583c (patch) | |
tree | 9aad2bdecdfc68e00a7f9714730b36ddad366a73 | |
parent | 67bb651ad165f56cdc7ceacd10e860b291572a58 (diff) | |
download | bcm5719-llvm-b52206decce2fb92e132af8db02b66b5cee3583c.tar.gz bcm5719-llvm-b52206decce2fb92e132af8db02b66b5cee3583c.zip |
Minidump plugin: functions parsing memory structures and filtering module list
Summary:
Now the Minidump parser can parse the:
1) MemoryInfoList - containing region info about memory ranges (readable,
writable, executable)
2) Memory64List - this is the stuct used when the Minidump is a
full-memory one.
3) Adding filtering of the module list (shared libraries list) - there
can be mutliple records in the module list under the same name but with
different load address (e.g. when the binary has non contigious
sections). FilterModuleList eliminates the duplicated modules, leaving
the one with the lowest load addr.
Added unit tests for everything.
Reviewers: labath, zturner
Subscribers: beanz, mgorny, modocache, lldb-commits, amccarth
Differential Revision: https://reviews.llvm.org/D25569
llvm-svn: 284593
-rw-r--r-- | lldb/source/Plugins/Process/minidump/MinidumpParser.cpp | 200 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/minidump/MinidumpParser.h | 24 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp | 57 | ||||
-rw-r--r-- | lldb/source/Plugins/Process/minidump/MinidumpTypes.h | 77 | ||||
-rw-r--r-- | lldb/unittests/Process/minidump/CMakeLists.txt | 4 | ||||
-rw-r--r-- | lldb/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp | bin | 0 -> 9280561 bytes | |||
-rw-r--r-- | lldb/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp | bin | 0 -> 63744 bytes | |||
-rw-r--r-- | lldb/unittests/Process/minidump/MinidumpParserTest.cpp | 104 |
8 files changed, 464 insertions, 2 deletions
diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp index 2ccea6839cb..abb0810c813 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -11,8 +11,11 @@ #include "MinidumpParser.h" // Other libraries and framework includes +#include "lldb/Target/MemoryRegionInfo.h" + // C includes // C++ includes +#include <map> using namespace lldb_private; using namespace minidump; @@ -100,6 +103,14 @@ llvm::ArrayRef<MinidumpThread> MinidumpParser::GetThreads() { return MinidumpThread::ParseThreadList(data); } +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContext(const MinidumpThread &td) { + if (td.thread_context.rva + td.thread_context.data_size > GetData().size()) + return llvm::None; + + return GetData().slice(td.thread_context.rva, td.thread_context.data_size); +} + const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() { llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo); @@ -216,6 +227,42 @@ llvm::ArrayRef<MinidumpModule> MinidumpParser::GetModuleList() { return MinidumpModule::ParseModuleList(data); } +std::vector<const MinidumpModule *> MinidumpParser::GetFilteredModuleList() { + llvm::ArrayRef<MinidumpModule> modules = GetModuleList(); + // mapping module_name to pair(load_address, pointer to module struct in + // memory) + llvm::StringMap<std::pair<uint64_t, const MinidumpModule *>> lowest_addr; + + std::vector<const MinidumpModule *> filtered_modules; + + llvm::Optional<std::string> name; + std::string module_name; + + for (const auto &module : modules) { + name = GetMinidumpString(module.module_name_rva); + + if (!name) + continue; + + module_name = name.getValue(); + + auto iter = lowest_addr.end(); + bool exists; + std::tie(iter, exists) = lowest_addr.try_emplace( + module_name, std::make_pair(module.base_of_image, &module)); + + if (exists && module.base_of_image < iter->second.first) + iter->second = std::make_pair(module.base_of_image, &module); + } + + filtered_modules.reserve(lowest_addr.size()); + for (const auto &module : lowest_addr) { + filtered_modules.push_back(module.second.second); + } + + return filtered_modules; +} + const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() { llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception); @@ -224,3 +271,156 @@ const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() { return MinidumpExceptionStream::Parse(data); } + +llvm::Optional<minidump::Range> +MinidumpParser::FindMemoryRange(lldb::addr_t addr) { + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryList); + llvm::ArrayRef<uint8_t> data64 = GetStream(MinidumpStreamType::Memory64List); + + if (data.empty() && data64.empty()) + return llvm::None; + + if (!data.empty()) { + llvm::ArrayRef<MinidumpMemoryDescriptor> memory_list = + MinidumpMemoryDescriptor::ParseMemoryList(data); + + if (memory_list.empty()) + return llvm::None; + + for (const auto &memory_desc : memory_list) { + const MinidumpLocationDescriptor &loc_desc = memory_desc.memory; + const lldb::addr_t range_start = memory_desc.start_of_memory_range; + const size_t range_size = loc_desc.data_size; + + if (loc_desc.rva + loc_desc.data_size > GetData().size()) + return llvm::None; + + if (range_start <= addr && addr < range_start + range_size) { + return minidump::Range(range_start, + GetData().slice(loc_desc.rva, range_size)); + } + } + } + + // Some Minidumps have a Memory64ListStream that captures all the heap + // memory (full-memory Minidumps). We can't exactly use the same loop as + // above, because the Minidump uses slightly different data structures to + // describe those + + if (!data64.empty()) { + llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; + uint64_t base_rva; + std::tie(memory64_list, base_rva) = + MinidumpMemoryDescriptor64::ParseMemory64List(data64); + + if (memory64_list.empty()) + return llvm::None; + + for (const auto &memory_desc64 : memory64_list) { + const lldb::addr_t range_start = memory_desc64.start_of_memory_range; + const size_t range_size = memory_desc64.data_size; + + if (base_rva + range_size > GetData().size()) + return llvm::None; + + if (range_start <= addr && addr < range_start + range_size) { + return minidump::Range(range_start, + GetData().slice(base_rva, range_size)); + } + base_rva += range_size; + } + } + + return llvm::None; +} + +llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr, + size_t size) { + // I don't have a sense of how frequently this is called or how many memory + // ranges a Minidump typically has, so I'm not sure if searching for the + // appropriate range linearly each time is stupid. Perhaps we should build + // an index for faster lookups. + llvm::Optional<minidump::Range> range = FindMemoryRange(addr); + if (!range) + return {}; + + // There's at least some overlap between the beginning of the desired range + // (addr) and the current range. Figure out where the overlap begins and + // how much overlap there is. + + const size_t offset = addr - range->start; + + if (addr < range->start || offset >= range->range_ref.size()) + return {}; + + const size_t overlap = std::min(size, range->range_ref.size() - offset); + 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); + if (data.empty()) + return llvm::None; + + 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); + + 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; + } + } + + // 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; +} diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/lldb/source/Plugins/Process/minidump/MinidumpParser.h index 76a8ece00b2..202ef37d8cd 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.h +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.h @@ -34,6 +34,16 @@ namespace lldb_private { namespace minidump { +// Describes a range of memory captured in the Minidump +struct Range { + lldb::addr_t start; // virtual address of the beginning of the range + // range_ref - absolute pointer to the first byte of the range and size + llvm::ArrayRef<uint8_t> range_ref; + + Range(lldb::addr_t start, llvm::ArrayRef<uint8_t> range_ref) + : start(start), range_ref(range_ref) {} +}; + class MinidumpParser { public: static llvm::Optional<MinidumpParser> @@ -47,6 +57,8 @@ public: llvm::ArrayRef<MinidumpThread> GetThreads(); + llvm::ArrayRef<uint8_t> GetThreadContext(const MinidumpThread &td); + const MinidumpSystemInfo *GetSystemInfo(); ArchSpec GetArchitecture(); @@ -59,8 +71,20 @@ public: llvm::ArrayRef<MinidumpModule> GetModuleList(); + // There are cases in which there is more than one record in the ModuleList + // for the same module name.(e.g. when the binary has non contiguous segments) + // So this function returns a filtered module list - if it finds records that + // have the same name, it keeps the copy with the lowest load address. + std::vector<const MinidumpModule *> GetFilteredModuleList(); + const MinidumpExceptionStream *GetExceptionStream(); + llvm::Optional<Range> FindMemoryRange(lldb::addr_t addr); + + llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size); + + llvm::Optional<MemoryRegionInfo> GetMemoryRegionInfo(lldb::addr_t); + private: lldb::DataBufferSP m_data_sp; const MinidumpHeader *m_header; diff --git a/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp b/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp index 5f8aa59397f..863d124a7cc 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp @@ -176,3 +176,60 @@ MinidumpExceptionStream::Parse(llvm::ArrayRef<uint8_t> &data) { return exception_stream; } + +llvm::ArrayRef<MinidumpMemoryDescriptor> +MinidumpMemoryDescriptor::ParseMemoryList(llvm::ArrayRef<uint8_t> &data) { + const llvm::support::ulittle32_t *mem_ranges_count; + Error error = consumeObject(data, mem_ranges_count); + if (error.Fail() || + *mem_ranges_count * sizeof(MinidumpMemoryDescriptor) > data.size()) + return {}; + + return llvm::makeArrayRef( + reinterpret_cast<const MinidumpMemoryDescriptor *>(data.data()), + *mem_ranges_count); +} + +std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t> +MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) { + const llvm::support::ulittle64_t *mem_ranges_count; + Error error = consumeObject(data, mem_ranges_count); + if (error.Fail() || + *mem_ranges_count * sizeof(MinidumpMemoryDescriptor64) > data.size()) + return {}; + + const llvm::support::ulittle64_t *base_rva; + error = consumeObject(data, base_rva); + if (error.Fail()) + return {}; + + return std::make_pair( + llvm::makeArrayRef( + reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()), + *mem_ranges_count), + *base_rva); +} + +std::vector<const MinidumpMemoryInfo *> +MinidumpMemoryInfo::ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data) { + const MinidumpMemoryInfoListHeader *header; + Error error = consumeObject(data, header); + if (error.Fail() || + header->size_of_header < sizeof(MinidumpMemoryInfoListHeader) || + header->size_of_entry < sizeof(MinidumpMemoryInfo)) + return {}; + + data = data.drop_front(header->size_of_header - + sizeof(MinidumpMemoryInfoListHeader)); + + if (header->size_of_entry * header->num_of_entries > data.size()) + return {}; + + std::vector<const MinidumpMemoryInfo *> result; + for (uint64_t i = 0; i < header->num_of_entries; ++i) { + result.push_back(reinterpret_cast<const MinidumpMemoryInfo *>( + data.data() + i * header->size_of_entry)); + } + + return result; +} diff --git a/lldb/source/Plugins/Process/minidump/MinidumpTypes.h b/lldb/source/Plugins/Process/minidump/MinidumpTypes.h index bc3d846550a..1b903075010 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpTypes.h +++ b/lldb/source/Plugins/Process/minidump/MinidumpTypes.h @@ -207,10 +207,23 @@ static_assert(sizeof(MinidumpLocationDescriptor) == 8, struct MinidumpMemoryDescriptor { llvm::support::ulittle64_t start_of_memory_range; MinidumpLocationDescriptor memory; + + static llvm::ArrayRef<MinidumpMemoryDescriptor> + ParseMemoryList(llvm::ArrayRef<uint8_t> &data); }; static_assert(sizeof(MinidumpMemoryDescriptor) == 16, "sizeof MinidumpMemoryDescriptor is not correct!"); +struct MinidumpMemoryDescriptor64 { + llvm::support::ulittle64_t start_of_memory_range; + llvm::support::ulittle64_t data_size; + + static std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t> + ParseMemory64List(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpMemoryDescriptor64) == 16, + "sizeof MinidumpMemoryDescriptor64 is not correct!"); + // Reference: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680365.aspx struct MinidumpDirectory { @@ -221,6 +234,70 @@ static_assert(sizeof(MinidumpDirectory) == 12, "sizeof MinidumpDirectory is not correct!"); // Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680385(v=vs.85).aspx +struct MinidumpMemoryInfoListHeader { + llvm::support::ulittle32_t size_of_header; + llvm::support::ulittle32_t size_of_entry; + llvm::support::ulittle64_t num_of_entries; +}; +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, + MemReserve = 0x2000, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemFree) +}; + +enum class MinidumpMemoryInfoType : uint32_t { + MemImage = 0x1000000, + MemMapped = 0x40000, + MemPrivate = 0x20000, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemImage) +}; + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx +enum class MinidumpMemoryProtectionContants : uint32_t { + PageExecute = 0x10, + PageExecuteRead = 0x20, + PageExecuteReadWrite = 0x40, + PageExecuteWriteCopy = 0x80, + PageNoAccess = 0x01, + PageReadOnly = 0x02, + PageReadWrite = 0x04, + PageWriteCopy = 0x08, + PageTargetsInvalid = 0x40000000, + PageTargetsNoUpdate = 0x40000000, + + PageWritable = PageExecuteReadWrite | PageExecuteWriteCopy | PageReadWrite | + PageWriteCopy, + PageExecutable = PageExecute | PageExecuteRead | PageExecuteReadWrite | + PageExecuteWriteCopy, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ PageTargetsInvalid) +}; + +// Reference: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680517(v=vs.85).aspx struct MinidumpThread { llvm::support::ulittle32_t thread_id; diff --git a/lldb/unittests/Process/minidump/CMakeLists.txt b/lldb/unittests/Process/minidump/CMakeLists.txt index f96f4a47d98..c24ef475a6c 100644 --- a/lldb/unittests/Process/minidump/CMakeLists.txt +++ b/lldb/unittests/Process/minidump/CMakeLists.txt @@ -4,6 +4,8 @@ add_lldb_unittest(LLDBMinidumpTests set(test_inputs linux-x86_64.dmp - fizzbuzz_no_heap.dmp) + linux-x86_64_not_crashed.dmp + fizzbuzz_no_heap.dmp + fizzbuzz_wow64.dmp) add_unittest_inputs(LLDBMinidumpTests "${test_inputs}") diff --git a/lldb/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp b/lldb/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp Binary files differnew file mode 100644 index 00000000000..3d97186f2cd --- /dev/null +++ b/lldb/unittests/Process/minidump/Inputs/fizzbuzz_wow64.dmp diff --git a/lldb/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp b/lldb/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp Binary files differnew file mode 100644 index 00000000000..ad4b61a7bbb --- /dev/null +++ b/lldb/unittests/Process/minidump/Inputs/linux-x86_64_not_crashed.dmp diff --git a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp index 7597fab36c2..4600a81afef 100644 --- a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp +++ b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp @@ -19,6 +19,7 @@ #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Host/FileSpec.h" +#include "lldb/Target/MemoryRegionInfo.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" @@ -68,7 +69,11 @@ TEST_F(MinidumpParserTest, GetThreads) { ASSERT_EQ(1UL, thread_list.size()); const MinidumpThread thread = thread_list[0]; - ASSERT_EQ(16001UL, thread.thread_id); + + EXPECT_EQ(16001UL, thread.thread_id); + + llvm::ArrayRef<uint8_t> context = parser->GetThreadContext(thread); + EXPECT_EQ(1232UL, context.size()); } TEST_F(MinidumpParserTest, GetThreadsTruncatedFile) { @@ -131,6 +136,28 @@ TEST_F(MinidumpParserTest, GetModuleList) { } } +TEST_F(MinidumpParserTest, GetFilteredModuleList) { + SetUpData("linux-x86_64_not_crashed.dmp"); + llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList(); + std::vector<const MinidumpModule *> filtered_modules = + parser->GetFilteredModuleList(); + EXPECT_EQ(10UL, modules.size()); + EXPECT_EQ(9UL, filtered_modules.size()); + // EXPECT_GT(modules.size(), filtered_modules.size()); + bool found = false; + for (size_t i = 0; i < filtered_modules.size(); ++i) { + llvm::Optional<std::string> name = + parser->GetMinidumpString(filtered_modules[i]->module_name_rva); + ASSERT_TRUE(name.hasValue()); + if (name.getValue() == "/tmp/test/linux-x86_64_not_crashed") { + ASSERT_FALSE(found) << "There should be only one module with this name " + "in the filtered module list"; + found = true; + ASSERT_EQ(0x400000UL, filtered_modules[i]->base_of_image); + } + } +} + TEST_F(MinidumpParserTest, GetExceptionStream) { SetUpData("linux-x86_64.dmp"); const MinidumpExceptionStream *exception_stream = @@ -139,6 +166,81 @@ TEST_F(MinidumpParserTest, GetExceptionStream) { ASSERT_EQ(11UL, exception_stream->exception_record.exception_code); } +void check_mem_range_exists(std::unique_ptr<MinidumpParser> &parser, + const uint64_t range_start, + const uint64_t range_size) { + llvm::Optional<minidump::Range> range = parser->FindMemoryRange(range_start); + ASSERT_TRUE(range.hasValue()) << "There is no range containing this address"; + EXPECT_EQ(range_start, range->start); + EXPECT_EQ(range_start + range_size, range->start + range->range_ref.size()); +} + +TEST_F(MinidumpParserTest, FindMemoryRange) { + SetUpData("linux-x86_64.dmp"); + // There are two memory ranges in the file (size is in bytes, decimal): + // 1) 0x401d46 256 + // 2) 0x7ffceb34a000 12288 + EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue()); + + check_mem_range_exists(parser, 0x401d46, 256); + EXPECT_FALSE(parser->FindMemoryRange(0x401d46 + 256).hasValue()); + + check_mem_range_exists(parser, 0x7ffceb34a000, 12288); + EXPECT_FALSE(parser->FindMemoryRange(0x7ffceb34a000 + 12288).hasValue()); +} + +TEST_F(MinidumpParserTest, GetMemory) { + SetUpData("linux-x86_64.dmp"); + + EXPECT_EQ(128UL, parser->GetMemory(0x401d46, 128).size()); + EXPECT_EQ(256UL, parser->GetMemory(0x401d46, 512).size()); + + EXPECT_EQ(12288UL, parser->GetMemory(0x7ffceb34a000, 12288).size()); + EXPECT_EQ(1024UL, parser->GetMemory(0x7ffceb34a000, 1024).size()); + + EXPECT_TRUE(parser->GetMemory(0x500000, 512).empty()); +} + +TEST_F(MinidumpParserTest, FindMemoryRangeWithFullMemoryMinidump) { + SetUpData("fizzbuzz_wow64.dmp"); + + // There are a lot of ranges in the file, just testing with some of them + EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue()); + check_mem_range_exists(parser, 0x10000, 65536); // first range + check_mem_range_exists(parser, 0x40000, 4096); + EXPECT_FALSE(parser->FindMemoryRange(0x40000 + 4096).hasValue()); + check_mem_range_exists(parser, 0x77c12000, 8192); + check_mem_range_exists(parser, 0x7ffe0000, 4096); // last range + 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) { + 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()); +} + +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); +} + // Windows Minidump tests // fizzbuzz_no_heap.dmp is copied from the WinMiniDump tests TEST_F(MinidumpParserTest, GetArchitectureWindows) { |