Skip to content

[LLDB][Minidump] Fix ProcessMinidump::GetMemoryRegions to include 64b regions when /proc/pid maps are missing. #101086

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1020,15 +1020,17 @@ MinidumpFileBuilder::AddMemoryList_32(Process::CoreFileMemoryRanges &ranges) {
// With a size of the number of ranges as a 32 bit num
// And then the size of all the ranges
error = AddDirectory(StreamType::MemoryList,
sizeof(llvm::support::ulittle32_t) +
sizeof(llvm::minidump::MemoryListHeader) +
descriptors.size() *
sizeof(llvm::minidump::MemoryDescriptor));
if (error.Fail())
return error;

llvm::minidump::MemoryListHeader list_header;
llvm::support::ulittle32_t memory_ranges_num =
static_cast<llvm::support::ulittle32_t>(descriptors.size());
m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t));
list_header.NumberOfMemoryRanges = memory_ranges_num;
m_data.AppendData(&list_header, sizeof(llvm::minidump::MemoryListHeader));
// For 32b we can get away with writing off the descriptors after the data.
// This means no cleanup loop needed.
m_data.AppendData(descriptors.data(),
Expand All @@ -1050,9 +1052,10 @@ MinidumpFileBuilder::AddMemoryList_64(Process::CoreFileMemoryRanges &ranges) {
if (error.Fail())
return error;

llvm::minidump::Memory64ListHeader list_header;
llvm::support::ulittle64_t memory_ranges_num =
static_cast<llvm::support::ulittle64_t>(ranges.size());
m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle64_t));
list_header.NumberOfMemoryRanges = memory_ranges_num;
// Capture the starting offset for all the descriptors so we can clean them up
// if needed.
offset_t starting_offset =
Expand All @@ -1064,8 +1067,8 @@ MinidumpFileBuilder::AddMemoryList_64(Process::CoreFileMemoryRanges &ranges) {
(ranges.size() * sizeof(llvm::minidump::MemoryDescriptor_64));
llvm::support::ulittle64_t memory_ranges_base_rva =
static_cast<llvm::support::ulittle64_t>(base_rva);
m_data.AppendData(&memory_ranges_base_rva,
sizeof(llvm::support::ulittle64_t));
list_header.BaseRVA = memory_ranges_base_rva;
m_data.AppendData(&list_header, sizeof(llvm::minidump::Memory64ListHeader));

bool cleanup_required = false;
std::vector<MemoryDescriptor_64> descriptors;
Expand Down
113 changes: 45 additions & 68 deletions lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,6 @@ const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() {

std::optional<minidump::Range>
MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
llvm::ArrayRef<uint8_t> data64 = GetStream(StreamType::Memory64List);
Log *log = GetLog(LLDBLog::Modules);

auto ExpectedMemory = GetMinidumpFile().getMemoryList();
Expand Down Expand Up @@ -457,33 +456,17 @@ MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
}
}

// 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 std::nullopt;

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 std::nullopt;

if (range_start <= addr && addr < range_start + range_size) {
return minidump::Range(range_start,
GetData().slice(base_rva, range_size));
if (!GetStream(StreamType::Memory64List).empty()) {
llvm::Error err = llvm::Error::success();
for (const auto &memory_desc : GetMinidumpFile().getMemory64List(err)) {
if (memory_desc.first.StartOfMemoryRange <= addr
&& addr < memory_desc.first.StartOfMemoryRange + memory_desc.first.DataSize) {
return minidump::Range(memory_desc.first.StartOfMemoryRange, memory_desc.second);
}
base_rva += range_size;
}

if (err)
LLDB_LOG_ERROR(log, std::move(err), "Failed to read memory64 list: {0}");
}

return std::nullopt;
Expand Down Expand Up @@ -512,6 +495,11 @@ llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
return range->range_ref.slice(offset, overlap);
}

llvm::iterator_range<FallibleMemory64Iterator> MinidumpParser::GetMemory64Iterator(llvm::Error &err) {
llvm::ErrorAsOutParameter ErrAsOutParam(&err);
return m_file->getMemory64List(err);
}

