[llvm] r356753 - [ObjectYAML] Add basic minidump generation support

Pavel Labath via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 22 07:47:26 PDT 2019


Author: labath
Date: Fri Mar 22 07:47:26 2019
New Revision: 356753

URL: http://llvm.org/viewvc/llvm-project?rev=356753&view=rev
Log:
[ObjectYAML] Add basic minidump generation support

Summary:
This patch adds the ability to read a yaml form of a minidump file and
write it out as binary. Apart from the minidump header and the stream
directory, only three basic stream kinds are supported:
- Text: This kind is used for streams which contain textual data. This
  is typically the contents of a /proc file on linux (e.g.
  /proc/PID/maps). In this case, we just put the raw stream contents
  into the yaml.
- SystemInfo: This stream contains various bits of information about the
  host system in binary form. We expose the data in a structured form.
- Raw: This kind is used as a fallback when we don't have any special
  knowledge about the stream. In this case, we just print the stream
  contents in hex.

For this code to be really useful, more stream kinds will need to be
added (particularly for things like lists of memory regions and loaded
modules). However, these can be added incrementally.

Reviewers: jhenderson, zturner, clayborg, aprantl

Subscribers: mgorny, lemo, llvm-commits, lldb-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D59482

Added:
    llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h
    llvm/trunk/lib/ObjectYAML/MinidumpYAML.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/yaml2minidump.cpp
    llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp
Modified:
    llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h
    llvm/trunk/lib/ObjectYAML/CMakeLists.txt
    llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp
    llvm/trunk/tools/yaml2obj/CMakeLists.txt
    llvm/trunk/tools/yaml2obj/yaml2obj.cpp
    llvm/trunk/tools/yaml2obj/yaml2obj.h
    llvm/trunk/unittests/ObjectYAML/CMakeLists.txt

Added: llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h?rev=356753&view=auto
==============================================================================
--- llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h (added)
+++ llvm/trunk/include/llvm/ObjectYAML/MinidumpYAML.h Fri Mar 22 07:47:26 2019
@@ -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

Modified: llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h?rev=356753&r1=356752&r2=356753&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h (original)
+++ llvm/trunk/include/llvm/ObjectYAML/ObjectYAML.h Fri Mar 22 07:47:26 2019
@@ -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 @@ struct YamlObjectFile {
   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;
 };
 

Modified: llvm/trunk/lib/ObjectYAML/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ObjectYAML/CMakeLists.txt?rev=356753&r1=356752&r2=356753&view=diff
==============================================================================
--- llvm/trunk/lib/ObjectYAML/CMakeLists.txt (original)
+++ llvm/trunk/lib/ObjectYAML/CMakeLists.txt Fri Mar 22 07:47:26 2019
@@ -10,6 +10,7 @@ add_llvm_library(LLVMObjectYAML
   ELFYAML.cpp
   MachOYAML.cpp
   ObjectYAML.cpp
+  MinidumpYAML.cpp
   WasmYAML.cpp
   YAML.cpp
   )

Added: llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp?rev=356753&view=auto
==============================================================================
--- llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp (added)
+++ llvm/trunk/lib/ObjectYAML/MinidumpYAML.cpp Fri Mar 22 07:47:26 2019
@@ -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();
+}

Modified: llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp?rev=356753&r1=356752&r2=356753&view=diff
==============================================================================
--- llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp (original)
+++ llvm/trunk/lib/ObjectYAML/ObjectYAML.cpp Fri Mar 22 07:47:26 2019
@@ -45,6 +45,9 @@ void MappingTraits<YamlObjectFile>::mapp
       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);

Added: llvm/trunk/test/tools/yaml2obj/minidump-raw-stream-small-size.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/yaml2obj/minidump-raw-stream-small-size.yaml?rev=356753&view=auto
==============================================================================
--- llvm/trunk/test/tools/yaml2obj/minidump-raw-stream-small-size.yaml (added)
+++ llvm/trunk/test/tools/yaml2obj/minidump-raw-stream-small-size.yaml Fri Mar 22 07:47:26 2019
@@ -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

Added: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-long.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-long.yaml?rev=356753&view=auto
==============================================================================
--- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-long.yaml (added)
+++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-long.yaml Fri Mar 22 07:47:26 2019
@@ -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

