Index: include/llvm/ObjectYAML/MinidumpYAML.h =================================================================== --- include/llvm/ObjectYAML/MinidumpYAML.h +++ include/llvm/ObjectYAML/MinidumpYAML.h @@ -30,6 +30,7 @@ RawContent, SystemInfo, TextContent, + ThreadList, }; Stream(StreamKind Kind, minidump::StreamType Type) : Kind(Kind), Type(Type) {} @@ -50,29 +51,51 @@ const object::MinidumpFile &File); }; -/// A stream representing the list of modules loaded in the process. On disk, it -/// is represented as a sequence of minidump::Module structures. These contain -/// pointers to other data structures, like the module's name and CodeView -/// record. In memory, we represent these as the ParsedModule struct, which -/// groups minidump::Module with all of its dependant structures in a single -/// entity. -struct ModuleListStream : public Stream { - struct ParsedModule { - minidump::Module Module; - std::string Name; - yaml::BinaryRef CvRecord; - yaml::BinaryRef MiscRecord; - }; - std::vector Modules; +namespace detail { +/// A stream representing a list of abstract entries in a minidump stream. Its +/// instantiations can be used to represent the ModuleList stream and other +/// streams with similar structure. +template +struct ListStream : public Stream { + using entry_type = EntryT; - ModuleListStream(std::vector Modules = {}) - : Stream(StreamKind::ModuleList, minidump::StreamType::ModuleList), - Modules(std::move(Modules)) {} + std::vector Entries; - static bool classof(const Stream *S) { - return S->Kind == StreamKind::ModuleList; - } + ListStream(std::vector Entries = {}) + : Stream(KindV, TypeV), Entries(std::move(Entries)) {} + + static bool classof(const Stream *S) { return S->Kind == KindV; } +}; + +/// A structure containing all data belonging to a single minidump module. On +/// disk, these are placed at various places in the minidump file and +/// cross-referenced via their offsets, but for ease of use, we group them +/// together in the logical memory view. +struct ParsedModule { + minidump::Module Entry; + std::string Name; + yaml::BinaryRef CvRecord; + yaml::BinaryRef MiscRecord; +}; + +/// A structure containing all data belonging to a single minidump thread. On +/// disk, these are placed at various places in the minidump file and +/// cross-referenced via their offsets, but for ease of use, we group them +/// together in the logical memory view. +struct ParsedThread { + minidump::Thread Entry; + yaml::BinaryRef Stack; + yaml::BinaryRef Context; }; +} // namespace detail + +using ModuleListStream = + detail::ListStream; + +using ThreadListStream = + 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. @@ -176,6 +199,11 @@ static StringRef validate(IO &IO, std::unique_ptr &S); }; +template <> struct MappingContextTraits { + static void mapping(IO &IO, minidump::MemoryDescriptor &Memory, + BinaryRef &Content); +}; + } // namespace yaml } // namespace llvm @@ -188,11 +216,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::ModuleListStream::entry_type) LLVM_YAML_DECLARE_MAPPING_TRAITS( - llvm::MinidumpYAML::ModuleListStream::ParsedModule) + llvm::MinidumpYAML::ThreadListStream::entry_type) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) -LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ModuleListStream::ParsedModule) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ModuleListStream::entry_type) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MinidumpYAML::ThreadListStream::entry_type) LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::MinidumpYAML::Object) Index: lib/ObjectYAML/MinidumpYAML.cpp =================================================================== --- lib/ObjectYAML/MinidumpYAML.cpp +++ lib/ObjectYAML/MinidumpYAML.cpp @@ -180,6 +180,8 @@ case StreamType::LinuxProcStat: case StreamType::LinuxProcUptime: return StreamKind::TextContent; + case StreamType::ThreadList: + return StreamKind::ThreadList; default: return StreamKind::RawContent; } @@ -196,6 +198,8 @@ return llvm::make_unique(); case StreamKind::TextContent: return llvm::make_unique(Type); + case StreamKind::ThreadList: + return llvm::make_unique(); } llvm_unreachable("Unhandled stream kind!"); } @@ -323,19 +327,19 @@ mapOptionalHex(IO, "File Date Low", Info.FileDateLow, 0); } -void yaml::MappingTraits::mapping( - IO &IO, ModuleListStream::ParsedModule &M) { - mapRequiredHex(IO, "Base of Image", M.Module.BaseOfImage); - mapRequiredHex(IO, "Size of Image", M.Module.SizeOfImage); - mapOptionalHex(IO, "Checksum", M.Module.Checksum, 0); - IO.mapOptional("Time Date Stamp", M.Module.TimeDateStamp, +void yaml::MappingTraits::mapping( + IO &IO, ModuleListStream::entry_type &M) { + mapRequiredHex(IO, "Base of Image", M.Entry.BaseOfImage); + mapRequiredHex(IO, "Size of Image", M.Entry.SizeOfImage); + mapOptionalHex(IO, "Checksum", M.Entry.Checksum, 0); + IO.mapOptional("Time Date Stamp", M.Entry.TimeDateStamp, support::ulittle32_t(0)); IO.mapRequired("Module Name", M.Name); - IO.mapOptional("Version Info", M.Module.VersionInfo, VSFixedFileInfo()); + IO.mapOptional("Version Info", M.Entry.VersionInfo, VSFixedFileInfo()); IO.mapRequired("CodeView Record", M.CvRecord); IO.mapOptional("Misc Record", M.MiscRecord, yaml::BinaryRef()); - mapOptionalHex(IO, "Reserved0", M.Module.Reserved0, 0); - mapOptionalHex(IO, "Reserved1", M.Module.Reserved1, 0); + mapOptionalHex(IO, "Reserved0", M.Entry.Reserved0, 0); + mapOptionalHex(IO, "Reserved1", M.Entry.Reserved1, 0); } static void streamMapping(yaml::IO &IO, RawContentStream &Stream) { @@ -350,7 +354,7 @@ } static void streamMapping(yaml::IO &IO, ModuleListStream &Stream) { - IO.mapRequired("Modules", Stream.Modules); + IO.mapRequired("Modules", Stream.Entries); } static void streamMapping(yaml::IO &IO, SystemInfoStream &Stream) { @@ -386,6 +390,27 @@ IO.mapOptional("Text", Stream.Text); } +void yaml::MappingContextTraits::mapping( + IO &IO, MemoryDescriptor &Memory, BinaryRef &Content) { + mapRequiredHex(IO, "Start of Memory Range", Memory.StartOfMemoryRange); + IO.mapRequired("Content", Content); +} + +void yaml::MappingTraits::mapping( + IO &IO, ThreadListStream::entry_type &T) { + mapRequiredHex(IO, "Thread Id", T.Entry.ThreadId); + mapOptionalHex(IO, "Suspend Count", T.Entry.SuspendCount, 0); + mapOptionalHex(IO, "Priority Class", T.Entry.PriorityClass, 0); + mapOptionalHex(IO, "Priority", T.Entry.Priority, 0); + mapOptionalHex(IO, "Environment Block", T.Entry.EnvironmentBlock, 0); + IO.mapRequired("Stack", T.Entry.Stack, T.Stack); + IO.mapRequired("Context", T.Context); +} + +static void streamMapping(yaml::IO &IO, ThreadListStream &Stream) { + IO.mapRequired("Threads", Stream.Entries); +} + void yaml::MappingTraits>::mapping( yaml::IO &IO, std::unique_ptr &S) { StreamType Type; @@ -408,6 +433,9 @@ case MinidumpYAML::Stream::StreamKind::TextContent: streamMapping(IO, llvm::cast(*S)); break; + case MinidumpYAML::Stream::StreamKind::ThreadList: + streamMapping(IO, llvm::cast(*S)); + break; } } @@ -419,6 +447,7 @@ case MinidumpYAML::Stream::StreamKind::ModuleList: case MinidumpYAML::Stream::StreamKind::SystemInfo: case MinidumpYAML::Stream::StreamKind::TextContent: + case MinidumpYAML::Stream::StreamKind::ThreadList: return ""; } llvm_unreachable("Fully covered switch above!"); @@ -432,32 +461,52 @@ IO.mapRequired("Streams", O.Streams); } +static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) { + return {support::ulittle32_t(Data.binary_size()), + support::ulittle32_t(File.allocateBytes(Data))}; +} + +static void layout(BlobAllocator &File, ModuleListStream::entry_type &M) { + M.Entry.ModuleNameRVA = File.allocateString(M.Name); + + M.Entry.CvRecord = layout(File, M.CvRecord); + M.Entry.MiscRecord = layout(File, M.MiscRecord); +} + +static void layout(BlobAllocator &File, ThreadListStream::entry_type &T) { + T.Entry.Stack.Memory = layout(File, T.Stack); + T.Entry.Context = layout(File, T.Context); +} + +template +static size_t +layout(BlobAllocator &File, + MinidumpYAML::detail::ListStream &S) { + using ListStream = MinidumpYAML::detail::ListStream; + + File.allocateNewObject(S.Entries.size()); + for (typename ListStream::entry_type &E : S.Entries) + File.allocateObject(E.Entry); + + size_t DataEnd = File.tell(); + + // Lay out the auxiliary data, (which is not a part of the stream). + DataEnd = File.tell(); + for (typename ListStream::entry_type &E : S.Entries) + layout(File, E); + + return DataEnd; +} + static Directory layout(BlobAllocator &File, Stream &S) { Directory Result; Result.Type = S.Type; Result.Location.RVA = File.tell(); Optional DataEnd; switch (S.Kind) { - case Stream::StreamKind::ModuleList: { - ModuleListStream &List = cast(S); - - File.allocateNewObject(List.Modules.size()); - for (ModuleListStream::ParsedModule &M : List.Modules) - File.allocateObject(M.Module); - - // Module names and CodeView/Misc records are not a part of the stream. - DataEnd = File.tell(); - for (ModuleListStream::ParsedModule &M : List.Modules) { - M.Module.ModuleNameRVA = File.allocateString(M.Name); - - M.Module.CvRecord.RVA = File.allocateBytes(M.CvRecord); - M.Module.CvRecord.DataSize = M.CvRecord.binary_size(); - - M.Module.MiscRecord.RVA = File.allocateBytes(M.MiscRecord); - M.Module.MiscRecord.DataSize = M.MiscRecord.binary_size(); - } + case Stream::StreamKind::ModuleList: + DataEnd = layout(File, cast(S)); break; - } case Stream::StreamKind::RawContent: { RawContentStream &Raw = cast(S); File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) { @@ -478,6 +527,9 @@ case Stream::StreamKind::TextContent: File.allocateArray(arrayRefFromStringRef(cast(S).Text)); break; + case Stream::StreamKind::ThreadList: + DataEnd = layout(File, cast(S)); + break; } // If DataEnd is not set, we assume everything we generated is a part of the // stream. @@ -520,7 +572,7 @@ auto ExpectedList = File.getModuleList(); if (!ExpectedList) return ExpectedList.takeError(); - std::vector Modules; + std::vector Modules; for (const Module &M : *ExpectedList) { auto ExpectedName = File.getString(M.ModuleNameRVA); if (!ExpectedName) @@ -552,6 +604,22 @@ case StreamKind::TextContent: return llvm::make_unique( StreamDesc.Type, toStringRef(File.getRawStream(StreamDesc))); + case StreamKind::ThreadList: { + auto ExpectedList = File.getThreadList(); + if (!ExpectedList) + return ExpectedList.takeError(); + std::vector Threads; + for (const Thread &T : *ExpectedList) { + auto ExpectedStack = File.getRawData(T.Stack.Memory); + if (!ExpectedStack) + return ExpectedStack.takeError(); + auto ExpectedContext = File.getRawData(T.Context); + if (!ExpectedContext) + return ExpectedContext.takeError(); + Threads.push_back({T, *ExpectedStack, *ExpectedContext}); + } + return llvm::make_unique(std::move(Threads)); + } } llvm_unreachable("Unhandled stream kind!"); } Index: test/tools/obj2yaml/basic-minidump.yaml =================================================================== --- test/tools/obj2yaml/basic-minidump.yaml +++ test/tools/obj2yaml/basic-minidump.yaml @@ -42,6 +42,15 @@ Size of Image: 0x54555657 Module Name: libb.so CodeView Record: 58595A5B + - Type: ThreadList + Threads: + - Thread Id: 0x5C5D5E5F + Priority Class: 0x60616263 + Environment Block: 0x6465666768696A6B + Stack: + Start of Memory Range: 0x6C6D6E6F70717273 + Content: 7475767778797A7B + Context: 7C7D7E7F80818283 ... # CHECK: --- !minidump @@ -86,4 +95,13 @@ # CHECK-NEXT: Size of Image: 0x54555657 # CHECK-NEXT: Module Name: libb.so # CHECK-NEXT: CodeView Record: 58595A5B +# CHECK-NEXT: - Type: ThreadList +# CHECK-NEXT: Threads: +# CHECK-NEXT: - Thread Id: 0x5C5D5E5F +# CHECK-NEXT: Priority Class: 0x60616263 +# CHECK-NEXT: Environment Block: 0x6465666768696A6B +# CHECK-NEXT: Stack: +# CHECK-NEXT: Start of Memory Range: 0x6C6D6E6F70717273 +# CHECK-NEXT: Content: 7475767778797A7B +# CHECK-NEXT: Context: 7C7D7E7F80818283 # CHECK-NEXT: ...