blob: 38d23d9dfc65dcd2bf9a579e7532eadb1253c380 [file] [log] [blame]
Pavel Labath69de7a92019-03-22 14:47:26 +00001//===- MinidumpYAML.cpp - Minidump YAMLIO implementation ------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/ObjectYAML/MinidumpYAML.h"
Pavel Labath51d9fa02019-04-05 08:06:26 +000010#include "llvm/Support/ConvertUTF.h"
Pavel Labath69de7a92019-03-22 14:47:26 +000011
12using namespace llvm;
13using namespace llvm::MinidumpYAML;
14using namespace llvm::minidump;
15
16namespace {
17class BlobAllocator {
18public:
19 size_t tell() const { return NextOffset; }
20
Pavel Labathc5f79052019-03-25 14:45:31 +000021 size_t allocateCallback(size_t Size,
Pavel Labath69de7a92019-03-22 14:47:26 +000022 std::function<void(raw_ostream &)> Callback) {
23 size_t Offset = NextOffset;
24 NextOffset += Size;
25 Callbacks.push_back(std::move(Callback));
26 return Offset;
27 }
28
Pavel Labathc5f79052019-03-25 14:45:31 +000029 size_t allocateBytes(ArrayRef<uint8_t> Data) {
30 return allocateCallback(
Pavel Labath69de7a92019-03-22 14:47:26 +000031 Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); });
32 }
33
Pavel Labathc5f79052019-03-25 14:45:31 +000034 template <typename T> size_t allocateArray(ArrayRef<T> Data) {
35 return allocateBytes({reinterpret_cast<const uint8_t *>(Data.data()),
Pavel Labath69de7a92019-03-22 14:47:26 +000036 sizeof(T) * Data.size()});
37 }
38
Pavel Labathc5f79052019-03-25 14:45:31 +000039 template <typename T> size_t allocateObject(const T &Data) {
40 return allocateArray(makeArrayRef(Data));
Pavel Labath69de7a92019-03-22 14:47:26 +000041 }
42
Pavel Labath51d9fa02019-04-05 08:06:26 +000043 size_t allocateString(StringRef Str);
44
Pavel Labath69de7a92019-03-22 14:47:26 +000045 void writeTo(raw_ostream &OS) const;
46
47private:
48 size_t NextOffset = 0;
49
50 std::vector<std::function<void(raw_ostream &)>> Callbacks;
51};
52} // namespace
53
Pavel Labath51d9fa02019-04-05 08:06:26 +000054size_t BlobAllocator::allocateString(StringRef Str) {
55 SmallVector<UTF16, 32> WStr;
56 bool OK = convertUTF8ToUTF16String(Str, WStr);
57 assert(OK && "Invalid UTF8 in Str?");
58 (void)OK;
59
60 SmallVector<support::ulittle16_t, 32> EndianStr(WStr.size() + 1,
61 support::ulittle16_t());
62 copy(WStr, EndianStr.begin());
63 return allocateCallback(
64 sizeof(uint32_t) + EndianStr.size() * sizeof(support::ulittle16_t),
65 [EndianStr](raw_ostream &OS) {
66 // Length does not include the null-terminator.
67 support::ulittle32_t Length(2 * (EndianStr.size() - 1));
68 OS.write(reinterpret_cast<const char *>(&Length), sizeof(Length));
69 OS.write(reinterpret_cast<const char *>(EndianStr.begin()),
70 sizeof(support::ulittle16_t) * EndianStr.size());
71 });
72}
73
Pavel Labath69de7a92019-03-22 14:47:26 +000074void BlobAllocator::writeTo(raw_ostream &OS) const {
75 size_t BeginOffset = OS.tell();
76 for (const auto &Callback : Callbacks)
77 Callback(OS);
78 assert(OS.tell() == BeginOffset + NextOffset &&
79 "Callbacks wrote an unexpected number of bytes.");
80 (void)BeginOffset;
81}
82
83/// Perform an optional yaml-mapping of an endian-aware type EndianType. The
84/// only purpose of this function is to avoid casting the Default value to the
85/// endian type;
86template <typename EndianType>
87static inline void mapOptional(yaml::IO &IO, const char *Key, EndianType &Val,
88 typename EndianType::value_type Default) {
89 IO.mapOptional(Key, Val, EndianType(Default));
90}
91
92/// Yaml-map an endian-aware type EndianType as some other type MapType.
93template <typename MapType, typename EndianType>
94static inline void mapRequiredAs(yaml::IO &IO, const char *Key,
95 EndianType &Val) {
96 MapType Mapped = static_cast<typename EndianType::value_type>(Val);
97 IO.mapRequired(Key, Mapped);
98 Val = static_cast<typename EndianType::value_type>(Mapped);
99}
100
101/// Perform an optional yaml-mapping of an endian-aware type EndianType as some
102/// other type MapType.
103template <typename MapType, typename EndianType>
104static inline void mapOptionalAs(yaml::IO &IO, const char *Key, EndianType &Val,
105 MapType Default) {
106 MapType Mapped = static_cast<typename EndianType::value_type>(Val);
107 IO.mapOptional(Key, Mapped, Default);
108 Val = static_cast<typename EndianType::value_type>(Mapped);
109}
110
111namespace {
112/// Return the appropriate yaml Hex type for a given endian-aware type.
113template <typename EndianType> struct HexType;
114template <> struct HexType<support::ulittle16_t> { using type = yaml::Hex16; };
115template <> struct HexType<support::ulittle32_t> { using type = yaml::Hex32; };
116template <> struct HexType<support::ulittle64_t> { using type = yaml::Hex64; };
117} // namespace
118
119/// Yaml-map an endian-aware type as an appropriately-sized hex value.
120template <typename EndianType>
121static inline void mapRequiredHex(yaml::IO &IO, const char *Key,
122 EndianType &Val) {
123 mapRequiredAs<typename HexType<EndianType>::type>(IO, Key, Val);
124}
125
126/// Perform an optional yaml-mapping of an endian-aware type as an
127/// appropriately-sized hex value.
128template <typename EndianType>
129static inline void mapOptionalHex(yaml::IO &IO, const char *Key,
130 EndianType &Val,
131 typename EndianType::value_type Default) {
132 mapOptionalAs<typename HexType<EndianType>::type>(IO, Key, Val, Default);
133}
134
135Stream::~Stream() = default;
136
137Stream::StreamKind Stream::getKind(StreamType Type) {
138 switch (Type) {
139 case StreamType::SystemInfo:
140 return StreamKind::SystemInfo;
141 case StreamType::LinuxCPUInfo:
142 case StreamType::LinuxProcStatus:
143 case StreamType::LinuxLSBRelease:
144 case StreamType::LinuxCMDLine:
145 case StreamType::LinuxMaps:
146 case StreamType::LinuxProcStat:
147 case StreamType::LinuxProcUptime:
148 return StreamKind::TextContent;
149 default:
150 return StreamKind::RawContent;
151 }
152}
153
154std::unique_ptr<Stream> Stream::create(StreamType Type) {
155 StreamKind Kind = getKind(Type);
156 switch (Kind) {
157 case StreamKind::RawContent:
158 return llvm::make_unique<RawContentStream>(Type);
159 case StreamKind::SystemInfo:
160 return llvm::make_unique<SystemInfoStream>();
161 case StreamKind::TextContent:
162 return llvm::make_unique<TextContentStream>(Type);
163 }
164 llvm_unreachable("Unhandled stream kind!");
165}
166
167void yaml::ScalarEnumerationTraits<ProcessorArchitecture>::enumeration(
168 IO &IO, ProcessorArchitecture &Arch) {
169#define HANDLE_MDMP_ARCH(CODE, NAME) \
170 IO.enumCase(Arch, #NAME, ProcessorArchitecture::NAME);
171#include "llvm/BinaryFormat/MinidumpConstants.def"
172 IO.enumFallback<Hex16>(Arch);
173}
174
175void yaml::ScalarEnumerationTraits<OSPlatform>::enumeration(IO &IO,
176 OSPlatform &Plat) {
177#define HANDLE_MDMP_PLATFORM(CODE, NAME) \
178 IO.enumCase(Plat, #NAME, OSPlatform::NAME);
179#include "llvm/BinaryFormat/MinidumpConstants.def"
180 IO.enumFallback<Hex32>(Plat);
181}
182
183void yaml::ScalarEnumerationTraits<StreamType>::enumeration(IO &IO,
184 StreamType &Type) {
185#define HANDLE_MDMP_STREAM_TYPE(CODE, NAME) \
186 IO.enumCase(Type, #NAME, StreamType::NAME);
187#include "llvm/BinaryFormat/MinidumpConstants.def"
188 IO.enumFallback<Hex32>(Type);
189}
190
191void yaml::MappingTraits<CPUInfo::ArmInfo>::mapping(IO &IO,
192 CPUInfo::ArmInfo &Info) {
193 mapRequiredHex(IO, "CPUID", Info.CPUID);
194 mapOptionalHex(IO, "ELF hwcaps", Info.ElfHWCaps, 0);
195}
196
197namespace {
198template <std::size_t N> struct FixedSizeHex {
199 FixedSizeHex(uint8_t (&Storage)[N]) : Storage(Storage) {}
200
201 uint8_t (&Storage)[N];
202};
203} // namespace
204
205namespace llvm {
206namespace yaml {
207template <std::size_t N> struct ScalarTraits<FixedSizeHex<N>> {
208 static void output(const FixedSizeHex<N> &Fixed, void *, raw_ostream &OS) {
209 OS << toHex(makeArrayRef(Fixed.Storage));
210 }
211
212 static StringRef input(StringRef Scalar, void *, FixedSizeHex<N> &Fixed) {
213 if (!all_of(Scalar, isHexDigit))
214 return "Invalid hex digit in input";
215 if (Scalar.size() < 2 * N)
216 return "String too short";
217 if (Scalar.size() > 2 * N)
218 return "String too long";
219 copy(fromHex(Scalar), Fixed.Storage);
220 return "";
221 }
222
223 static QuotingType mustQuote(StringRef S) { return QuotingType::None; }
224};
225} // namespace yaml
226} // namespace llvm
227void yaml::MappingTraits<CPUInfo::OtherInfo>::mapping(
228 IO &IO, CPUInfo::OtherInfo &Info) {
229 FixedSizeHex<sizeof(Info.ProcessorFeatures)> Features(Info.ProcessorFeatures);
230 IO.mapRequired("Features", Features);
231}
232
233namespace {
234/// A type which only accepts strings of a fixed size for yaml conversion.
235template <std::size_t N> struct FixedSizeString {
236 FixedSizeString(char (&Storage)[N]) : Storage(Storage) {}
237
238 char (&Storage)[N];
239};
240} // namespace
241
242namespace llvm {
243namespace yaml {
244template <std::size_t N> struct ScalarTraits<FixedSizeString<N>> {
245 static void output(const FixedSizeString<N> &Fixed, void *, raw_ostream &OS) {
246 OS << StringRef(Fixed.Storage, N);
247 }
248
249 static StringRef input(StringRef Scalar, void *, FixedSizeString<N> &Fixed) {
250 if (Scalar.size() < N)
251 return "String too short";
252 if (Scalar.size() > N)
253 return "String too long";
254 copy(Scalar, Fixed.Storage);
255 return "";
256 }
257
258 static QuotingType mustQuote(StringRef S) { return needsQuotes(S); }
259};
260} // namespace yaml
261} // namespace llvm
262
263void yaml::MappingTraits<CPUInfo::X86Info>::mapping(IO &IO,
264 CPUInfo::X86Info &Info) {
265 FixedSizeString<sizeof(Info.VendorID)> VendorID(Info.VendorID);
266 IO.mapRequired("Vendor ID", VendorID);
267
268 mapRequiredHex(IO, "Version Info", Info.VersionInfo);
269 mapRequiredHex(IO, "Feature Info", Info.FeatureInfo);
270 mapOptionalHex(IO, "AMD Extended Features", Info.AMDExtendedFeatures, 0);
271}
272
273static void streamMapping(yaml::IO &IO, RawContentStream &Stream) {
274 IO.mapOptional("Content", Stream.Content);
275 IO.mapOptional("Size", Stream.Size, Stream.Content.binary_size());
276}
277
278static StringRef streamValidate(RawContentStream &Stream) {
279 if (Stream.Size.value < Stream.Content.binary_size())
280 return "Stream size must be greater or equal to the content size";
281 return "";
282}
283
284static void streamMapping(yaml::IO &IO, SystemInfoStream &Stream) {
285 SystemInfo &Info = Stream.Info;
286 IO.mapRequired("Processor Arch", Info.ProcessorArch);
287 mapOptional(IO, "Processor Level", Info.ProcessorLevel, 0);
288 mapOptional(IO, "Processor Revision", Info.ProcessorRevision, 0);
289 IO.mapOptional("Number of Processors", Info.NumberOfProcessors, 0);
290 IO.mapOptional("Product type", Info.ProductType, 0);
291 mapOptional(IO, "Major Version", Info.MajorVersion, 0);
292 mapOptional(IO, "Minor Version", Info.MinorVersion, 0);
293 mapOptional(IO, "Build Number", Info.BuildNumber, 0);
294 IO.mapRequired("Platform ID", Info.PlatformId);
Pavel Labath51d9fa02019-04-05 08:06:26 +0000295 IO.mapOptional("CSD Version", Stream.CSDVersion, "");
Pavel Labath69de7a92019-03-22 14:47:26 +0000296 mapOptionalHex(IO, "Suite Mask", Info.SuiteMask, 0);
297 mapOptionalHex(IO, "Reserved", Info.Reserved, 0);
298 switch (static_cast<ProcessorArchitecture>(Info.ProcessorArch)) {
299 case ProcessorArchitecture::X86:
300 case ProcessorArchitecture::AMD64:
301 IO.mapOptional("CPU", Info.CPU.X86);
302 break;
303 case ProcessorArchitecture::ARM:
304 case ProcessorArchitecture::ARM64:
305 IO.mapOptional("CPU", Info.CPU.Arm);
306 break;
307 default:
308 IO.mapOptional("CPU", Info.CPU.Other);
309 break;
310 }
311}
312
313static void streamMapping(yaml::IO &IO, TextContentStream &Stream) {
314 IO.mapOptional("Text", Stream.Text);
315}
316
317void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping(
318 yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) {
319 StreamType Type;
320 if (IO.outputting())
321 Type = S->Type;
322 IO.mapRequired("Type", Type);
323
324 if (!IO.outputting())
325 S = MinidumpYAML::Stream::create(Type);
326 switch (S->Kind) {
327 case MinidumpYAML::Stream::StreamKind::RawContent:
328 streamMapping(IO, llvm::cast<RawContentStream>(*S));
329 break;
330 case MinidumpYAML::Stream::StreamKind::SystemInfo:
331 streamMapping(IO, llvm::cast<SystemInfoStream>(*S));
332 break;
333 case MinidumpYAML::Stream::StreamKind::TextContent:
334 streamMapping(IO, llvm::cast<TextContentStream>(*S));
335 break;
336 }
337}
338
339StringRef yaml::MappingTraits<std::unique_ptr<Stream>>::validate(
340 yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) {
341 switch (S->Kind) {
342 case MinidumpYAML::Stream::StreamKind::RawContent:
343 return streamValidate(cast<RawContentStream>(*S));
344 case MinidumpYAML::Stream::StreamKind::SystemInfo:
345 case MinidumpYAML::Stream::StreamKind::TextContent:
346 return "";
347 }
348 llvm_unreachable("Fully covered switch above!");
349}
350
351void yaml::MappingTraits<Object>::mapping(IO &IO, Object &O) {
352 IO.mapTag("!minidump", true);
353 mapOptionalHex(IO, "Signature", O.Header.Signature, Header::MagicSignature);
354 mapOptionalHex(IO, "Version", O.Header.Version, Header::MagicVersion);
355 mapOptionalHex(IO, "Flags", O.Header.Flags, 0);
356 IO.mapRequired("Streams", O.Streams);
357}
358
359static Directory layout(BlobAllocator &File, Stream &S) {
360 Directory Result;
361 Result.Type = S.Type;
362 Result.Location.RVA = File.tell();
Pavel Labath51d9fa02019-04-05 08:06:26 +0000363 Optional<size_t> DataEnd;
Pavel Labath69de7a92019-03-22 14:47:26 +0000364 switch (S.Kind) {
365 case Stream::StreamKind::RawContent: {
366 RawContentStream &Raw = cast<RawContentStream>(S);
Pavel Labathc5f79052019-03-25 14:45:31 +0000367 File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) {
Pavel Labath69de7a92019-03-22 14:47:26 +0000368 Raw.Content.writeAsBinary(OS);
369 assert(Raw.Content.binary_size() <= Raw.Size);
370 OS << std::string(Raw.Size - Raw.Content.binary_size(), '\0');
371 });
372 break;
373 }
Pavel Labath51d9fa02019-04-05 08:06:26 +0000374 case Stream::StreamKind::SystemInfo: {
375 SystemInfoStream &SystemInfo = cast<SystemInfoStream>(S);
376 File.allocateObject(SystemInfo.Info);
377 // The CSD string is not a part of the stream.
378 DataEnd = File.tell();
379 SystemInfo.Info.CSDVersionRVA = File.allocateString(SystemInfo.CSDVersion);
Pavel Labath69de7a92019-03-22 14:47:26 +0000380 break;
Pavel Labath51d9fa02019-04-05 08:06:26 +0000381 }
Pavel Labath69de7a92019-03-22 14:47:26 +0000382 case Stream::StreamKind::TextContent:
Pavel Labathc5f79052019-03-25 14:45:31 +0000383 File.allocateArray(arrayRefFromStringRef(cast<TextContentStream>(S).Text));
Pavel Labath69de7a92019-03-22 14:47:26 +0000384 break;
385 }
Pavel Labath51d9fa02019-04-05 08:06:26 +0000386 // If DataEnd is not set, we assume everything we generated is a part of the
387 // stream.
388 Result.Location.DataSize =
389 DataEnd.getValueOr(File.tell()) - Result.Location.RVA;
Pavel Labath69de7a92019-03-22 14:47:26 +0000390 return Result;
391}
392
393void MinidumpYAML::writeAsBinary(Object &Obj, raw_ostream &OS) {
394 BlobAllocator File;
Pavel Labathc5f79052019-03-25 14:45:31 +0000395 File.allocateObject(Obj.Header);
Pavel Labath69de7a92019-03-22 14:47:26 +0000396
397 std::vector<Directory> StreamDirectory(Obj.Streams.size());
398 Obj.Header.StreamDirectoryRVA =
Pavel Labathc5f79052019-03-25 14:45:31 +0000399 File.allocateArray(makeArrayRef(StreamDirectory));
Pavel Labath69de7a92019-03-22 14:47:26 +0000400 Obj.Header.NumberOfStreams = StreamDirectory.size();
401
402 for (auto &Stream : enumerate(Obj.Streams))
403 StreamDirectory[Stream.index()] = layout(File, *Stream.value());
404
405 File.writeTo(OS);
406}
407
408Error MinidumpYAML::writeAsBinary(StringRef Yaml, raw_ostream &OS) {
409 yaml::Input Input(Yaml);
410 Object Obj;
411 Input >> Obj;
412 if (std::error_code EC = Input.error())
413 return errorCodeToError(EC);
414
415 writeAsBinary(Obj, OS);
416 return Error::success();
417}
Pavel Labath3cee6632019-04-02 11:58:37 +0000418
419Expected<std::unique_ptr<Stream>>
420Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) {
421 StreamKind Kind = getKind(StreamDesc.Type);
422 switch (Kind) {
423 case StreamKind::RawContent:
424 return make_unique<RawContentStream>(StreamDesc.Type,
425 File.getRawStream(StreamDesc));
426 case StreamKind::SystemInfo: {
427 auto ExpectedInfo = File.getSystemInfo();
428 if (!ExpectedInfo)
429 return ExpectedInfo.takeError();
Pavel Labath51d9fa02019-04-05 08:06:26 +0000430 auto ExpectedCSDVersion = File.getString(ExpectedInfo->CSDVersionRVA);
431 if (!ExpectedCSDVersion)
432 return ExpectedInfo.takeError();
433 return make_unique<SystemInfoStream>(*ExpectedInfo,
434 std::move(*ExpectedCSDVersion));
Pavel Labath3cee6632019-04-02 11:58:37 +0000435 }
436 case StreamKind::TextContent:
437 return make_unique<TextContentStream>(
438 StreamDesc.Type, toStringRef(File.getRawStream(StreamDesc)));
439 }
440 llvm_unreachable("Unhandled stream kind!");
441}
442
443Expected<Object> Object::create(const object::MinidumpFile &File) {
444 std::vector<std::unique_ptr<Stream>> Streams;
445 Streams.reserve(File.streams().size());
446 for (const Directory &StreamDesc : File.streams()) {
447 auto ExpectedStream = Stream::create(StreamDesc, File);
448 if (!ExpectedStream)
449 return ExpectedStream.takeError();
450 Streams.push_back(std::move(*ExpectedStream));
451 }
452 return Object(File.header(), std::move(Streams));
453}