Index: llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h =================================================================== --- llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h +++ llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h @@ -30,6 +30,7 @@ RawContent, SystemInfo, TextContent, + ThreadList, }; Stream(StreamKind Kind, minidump::StreamType Type) : Kind(Kind), Type(Type) {} @@ -50,30 +51,46 @@ 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 a 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; - } + explicit ListStream(std::vector Entries = {}) + : Stream(EntryT::Kind, EntryT::Type), Entries(std::move(Entries)) {} + + static bool classof(const Stream *S) { return S->Kind == EntryT::Kind; } }; +/// A structure containing all data belonging to a single minidump module. +struct ParsedModule { + static constexpr Stream::StreamKind Kind = Stream::StreamKind::ModuleList; + static constexpr minidump::StreamType Type = minidump::StreamType::ModuleList; + + minidump::Module Entry; + std::string Name; + yaml::BinaryRef CvRecord; + yaml::BinaryRef MiscRecord; +}; + +/// A structure containing all data belonging to a single minidump thread. +struct ParsedThread { + static constexpr Stream::StreamKind Kind = Stream::StreamKind::ThreadList; + static constexpr minidump::StreamType Type = minidump::StreamType::ThreadList; + + 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. struct RawContentStream : public Stream { @@ -176,6 +193,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 +210,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: llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp +++ llvm/trunk/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("Context", T.Context); + IO.mapRequired("Stack", T.Entry.Stack, T.Stack); +} + +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,50 @@ 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) { + + File.allocateNewObject(S.Entries.size()); + for (auto &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 (auto &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 +525,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 +570,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 +602,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: llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml =================================================================== --- llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml +++ llvm/trunk/test/tools/obj2yaml/basic-minidump.yaml @@ -1,12 +1,12 @@ # RUN: yaml2obj %s | obj2yaml - | FileCheck %s --- !minidump -Streams: +Streams: - Type: SystemInfo Processor Arch: ARM64 Platform ID: Linux CSD Version: Linux 3.13.0-91-generic - CPU: + CPU: CPUID: 0x05060708 - Type: LinuxAuxv Content: DEADBEEFBAADF00D @@ -16,41 +16,50 @@ 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process - Type: ModuleList - Modules: + Modules: - Base of Image: 0x0001020304050607 Size of Image: 0x08090A0B Checksum: 0x0C0D0E0F Time Date Stamp: 47 Module Name: a.out - Version Info: - Signature: 0x10111213 - Struct Version: 0x14151617 - File Version High: 0x18191A1B - File Version Low: 0x1C1D1E1F + Version Info: + Signature: 0x10111213 + Struct Version: 0x14151617 + File Version High: 0x18191A1B + File Version Low: 0x1C1D1E1F Product Version High: 0x20212223 - Product Version Low: 0x24252627 - File Flags Mask: 0x28292A2B - File Flags: 0x2C2D2E2F - File OS: 0x30313233 - File Type: 0x34353637 - File Subtype: 0x38393A3B - File Date High: 0x3C3D3E3F - File Date Low: 0x40414243 + Product Version Low: 0x24252627 + File Flags Mask: 0x28292A2B + File Flags: 0x2C2D2E2F + File OS: 0x30313233 + File Type: 0x34353637 + File Subtype: 0x38393A3B + File Date High: 0x3C3D3E3F + File Date Low: 0x40414243 CodeView Record: '44454647' Misc Record: 48494A4B - Base of Image: 0x4C4D4E4F50515253 Size of Image: 0x54555657 Module Name: libb.so CodeView Record: 58595A5B + - Type: ThreadList + Threads: + - Thread Id: 0x5C5D5E5F + Priority Class: 0x60616263 + Environment Block: 0x6465666768696A6B + Context: 7C7D7E7F80818283 + Stack: + Start of Memory Range: 0x6C6D6E6F70717273 + Content: 7475767778797A7B ... # CHECK: --- !minidump -# CHECK-NEXT: Streams: +# CHECK-NEXT: Streams: # CHECK-NEXT: - Type: SystemInfo # CHECK-NEXT: Processor Arch: ARM64 # CHECK-NEXT: Platform ID: Linux # CHECK-NEXT: CSD Version: Linux 3.13.0-91-generic -# CHECK-NEXT: CPU: +# CHECK-NEXT: CPU: # CHECK-NEXT: CPUID: 0x05060708 # CHECK-NEXT: - Type: LinuxAuxv # CHECK-NEXT: Content: DEADBEEFBAADF00D @@ -60,30 +69,39 @@ # CHECK-NEXT: 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process # CHECK-EMPTY: # CHECK-NEXT: - Type: ModuleList -# CHECK-NEXT: Modules: +# CHECK-NEXT: Modules: # CHECK-NEXT: - Base of Image: 0x0001020304050607 # CHECK-NEXT: Size of Image: 0x08090A0B # CHECK-NEXT: Checksum: 0x0C0D0E0F # CHECK-NEXT: Time Date Stamp: 47 # CHECK-NEXT: Module Name: a.out -# CHECK-NEXT: Version Info: -# CHECK-NEXT: Signature: 0x10111213 -# CHECK-NEXT: Struct Version: 0x14151617 -# CHECK-NEXT: File Version High: 0x18191A1B -# CHECK-NEXT: File Version Low: 0x1C1D1E1F +# CHECK-NEXT: Version Info: +# CHECK-NEXT: Signature: 0x10111213 +# CHECK-NEXT: Struct Version: 0x14151617 +# CHECK-NEXT: File Version High: 0x18191A1B +# CHECK-NEXT: File Version Low: 0x1C1D1E1F # CHECK-NEXT: Product Version High: 0x20212223 -# CHECK-NEXT: Product Version Low: 0x24252627 -# CHECK-NEXT: File Flags Mask: 0x28292A2B -# CHECK-NEXT: File Flags: 0x2C2D2E2F -# CHECK-NEXT: File OS: 0x30313233 -# CHECK-NEXT: File Type: 0x34353637 -# CHECK-NEXT: File Subtype: 0x38393A3B -# CHECK-NEXT: File Date High: 0x3C3D3E3F -# CHECK-NEXT: File Date Low: 0x40414243 +# CHECK-NEXT: Product Version Low: 0x24252627 +# CHECK-NEXT: File Flags Mask: 0x28292A2B +# CHECK-NEXT: File Flags: 0x2C2D2E2F +# CHECK-NEXT: File OS: 0x30313233 +# CHECK-NEXT: File Type: 0x34353637 +# CHECK-NEXT: File Subtype: 0x38393A3B +# CHECK-NEXT: File Date High: 0x3C3D3E3F +# CHECK-NEXT: File Date Low: 0x40414243 # CHECK-NEXT: CodeView Record: '44454647' # CHECK-NEXT: Misc Record: 48494A4B # CHECK-NEXT: - Base of Image: 0x4C4D4E4F50515253 # 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: Context: 7C7D7E7F80818283 +# CHECK-NEXT: Stack: +# CHECK-NEXT: Start of Memory Range: 0x6C6D6E6F70717273 +# CHECK-NEXT: Content: 7475767778797A7B # CHECK-NEXT: ...