Minidump: Add support for the MemoryList stream

Summary:
the stream format is exactly the same as for ThreadList and ModuleList
streams, only the entry types are slightly different, so the changes in
this patch are just straight-forward applications of established
patterns.

Reviewers: amccarth, jhenderson, clayborg

Subscribers: markmentovai, lldb-commits, llvm-commits

Tags: #llvm

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

llvm-svn: 360908
This commit is contained in:
Pavel Labath 2019-05-16 15:17:30 +00:00
parent 80ec90264f
commit 4adc6bd3dd
6 changed files with 128 additions and 8 deletions

View File

@ -80,6 +80,16 @@ public:
return getListStream<minidump::Thread>(minidump::StreamType::ThreadList);
}
/// Returns the list of memory ranges embedded in the MemoryList stream. An
/// error is returned if the file does not contain this stream, or if the
/// stream is not large enough to contain the number of memory descriptors
/// declared in the stream header. The consistency of the MemoryDescriptor
/// entries themselves is not checked in any way.
Expected<ArrayRef<minidump::MemoryDescriptor>> getMemoryList() const {
return getListStream<minidump::MemoryDescriptor>(
minidump::StreamType::MemoryList);
}
private:
static Error createError(StringRef Str) {
return make_error<GenericBinaryError>(Str, object_error::parse_failed);

View File

@ -26,6 +26,7 @@ namespace MinidumpYAML {
/// from Types to Kinds is fixed and given by the static getKind function.
struct Stream {
enum class StreamKind {
MemoryList,
ModuleList,
RawContent,
SystemInfo,
@ -86,10 +87,20 @@ struct ParsedThread {
yaml::BinaryRef Stack;
yaml::BinaryRef Context;
};
/// A structure containing all data describing a single memory region.
struct ParsedMemoryDescriptor {
static constexpr Stream::StreamKind Kind = Stream::StreamKind::MemoryList;
static constexpr minidump::StreamType Type = minidump::StreamType::MemoryList;
minidump::MemoryDescriptor Entry;
yaml::BinaryRef Content;
};
} // namespace detail
using ModuleListStream = detail::ListStream<detail::ParsedModule>;
using ThreadListStream = detail::ListStream<detail::ParsedThread>;
using MemoryListStream = detail::ListStream<detail::ParsedMemoryDescriptor>;
/// A minidump stream represented as a sequence of hex bytes. This is used as a
/// fallback when no other stream kind is suitable.
@ -211,12 +222,15 @@ LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::OtherInfo)
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::X86Info)
LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::VSFixedFileInfo)
LLVM_YAML_DECLARE_MAPPING_TRAITS(
llvm::MinidumpYAML::MemoryListStream::entry_type)
LLVM_YAML_DECLARE_MAPPING_TRAITS(
llvm::MinidumpYAML::ModuleListStream::entry_type)
LLVM_YAML_DECLARE_MAPPING_TRAITS(
llvm::MinidumpYAML::ThreadListStream::entry_type)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<llvm::MinidumpYAML::Stream>)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::MemoryListStream::entry_type)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ModuleListStream::entry_type)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ThreadListStream::entry_type)

View File

