1 //===- MinidumpYAML.cpp - Minidump YAMLIO implementation ------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ObjectYAML/MinidumpYAML.h"
10 #include "llvm/Support/Allocator.h"
13 using namespace llvm::MinidumpYAML
;
14 using namespace llvm::minidump
;
16 /// Perform an optional yaml-mapping of an endian-aware type EndianType. The
17 /// only purpose of this function is to avoid casting the Default value to the
19 template <typename EndianType
>
20 static inline void mapOptional(yaml::IO
&IO
, const char *Key
, EndianType
&Val
,
21 typename
EndianType::value_type Default
) {
22 IO
.mapOptional(Key
, Val
, EndianType(Default
));
25 /// Yaml-map an endian-aware type EndianType as some other type MapType.
26 template <typename MapType
, typename EndianType
>
27 static inline void mapRequiredAs(yaml::IO
&IO
, const char *Key
,
29 MapType Mapped
= static_cast<typename
EndianType::value_type
>(Val
);
30 IO
.mapRequired(Key
, Mapped
);
31 Val
= static_cast<typename
EndianType::value_type
>(Mapped
);
34 /// Perform an optional yaml-mapping of an endian-aware type EndianType as some
35 /// other type MapType.
36 template <typename MapType
, typename EndianType
>
37 static inline void mapOptionalAs(yaml::IO
&IO
, const char *Key
, EndianType
&Val
,
39 MapType Mapped
= static_cast<typename
EndianType::value_type
>(Val
);
40 IO
.mapOptional(Key
, Mapped
, Default
);
41 Val
= static_cast<typename
EndianType::value_type
>(Mapped
);
45 /// Return the appropriate yaml Hex type for a given endian-aware type.
46 template <typename EndianType
> struct HexType
;
47 template <> struct HexType
<support::ulittle16_t
> { using type
= yaml::Hex16
; };
48 template <> struct HexType
<support::ulittle32_t
> { using type
= yaml::Hex32
; };
49 template <> struct HexType
<support::ulittle64_t
> { using type
= yaml::Hex64
; };
52 /// Yaml-map an endian-aware type as an appropriately-sized hex value.
53 template <typename EndianType
>
54 static inline void mapRequiredHex(yaml::IO
&IO
, const char *Key
,
56 mapRequiredAs
<typename HexType
<EndianType
>::type
>(IO
, Key
, Val
);
59 /// Perform an optional yaml-mapping of an endian-aware type as an
60 /// appropriately-sized hex value.
61 template <typename EndianType
>
62 static inline void mapOptionalHex(yaml::IO
&IO
, const char *Key
,
64 typename
EndianType::value_type Default
) {
65 mapOptionalAs
<typename HexType
<EndianType
>::type
>(IO
, Key
, Val
, Default
);
68 Stream::~Stream() = default;
70 Stream::StreamKind
Stream::getKind(StreamType Type
) {
72 case StreamType::MemoryList
:
73 return StreamKind::MemoryList
;
74 case StreamType::ModuleList
:
75 return StreamKind::ModuleList
;
76 case StreamType::SystemInfo
:
77 return StreamKind::SystemInfo
;
78 case StreamType::LinuxCPUInfo
:
79 case StreamType::LinuxProcStatus
:
80 case StreamType::LinuxLSBRelease
:
81 case StreamType::LinuxCMDLine
:
82 case StreamType::LinuxMaps
:
83 case StreamType::LinuxProcStat
:
84 case StreamType::LinuxProcUptime
:
85 return StreamKind::TextContent
;
86 case StreamType::ThreadList
:
87 return StreamKind::ThreadList
;
89 return StreamKind::RawContent
;
93 std::unique_ptr
<Stream
> Stream::create(StreamType Type
) {
94 StreamKind Kind
= getKind(Type
);
96 case StreamKind::MemoryList
:
97 return std::make_unique
<MemoryListStream
>();
98 case StreamKind::ModuleList
:
99 return std::make_unique
<ModuleListStream
>();
100 case StreamKind::RawContent
:
101 return std::make_unique
<RawContentStream
>(Type
);
102 case StreamKind::SystemInfo
:
103 return std::make_unique
<SystemInfoStream
>();
104 case StreamKind::TextContent
:
105 return std::make_unique
<TextContentStream
>(Type
);
106 case StreamKind::ThreadList
:
107 return std::make_unique
<ThreadListStream
>();
109 llvm_unreachable("Unhandled stream kind!");
112 void yaml::ScalarEnumerationTraits
<ProcessorArchitecture
>::enumeration(
113 IO
&IO
, ProcessorArchitecture
&Arch
) {
114 #define HANDLE_MDMP_ARCH(CODE, NAME) \
115 IO.enumCase(Arch, #NAME, ProcessorArchitecture::NAME);
116 #include "llvm/BinaryFormat/MinidumpConstants.def"
117 IO
.enumFallback
<Hex16
>(Arch
);
120 void yaml::ScalarEnumerationTraits
<OSPlatform
>::enumeration(IO
&IO
,
122 #define HANDLE_MDMP_PLATFORM(CODE, NAME) \
123 IO.enumCase(Plat, #NAME, OSPlatform::NAME);
124 #include "llvm/BinaryFormat/MinidumpConstants.def"
125 IO
.enumFallback
<Hex32
>(Plat
);
128 void yaml::ScalarEnumerationTraits
<StreamType
>::enumeration(IO
&IO
,
130 #define HANDLE_MDMP_STREAM_TYPE(CODE, NAME) \
131 IO.enumCase(Type, #NAME, StreamType::NAME);
132 #include "llvm/BinaryFormat/MinidumpConstants.def"
133 IO
.enumFallback
<Hex32
>(Type
);
136 void yaml::MappingTraits
<CPUInfo::ArmInfo
>::mapping(IO
&IO
,
137 CPUInfo::ArmInfo
&Info
) {
138 mapRequiredHex(IO
, "CPUID", Info
.CPUID
);
139 mapOptionalHex(IO
, "ELF hwcaps", Info
.ElfHWCaps
, 0);
143 template <std::size_t N
> struct FixedSizeHex
{
144 FixedSizeHex(uint8_t (&Storage
)[N
]) : Storage(Storage
) {}
146 uint8_t (&Storage
)[N
];
152 template <std::size_t N
> struct ScalarTraits
<FixedSizeHex
<N
>> {
153 static void output(const FixedSizeHex
<N
> &Fixed
, void *, raw_ostream
&OS
) {
154 OS
<< toHex(makeArrayRef(Fixed
.Storage
));
157 static StringRef
input(StringRef Scalar
, void *, FixedSizeHex
<N
> &Fixed
) {
158 if (!all_of(Scalar
, isHexDigit
))
159 return "Invalid hex digit in input";
160 if (Scalar
.size() < 2 * N
)
161 return "String too short";
162 if (Scalar
.size() > 2 * N
)
163 return "String too long";
164 copy(fromHex(Scalar
), Fixed
.Storage
);
168 static QuotingType
mustQuote(StringRef S
) { return QuotingType::None
; }
172 void yaml::MappingTraits
<CPUInfo::OtherInfo
>::mapping(
173 IO
&IO
, CPUInfo::OtherInfo
&Info
) {
174 FixedSizeHex
<sizeof(Info
.ProcessorFeatures
)> Features(Info
.ProcessorFeatures
);
175 IO
.mapRequired("Features", Features
);
179 /// A type which only accepts strings of a fixed size for yaml conversion.
180 template <std::size_t N
> struct FixedSizeString
{
181 FixedSizeString(char (&Storage
)[N
]) : Storage(Storage
) {}
189 template <std::size_t N
> struct ScalarTraits
<FixedSizeString
<N
>> {
190 static void output(const FixedSizeString
<N
> &Fixed
, void *, raw_ostream
&OS
) {
191 OS
<< StringRef(Fixed
.Storage
, N
);
194 static StringRef
input(StringRef Scalar
, void *, FixedSizeString
<N
> &Fixed
) {
195 if (Scalar
.size() < N
)
196 return "String too short";
197 if (Scalar
.size() > N
)
198 return "String too long";
199 copy(Scalar
, Fixed
.Storage
);
203 static QuotingType
mustQuote(StringRef S
) { return needsQuotes(S
); }
208 void yaml::MappingTraits
<CPUInfo::X86Info
>::mapping(IO
&IO
,
209 CPUInfo::X86Info
&Info
) {
210 FixedSizeString
<sizeof(Info
.VendorID
)> VendorID(Info
.VendorID
);
211 IO
.mapRequired("Vendor ID", VendorID
);
213 mapRequiredHex(IO
, "Version Info", Info
.VersionInfo
);
214 mapRequiredHex(IO
, "Feature Info", Info
.FeatureInfo
);
215 mapOptionalHex(IO
, "AMD Extended Features", Info
.AMDExtendedFeatures
, 0);
218 void yaml::MappingTraits
<VSFixedFileInfo
>::mapping(IO
&IO
,
219 VSFixedFileInfo
&Info
) {
220 mapOptionalHex(IO
, "Signature", Info
.Signature
, 0);
221 mapOptionalHex(IO
, "Struct Version", Info
.StructVersion
, 0);
222 mapOptionalHex(IO
, "File Version High", Info
.FileVersionHigh
, 0);
223 mapOptionalHex(IO
, "File Version Low", Info
.FileVersionLow
, 0);
224 mapOptionalHex(IO
, "Product Version High", Info
.ProductVersionHigh
, 0);
225 mapOptionalHex(IO
, "Product Version Low", Info
.ProductVersionLow
, 0);
226 mapOptionalHex(IO
, "File Flags Mask", Info
.FileFlagsMask
, 0);
227 mapOptionalHex(IO
, "File Flags", Info
.FileFlags
, 0);
228 mapOptionalHex(IO
, "File OS", Info
.FileOS
, 0);
229 mapOptionalHex(IO
, "File Type", Info
.FileType
, 0);
230 mapOptionalHex(IO
, "File Subtype", Info
.FileSubtype
, 0);
231 mapOptionalHex(IO
, "File Date High", Info
.FileDateHigh
, 0);
232 mapOptionalHex(IO
, "File Date Low", Info
.FileDateLow
, 0);
235 void yaml::MappingTraits
<ModuleListStream::entry_type
>::mapping(
236 IO
&IO
, ModuleListStream::entry_type
&M
) {
237 mapRequiredHex(IO
, "Base of Image", M
.Entry
.BaseOfImage
);
238 mapRequiredHex(IO
, "Size of Image", M
.Entry
.SizeOfImage
);
239 mapOptionalHex(IO
, "Checksum", M
.Entry
.Checksum
, 0);
240 IO
.mapOptional("Time Date Stamp", M
.Entry
.TimeDateStamp
,
241 support::ulittle32_t(0));
242 IO
.mapRequired("Module Name", M
.Name
);
243 IO
.mapOptional("Version Info", M
.Entry
.VersionInfo
, VSFixedFileInfo());
244 IO
.mapRequired("CodeView Record", M
.CvRecord
);
245 IO
.mapOptional("Misc Record", M
.MiscRecord
, yaml::BinaryRef());
246 mapOptionalHex(IO
, "Reserved0", M
.Entry
.Reserved0
, 0);
247 mapOptionalHex(IO
, "Reserved1", M
.Entry
.Reserved1
, 0);
250 static void streamMapping(yaml::IO
&IO
, RawContentStream
&Stream
) {
251 IO
.mapOptional("Content", Stream
.Content
);
252 IO
.mapOptional("Size", Stream
.Size
, Stream
.Content
.binary_size());
255 static StringRef
streamValidate(RawContentStream
&Stream
) {
256 if (Stream
.Size
.value
< Stream
.Content
.binary_size())
257 return "Stream size must be greater or equal to the content size";
261 void yaml::MappingTraits
<MemoryListStream::entry_type
>::mapping(
262 IO
&IO
, MemoryListStream::entry_type
&Range
) {
263 MappingContextTraits
<MemoryDescriptor
, yaml::BinaryRef
>::mapping(
264 IO
, Range
.Entry
, Range
.Content
);
267 static void streamMapping(yaml::IO
&IO
, MemoryListStream
&Stream
) {
268 IO
.mapRequired("Memory Ranges", Stream
.Entries
);
271 static void streamMapping(yaml::IO
&IO
, ModuleListStream
&Stream
) {
272 IO
.mapRequired("Modules", Stream
.Entries
);
275 static void streamMapping(yaml::IO
&IO
, SystemInfoStream
&Stream
) {
276 SystemInfo
&Info
= Stream
.Info
;
277 IO
.mapRequired("Processor Arch", Info
.ProcessorArch
);
278 mapOptional(IO
, "Processor Level", Info
.ProcessorLevel
, 0);
279 mapOptional(IO
, "Processor Revision", Info
.ProcessorRevision
, 0);
280 IO
.mapOptional("Number of Processors", Info
.NumberOfProcessors
, 0);
281 IO
.mapOptional("Product type", Info
.ProductType
, 0);
282 mapOptional(IO
, "Major Version", Info
.MajorVersion
, 0);
283 mapOptional(IO
, "Minor Version", Info
.MinorVersion
, 0);
284 mapOptional(IO
, "Build Number", Info
.BuildNumber
, 0);
285 IO
.mapRequired("Platform ID", Info
.PlatformId
);
286 IO
.mapOptional("CSD Version", Stream
.CSDVersion
, "");
287 mapOptionalHex(IO
, "Suite Mask", Info
.SuiteMask
, 0);
288 mapOptionalHex(IO
, "Reserved", Info
.Reserved
, 0);
289 switch (static_cast<ProcessorArchitecture
>(Info
.ProcessorArch
)) {
290 case ProcessorArchitecture::X86
:
291 case ProcessorArchitecture::AMD64
:
292 IO
.mapOptional("CPU", Info
.CPU
.X86
);
294 case ProcessorArchitecture::ARM
:
295 case ProcessorArchitecture::ARM64
:
296 IO
.mapOptional("CPU", Info
.CPU
.Arm
);
299 IO
.mapOptional("CPU", Info
.CPU
.Other
);
304 static void streamMapping(yaml::IO
&IO
, TextContentStream
&Stream
) {
305 IO
.mapOptional("Text", Stream
.Text
);
308 void yaml::MappingContextTraits
<MemoryDescriptor
, yaml::BinaryRef
>::mapping(
309 IO
&IO
, MemoryDescriptor
&Memory
, BinaryRef
&Content
) {
310 mapRequiredHex(IO
, "Start of Memory Range", Memory
.StartOfMemoryRange
);
311 IO
.mapRequired("Content", Content
);
314 void yaml::MappingTraits
<ThreadListStream::entry_type
>::mapping(
315 IO
&IO
, ThreadListStream::entry_type
&T
) {
316 mapRequiredHex(IO
, "Thread Id", T
.Entry
.ThreadId
);
317 mapOptionalHex(IO
, "Suspend Count", T
.Entry
.SuspendCount
, 0);
318 mapOptionalHex(IO
, "Priority Class", T
.Entry
.PriorityClass
, 0);
319 mapOptionalHex(IO
, "Priority", T
.Entry
.Priority
, 0);
320 mapOptionalHex(IO
, "Environment Block", T
.Entry
.EnvironmentBlock
, 0);
321 IO
.mapRequired("Context", T
.Context
);
322 IO
.mapRequired("Stack", T
.Entry
.Stack
, T
.Stack
);
325 static void streamMapping(yaml::IO
&IO
, ThreadListStream
&Stream
) {
326 IO
.mapRequired("Threads", Stream
.Entries
);
329 void yaml::MappingTraits
<std::unique_ptr
<Stream
>>::mapping(
330 yaml::IO
&IO
, std::unique_ptr
<MinidumpYAML::Stream
> &S
) {
334 IO
.mapRequired("Type", Type
);
336 if (!IO
.outputting())
337 S
= MinidumpYAML::Stream::create(Type
);
339 case MinidumpYAML::Stream::StreamKind::MemoryList
:
340 streamMapping(IO
, llvm::cast
<MemoryListStream
>(*S
));
342 case MinidumpYAML::Stream::StreamKind::ModuleList
:
343 streamMapping(IO
, llvm::cast
<ModuleListStream
>(*S
));
345 case MinidumpYAML::Stream::StreamKind::RawContent
:
346 streamMapping(IO
, llvm::cast
<RawContentStream
>(*S
));
348 case MinidumpYAML::Stream::StreamKind::SystemInfo
:
349 streamMapping(IO
, llvm::cast
<SystemInfoStream
>(*S
));
351 case MinidumpYAML::Stream::StreamKind::TextContent
:
352 streamMapping(IO
, llvm::cast
<TextContentStream
>(*S
));
354 case MinidumpYAML::Stream::StreamKind::ThreadList
:
355 streamMapping(IO
, llvm::cast
<ThreadListStream
>(*S
));
360 StringRef
yaml::MappingTraits
<std::unique_ptr
<Stream
>>::validate(
361 yaml::IO
&IO
, std::unique_ptr
<MinidumpYAML::Stream
> &S
) {
363 case MinidumpYAML::Stream::StreamKind::RawContent
:
364 return streamValidate(cast
<RawContentStream
>(*S
));
365 case MinidumpYAML::Stream::StreamKind::MemoryList
:
366 case MinidumpYAML::Stream::StreamKind::ModuleList
:
367 case MinidumpYAML::Stream::StreamKind::SystemInfo
:
368 case MinidumpYAML::Stream::StreamKind::TextContent
:
369 case MinidumpYAML::Stream::StreamKind::ThreadList
:
372 llvm_unreachable("Fully covered switch above!");
375 void yaml::MappingTraits
<Object
>::mapping(IO
&IO
, Object
&O
) {
376 IO
.mapTag("!minidump", true);
377 mapOptionalHex(IO
, "Signature", O
.Header
.Signature
, Header::MagicSignature
);
378 mapOptionalHex(IO
, "Version", O
.Header
.Version
, Header::MagicVersion
);
379 mapOptionalHex(IO
, "Flags", O
.Header
.Flags
, 0);
380 IO
.mapRequired("Streams", O
.Streams
);
383 Expected
<std::unique_ptr
<Stream
>>
384 Stream::create(const Directory
&StreamDesc
, const object::MinidumpFile
&File
) {
385 StreamKind Kind
= getKind(StreamDesc
.Type
);
387 case StreamKind::MemoryList
: {
388 auto ExpectedList
= File
.getMemoryList();
390 return ExpectedList
.takeError();
391 std::vector
<MemoryListStream::entry_type
> Ranges
;
392 for (const MemoryDescriptor
&MD
: *ExpectedList
) {
393 auto ExpectedContent
= File
.getRawData(MD
.Memory
);
394 if (!ExpectedContent
)
395 return ExpectedContent
.takeError();
396 Ranges
.push_back({MD
, *ExpectedContent
});
398 return std::make_unique
<MemoryListStream
>(std::move(Ranges
));
400 case StreamKind::ModuleList
: {
401 auto ExpectedList
= File
.getModuleList();
403 return ExpectedList
.takeError();
404 std::vector
<ModuleListStream::entry_type
> Modules
;
405 for (const Module
&M
: *ExpectedList
) {
406 auto ExpectedName
= File
.getString(M
.ModuleNameRVA
);
408 return ExpectedName
.takeError();
409 auto ExpectedCv
= File
.getRawData(M
.CvRecord
);
411 return ExpectedCv
.takeError();
412 auto ExpectedMisc
= File
.getRawData(M
.MiscRecord
);
414 return ExpectedMisc
.takeError();
416 {M
, std::move(*ExpectedName
), *ExpectedCv
, *ExpectedMisc
});
418 return std::make_unique
<ModuleListStream
>(std::move(Modules
));
420 case StreamKind::RawContent
:
421 return std::make_unique
<RawContentStream
>(StreamDesc
.Type
,
422 File
.getRawStream(StreamDesc
));
423 case StreamKind::SystemInfo
: {
424 auto ExpectedInfo
= File
.getSystemInfo();
426 return ExpectedInfo
.takeError();
427 auto ExpectedCSDVersion
= File
.getString(ExpectedInfo
->CSDVersionRVA
);
428 if (!ExpectedCSDVersion
)
429 return ExpectedInfo
.takeError();
430 return std::make_unique
<SystemInfoStream
>(*ExpectedInfo
,
431 std::move(*ExpectedCSDVersion
));
433 case StreamKind::TextContent
:
434 return std::make_unique
<TextContentStream
>(
435 StreamDesc
.Type
, toStringRef(File
.getRawStream(StreamDesc
)));
436 case StreamKind::ThreadList
: {
437 auto ExpectedList
= File
.getThreadList();
439 return ExpectedList
.takeError();
440 std::vector
<ThreadListStream::entry_type
> Threads
;
441 for (const Thread
&T
: *ExpectedList
) {
442 auto ExpectedStack
= File
.getRawData(T
.Stack
.Memory
);
444 return ExpectedStack
.takeError();
445 auto ExpectedContext
= File
.getRawData(T
.Context
);
446 if (!ExpectedContext
)
447 return ExpectedContext
.takeError();
448 Threads
.push_back({T
, *ExpectedStack
, *ExpectedContext
});
450 return std::make_unique
<ThreadListStream
>(std::move(Threads
));
453 llvm_unreachable("Unhandled stream kind!");
456 Expected
<Object
> Object::create(const object::MinidumpFile
&File
) {
457 std::vector
<std::unique_ptr
<Stream
>> Streams
;
458 Streams
.reserve(File
.streams().size());
459 for (const Directory
&StreamDesc
: File
.streams()) {
460 auto ExpectedStream
= Stream::create(StreamDesc
, File
);
462 return ExpectedStream
.takeError();
463 Streams
.push_back(std::move(*ExpectedStream
));
465 return Object(File
.header(), std::move(Streams
));