Index: llvm/trunk/include/llvm/Object/Minidump.h =================================================================== --- llvm/trunk/include/llvm/Object/Minidump.h +++ llvm/trunk/include/llvm/Object/Minidump.h @@ -80,6 +80,16 @@ return getListStream(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> getMemoryList() const { + return getListStream( + minidump::StreamType::MemoryList); + } + private: static Error createError(StringRef Str) { return make_error(Str, object_error::parse_failed); Index: llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h =================================================================== --- llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h +++ llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h @@ -26,6 +26,7 @@ /// 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 @@ 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; using ThreadListStream = detail::ListStream; +using MemoryListStream = detail::ListStream; /// A minidump stream represented as a sequence of hex bytes. This is used as a /// fallback when no other stream kind is suitable. @@ -212,11 +223,14 @@ 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_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) Index: llvm/trunk/lib/Object/Minidump.cpp =================================================================== --- llvm/trunk/lib/Object/Minidump.cpp +++ llvm/trunk/lib/Object/Minidump.cpp @@ -78,6 +78,8 @@ MinidumpFile::getListStream(StreamType) const; template Expected> MinidumpFile::getListStream(StreamType) const; +template Expected> + MinidumpFile::getListStream(StreamType) const; Expected> MinidumpFile::getDataSlice(ArrayRef Data, size_t Offset, size_t Size) { Index: llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp +++ llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp @@ -168,6 +168,8 @@ 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 @@ std::unique_ptr Stream::create(StreamType Type) { StreamKind Kind = getKind(Type); switch (Kind) { + case StreamKind::MemoryList: + return llvm::make_unique(); case StreamKind::ModuleList: return llvm::make_unique(); case StreamKind::RawContent: @@ -353,6 +357,16 @@ return ""; } +void yaml::MappingTraits::mapping( + IO &IO, MemoryListStream::entry_type &Range) { + MappingContextTraits::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 @@ if (!IO.outputting()) S = MinidumpYAML::Stream::create(Type); switch (S->Kind) { + case MinidumpYAML::Stream::StreamKind::MemoryList: + streamMapping(IO, llvm::cast(*S)); + break; case MinidumpYAML::Stream::StreamKind::ModuleList: streamMapping(IO, llvm::cast(*S)); break; @@ -444,6 +461,7 @@ switch (S->Kind) { case MinidumpYAML::Stream::StreamKind::RawContent: return streamValidate(cast(*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 @@ 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 @@ Result.Location.RVA = File.tell(); Optional DataEnd; switch (S.Kind) { + case Stream::StreamKind::MemoryList: + DataEnd = layout(File, cast(S)); + break; case Stream::StreamKind::ModuleList: DataEnd = layout(File, cast(S)); break; @@ -566,6 +591,19 @@ 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 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(std::move(Ranges)); + } case StreamKind::ModuleList: { auto ExpectedList = File.getModuleList(); if (!ExpectedList) Index: llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml =================================================================== --- llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml +++ llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml @@ -37,20 +37,24 @@ 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 @@ # 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: ... Index: llvm/trunk/unittests/Object/MinidumpTest.cpp =================================================================== --- llvm/trunk/unittests/Object/MinidumpTest.cpp +++ llvm/trunk/unittests/Object/MinidumpTest.cpp @@ -463,3 +463,51 @@ EXPECT_EQ(0x08070605u, T.Context.RVA); } } + +TEST(MinidumpFile, getMemoryList) { + std::vector 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 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 Data : {OneRange, PaddedRange}) { + auto ExpectedFile = create(Data); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + const MinidumpFile &File = **ExpectedFile; + Expected> 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); + } +}