@ -78,6 +78,8 @@ template Expected<ArrayRef<Module>>
MinidumpFile::getListStream(StreamType) const;
template Expected<ArrayRef<Thread>>
MinidumpFile::getListStream(StreamType) const;
template Expected<ArrayRef<MemoryDescriptor>>
MinidumpFile::getListStream(StreamType) const;
Expected<ArrayRef<uint8_t>>
MinidumpFile::getDataSlice(ArrayRef<uint8_t> Data, size_t Offset, size_t Size) {

View File

@ -168,6 +168,8 @@ Stream::~Stream() = default;
Stream::StreamKind Stream::getKind(StreamType Type) {
switch (Type) {
case StreamType::MemoryList:
return StreamKind::MemoryList;
case StreamType::ModuleList:
return StreamKind::ModuleList;
case StreamType::SystemInfo:
@ -190,6 +192,8 @@ Stream::StreamKind Stream::getKind(StreamType Type) {
std::unique_ptr<Stream> Stream::create(StreamType Type) {
StreamKind Kind = getKind(Type);
switch (Kind) {
case StreamKind::MemoryList:
return llvm::make_unique<MemoryListStream>();
case StreamKind::ModuleList:
return llvm::make_unique<ModuleListStream>();
case StreamKind::RawContent:
@ -353,6 +357,16 @@ static StringRef streamValidate(RawContentStream &Stream) {
return "";
}
void yaml::MappingTraits<MemoryListStream::entry_type>::mapping(
IO &IO, MemoryListStream::entry_type &Range) {
MappingContextTraits<MemoryDescriptor, yaml::BinaryRef>::mapping(
IO, Range.Entry, Range.Content);
}
static void streamMapping(yaml::IO &IO, MemoryListStream &Stream) {
IO.mapRequired("Memory Ranges", Stream.Entries);
}
static void streamMapping(yaml::IO &IO, ModuleListStream &Stream) {
IO.mapRequired("Modules", Stream.Entries);
}
@ -421,6 +435,9 @@ void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping(
if (!IO.outputting())
S = MinidumpYAML::Stream::create(Type);
switch (S->Kind) {
case MinidumpYAML::Stream::StreamKind::MemoryList:
streamMapping(IO, llvm::cast<MemoryListStream>(*S));
break;
case MinidumpYAML::Stream::StreamKind::ModuleList:
streamMapping(IO, llvm::cast<ModuleListStream>(*S));
break;
@ -444,6 +461,7 @@ StringRef yaml::MappingTraits<std::unique_ptr<Stream>>::validate(
switch (S->Kind) {
case MinidumpYAML::Stream::StreamKind::RawContent:
return streamValidate(cast<RawContentStream>(*S));
case MinidumpYAML::Stream::StreamKind::MemoryList:
case MinidumpYAML::Stream::StreamKind::ModuleList:
case MinidumpYAML::Stream::StreamKind::SystemInfo:
case MinidumpYAML::Stream::StreamKind::TextContent:
@ -466,6 +484,10 @@ static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) {
support::ulittle32_t(File.allocateBytes(Data))};
}
static void layout(BlobAllocator &File, MemoryListStream::entry_type &Range) {
Range.Entry.Memory = layout(File, Range.Content);
}
static void layout(BlobAllocator &File, ModuleListStream::entry_type &M) {
M.Entry.ModuleNameRVA = File.allocateString(M.Name);
@ -502,6 +524,9 @@ static Directory layout(BlobAllocator &File, Stream &S) {
Result.Location.RVA = File.tell();
Optional<size_t> DataEnd;
switch (S.Kind) {
case Stream::StreamKind::MemoryList:
DataEnd = layout(File, cast<MemoryListStream>(S));
break;
case Stream::StreamKind::ModuleList:
DataEnd = layout(File, cast<ModuleListStream>(S));
break;
@ -566,6 +591,19 @@ Expected<std::unique_ptr<Stream>>
Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) {
StreamKind Kind = getKind(StreamDesc.Type);
switch (Kind) {
case StreamKind::MemoryList: {
auto ExpectedList = File.getMemoryList();
if (!ExpectedList)
return ExpectedList.takeError();
std::vector<MemoryListStream::entry_type> Ranges;
for (const MemoryDescriptor &MD : *ExpectedList) {
auto ExpectedContent = File.getRawData(MD.Memory);
if (!ExpectedContent)
return ExpectedContent.takeError();
Ranges.push_back({MD, *ExpectedContent});
}
return llvm::make_unique<MemoryListStream>(std::move(Ranges));
}
case StreamKind::ModuleList: {
auto ExpectedList = File.getModuleList();
if (!ExpectedList)

View File

@ -37,20 +37,24 @@ Streams:
File Date High: 0x3C3D3E3F
File Date Low: 0x40414243
CodeView Record: '44454647'
Misc Record: 48494A4B
Misc Record: '48494A4B'
- Base of Image: 0x4C4D4E4F50515253
Size of Image: 0x54555657
Module Name: libb.so
CodeView Record: 58595A5B
CodeView Record: '58595A5B'
- Type: ThreadList
Threads:
- Thread Id: 0x5C5D5E5F
Priority Class: 0x60616263
Environment Block: 0x6465666768696A6B
Context: 7C7D7E7F80818283
Context: '7C7D7E7F80818283'
Stack:
Start of Memory Range: 0x6C6D6E6F70717273
Content: 7475767778797A7B
Content: '7475767778797A7B'
- Type: MemoryList
Memory Ranges:
- Start of Memory Range: 0x7C7D7E7F80818283
Content: '8485868788'
...
# CHECK: --- !minidump
@ -97,11 +101,15 @@ Streams:
# CHECK-NEXT: CodeView Record: 58595A5B
# CHECK-NEXT: - Type: ThreadList
# CHECK-NEXT: Threads:
# CHECK-NEXT: - Thread Id: 0x5C5D5E5F
# CHECK-NEXT: Priority Class: 0x60616263
# CHECK-NEXT: - Thread Id: 0x5C5D5E5F
# CHECK-NEXT: Priority Class: 0x60616263
# CHECK-NEXT: Environment Block: 0x6465666768696A6B
# CHECK-NEXT: Context: 7C7D7E7F80818283
# CHECK-NEXT: Context: 7C7D7E7F80818283
# CHECK-NEXT: Stack:
# CHECK-NEXT: Start of Memory Range: 0x6C6D6E6F70717273
# CHECK-NEXT: Content: 7475767778797A7B
# CHECK-NEXT: Content: 7475767778797A7B
# CHECK-NEXT: - Type: MemoryList
# CHECK-NEXT: Memory Ranges:
# CHECK-NEXT: - Start of Memory Range: 0x7C7D7E7F80818283
# CHECK-NEXT: Content: '8485868788'
# CHECK-NEXT: ...

View File

@ -463,3 +463,51 @@ TEST(MinidumpFile, getThreadList) {
EXPECT_EQ(0x08070605u, T.Context.RVA);
}
}
TEST(MinidumpFile, getMemoryList) {
std::vector<uint8_t> OneRange{
// Header
'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
1, 0, 0, 0, // NumberOfStreams,
32, 0, 0, 0, // StreamDirectoryRVA
0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
0, 0, 0, 0, 0, 0, 0, 0, // Flags
// Stream Directory
5, 0, 0, 0, 20, 0, 0, 0, // Type, DataSize,
44, 0, 0, 0, // RVA
// MemoryDescriptor
1, 0, 0, 0, // NumberOfMemoryRanges
5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
};
// Same as before, but with a padded memory list.
std::vector<uint8_t> PaddedRange{
// Header
'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
1, 0, 0, 0, // NumberOfStreams,
32, 0, 0, 0, // StreamDirectoryRVA
0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
0, 0, 0, 0, 0, 0, 0, 0, // Flags
// Stream Directory
5, 0, 0, 0, 24, 0, 0, 0, // Type, DataSize,
44, 0, 0, 0, // RVA
// MemoryDescriptor
1, 0, 0, 0, // NumberOfMemoryRanges
0, 0, 0, 0, // Padding
5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
};
for (ArrayRef<uint8_t> Data : {OneRange, PaddedRange}) {
auto ExpectedFile = create(Data);
ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
const MinidumpFile &File = **ExpectedFile;
Expected<ArrayRef<MemoryDescriptor>> ExpectedRanges = File.getMemoryList();
ASSERT_THAT_EXPECTED(ExpectedRanges, Succeeded());
ASSERT_EQ(1u, ExpectedRanges->size());
const MemoryDescriptor &MD = ExpectedRanges.get()[0];
EXPECT_EQ(0x0201000908070605u, MD.StartOfMemoryRange);
EXPECT_EQ(0x06050403u, MD.Memory.DataSize);
EXPECT_EQ(0x00090807u, MD.Memory.RVA);
}
}