Added: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-not-hex.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-not-hex.yaml?rev=356753&view=auto
==============================================================================
--- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-not-hex.yaml (added)
+++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-not-hex.yaml Fri Mar 22 07:47:26 2019
@@ -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

Added: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-short.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-short.yaml?rev=356753&view=auto
==============================================================================
--- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-short.yaml (added)
+++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-other-short.yaml Fri Mar 22 07:47:26 2019
@@ -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

Added: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-long.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-long.yaml?rev=356753&view=auto
==============================================================================
--- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-long.yaml (added)
+++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-long.yaml Fri Mar 22 07:47:26 2019
@@ -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

Added: llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-short.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-short.yaml?rev=356753&view=auto
==============================================================================
--- llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-short.yaml (added)
+++ llvm/trunk/test/tools/yaml2obj/minidump-systeminfo-x86-short.yaml Fri Mar 22 07:47:26 2019
@@ -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

Modified: llvm/trunk/tools/yaml2obj/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/yaml2obj/CMakeLists.txt?rev=356753&r1=356752&r2=356753&view=diff
==============================================================================
--- llvm/trunk/tools/yaml2obj/CMakeLists.txt (original)
+++ llvm/trunk/tools/yaml2obj/CMakeLists.txt Fri Mar 22 07:47:26 2019
@@ -11,5 +11,6 @@ add_llvm_tool(yaml2obj
   yaml2coff.cpp
   yaml2elf.cpp
   yaml2macho.cpp
+  yaml2minidump.cpp
   yaml2wasm.cpp
   )

Added: llvm/trunk/tools/yaml2obj/yaml2minidump.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/yaml2obj/yaml2minidump.cpp?rev=356753&view=auto
==============================================================================
--- llvm/trunk/tools/yaml2obj/yaml2minidump.cpp (added)
+++ llvm/trunk/tools/yaml2obj/yaml2minidump.cpp Fri Mar 22 07:47:26 2019
@@ -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;
+}

Modified: llvm/trunk/tools/yaml2obj/yaml2obj.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/yaml2obj/yaml2obj.cpp?rev=356753&r1=356752&r2=356753&view=diff
==============================================================================
--- llvm/trunk/tools/yaml2obj/yaml2obj.cpp (original)
+++ llvm/trunk/tools/yaml2obj/yaml2obj.cpp Fri Mar 22 07:47:26 2019
@@ -56,6 +56,8 @@ static int convertYAML(yaml::Input &YIn,
         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!");

Modified: llvm/trunk/tools/yaml2obj/yaml2obj.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/yaml2obj/yaml2obj.h?rev=356753&r1=356752&r2=356753&view=diff
==============================================================================
--- llvm/trunk/tools/yaml2obj/yaml2obj.h (original)
+++ llvm/trunk/tools/yaml2obj/yaml2obj.h Fri Mar 22 07:47:26 2019
@@ -22,6 +22,10 @@ namespace ELFYAML {
 struct Object;
 }
 
+namespace MinidumpYAML {
+struct Object;
+}
+
 namespace WasmYAML {
 struct Object;
 }
@@ -35,6 +39,7 @@ struct YamlObjectFile;
 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

Modified: llvm/trunk/unittests/ObjectYAML/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ObjectYAML/CMakeLists.txt?rev=356753&r1=356752&r2=356753&view=diff
==============================================================================
--- llvm/trunk/unittests/ObjectYAML/CMakeLists.txt (original)
+++ llvm/trunk/unittests/ObjectYAML/CMakeLists.txt Fri Mar 22 07:47:26 2019
@@ -1,8 +1,11 @@
 set(LLVM_LINK_COMPONENTS
+  Object
   ObjectYAML
   )
 
 add_llvm_unittest(ObjectYAMLTests
+  MinidumpYAMLTest.cpp
   YAMLTest.cpp
   )
 
+target_link_libraries(ObjectYAMLTests PRIVATE LLVMTestingSupport)

Added: llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp?rev=356753&view=auto
==============================================================================
--- llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp (added)
+++ llvm/trunk/unittests/ObjectYAML/MinidumpYAMLTest.cpp Fri Mar 22 07:47:26 2019
@@ -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));
+}




More information about the llvm-commits mailing list