static bool
CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser,
std::vector<MemoryRegionInfo> &regions) {
Expand Down Expand Up @@ -553,53 +541,44 @@ static bool
CreateRegionsCacheFromMemoryList(MinidumpParser &parser,
std::vector<MemoryRegionInfo> &regions) {
Log *log = GetLog(LLDBLog::Modules);
// Cache the expected memory32 into an optional
// because it is possible to just have a memory64 list
auto ExpectedMemory = parser.GetMinidumpFile().getMemoryList();
if (!ExpectedMemory) {
LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),
"Failed to read memory list: {0}");
return false;
}
regions.reserve(ExpectedMemory->size());
for (const MemoryDescriptor &memory_desc : *ExpectedMemory) {
if (memory_desc.Memory.DataSize == 0)
continue;
MemoryRegionInfo region;
region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange);
region.GetRange().SetByteSize(memory_desc.Memory.DataSize);
region.SetReadable(MemoryRegionInfo::eYes);
region.SetMapped(MemoryRegionInfo::eYes);
regions.push_back(region);
} else {
for (const MemoryDescriptor &memory_desc : *ExpectedMemory) {
if (memory_desc.Memory.DataSize == 0)
continue;
MemoryRegionInfo region;
region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange);
region.GetRange().SetByteSize(memory_desc.Memory.DataSize);
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<MemoryRegionInfo> &regions) {
llvm::ArrayRef<uint8_t> data =
parser.GetStream(StreamType::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;
if (!parser.GetStream(StreamType::Memory64List).empty()) {
llvm::Error err = llvm::Error::success();
for (const auto &memory_desc : parser.GetMemory64Iterator(err)) {
if (memory_desc.first.DataSize == 0)
continue;
MemoryRegionInfo region;
region.GetRange().SetRangeBase(memory_desc.first.StartOfMemoryRange);
region.GetRange().SetByteSize(memory_desc.first.DataSize);
region.SetReadable(MemoryRegionInfo::eYes);
region.SetMapped(MemoryRegionInfo::eYes);
regions.push_back(region);
}

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);
if (err) {
LLDB_LOG_ERROR(log, std::move(err), "Failed to read memory64 list: {0}");
return false;
}
}

regions.shrink_to_fit();
return !regions.empty();
}
Expand All @@ -620,9 +599,7 @@ std::pair<MemoryRegionInfos, bool> MinidumpParser::BuildMemoryRegions() {
return return_sorted(true);
if (CreateRegionsCacheFromMemoryInfoList(*this, result))
return return_sorted(true);
if (CreateRegionsCacheFromMemoryList(*this, result))
return return_sorted(false);
CreateRegionsCacheFromMemory64List(*this, result);
CreateRegionsCacheFromMemoryList(*this, result);
return return_sorted(false);
}

Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Plugins/Process/minidump/MinidumpParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ struct Range {
}
};

using FallibleMemory64Iterator = llvm::object::MinidumpFile::FallibleMemory64Iterator;

class MinidumpParser {
public:
static llvm::Expected<MinidumpParser>
Expand Down Expand Up @@ -92,6 +94,8 @@ class MinidumpParser {
/// complete (includes all regions mapped into the process memory).
std::pair<MemoryRegionInfos, bool> BuildMemoryRegions();

llvm::iterator_range<FallibleMemory64Iterator> GetMemory64Iterator(llvm::Error &err);

static llvm::StringRef GetStreamTypeAsString(StreamType stream_type);

llvm::object::MinidumpFile &GetMinidumpFile() { return *m_file; }
Expand Down
20 changes: 0 additions & 20 deletions lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,3 @@ LinuxProcStatus::Parse(llvm::ArrayRef<uint8_t> &data) {
}

lldb::pid_t LinuxProcStatus::GetPid() const { return pid; }

std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t>
MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) {
const llvm::support::ulittle64_t *mem_ranges_count;
Status 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::ArrayRef(
reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()),
*mem_ranges_count),
*base_rva);
}
3 changes: 0 additions & 3 deletions lldb/source/Plugins/Process/minidump/MinidumpTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,6 @@ Status consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) {
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!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,3 +491,22 @@ def test_minidump_sysroot(self):
spec_dir_norm = os.path.normcase(module.GetFileSpec().GetDirectory())
exe_dir_norm = os.path.normcase(exe_dir)
self.assertEqual(spec_dir_norm, exe_dir_norm)

def test_minidump_memory64list(self):
"""Test that lldb can read from the memory64list in a minidump."""
self.process_from_yaml("linux-x86_64_mem64.yaml")

region_count = 3
region_info_list = self.process.GetMemoryRegions()
self.assertEqual(region_info_list.GetSize(), region_count)

region = lldb.SBMemoryRegionInfo()
self.assertTrue(region_info_list.GetMemoryRegionAtIndex(0, region))
self.assertEqual(region.GetRegionBase(), 0x7FFF12A84030)
self.assertTrue(region.GetRegionEnd(), 0x7FFF12A84030 + 0x2FD0)
self.assertTrue(region_info_list.GetMemoryRegionAtIndex(1, region))
self.assertEqual(region.GetRegionBase(), 0x00007fff12a87000)
self.assertTrue(region.GetRegionEnd(), 0x00007fff12a87000 + 0x00000018)
self.assertTrue(region_info_list.GetMemoryRegionAtIndex(2, region))
self.assertEqual(region.GetRegionBase(), 0x00007fff12a87018)
self.assertTrue(region.GetRegionEnd(), 0x00007fff12a87018 + 0x00000400)
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
--- !minidump
Streams:
- Type: SystemInfo
Processor Arch: AMD64
Processor Level: 6
Processor Revision: 15876
Number of Processors: 40
Platform ID: Linux
CSD Version: 'Linux 3.13.0-91-generic'
CPU:
Vendor ID: GenuineIntel
Version Info: 0x00000000
Feature Info: 0x00000000
- Type: LinuxProcStatus
Text: |
Name: linux-x86_64
State: t (tracing stop)
Tgid: 29917
Ngid: 0
Pid: 29917
PPid: 29370
TracerPid: 29918
Uid: 1001 1001 1001 1001
Gid: 1001 1001 1001 1001
- Type: ThreadList
Threads:
- Thread Id: 0x2896BB
Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700100000000000FFFFFFFF0000FFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B040A812FF7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050D0A75BBA7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Stack:
Start of Memory Range: 0x0
Content: ''
- Type: Memory64List
Memory Ranges:
- Start of Memory Range: 0x7FFF12A84030
Data Size: 0x2FD0
Content : ''
- Start of Memory Range: 0x00007fff12a87000
Data Size: 0x00000018
Content : ''
- Start of Memory Range: 0x00007fff12a87018
Data Size: 0x00000400
Content : ''
...
Loading