This revision was automatically updated to reflect the committed changes.
Closed by commit rL356753: [ObjectYAML] Add basic minidump generation support 
(authored by labath, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D59482?vs=191653&id=191879#toc

Repository:
  rL LLVM

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59482/new/

https://reviews.llvm.org/D59482

Files:
  llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h
  llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h
  llvm/trunk/lib/ObjectYAML/CMakeLists.txt
  llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp
  llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp
  llvm/trunk/test/tools/yaml2obj/minidump-raw-stream-small-size.yaml
  llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-long.yaml
  llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-not-hex.yaml
  llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-short.yaml
  llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-long.yaml
  llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-short.yaml
  llvm/trunk/tools/yaml2obj/CMakeLists.txt
  llvm/trunk/tools/yaml2obj/yaml2minidump.cpp
  llvm/trunk/tools/yaml2obj/yaml2obj.cpp
  llvm/trunk/tools/yaml2obj/yaml2obj.h
  llvm/trunk/unittests/ObjectYAML/CMakeLists.txt
  llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.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<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();
+}
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/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<MachOYAML::UniversalBinary>::mapping(IO,
                                                          *ObjectFile.FatMachO);
+    } else if (IO.mapTag("!minidump")) {
+      ObjectFile.Minidump.reset(new MinidumpYAML::Object());
+      MappingTraits<MinidumpYAML::Object>::mapping(IO, *ObjectFile.Minidump);
     } else if (IO.mapTag("!WASM")) {
       ObjectFile.Wasm.reset(new WasmYAML::Object());
       MappingTraits<WasmYAML::Object>::mapping(IO, *ObjectFile.Wasm);
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<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));
+}
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/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/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/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 <memory>
@@ -26,6 +27,7 @@
   std::unique_ptr<COFFYAML::Object> Coff;
   std::unique_ptr<MachOYAML::Object> MachO;
   std::unique_ptr<MachOYAML::UniversalBinary> FatMachO;
+  std::unique_ptr<MinidumpYAML::Object> Minidump;
   std::unique_ptr<WasmYAML::Object> Wasm;
 };
 
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<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
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-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-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-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
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to