Index: include/llvm/ObjectYAML/MinidumpYAML.h =================================================================== --- /dev/null +++ include/llvm/ObjectYAML/MinidumpYAML.h @@ -0,0 +1,143 @@ +//===- 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 HexStream). The mapping from +/// Types to Kinds is fixed and given by the static getKind function. +struct Stream { + enum StreamKind { + HexKind, + SystemInfoKind, + TextKind, + }; + + 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 HexStream : public Stream { + yaml::BinaryRef Content; + yaml::Hex32 Size; + + HexStream(minidump::StreamType Type, ArrayRef Content = {}) + : Stream(HexKind, Type), Content(Content), Size(Content.size()) {} + + static bool classof(const Stream *S) { return S->Kind == HexKind; } +}; + +/// SystemInfo minidump stream. +struct SystemInfoStream : public Stream { + minidump::SystemInfo Info; + + explicit SystemInfoStream(const minidump::SystemInfo &Info) + : Stream(SystemInfoKind, minidump::StreamType::SystemInfo), Info(Info) {} + + SystemInfoStream() + : Stream(SystemInfoKind, minidump::StreamType::SystemInfo) { + memset(&Info, 0, sizeof(Info)); + } + + static bool classof(const Stream *S) { return S->Kind == SystemInfoKind; } +}; + +/// 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 TextStream : public Stream { + BlockStringRef Text; + + TextStream(minidump::StreamType Type, StringRef Text = {}) + : Stream(TextKind, Type), Text(Text) {} + + static bool classof(const Stream *S) { return S->Kind == TextKind; } +}; + +/// 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 ""; + } +}; +} // 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_DECLARE_MAPPING_TRAITS(std::unique_ptr) +LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) + +LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::MinidumpYAML::Object) + +#endif // LLVM_OBJECTYAML_MINIDUMPYAML_H Index: include/llvm/ObjectYAML/ObjectYAML.h =================================================================== --- include/llvm/ObjectYAML/ObjectYAML.h +++ 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: lib/ObjectYAML/CMakeLists.txt =================================================================== --- lib/ObjectYAML/CMakeLists.txt +++ lib/ObjectYAML/CMakeLists.txt @@ -10,6 +10,7 @@ ELFYAML.cpp MachOYAML.cpp ObjectYAML.cpp + MinidumpYAML.cpp WasmYAML.cpp YAML.cpp ) Index: lib/ObjectYAML/MinidumpYAML.cpp =================================================================== --- /dev/null +++ lib/ObjectYAML/MinidumpYAML.cpp @@ -0,0 +1,314 @@ +//===- 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); + (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 mapAs(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 mapHex(yaml::IO &IO, const char *Key, EndianType &Val) { + mapAs::type>(IO, Key, Val); +} + +/// Perform an optionam 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 SystemInfoKind; + case StreamType::LinuxCPUInfo: + case StreamType::LinuxProcStatus: + case StreamType::LinuxLSBRelease: + case StreamType::LinuxCMDLine: + case StreamType::LinuxMaps: + case StreamType::LinuxProcStat: + case StreamType::LinuxProcUptime: + return TextKind; + default: + return HexKind; + } +} + +std::unique_ptr Stream::create(StreamType Type) { + StreamKind Kind = getKind(Type); + switch (Kind) { + case HexKind: + return llvm::make_unique(Type); + case SystemInfoKind: + return llvm::make_unique(); + case TextKind: + 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) { + mapHex(IO, "CPUID", Info.CPUID); + mapOptionalHex(IO, "ELF hwcaps", Info.ElfHWCaps, 0); +} + +void yaml::MappingTraits::mapping( + IO &IO, CPUInfo::OtherInfo &Info) { + llvm::MutableArrayRef Features(Info.ProcessorFeatures); + yaml::BinaryRef FeaturesRef(Features); + + IO.mapRequired("Features", FeaturesRef); + + llvm::SmallString<16> FeaturesStorage; + llvm::raw_svector_ostream FeaturesStream(FeaturesStorage); + FeaturesRef.writeAsBinary(FeaturesStream); + std::fill(Features.begin(), Features.end(), 0); + std::memcpy(Features.begin(), FeaturesStorage.begin(), + std::min(FeaturesStorage.size(), Features.size())); +} + +void yaml::MappingTraits::mapping(IO &IO, + CPUInfo::X86Info &Info) { + llvm::StringRef VendorID(Info.VendorID, sizeof(Info.VendorID)); + IO.mapRequired("Vendor ID", VendorID); + std::fill_n(Info.VendorID, sizeof(Info.VendorID), 0); + std::copy_n(VendorID.begin(), + std::min(VendorID.size(), sizeof(Info.VendorID)), Info.VendorID); + + mapHex(IO, "Version Info", Info.VersionInfo); + mapHex(IO, "Feature Info", Info.FeatureInfo); + mapOptionalHex(IO, "AMD Extended Features", Info.AMDExtendedFeatures, 0); +} + +static void streamMapping(yaml::IO &IO, HexStream &Stream) { + IO.mapOptional("Content", Stream.Content); + IO.mapOptional("Size", Stream.Size, Stream.Content.binary_size()); +} + +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, TextStream &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::HexKind: + streamMapping(IO, llvm::cast(*S)); + break; + case MinidumpYAML::Stream::SystemInfoKind: + streamMapping(IO, llvm::cast(*S)); + break; + case MinidumpYAML::Stream::TextKind: + streamMapping(IO, llvm::cast(*S)); + break; + } +} + +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::HexKind: { + HexStream &Hex = cast(S); + File.AllocateCallback(Hex.Content.binary_size(), [&Hex](raw_ostream &OS) { + Hex.Content.writeAsBinary(OS); + }); + break; + } + case Stream::SystemInfoKind: + File.AllocateObject(cast(S).Info); + break; + case Stream::TextKind: + 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 yin(Yaml); + Object Obj; + yin >> Obj; + if (std::error_code EC = yin.error()) + return errorCodeToError(EC); + + writeAsBinary(Obj, OS); + return Error::success(); +} Index: lib/ObjectYAML/ObjectYAML.cpp =================================================================== --- lib/ObjectYAML/ObjectYAML.cpp +++ 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: tools/yaml2obj/yaml2obj.h =================================================================== --- tools/yaml2obj/yaml2obj.h +++ 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: tools/yaml2obj/yaml2obj.cpp =================================================================== --- tools/yaml2obj/yaml2obj.cpp +++ tools/yaml2obj/yaml2obj.cpp @@ -15,6 +15,7 @@ #include "yaml2obj.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ObjectYAML/MinidumpYAML.h" #include "llvm/ObjectYAML/ObjectYAML.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -56,6 +57,10 @@ return yaml2coff(*Doc.Coff, Out); if (Doc.MachO || Doc.FatMachO) return yaml2macho(Doc, Out); + if (Doc.Minidump) { + MinidumpYAML::writeAsBinary(*Doc.Minidump, Out); + return 0; + } if (Doc.Wasm) return yaml2wasm(*Doc.Wasm, Out); error("yaml2obj: Unknown document type!"); Index: unittests/ObjectYAML/CMakeLists.txt =================================================================== --- unittests/ObjectYAML/CMakeLists.txt +++ 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: unittests/ObjectYAML/MinidumpYAMLTest.cpp =================================================================== --- /dev/null +++ unittests/ObjectYAML/MinidumpYAMLTest.cpp @@ -0,0 +1,121 @@ +//===- 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(StringRef Yaml) { + SmallString<0> Binary; + raw_svector_ostream OS(Binary); + if (Error E = MinidumpYAML::writeAsBinary(Yaml, OS)) + return std::move(E); + + return object::MinidumpFile::create(MemoryBufferRef(Binary, "Binary")); +} + +TEST(MinidumpYAML, Basic) { + auto ExpectedFile = toBinary(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, X86SystemInfo) { + auto ExpectedFile = toBinary(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) { + auto ExpectedFile = toBinary(R"( +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: PPC + Platform ID: Linux + CPU: + Features: 00010203040506070809)"); + 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, 0, 0, 0, 0, 0, 0}), + makeArrayRef(SysInfo.CPU.Other.ProcessorFeatures)); +}