Skip to content

Commit a96a3f1

Browse files
authored
[lldb][Minidump Parser] Implement a range data vector for minidump memory ranges (#136040)
Recently I was debugging a Minidump with a few thousand ranges, and came across the (now deleted) comment: ``` // 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. ``` blaming this comment, it's 9 years old! Much overdue for this simple fix with a range data vector. I had to add a default constructor to Range in order to implement the RangeDataVector, but otherwise this just a replacement of look up logic.
1 parent 8ddada4 commit a96a3f1

File tree

2 files changed

+55
-38
lines changed

2 files changed

+55
-38
lines changed

lldb/source/Plugins/Process/minidump/MinidumpParser.cpp

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#include <algorithm>
2121
#include <map>
2222
#include <optional>
23-
#include <vector>
2423
#include <utility>
24+
#include <vector>
2525

2626
using namespace lldb_private;
2727
using namespace minidump;
@@ -75,8 +75,7 @@ UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) {
7575
if (GetArchitecture().GetTriple().isOSBinFormatELF()) {
7676
if (pdb70_uuid->Age != 0)
7777
return UUID(pdb70_uuid, sizeof(*pdb70_uuid));
78-
return UUID(&pdb70_uuid->Uuid,
79-
sizeof(pdb70_uuid->Uuid));
78+
return UUID(&pdb70_uuid->Uuid, sizeof(pdb70_uuid->Uuid));
8079
}
8180
return UUID(*pdb70_uuid);
8281
} else if (cv_signature == CvSignature::ElfBuildId)
@@ -429,62 +428,65 @@ MinidumpParser::GetExceptionStreams() {
429428

430429
std::optional<minidump::Range>
431430
MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
432-
Log *log = GetLog(LLDBLog::Modules);
431+
if (m_memory_ranges.IsEmpty())
432+
PopulateMemoryRanges();
433+
434+
const MemoryRangeVector::Entry *entry =
435+
m_memory_ranges.FindEntryThatContains(addr);
436+
if (!entry)
437+
return std::nullopt;
433438

439+
return entry->data;
440+
}
441+
442+
void MinidumpParser::PopulateMemoryRanges() {
443+
Log *log = GetLog(LLDBLog::Modules);
434444
auto ExpectedMemory = GetMinidumpFile().getMemoryList();
435-
if (!ExpectedMemory) {
436-
LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),
437-
"Failed to read memory list: {0}");
438-
} else {
445+
if (ExpectedMemory) {
439446
for (const auto &memory_desc : *ExpectedMemory) {
440447
const LocationDescriptor &loc_desc = memory_desc.Memory;
441448
const lldb::addr_t range_start = memory_desc.StartOfMemoryRange;
442449
const size_t range_size = loc_desc.DataSize;
443-
444-
if (loc_desc.RVA + loc_desc.DataSize > GetData().size())
445-
return std::nullopt;
446-
447-
if (range_start <= addr && addr < range_start + range_size) {
448-
auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc);
449-
if (!ExpectedSlice) {
450-
LLDB_LOG_ERROR(log, ExpectedSlice.takeError(),
451-
"Failed to get memory slice: {0}");
452-
return std::nullopt;
453-
}
454-
return minidump::Range(range_start, *ExpectedSlice);
450+
auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc);
451+
if (!ExpectedSlice) {
452+
LLDB_LOG_ERROR(log, ExpectedSlice.takeError(),
453+
"Failed to get memory slice: {0}");
454+
continue;
455455
}
456+
m_memory_ranges.Append(MemoryRangeVector::Entry(
457+
range_start, range_size,
458+
minidump::Range(range_start, *ExpectedSlice)));
456459
}
460+
} else {
461+
LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),
462+
"Failed to read memory list: {0}");
457463
}
458464

459465
if (!GetStream(StreamType::Memory64List).empty()) {
460466
llvm::Error err = llvm::Error::success();
461-
for (const auto &memory_desc : GetMinidumpFile().getMemory64List(err)) {
462-
if (memory_desc.first.StartOfMemoryRange <= addr
463-
&& addr < memory_desc.first.StartOfMemoryRange + memory_desc.first.DataSize) {
464-
return minidump::Range(memory_desc.first.StartOfMemoryRange, memory_desc.second);
465-
}
467+
for (const auto &memory_desc : GetMinidumpFile().getMemory64List(err)) {
468+
m_memory_ranges.Append(MemoryRangeVector::Entry(
469+
memory_desc.first.StartOfMemoryRange, memory_desc.first.DataSize,
470+
minidump::Range(memory_desc.first.StartOfMemoryRange,
471+
memory_desc.second)));
466472
}
467473

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

472-
return std::nullopt;
478+
m_memory_ranges.Sort();
473479
}
474480

475481
llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
476482
size_t size) {
477-
// I don't have a sense of how frequently this is called or how many memory
478-
// ranges a Minidump typically has, so I'm not sure if searching for the
479-
// appropriate range linearly each time is stupid. Perhaps we should build
480-
// an index for faster lookups.
481483
std::optional<minidump::Range> range = FindMemoryRange(addr);
482484
if (!range)
483485
return {};
484486

485487
// There's at least some overlap between the beginning of the desired range
486-
// (addr) and the current range. Figure out where the overlap begins and how
487-
// much overlap there is.
488+
// (addr) and the current range. Figure out where the overlap begins and
489+
// how much overlap there is.
488490

489491
const size_t offset = addr - range->start;
490492

@@ -495,7 +497,8 @@ llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
495497
return range->range_ref.slice(offset, overlap);
496498
}
497499

