Index: llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h =================================================================== --- llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h +++ llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h @@ -0,0 +1,156 @@ +//===- MinidumpYAML.h - Minidump YAMLIO implementation ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJECTYAML_MINIDUMPYAML_H +#define LLVM_OBJECTYAML_MINIDUMPYAML_H + +#include "llvm/BinaryFormat/Minidump.h" +#include "llvm/ObjectYAML/YAML.h" +#include "llvm/Support/YAMLTraits.h" + +namespace llvm { +namespace MinidumpYAML { + +/// The base class for all minidump streams. The "Type" of the stream +/// corresponds to the Stream Type field in the minidump file. The "Kind" field +/// specifies how are we going to treat it. For highly specialized streams (e.g. +/// SystemInfo), there is a 1:1 mapping between Types and Kinds, but in general +/// one stream Kind can be used to represent multiple stream Types (e.g. any +/// unrecognised stream Type will be handled via RawContentStream). The mapping +/// from Types to Kinds is fixed and given by the static getKind function. +struct Stream { + enum class StreamKind { + RawContent, + SystemInfo, + TextContent, + }; + + Stream(StreamKind Kind, minidump::StreamType Type) : Kind(Kind), Type(Type) {} + virtual ~Stream(); // anchor + + const StreamKind Kind; + const minidump::StreamType Type; + + /// Get the stream Kind used for representing streams of a given Type. + static StreamKind getKind(minidump::StreamType Type); + + /// Create an empty stream of the given Type. + static std::unique_ptr create(minidump::StreamType Type); +}; + +/// 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 { + yaml::BinaryRef Content; + yaml::Hex32 Size; + + RawContentStream(minidump::StreamType Type, ArrayRef Content = {}) + : Stream(StreamKind::RawContent, Type), Content(Content), + Size(Content.size()) {} + + static bool classof(const Stream *S) { + return S->Kind == StreamKind::RawContent; + } +}; + +/// SystemInfo minidump stream. +struct SystemInfoStream : public Stream { + minidump::SystemInfo Info; + + explicit SystemInfoStream(const minidump::SystemInfo &Info) + : Stream(StreamKind::SystemInfo, minidump::StreamType::SystemInfo), + Info(Info) {} + + SystemInfoStream() + : Stream(StreamKind::SystemInfo, minidump::StreamType::SystemInfo) { + memset(&Info, 0, sizeof(Info)); + } + + static bool classof(const Stream *S) { + return S->Kind == StreamKind::SystemInfo; + } +}; + +/// A StringRef, which is printed using YAML block notation. +LLVM_YAML_STRONG_TYPEDEF(StringRef, BlockStringRef) + +/// A minidump stream containing textual data (typically, the contents of a +/// /proc/ file on linux). +struct TextContentStream : public Stream { + BlockStringRef Text; + + TextContentStream(minidump::StreamType Type, StringRef Text = {}) + : Stream(StreamKind::TextContent, Type), Text(Text) {} + + static bool classof(const Stream *S) { + return S->Kind == StreamKind::TextContent; + } +}; + +/// The top level structure representing a minidump object, consisting of a +/// minidump header, and zero or more streams. To construct an Object from a +/// minidump file, use the static create function. To serialize to/from yaml, +/// use the appropriate streaming operator on a yaml stream. +struct Object { + Object() = default; + Object(const Object &) = delete; + Object &operator=(const Object &) = delete; + Object(Object &&) = default; + Object &operator=(Object &&) = default; + + /// The minidump header. + minidump::Header Header; + + /// The list of streams in this minidump object. + std::vector> Streams; +}; + +/// Serialize the minidump file represented by Obj to OS in binary form. +void writeAsBinary(Object &Obj, raw_ostream &OS); + +/// Serialize the yaml string as a minidump file to OS in binary form. +Error writeAsBinary(StringRef Yaml, raw_ostream &OS); + +} // namespace MinidumpYAML + +namespace yaml { +template <> struct BlockScalarTraits { + static void output(const MinidumpYAML::BlockStringRef &Text, void *, + raw_ostream &OS) { + OS << Text; + } + + static StringRef input(StringRef Scalar, void *, + MinidumpYAML::BlockStringRef &Text) { + Text = Scalar; + return ""; + } +}; + +template <> struct MappingTraits> { + static void mapping(IO &IO, std::unique_ptr &S); + static StringRef validate(IO &IO, std::unique_ptr &S); +}; + +} // namespace yaml + +} // namespace llvm + +LLVM_YAML_DECLARE_ENUM_TRAITS(llvm::minidump::ProcessorArchitecture) +LLVM_YAML_DECLARE_ENUM_TRAITS(llvm::minidump::OSPlatform) +LLVM_YAML_DECLARE_ENUM_TRAITS(llvm::minidump::StreamType) + +LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::ArmInfo) +LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::OtherInfo) +LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::minidump::CPUInfo::X86Info) + +LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) + +LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::MinidumpYAML::Object) + +#endif // LLVM_OBJECTYAML_MINIDUMPYAML_H Index: llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h =================================================================== --- llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h +++ llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h @@ -12,6 +12,7 @@ #include "llvm/ObjectYAML/COFFYAML.h" #include "llvm/ObjectYAML/ELFYAML.h" #include "llvm/ObjectYAML/MachOYAML.h" +#include "llvm/ObjectYAML/MinidumpYAML.h" #include "llvm/ObjectYAML/WasmYAML.h" #include "llvm/Support/YAMLTraits.h" #include @@ -26,6 +27,7 @@ std::unique_ptr Coff; std::unique_ptr MachO; std::unique_ptr FatMachO; + std::unique_ptr Minidump; std::unique_ptr Wasm; }; Index: llvm/trunk/lib/ObjectYAML/CMakeLists.txt =================================================================== --- llvm/trunk/lib/ObjectYAML/CMakeLists.txt +++ llvm/trunk/lib/ObjectYAML/CMakeLists.txt @@ -10,6 +10,7 @@ ELFYAML.cpp MachOYAML.cpp ObjectYAML.cpp + MinidumpYAML.cpp WasmYAML.cpp YAML.cpp ) Index: llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp +++ llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp @@ -0,0 +1,385 @@ +//===- MinidumpYAML.cpp - Minidump YAMLIO implementation ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ObjectYAML/MinidumpYAML.h" + +using namespace llvm; +using namespace llvm::MinidumpYAML; +using namespace llvm::minidump; + +namespace { +class BlobAllocator { +public: + size_t tell() const { return NextOffset; } + + size_t AllocateCallback(size_t Size, + std::function Callback) { + size_t Offset = NextOffset; + NextOffset += Size; + Callbacks.push_back(std::move(Callback)); + return Offset; + } + + size_t AllocateBytes(ArrayRef Data) { + return AllocateCallback( + Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); }); + } + + template size_t AllocateArray(ArrayRef Data) { + return AllocateBytes({reinterpret_cast(Data.data()), + sizeof(T) * Data.size()}); + } + + template size_t AllocateObject(const T &Data) { + return AllocateArray(makeArrayRef(Data)); + } + + void writeTo(raw_ostream &OS) const; + +private: + size_t NextOffset = 0; + + std::vector> Callbacks; +}; +} // namespace + +void BlobAllocator::writeTo(raw_ostream &OS) const { + size_t BeginOffset = OS.tell(); + for (const auto &Callback : Callbacks) + Callback(OS); + assert(OS.tell() == BeginOffset + NextOffset && + "Callbacks wrote an unexpected number of bytes."); + (void)BeginOffset; +} + +/// Perform an optional yaml-mapping of an endian-aware type EndianType. The +/// only purpose of this function is to avoid casting the Default value to the +/// endian type; +template +static inline void mapOptional(yaml::IO &IO, const char *Key, EndianType &Val, + typename EndianType::value_type Default) { + IO.mapOptional(Key, Val, EndianType(Default)); +} + +/// Yaml-map an endian-aware type EndianType as some other type MapType. +template +static inline void mapRequiredAs(yaml::IO &IO, const char *Key, + EndianType &Val) { + MapType Mapped = static_cast(Val); + IO.mapRequired(Key, Mapped); + Val = static_cast(Mapped); +} + +/// Perform an optional yaml-mapping of an endian-aware type EndianType as some +/// other type MapType. +template +static inline void mapOptionalAs(yaml::IO &IO, const char *Key, EndianType &Val, + MapType Default) { + MapType Mapped = static_cast(Val); + IO.mapOptional(Key, Mapped, Default); + Val = static_cast(Mapped); +} + +namespace { +/// Return the appropriate yaml Hex type for a given endian-aware type. +template struct HexType; +template <> struct HexType { using type = yaml::Hex16; }; +template <> struct HexType { using type = yaml::Hex32; }; +template <> struct HexType { using type = yaml::Hex64; }; +} // namespace + +/// Yaml-map an endian-aware type as an appropriately-sized hex value. +template +static inline void mapRequiredHex(yaml::IO &IO, const char *Key, + EndianType &Val) { + mapRequiredAs::type>(IO, Key, Val); +} + +/// Perform an optional yaml-mapping of an endian-aware type as an +/// appropriately-sized hex value. +template +static inline void mapOptionalHex(yaml::IO &IO, const char *Key, + EndianType &Val, + typename EndianType::value_type Default) { + mapOptionalAs::type>(IO, Key, Val, Default); +} + +Stream::~Stream() = default; + +Stream::StreamKind Stream::getKind(StreamType Type) { + switch (Type) { + case StreamType::SystemInfo: + return StreamKind::SystemInfo; + case StreamType::LinuxCPUInfo: + case StreamType::LinuxProcStatus: + case StreamType::LinuxLSBRelease: + case StreamType::LinuxCMDLine: + case StreamType::LinuxMaps: + case StreamType::LinuxProcStat: + case StreamType::LinuxProcUptime: + return StreamKind::TextContent; + default: + return StreamKind::RawContent; + } +} + +std::unique_ptr Stream::create(StreamType Type) { + StreamKind Kind = getKind(Type); + switch (Kind) { + case StreamKind::RawContent: + return llvm::make_unique(Type); + case StreamKind::SystemInfo: + return llvm::make_unique(); + case StreamKind::TextContent: + return llvm::make_unique(Type); + } + llvm_unreachable("Unhandled stream kind!"); +} + +void yaml::ScalarEnumerationTraits::enumeration( + IO &IO, ProcessorArchitecture &Arch) { +#define HANDLE_MDMP_ARCH(CODE, NAME) \ + IO.enumCase(Arch, #NAME, ProcessorArchitecture::NAME); +#include "llvm/BinaryFormat/MinidumpConstants.def" + IO.enumFallback(Arch); +} + +void yaml::ScalarEnumerationTraits::enumeration(IO &IO, + OSPlatform &Plat) { +#define HANDLE_MDMP_PLATFORM(CODE, NAME) \ + IO.enumCase(Plat, #NAME, OSPlatform::NAME); +#include "llvm/BinaryFormat/MinidumpConstants.def" + IO.enumFallback(Plat); +} + +void yaml::ScalarEnumerationTraits::enumeration(IO &IO, + StreamType &Type) { +#define HANDLE_MDMP_STREAM_TYPE(CODE, NAME) \ + IO.enumCase(Type, #NAME, StreamType::NAME); +#include "llvm/BinaryFormat/MinidumpConstants.def" + IO.enumFallback(Type); +} + +void yaml::MappingTraits::mapping(IO &IO, + CPUInfo::ArmInfo &Info) { + mapRequiredHex(IO, "CPUID", Info.CPUID); + mapOptionalHex(IO, "ELF hwcaps", Info.ElfHWCaps, 0); +} + +namespace { +template struct FixedSizeHex { + FixedSizeHex(uint8_t (&Storage)[N]) : Storage(Storage) {} + + uint8_t (&Storage)[N]; +}; +} // namespace + +namespace llvm { +namespace yaml { +template struct ScalarTraits> { + static void output(const FixedSizeHex &Fixed, void *, raw_ostream &OS) { + OS << toHex(makeArrayRef(Fixed.Storage)); + } + + static StringRef input(StringRef Scalar, void *, FixedSizeHex &Fixed) { + if (!all_of(Scalar, isHexDigit)) + return "Invalid hex digit in input"; + if (Scalar.size() < 2 * N) + return "String too short"; + if (Scalar.size() > 2 * N) + return "String too long"; + copy(fromHex(Scalar), Fixed.Storage); + return ""; + } + + static QuotingType mustQuote(StringRef S) { return QuotingType::None; } +}; +} // namespace yaml +} // namespace llvm +void yaml::MappingTraits::mapping( + IO &IO, CPUInfo::OtherInfo &Info) { + FixedSizeHex Features(Info.ProcessorFeatures); + IO.mapRequired("Features", Features); +} + +namespace { +/// A type which only accepts strings of a fixed size for yaml conversion. +template struct FixedSizeString { + FixedSizeString(char (&Storage)[N]) : Storage(Storage) {} + + char (&Storage)[N]; +}; +} // namespace + +namespace llvm { +namespace yaml { +template struct ScalarTraits> { + static void output(const FixedSizeString &Fixed, void *, raw_ostream &OS) { + OS << StringRef(Fixed.Storage, N); + } + + static StringRef input(StringRef Scalar, void *, FixedSizeString &Fixed) { + if (Scalar.size() < N) + return "String too short"; + if (Scalar.size() > N) + return "String too long"; + copy(Scalar, Fixed.Storage); + return ""; + } + + static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } +}; +} // namespace yaml +} // namespace llvm + +void yaml::MappingTraits::mapping(IO &IO, + CPUInfo::X86Info &Info) { + FixedSizeString VendorID(Info.VendorID); + IO.mapRequired("Vendor ID", VendorID); + + mapRequiredHex(IO, "Version Info", Info.VersionInfo); + mapRequiredHex(IO, "Feature Info", Info.FeatureInfo); + mapOptionalHex(IO, "AMD Extended Features", Info.AMDExtendedFeatures, 0); +} + +static void streamMapping(yaml::IO &IO, RawContentStream &Stream) { + IO.mapOptional("Content", Stream.Content); + IO.mapOptional("Size", Stream.Size, Stream.Content.binary_size()); +} + +static StringRef streamValidate(RawContentStream &Stream) { + if (Stream.Size.value < Stream.Content.binary_size()) + return "Stream size must be greater or equal to the content size"; + return ""; +} + +static void streamMapping(yaml::IO &IO, SystemInfoStream &Stream) { + SystemInfo &Info = Stream.Info; + IO.mapRequired("Processor Arch", Info.ProcessorArch); + mapOptional(IO, "Processor Level", Info.ProcessorLevel, 0); + mapOptional(IO, "Processor Revision", Info.ProcessorRevision, 0); + IO.mapOptional("Number of Processors", Info.NumberOfProcessors, 0); + IO.mapOptional("Product type", Info.ProductType, 0); + mapOptional(IO, "Major Version", Info.MajorVersion, 0); + mapOptional(IO, "Minor Version", Info.MinorVersion, 0); + mapOptional(IO, "Build Number", Info.BuildNumber, 0); + IO.mapRequired("Platform ID", Info.PlatformId); + mapOptionalHex(IO, "CSD Version RVA", Info.CSDVersionRVA, 0); + mapOptionalHex(IO, "Suite Mask", Info.SuiteMask, 0); + mapOptionalHex(IO, "Reserved", Info.Reserved, 0); + switch (static_cast(Info.ProcessorArch)) { + case ProcessorArchitecture::X86: + case ProcessorArchitecture::AMD64: + IO.mapOptional("CPU", Info.CPU.X86); + break; + case ProcessorArchitecture::ARM: + case ProcessorArchitecture::ARM64: + IO.mapOptional("CPU", Info.CPU.Arm); + break; + default: + IO.mapOptional("CPU", Info.CPU.Other); + break; + } +} + +static void streamMapping(yaml::IO &IO, TextContentStream &Stream) { + IO.mapOptional("Text", Stream.Text); +} + +void yaml::MappingTraits>::mapping( + yaml::IO &IO, std::unique_ptr &S) { + StreamType Type; + if (IO.outputting()) + Type = S->Type; + IO.mapRequired("Type", Type); + + if (!IO.outputting()) + S = MinidumpYAML::Stream::create(Type); + switch (S->Kind) { + case MinidumpYAML::Stream::StreamKind::RawContent: + streamMapping(IO, llvm::cast(*S)); + break; + case MinidumpYAML::Stream::StreamKind::SystemInfo: + streamMapping(IO, llvm::cast(*S)); + break; + case MinidumpYAML::Stream::StreamKind::TextContent: + streamMapping(IO, llvm::cast(*S)); + break; + } +} + +StringRef yaml::MappingTraits>::validate( + yaml::IO &IO, std::unique_ptr &S) { + switch (S->Kind) { + case MinidumpYAML::Stream::StreamKind::RawContent: + return streamValidate(cast(*S)); + case MinidumpYAML::Stream::StreamKind::SystemInfo: + case MinidumpYAML::Stream::StreamKind::TextContent: + return ""; + } + llvm_unreachable("Fully covered switch above!"); +} + +void yaml::MappingTraits::mapping(IO &IO, Object &O) { + IO.mapTag("!minidump", true); + mapOptionalHex(IO, "Signature", O.Header.Signature, Header::MagicSignature); + mapOptionalHex(IO, "Version", O.Header.Version, Header::MagicVersion); + mapOptionalHex(IO, "Flags", O.Header.Flags, 0); + IO.mapRequired("Streams", O.Streams); +} + +static Directory layout(BlobAllocator &File, Stream &S) { + Directory Result; + Result.Type = S.Type; + Result.Location.RVA = File.tell(); + switch (S.Kind) { + case Stream::StreamKind::RawContent: { + RawContentStream &Raw = cast(S); + File.AllocateCallback(Raw.Size, [&Raw](raw_ostream &OS) { + Raw.Content.writeAsBinary(OS); + assert(Raw.Content.binary_size() <= Raw.Size); + OS << std::string(Raw.Size - Raw.Content.binary_size(), '\0'); + }); + break; + } + case Stream::StreamKind::SystemInfo: + File.AllocateObject(cast(S).Info); + break; + case Stream::StreamKind::TextContent: + File.AllocateArray(arrayRefFromStringRef(cast(S).Text)); + break; + } + Result.Location.DataSize = File.tell() - Result.Location.RVA; + return Result; +} + +void MinidumpYAML::writeAsBinary(Object &Obj, raw_ostream &OS) { + BlobAllocator File; + File.AllocateObject(Obj.Header); + + std::vector StreamDirectory(Obj.Streams.size()); + Obj.Header.StreamDirectoryRVA = + File.AllocateArray(makeArrayRef(StreamDirectory)); + Obj.Header.NumberOfStreams = StreamDirectory.size(); + + for (auto &Stream : enumerate(Obj.Streams)) + StreamDirectory[Stream.index()] = layout(File, *Stream.value()); + + File.writeTo(OS); +} + +Error MinidumpYAML::writeAsBinary(StringRef Yaml, raw_ostream &OS) { + yaml::Input Input(Yaml); + Object Obj; + Input >> Obj; + if (std::error_code EC = Input.error()) + return errorCodeToError(EC); + + writeAsBinary(Obj, OS); + return Error::success(); +} Index: llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp +++ llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp @@ -45,6 +45,9 @@ ObjectFile.FatMachO.reset(new MachOYAML::UniversalBinary()); MappingTraits::mapping(IO, *ObjectFile.FatMachO); + } else if (IO.mapTag("!minidump")) { + ObjectFile.Minidump.reset(new MinidumpYAML::Object()); + MappingTraits::mapping(IO, *ObjectFile.Minidump); } else if (IO.mapTag("!WASM")) { ObjectFile.Wasm.reset(new WasmYAML::Object()); MappingTraits::mapping(IO, *ObjectFile.Wasm); Index: llvm/trunk/test/tools/yaml2obj/minidump-raw-stream-small-size.yaml =================================================================== --- llvm/trunk/test/tools/yaml2obj/minidump-raw-stream-small-size.yaml +++ llvm/trunk/test/tools/yaml2obj/minidump-raw-stream-small-size.yaml @@ -0,0 +1,9 @@ +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +--- !minidump +Streams: + - Type: LinuxAuxv + Size: 7 + Content: DEADBEEFBAADF00D + +# CHECK: Stream size must be greater or equal to the content size Index: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-long.yaml =================================================================== --- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-long.yaml +++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-long.yaml @@ -0,0 +1,13 @@ +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: PPC + Platform ID: Linux + CPU: + Features: 000102030405060708090a0b0c0d0e0f0 + + +# CHECK: String too long +# CHECK-NEXT: Features: 000102030405060708090a0b0c0d0e0f0 Index: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-not-hex.yaml =================================================================== --- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-not-hex.yaml +++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-not-hex.yaml @@ -0,0 +1,13 @@ +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: PPC + Platform ID: Linux + CPU: + Features: 000102030405060708090a0b0c0d0e0g + + +# CHECK: Invalid hex digit in input +# CHECK-NEXT: Features: 000102030405060708090a0b0c0d0e0g Index: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-short.yaml =================================================================== --- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-short.yaml +++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-short.yaml @@ -0,0 +1,13 @@ +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: PPC + Platform ID: Linux + CPU: + Features: 000102030405060708090a0b0c0d0e0 + + +# CHECK: String too short +# CHECK-NEXT: Features: 000102030405060708090a0b0c0d0e0 Index: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-long.yaml =================================================================== --- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-long.yaml +++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-long.yaml @@ -0,0 +1,15 @@ +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: X86 + Platform ID: Linux + CPU: + Vendor ID: LLVMLLVMLLVML + Version Info: 0x01020304 + Feature Info: 0x05060708 + AMD Extended Features: 0x09000102 + +# CHECK: String too long +# CHECK-NEXT: Vendor ID: LLVMLLVMLLVML Index: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-short.yaml =================================================================== --- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-short.yaml +++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-short.yaml @@ -0,0 +1,15 @@ +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: X86 + Platform ID: Linux + CPU: + Vendor ID: LLVMLLVMLLV + Version Info: 0x01020304 + Feature Info: 0x05060708 + AMD Extended Features: 0x09000102 + +# CHECK: String too short +# CHECK-NEXT: Vendor ID: LLVMLLVMLLV Index: llvm/trunk/tools/yaml2obj/CMakeLists.txt =================================================================== --- llvm/trunk/tools/yaml2obj/CMakeLists.txt +++ llvm/trunk/tools/yaml2obj/CMakeLists.txt @@ -11,5 +11,6 @@ yaml2coff.cpp yaml2elf.cpp yaml2macho.cpp + yaml2minidump.cpp yaml2wasm.cpp ) Index: llvm/trunk/tools/yaml2obj/yaml2minidump.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2minidump.cpp +++ llvm/trunk/tools/yaml2obj/yaml2minidump.cpp @@ -0,0 +1,18 @@ +//===- yaml2minidump.cpp - Convert a YAML file to a minidump file ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "yaml2obj.h" +#include "llvm/ObjectYAML/MinidumpYAML.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +int yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out) { + writeAsBinary(Doc, Out); + return 0; +} Index: llvm/trunk/tools/yaml2obj/yaml2obj.h =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2obj.h +++ llvm/trunk/tools/yaml2obj/yaml2obj.h @@ -22,6 +22,10 @@ struct Object; } +namespace MinidumpYAML { +struct Object; +} + namespace WasmYAML { struct Object; } @@ -35,6 +39,7 @@ int yaml2coff(llvm::COFFYAML::Object &Doc, llvm::raw_ostream &Out); int yaml2elf(llvm::ELFYAML::Object &Doc, llvm::raw_ostream &Out); int yaml2macho(llvm::yaml::YamlObjectFile &Doc, llvm::raw_ostream &Out); +int yaml2minidump(llvm::MinidumpYAML::Object &Doc, llvm::raw_ostream &Out); int yaml2wasm(llvm::WasmYAML::Object &Doc, llvm::raw_ostream &Out); #endif Index: llvm/trunk/tools/yaml2obj/yaml2obj.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2obj.cpp +++ llvm/trunk/tools/yaml2obj/yaml2obj.cpp @@ -56,6 +56,8 @@ return yaml2coff(*Doc.Coff, Out); if (Doc.MachO || Doc.FatMachO) return yaml2macho(Doc, Out); + if (Doc.Minidump) + return yaml2minidump(*Doc.Minidump, Out); if (Doc.Wasm) return yaml2wasm(*Doc.Wasm, Out); error("yaml2obj: Unknown document type!"); Index: llvm/trunk/unittests/ObjectYAML/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/ObjectYAML/CMakeLists.txt +++ llvm/trunk/unittests/ObjectYAML/CMakeLists.txt @@ -1,8 +1,11 @@ set(LLVM_LINK_COMPONENTS + Object ObjectYAML ) add_llvm_unittest(ObjectYAMLTests + MinidumpYAMLTest.cpp YAMLTest.cpp ) +target_link_libraries(ObjectYAMLTests PRIVATE LLVMTestingSupport) Index: llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp =================================================================== --- llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp +++ llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp @@ -0,0 +1,141 @@ +//===- MinidumpYAMLTest.cpp - Tests for Minidump<->YAML code --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ObjectYAML/MinidumpYAML.h" +#include "llvm/Object/Minidump.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::minidump; + +static Expected> +toBinary(SmallVectorImpl &Storage, StringRef Yaml) { + Storage.clear(); + raw_svector_ostream OS(Storage); + if (Error E = MinidumpYAML::writeAsBinary(Yaml, OS)) + return std::move(E); + + return object::MinidumpFile::create(MemoryBufferRef(OS.str(), "Binary")); +} + +TEST(MinidumpYAML, Basic) { + SmallString<0> Storage; + auto ExpectedFile = toBinary(Storage, R"( +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: ARM64 + Platform ID: Linux + CSD Version RVA: 0x01020304 + CPU: + CPUID: 0x05060708 + - Type: LinuxMaps + Text: | + 400d9000-400db000 r-xp 00000000 b3:04 227 /system/bin/app_process + 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process + + - Type: LinuxAuxv + Content: DEADBEEFBAADF00D)"); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + object::MinidumpFile &File = **ExpectedFile; + + ASSERT_EQ(3u, File.streams().size()); + + EXPECT_EQ(StreamType::SystemInfo, File.streams()[0].Type); + auto ExpectedSysInfo = File.getSystemInfo(); + ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded()); + const SystemInfo &SysInfo = *ExpectedSysInfo; + EXPECT_EQ(ProcessorArchitecture::ARM64, SysInfo.ProcessorArch); + EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId); + EXPECT_EQ(0x01020304u, SysInfo.CSDVersionRVA); + EXPECT_EQ(0x05060708u, SysInfo.CPU.Arm.CPUID); + + EXPECT_EQ(StreamType::LinuxMaps, File.streams()[1].Type); + EXPECT_EQ("400d9000-400db000 r-xp 00000000 b3:04 227 " + "/system/bin/app_process\n" + "400db000-400dc000 r--p 00001000 b3:04 227 " + "/system/bin/app_process\n", + toStringRef(*File.getRawStream(StreamType::LinuxMaps))); + + EXPECT_EQ(StreamType::LinuxAuxv, File.streams()[2].Type); + EXPECT_EQ((ArrayRef{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D}), + File.getRawStream(StreamType::LinuxAuxv)); +} + +TEST(MinidumpYAML, RawContent) { + SmallString<0> Storage; + auto ExpectedFile = toBinary(Storage, R"( +--- !minidump +Streams: + - Type: LinuxAuxv + Size: 9 + Content: DEADBEEFBAADF00D)"); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + object::MinidumpFile &File = **ExpectedFile; + + EXPECT_EQ( + (ArrayRef{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D, 0x00}), + File.getRawStream(StreamType::LinuxAuxv)); +} + +TEST(MinidumpYAML, X86SystemInfo) { + SmallString<0> Storage; + auto ExpectedFile = toBinary(Storage, R"( +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: X86 + Platform ID: Linux + CPU: + Vendor ID: LLVMLLVMLLVM + Version Info: 0x01020304 + Feature Info: 0x05060708 + AMD Extended Features: 0x09000102)"); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + object::MinidumpFile &File = **ExpectedFile; + + ASSERT_EQ(1u, File.streams().size()); + + auto ExpectedSysInfo = File.getSystemInfo(); + ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded()); + const SystemInfo &SysInfo = *ExpectedSysInfo; + EXPECT_EQ(ProcessorArchitecture::X86, SysInfo.ProcessorArch); + EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId); + EXPECT_EQ("LLVMLLVMLLVM", StringRef(SysInfo.CPU.X86.VendorID, + sizeof(SysInfo.CPU.X86.VendorID))); + EXPECT_EQ(0x01020304u, SysInfo.CPU.X86.VersionInfo); + EXPECT_EQ(0x05060708u, SysInfo.CPU.X86.FeatureInfo); + EXPECT_EQ(0x09000102u, SysInfo.CPU.X86.AMDExtendedFeatures); +} + +TEST(MinidumpYAML, OtherSystemInfo) { + SmallString<0> Storage; + auto ExpectedFile = toBinary(Storage, R"( +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: PPC + Platform ID: Linux + CPU: + Features: 000102030405060708090a0b0c0d0e0f)"); + ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); + object::MinidumpFile &File = **ExpectedFile; + + ASSERT_EQ(1u, File.streams().size()); + + auto ExpectedSysInfo = File.getSystemInfo(); + ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded()); + const SystemInfo &SysInfo = *ExpectedSysInfo; + EXPECT_EQ(ProcessorArchitecture::PPC, SysInfo.ProcessorArch); + EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId); + EXPECT_EQ( + (ArrayRef{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}), + makeArrayRef(SysInfo.CPU.Other.ProcessorFeatures)); +}