diff --git a/lldb/include/lldb/Target/MemoryRegionInfo.h b/lldb/include/lldb/Target/MemoryRegionInfo.h --- a/lldb/include/lldb/Target/MemoryRegionInfo.h +++ b/lldb/include/lldb/Target/MemoryRegionInfo.h @@ -21,11 +21,13 @@ enum OptionalBool { eDontKnow = -1, eNo = 0, eYes = 1 }; - MemoryRegionInfo() - : m_range(), m_read(eDontKnow), m_write(eDontKnow), m_execute(eDontKnow), - m_mapped(eDontKnow), m_flash(eDontKnow), m_blocksize(0) {} - - ~MemoryRegionInfo() {} + MemoryRegionInfo() = default; + MemoryRegionInfo(RangeType range, OptionalBool read, OptionalBool write, + OptionalBool execute, OptionalBool mapped, ConstString name, + OptionalBool flash, lldb::offset_t blocksize) + : m_range(range), m_read(read), m_write(write), m_execute(execute), + m_mapped(mapped), m_name(name), m_flash(flash), m_blocksize(blocksize) { + } RangeType &GetRange() { return m_range; } @@ -88,20 +90,21 @@ bool operator==(const MemoryRegionInfo &rhs) const { return m_range == rhs.m_range && m_read == rhs.m_read && m_write == rhs.m_write && m_execute == rhs.m_execute && - m_mapped == rhs.m_mapped; + m_mapped == rhs.m_mapped && m_name == rhs.m_name && + m_flash == rhs.m_flash && m_blocksize == rhs.m_blocksize; } bool operator!=(const MemoryRegionInfo &rhs) const { return !(*this == rhs); } protected: RangeType m_range; - OptionalBool m_read; - OptionalBool m_write; - OptionalBool m_execute; - OptionalBool m_mapped; + OptionalBool m_read = eDontKnow; + OptionalBool m_write = eDontKnow; + OptionalBool m_execute = eDontKnow; + OptionalBool m_mapped = eDontKnow; ConstString m_name; - OptionalBool m_flash; - lldb::offset_t m_blocksize; + OptionalBool m_flash = eDontKnow; + lldb::offset_t m_blocksize = 0; }; inline bool operator<(const MemoryRegionInfo &lhs, @@ -117,6 +120,9 @@ return lhs < rhs.GetRange().GetRangeBase(); } +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const MemoryRegionInfo &Info); + // Forward-declarable wrapper. class MemoryRegionInfos : public std::vector { public: diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/lldb/source/Plugins/Process/minidump/MinidumpParser.h --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.h +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.h @@ -88,9 +88,9 @@ llvm::ArrayRef GetMemory(lldb::addr_t addr, size_t size); - MemoryRegionInfo GetMemoryRegionInfo(lldb::addr_t load_addr); - - const MemoryRegionInfos &GetMemoryRegions(); + /// Returns a list of memory regions and a flag indicating whether the list is + /// complete (includes all regions mapped into the process memory). + std::pair BuildMemoryRegions(); static llvm::StringRef GetStreamTypeAsString(StreamType stream_type); @@ -100,14 +100,10 @@ MinidumpParser(lldb::DataBufferSP data_sp, std::unique_ptr file); - MemoryRegionInfo FindMemoryRegion(lldb::addr_t load_addr) const; - private: lldb::DataBufferSP m_data_sp; std::unique_ptr m_file; ArchSpec m_arch; - MemoryRegionInfos m_regions; - bool m_parsed_regions = false; }; } // end namespace minidump diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -518,58 +518,26 @@ 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) - GetMemoryRegions(); - return FindMemoryRegion(load_addr); -} - -const MemoryRegionInfos &MinidumpParser::GetMemoryRegions() { - 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); - llvm::sort(m_regions.begin(), m_regions.end()); - } - return m_regions; +std::pair MinidumpParser::BuildMemoryRegions() { + // 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 + MemoryRegionInfos result; + const auto &return_sorted = [&](bool is_complete) { + llvm::sort(result); + return std::make_pair(std::move(result), is_complete); + }; + if (CreateRegionsCacheFromLinuxMaps(*this, result)) + return return_sorted(true); + if (CreateRegionsCacheFromMemoryInfoList(*this, result)) + return return_sorted(true); + if (CreateRegionsCacheFromMemoryList(*this, result)) + return return_sorted(false); + CreateRegionsCacheFromMemory64List(*this, result); + return return_sorted(false); } #define ENUM_TO_CSTR(ST) \ diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h --- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h +++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h @@ -111,6 +111,9 @@ const minidump::ExceptionStream *m_active_exception; lldb::CommandObjectSP m_command_sp; bool m_is_wow64; + llvm::Optional m_memory_regions; + + void BuildMemoryRegions(); }; } // namespace minidump diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp --- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp +++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -334,15 +334,46 @@ return ArchSpec(triple); } +void ProcessMinidump::BuildMemoryRegions() { + if (m_memory_regions) + return; + m_memory_regions.emplace(); + bool is_complete; + std::tie(*m_memory_regions, is_complete) = + m_minidump_parser->BuildMemoryRegions(); + // TODO: Use loaded modules to complete the region list. +} + Status ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr, - MemoryRegionInfo &range_info) { - range_info = m_minidump_parser->GetMemoryRegionInfo(load_addr); + MemoryRegionInfo ®ion) { + BuildMemoryRegions(); + auto pos = llvm::upper_bound(*m_memory_regions, load_addr); + if (pos != m_memory_regions->begin() && + std::prev(pos)->GetRange().Contains(load_addr)) { + region = *std::prev(pos); + return Status(); + } + + if (pos == m_memory_regions->begin()) + region.GetRange().SetRangeBase(0); + else + region.GetRange().SetRangeBase(std::prev(pos)->GetRange().GetRangeEnd()); + + if (pos == m_memory_regions->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 Status(); } -Status ProcessMinidump::GetMemoryRegions( - lldb_private::MemoryRegionInfos ®ion_list) { - region_list = m_minidump_parser->GetMemoryRegions(); +Status ProcessMinidump::GetMemoryRegions(MemoryRegionInfos ®ion_list) { + BuildMemoryRegions(); + region_list = *m_memory_regions; return Status(); } diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -17,6 +17,7 @@ LanguageRuntime.cpp Memory.cpp MemoryHistory.cpp + MemoryRegionInfo.cpp ModuleCache.cpp OperatingSystem.cpp PathMappingList.cpp diff --git a/lldb/source/Target/MemoryRegionInfo.cpp b/lldb/source/Target/MemoryRegionInfo.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Target/MemoryRegionInfo.cpp @@ -0,0 +1,20 @@ +//===-- MemoryRegionInfo.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/MemoryRegionInfo.h" + +llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &OS, + const MemoryRegionInfo &Info) { + return OS << llvm::formatv("MemoryRegionInfo([{0}, {1}), {2}, {3}, {4}, {5}, " + "`{6}`, {7}, {8})", + Info.GetRange().GetRangeBase(), + Info.GetRange().GetRangeEnd(), Info.GetReadable(), + Info.GetWritable(), Info.GetExecutable(), + Info.GetMapped(), Info.GetName(), Info.GetFlash(), + Info.GetBlocksize()); +} diff --git a/lldb/test/Shell/Minidump/memory-region.yaml b/lldb/test/Shell/Minidump/memory-region.yaml new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/Minidump/memory-region.yaml @@ -0,0 +1,49 @@ +# Check that memory region parsing works correctly, with a particular emphasis +# on the boundary conditions. + +# RUN: yaml2obj %s > %t +# RUN: %lldb -b -c %t \ +# RUN: -o "memory region 0" -o "memory region 0xd9000" \ +# RUN: -o "memory region 0xd9001" -o "memory region 0xdafff" \ +# RUN: -o "memory region 0xdb000" -o "memory region 0xdd000" | FileCheck %s + +# CHECK-LABEL: (lldb) memory region 0 +# CHECK: [0x0000000000000000-0x00000000000d9000) --- +# CHECK-LABEL: (lldb) memory region 0xd9000 +# CHECK: [0x00000000000d9000-0x00000000000db000) r-x /system/bin/app_process +# CHECK-LABEL: (lldb) memory region 0xd9001 +# CHECK: [0x00000000000d9000-0x00000000000db000) r-x /system/bin/app_process +# CHECK-LABEL: (lldb) memory region 0xdafff +# CHECK: [0x00000000000d9000-0x00000000000db000) r-x /system/bin/app_process +# CHECK-LABEL: (lldb) memory region 0xdb000 +# CHECK: [0x00000000000db000-0x00000000000dc000) --- +# CHECK-LABEL: (lldb) memory region 0xdd000 +# CHECK: [0x00000000000dd000-0xffffffffffffffff) --- + +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: AMD64 + Platform ID: Linux + CPU: + Vendor ID: GenuineIntel + Version Info: 0x00000000 + Feature Info: 0x00000000 + - Type: LinuxProcStatus + Text: | + Name: nonexisting-module + State: t (tracing stop) + Tgid: 29939 + Ngid: 0 + Pid: 29939 + PPid: 29370 + TracerPid: 29940 + Uid: 1001 1001 1001 1001 + Gid: 1001 1001 1001 1001 + + - Type: LinuxMaps + Text: | + 000d9000-000db000 r-xp 00000000 b3:04 227 /system/bin/app_process + 000dc000-000dd000 rw-p 00000000 00:00 0 + +... diff --git a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp --- a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp +++ b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp @@ -332,34 +332,6 @@ EXPECT_FALSE(parser->FindMemoryRange(0x7ffe0000 + 4096).hasValue()); } -void check_region(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()) { - SCOPED_TRACE(addr); - auto range_info = parser.GetMemoryRegionInfo(addr); - 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(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; @@ -378,17 +350,7 @@ Type: [ ] - Base Address: 0x0000000000010000 Allocation Protect: [ PAGE_READ_WRITE ] - Region Size: 0x0000000000010000 - State: [ MEM_COMMIT ] - Type: [ MEM_MAPPED ] - - Base Address: 0x0000000000020000 - Allocation Protect: [ PAGE_READ_WRITE ] - Region Size: 0x0000000000010000 - State: [ MEM_COMMIT ] - Type: [ MEM_MAPPED ] - - Base Address: 0x0000000000030000 - Allocation Protect: [ PAGE_READ_WRITE ] - Region Size: 0x0000000000001000 + Region Size: 0x0000000000021000 State: [ MEM_COMMIT ] Type: [ MEM_MAPPED ] - Base Address: 0x0000000000040000 @@ -413,21 +375,20 @@ )"), llvm::Succeeded()); - 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); + EXPECT_THAT( + parser->BuildMemoryRegions(), + testing::Pair(testing::ElementsAre( + MemoryRegionInfo({0x0, 0x10000}, no, no, no, no, + ConstString(), unknown, 0), + MemoryRegionInfo({0x10000, 0x21000}, yes, yes, no, yes, + ConstString(), unknown, 0), + MemoryRegionInfo({0x40000, 0x1000}, yes, no, no, yes, + ConstString(), unknown, 0), + MemoryRegionInfo({0x7ffe0000, 0x1000}, yes, no, no, yes, + ConstString(), unknown, 0), + MemoryRegionInfo({0x7ffe1000, 0xf000}, no, no, no, yes, + ConstString(), unknown, 0)), + true)); } TEST_F(MinidumpParserTest, GetMemoryRegionInfoFromMemoryList) { @@ -443,30 +404,33 @@ ... )"), llvm::Succeeded()); + // 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); + EXPECT_THAT( + parser->BuildMemoryRegions(), + testing::Pair(testing::ElementsAre( + MemoryRegionInfo({0x1000, 0x10}, yes, unknown, unknown, + yes, ConstString(), unknown, 0), + MemoryRegionInfo({0x2000, 0x20}, yes, unknown, unknown, + yes, ConstString(), unknown, 0)), + false)); } 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); + EXPECT_THAT( + parser->BuildMemoryRegions(), + testing::Pair(testing::ElementsAre( + MemoryRegionInfo({0x1000, 0x10}, yes, unknown, unknown, + yes, ConstString(), unknown, 0), + MemoryRegionInfo({0x2000, 0x20}, yes, unknown, unknown, + yes, ConstString(), unknown, 0)), + false)); } TEST_F(MinidumpParserTest, GetMemoryRegionInfoLinuxMaps) { @@ -478,57 +442,34 @@ 400d9000-400db000 r-xp 00000000 b3:04 227 /system/bin/app_process 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process 400dc000-400dd000 rw-p 00000000 00:00 0 - 400dd000-400ec000 r-xp 00000000 b3:04 300 /system/bin/linker 400ec000-400ed000 r--p 00000000 00:00 0 - 400ed000-400ee000 r--p 0000f000 b3:04 300 /system/bin/linker 400ee000-400ef000 rw-p 00010000 b3:04 300 /system/bin/linker - 400ef000-400fb000 rw-p 00000000 00:00 0 - 400fb000-400fc000 r-xp 00000000 b3:04 1096 /system/lib/liblog.so 400fc000-400fd000 rwxp 00001000 b3:04 1096 /system/lib/liblog.so - 400fd000-400ff000 r-xp 00002000 b3:04 1096 /system/lib/liblog.so - 400ff000-40100000 r--p 00003000 b3:04 1096 /system/lib/liblog.so - 40100000-40101000 rw-p 00004000 b3:04 1096 /system/lib/liblog.so - 40101000-40122000 r-xp 00000000 b3:04 955 /system/lib/libc.so - 40122000-40123000 rwxp 00021000 b3:04 955 /system/lib/libc.so - 40123000-40167000 r-xp 00022000 b3:04 955 /system/lib/libc.so - 40167000-40169000 r--p 00065000 b3:04 955 /system/lib/libc.so - 40169000-4016b000 rw-p 00067000 b3:04 955 /system/lib/libc.so - 4016b000-40176000 rw-p 00000000 00:00 0 ... )"), llvm::Succeeded()); // 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); + ConstString app_process("/system/bin/app_process"); + ConstString linker("/system/bin/linker"); + ConstString liblog("/system/lib/liblog.so"); + EXPECT_THAT( + parser->BuildMemoryRegions(), + testing::Pair(testing::ElementsAre( + MemoryRegionInfo({0x400d9000, 0x2000}, yes, no, yes, + yes, app_process, unknown, 0), + MemoryRegionInfo({0x400db000, 0x1000}, yes, no, no, yes, + app_process, unknown, 0), + MemoryRegionInfo({0x400dc000, 0x1000}, yes, yes, no, + yes, ConstString(), unknown, 0), + MemoryRegionInfo({0x400ec000, 0x1000}, yes, no, no, yes, + ConstString(), unknown, 0), + MemoryRegionInfo({0x400ee000, 0x1000}, yes, yes, no, + yes, linker, unknown, 0), + MemoryRegionInfo({0x400fc000, 0x1000}, yes, yes, yes, + yes, liblog, unknown, 0)), + true)); } // Windows Minidump tests