498-
llvm::iterator_range<FallibleMemory64Iterator> MinidumpParser::GetMemory64Iterator(llvm::Error &err) {
500+
llvm::iterator_range<FallibleMemory64Iterator>
501+
MinidumpParser::GetMemory64Iterator(llvm::Error &err) {
499502
llvm::ErrorAsOutParameter ErrAsOutParam(&err);
500503
return m_file->getMemory64List(err);
501504
}
@@ -607,8 +610,7 @@ std::pair<MemoryRegionInfos, bool> MinidumpParser::BuildMemoryRegions() {
607610
case StreamType::ST: \
608611
return #ST
609612

610-
llvm::StringRef
611-
MinidumpParser::GetStreamTypeAsString(StreamType stream_type) {
613+
llvm::StringRef MinidumpParser::GetStreamTypeAsString(StreamType stream_type) {
612614
switch (stream_type) {
613615
ENUM_TO_CSTR(Unused);
614616
ENUM_TO_CSTR(ThreadList);

lldb/source/Plugins/Process/minidump/MinidumpParser.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "lldb/Utility/Status.h"
1818
#include "lldb/Utility/UUID.h"
1919

20+
#include "lldb/Utility/RangeMap.h"
2021
#include "llvm/ADT/ArrayRef.h"
2122
#include "llvm/ADT/DenseMap.h"
2223
#include "llvm/ADT/StringRef.h"
@@ -35,6 +36,9 @@ namespace minidump {
3536

3637
// Describes a range of memory captured in the Minidump
3738
struct Range {
39+
// Default constructor required for range data vector
40+
// but unusued.
41+
Range() = default;
3842
lldb::addr_t start; // virtual address of the beginning of the range
3943
// range_ref - absolute pointer to the first byte of the range and size
4044
llvm::ArrayRef<uint8_t> range_ref;
@@ -45,9 +49,18 @@ struct Range {
4549
friend bool operator==(const Range &lhs, const Range &rhs) {
4650
return lhs.start == rhs.start && lhs.range_ref == rhs.range_ref;
4751
}
52+
53+
friend bool operator<(const Range &lhs, const Range &rhs) {
54+
if (lhs.start == rhs.start)
55+
return lhs.range_ref.size() < rhs.range_ref.size();
56+
return lhs.start < rhs.start;
57+
}
4858
};
4959

50-
using FallibleMemory64Iterator = llvm::object::MinidumpFile::FallibleMemory64Iterator;
60+
using MemoryRangeVector =
61+
lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, minidump::Range>;
62+
using FallibleMemory64Iterator =
63+
llvm::object::MinidumpFile::FallibleMemory64Iterator;
5164
using ExceptionStreamsIterator =
5265
llvm::object::MinidumpFile::ExceptionStreamsIterator;
5366

@@ -97,7 +110,8 @@ class MinidumpParser {
97110
/// complete (includes all regions mapped into the process memory).
98111
std::pair<MemoryRegionInfos, bool> BuildMemoryRegions();
99112

100-
llvm::iterator_range<FallibleMemory64Iterator> GetMemory64Iterator(llvm::Error &err);
113+
llvm::iterator_range<FallibleMemory64Iterator>
114+
GetMemory64Iterator(llvm::Error &err);
101115

102116
static llvm::StringRef GetStreamTypeAsString(StreamType stream_type);
103117

@@ -109,10 +123,11 @@ class MinidumpParser {
109123
private:
110124
MinidumpParser(lldb::DataBufferSP data_sp,
111125
std::unique_ptr<llvm::object::MinidumpFile> file);
112-
126+
void PopulateMemoryRanges();
113127
lldb::DataBufferSP m_data_sp;
114128
std::unique_ptr<llvm::object::MinidumpFile> m_file;
115129
ArchSpec m_arch;
130+
MemoryRangeVector m_memory_ranges;
116131
};
117132

118133
} // end namespace minidump

0 commit comments

Comments
 (0)