--- /dev/null
+//===- 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<Stream> 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<uint8_t> 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/<pid> 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<std::unique_ptr<Stream>> 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<MinidumpYAML::BlockStringRef> {
+ 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<std::unique_ptr<MinidumpYAML::Stream>> {
+ static void mapping(IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S);
+ static StringRef validate(IO &IO, std::unique_ptr<MinidumpYAML::Stream> &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::MinidumpYAML::Stream>)
+
+LLVM_YAML_DECLARE_MAPPING_TRAITS(llvm::MinidumpYAML::Object)
+
+#endif // LLVM_OBJECTYAML_MINIDUMPYAML_H
--- /dev/null
+//===- 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<void(raw_ostream &)> Callback) {
+ size_t Offset = NextOffset;
+ NextOffset += Size;
+ Callbacks.push_back(std::move(Callback));
+ return Offset;
+ }
+
+ size_t AllocateBytes(ArrayRef<uint8_t> Data) {
+ return AllocateCallback(
+ Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); });
+ }
+
+ template <typename T> size_t AllocateArray(ArrayRef<T> Data) {
+ return AllocateBytes({reinterpret_cast<const uint8_t *>(Data.data()),
+ sizeof(T) * Data.size()});
+ }
+
+ template <typename T> size_t AllocateObject(const T &Data) {
+ return AllocateArray(makeArrayRef(Data));
+ }
+
+ void writeTo(raw_ostream &OS) const;
+
+private:
+ size_t NextOffset = 0;
+
+ std::vector<std::function<void(raw_ostream &)>> 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 <typename EndianType>
+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 <typename MapType, typename EndianType>
+static inline void mapRequiredAs(yaml::IO &IO, const char *Key,
+ EndianType &Val) {
+ MapType Mapped = static_cast<typename EndianType::value_type>(Val);
+ IO.mapRequired(Key, Mapped);
+ Val = static_cast<typename EndianType::value_type>(Mapped);
+}
+
+/// Perform an optional yaml-mapping of an endian-aware type EndianType as some
+/// other type MapType.
+template <typename MapType, typename EndianType>
+static inline void mapOptionalAs(yaml::IO &IO, const char *Key, EndianType &Val,
+ MapType Default) {
+ MapType Mapped = static_cast<typename EndianType::value_type>(Val);
+ IO.mapOptional(Key, Mapped, Default);
+ Val = static_cast<typename EndianType::value_type>(Mapped);
+}
+
+namespace {
+/// Return the appropriate yaml Hex type for a given endian-aware type.
+template <typename EndianType> struct HexType;
+template <> struct HexType<support::ulittle16_t> { using type = yaml::Hex16; };
+template <> struct HexType<support::ulittle32_t> { using type = yaml::Hex32; };
+template <> struct HexType<support::ulittle64_t> { using type = yaml::Hex64; };
+} // namespace
+
+/// Yaml-map an endian-aware type as an appropriately-sized hex value.
+template <typename EndianType>
+static inline void mapRequiredHex(yaml::IO &IO, const char *Key,
+ EndianType &Val) {
+ mapRequiredAs<typename HexType<EndianType>::type>(IO, Key, Val);
+}
+
+/// Perform an optional yaml-mapping of an endian-aware type as an
+/// appropriately-sized hex value.
+template <typename EndianType>
+static inline void mapOptionalHex(yaml::IO &IO, const char *Key,
+ EndianType &Val,
+ typename EndianType::value_type Default) {
+ mapOptionalAs<typename HexType<EndianType>::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> Stream::create(StreamType Type) {
+ StreamKind Kind = getKind(Type);
+ switch (Kind) {
+ case StreamKind::RawContent:
+ return llvm::make_unique<RawContentStream>(Type);
+ case StreamKind::SystemInfo:
+ return llvm::make_unique<SystemInfoStream>();
+ case StreamKind::TextContent:
+ return llvm::make_unique<TextContentStream>(Type);
+ }
+ llvm_unreachable("Unhandled stream kind!");
+}
+
+void yaml::ScalarEnumerationTraits<ProcessorArchitecture>::enumeration(
+ IO &IO, ProcessorArchitecture &Arch) {
+#define HANDLE_MDMP_ARCH(CODE, NAME) \
+ IO.enumCase(Arch, #NAME, ProcessorArchitecture::NAME);
+#include "llvm/BinaryFormat/MinidumpConstants.def"
+ IO.enumFallback<Hex16>(Arch);
+}
+
+void yaml::ScalarEnumerationTraits<OSPlatform>::enumeration(IO &IO,
+ OSPlatform &Plat) {
+#define HANDLE_MDMP_PLATFORM(CODE, NAME) \
+ IO.enumCase(Plat, #NAME, OSPlatform::NAME);
+#include "llvm/BinaryFormat/MinidumpConstants.def"
+ IO.enumFallback<Hex32>(Plat);
+}
+
+void yaml::ScalarEnumerationTraits<StreamType>::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<Hex32>(Type);
+}
+
+void yaml::MappingTraits<CPUInfo::ArmInfo>::mapping(IO &IO,
+ CPUInfo::ArmInfo &Info) {
+ mapRequiredHex(IO, "CPUID", Info.CPUID);
+ mapOptionalHex(IO, "ELF hwcaps", Info.ElfHWCaps, 0);
+}
+
+namespace {
+template <std::size_t N> struct FixedSizeHex {
+ FixedSizeHex(uint8_t (&Storage)[N]) : Storage(Storage) {}
+
+ uint8_t (&Storage)[N];
+};
+} // namespace
+
+namespace llvm {
+namespace yaml {
+template <std::size_t N> struct ScalarTraits<FixedSizeHex<N>> {
+ static void output(const FixedSizeHex<N> &Fixed, void *, raw_ostream &OS) {
+ OS << toHex(makeArrayRef(Fixed.Storage));
+ }
+
+ static StringRef input(StringRef Scalar, void *, FixedSizeHex<N> &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<CPUInfo::OtherInfo>::mapping(
+ IO &IO, CPUInfo::OtherInfo &Info) {
+ FixedSizeHex<sizeof(Info.ProcessorFeatures)> Features(Info.ProcessorFeatures);
+ IO.mapRequired("Features", Features);
+}
+
+namespace {
+/// A type which only accepts strings of a fixed size for yaml conversion.
+template <std::size_t N> struct FixedSizeString {
+ FixedSizeString(char (&Storage)[N]) : Storage(Storage) {}
+
+ char (&Storage)[N];
+};
+} // namespace
+
+namespace llvm {
+namespace yaml {
+template <std::size_t N> struct ScalarTraits<FixedSizeString<N>> {
+ static void output(const FixedSizeString<N> &Fixed, void *, raw_ostream &OS) {
+ OS << StringRef(Fixed.Storage, N);
+ }
+
+ static StringRef input(StringRef Scalar, void *, FixedSizeString<N> &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<CPUInfo::X86Info>::mapping(IO &IO,
+ CPUInfo::X86Info &Info) {
+ FixedSizeString<sizeof(Info.VendorID)> 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<ProcessorArchitecture>(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<std::unique_ptr<Stream>>::mapping(
+ yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &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<RawContentStream>(*S));
+ break;
+ case MinidumpYAML::Stream::StreamKind::SystemInfo:
+ streamMapping(IO, llvm::cast<SystemInfoStream>(*S));
+ break;
+ case MinidumpYAML::Stream::StreamKind::TextContent:
+ streamMapping(IO, llvm::cast<TextContentStream>(*S));
+ break;
+ }
+}
+
+StringRef yaml::MappingTraits<std::unique_ptr<Stream>>::validate(
+ yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) {
+ switch (S->Kind) {
+ case MinidumpYAML::Stream::StreamKind::RawContent:
+ return streamValidate(cast<RawContentStream>(*S));
+ case MinidumpYAML::Stream::StreamKind::SystemInfo:
+ case MinidumpYAML::Stream::StreamKind::TextContent:
+ return "";
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+void yaml::MappingTraits<Object>::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<RawContentStream>(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<SystemInfoStream>(S).Info);
+ break;
+ case Stream::StreamKind::TextContent:
+ File.AllocateArray(arrayRefFromStringRef(cast<TextContentStream>(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<Directory> 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();
+}
--- /dev/null
+//===- 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<std::unique_ptr<object::MinidumpFile>>
+toBinary(SmallVectorImpl<char> &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<uint8_t>{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<uint8_t>{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<uint8_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
+ makeArrayRef(SysInfo.CPU.Other.ProcessorFeatures));
+}