1 | //===- 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" |
10 | |
11 | using namespace llvm; |
12 | using namespace llvm::MinidumpYAML; |
13 | using namespace llvm::minidump; |
14 | |
15 | /// Perform an optional yaml-mapping of an endian-aware type EndianType. The |
16 | /// only purpose of this function is to avoid casting the Default value to the |
17 | /// endian type; |
18 | template <typename EndianType> |
19 | static inline void mapOptional(yaml::IO &IO, const char *Key, EndianType &Val, |
20 | typename EndianType::value_type Default) { |
21 | IO.mapOptional(Key, Val, EndianType(Default)); |
22 | } |
23 | |
24 | /// Yaml-map an endian-aware type EndianType as some other type MapType. |
25 | template <typename MapType, typename EndianType> |
26 | static inline void mapRequiredAs(yaml::IO &IO, const char *Key, |
27 | EndianType &Val) { |
28 | MapType Mapped = static_cast<typename EndianType::value_type>(Val); |
29 | IO.mapRequired(Key, Mapped); |
30 | Val = static_cast<typename EndianType::value_type>(Mapped); |
31 | } |
32 | |
33 | /// Perform an optional yaml-mapping of an endian-aware type EndianType as some |
34 | /// other type MapType. |
35 | template <typename MapType, typename EndianType> |
36 | static inline void mapOptionalAs(yaml::IO &IO, const char *Key, EndianType &Val, |
37 | MapType Default) { |
38 | MapType Mapped = static_cast<typename EndianType::value_type>(Val); |
39 | IO.mapOptional(Key, Mapped, Default); |
40 | Val = static_cast<typename EndianType::value_type>(Mapped); |
41 | } |
42 | |
43 | namespace { |
44 | /// Return the appropriate yaml Hex type for a given endian-aware type. |
45 | template <typename EndianType> struct HexType; |
46 | template <> struct HexType<support::ulittle16_t> { using type = yaml::Hex16; }; |
47 | template <> struct HexType<support::ulittle32_t> { using type = yaml::Hex32; }; |
48 | template <> struct HexType<support::ulittle64_t> { using type = yaml::Hex64; }; |
49 | } // namespace |
50 | |
51 | /// Yaml-map an endian-aware type as an appropriately-sized hex value. |
52 | template <typename EndianType> |
53 | static inline void mapRequiredHex(yaml::IO &IO, const char *Key, |
54 | EndianType &Val) { |
55 | mapRequiredAs<typename HexType<EndianType>::type>(IO, Key, Val); |
56 | } |
57 | |
58 | /// Perform an optional yaml-mapping of an endian-aware type as an |
59 | /// appropriately-sized hex value. |
60 | template <typename EndianType> |
61 | static inline void mapOptionalHex(yaml::IO &IO, const char *Key, |
62 | EndianType &Val, |
63 | typename EndianType::value_type Default) { |
64 | mapOptionalAs<typename HexType<EndianType>::type>(IO, Key, Val, Default); |
65 | } |
66 | |
67 | Stream::~Stream() = default; |
68 | |
69 | Stream::StreamKind Stream::getKind(StreamType Type) { |
70 | switch (Type) { |
71 | case StreamType::Exception: |
72 | return StreamKind::Exception; |
73 | case StreamType::MemoryInfoList: |
74 | return StreamKind::MemoryInfoList; |
75 | case StreamType::MemoryList: |
76 | return StreamKind::MemoryList; |
77 | case StreamType::Memory64List: |
78 | return StreamKind::Memory64List; |
79 | case StreamType::ModuleList: |
80 | return StreamKind::ModuleList; |
81 | case StreamType::SystemInfo: |
82 | return StreamKind::SystemInfo; |
83 | case StreamType::LinuxCPUInfo: |
84 | case StreamType::LinuxProcStatus: |
85 | case StreamType::LinuxLSBRelease: |
86 | case StreamType::LinuxCMDLine: |
87 | case StreamType::LinuxMaps: |
88 | case StreamType::LinuxProcStat: |
89 | case StreamType::LinuxProcUptime: |
90 | return StreamKind::TextContent; |
91 | case StreamType::ThreadList: |
92 | return StreamKind::ThreadList; |
93 | default: |
94 | return StreamKind::RawContent; |
95 | } |
96 | } |
97 | |
98 | std::unique_ptr<Stream> Stream::create(StreamType Type) { |
99 | StreamKind Kind = getKind(Type); |
100 | switch (Kind) { |
101 | case StreamKind::Exception: |
102 | return std::make_unique<ExceptionStream>(); |
103 | case StreamKind::MemoryInfoList: |
104 | return std::make_unique<MemoryInfoListStream>(); |
105 | case StreamKind::MemoryList: |
106 | return std::make_unique<MemoryListStream>(); |
107 | case StreamKind::Memory64List: |
108 | return std::make_unique<Memory64ListStream>(); |
109 | case StreamKind::ModuleList: |
110 | return std::make_unique<ModuleListStream>(); |
111 | case StreamKind::RawContent: |
112 | return std::make_unique<RawContentStream>(args&: Type); |
113 | case StreamKind::SystemInfo: |
114 | return std::make_unique<SystemInfoStream>(); |
115 | case StreamKind::TextContent: |
116 | return std::make_unique<TextContentStream>(args&: Type); |
117 | case StreamKind::ThreadList: |
118 | return std::make_unique<ThreadListStream>(); |
119 | } |
120 | llvm_unreachable("Unhandled stream kind!" ); |
121 | } |
122 | |
123 | void yaml::ScalarBitSetTraits<MemoryProtection>::bitset( |
124 | IO &IO, MemoryProtection &Protect) { |
125 | #define HANDLE_MDMP_PROTECT(CODE, NAME, NATIVENAME) \ |
126 | IO.bitSetCase(Protect, #NATIVENAME, MemoryProtection::NAME); |
127 | #include "llvm/BinaryFormat/MinidumpConstants.def" |
128 | } |
129 | |
130 | void yaml::ScalarBitSetTraits<MemoryState>::bitset(IO &IO, MemoryState &State) { |
131 | #define HANDLE_MDMP_MEMSTATE(CODE, NAME, NATIVENAME) \ |
132 | IO.bitSetCase(State, #NATIVENAME, MemoryState::NAME); |
133 | #include "llvm/BinaryFormat/MinidumpConstants.def" |
134 | } |
135 | |
136 | void yaml::ScalarBitSetTraits<MemoryType>::bitset(IO &IO, MemoryType &Type) { |
137 | #define HANDLE_MDMP_MEMTYPE(CODE, NAME, NATIVENAME) \ |
138 | IO.bitSetCase(Type, #NATIVENAME, MemoryType::NAME); |
139 | #include "llvm/BinaryFormat/MinidumpConstants.def" |
140 | } |
141 | |
142 | void yaml::ScalarEnumerationTraits<ProcessorArchitecture>::enumeration( |
143 | IO &IO, ProcessorArchitecture &Arch) { |
144 | #define HANDLE_MDMP_ARCH(CODE, NAME) \ |
145 | IO.enumCase(Arch, #NAME, ProcessorArchitecture::NAME); |
146 | #include "llvm/BinaryFormat/MinidumpConstants.def" |
147 | IO.enumFallback<Hex16>(Val&: Arch); |
148 | } |
149 | |
150 | void yaml::ScalarEnumerationTraits<OSPlatform>::enumeration(IO &IO, |
151 | OSPlatform &Plat) { |
152 | #define HANDLE_MDMP_PLATFORM(CODE, NAME) \ |
153 | IO.enumCase(Plat, #NAME, OSPlatform::NAME); |
154 | #include "llvm/BinaryFormat/MinidumpConstants.def" |
155 | IO.enumFallback<Hex32>(Val&: Plat); |
156 | } |
157 | |
158 | void yaml::ScalarEnumerationTraits<StreamType>::enumeration(IO &IO, |
159 | StreamType &Type) { |
160 | #define HANDLE_MDMP_STREAM_TYPE(CODE, NAME) \ |
161 | IO.enumCase(Type, #NAME, StreamType::NAME); |
162 | #include "llvm/BinaryFormat/MinidumpConstants.def" |
163 | IO.enumFallback<Hex32>(Val&: Type); |
164 | } |
165 | |
166 | void yaml::MappingTraits<CPUInfo::ArmInfo>::mapping(IO &IO, |
167 | CPUInfo::ArmInfo &Info) { |
168 | mapRequiredHex(IO, Key: "CPUID" , Val&: Info.CPUID); |
169 | mapOptionalHex(IO, Key: "ELF hwcaps" , Val&: Info.ElfHWCaps, Default: 0); |
170 | } |
171 | |
172 | namespace { |
173 | template <std::size_t N> struct FixedSizeHex { |
174 | FixedSizeHex(uint8_t (&Storage)[N]) : Storage(Storage) {} |
175 | |
176 | uint8_t (&Storage)[N]; |
177 | }; |
178 | } // namespace |
179 | |
180 | namespace llvm { |
181 | namespace yaml { |
182 | template <std::size_t N> struct ScalarTraits<FixedSizeHex<N>> { |
183 | static void output(const FixedSizeHex<N> &Fixed, void *, raw_ostream &OS) { |
184 | OS << toHex(ArrayRef(Fixed.Storage)); |
185 | } |
186 | |
187 | static StringRef input(StringRef Scalar, void *, FixedSizeHex<N> &Fixed) { |
188 | if (!all_of(Range&: Scalar, P: isHexDigit)) |
189 | return "Invalid hex digit in input" ; |
190 | if (Scalar.size() < 2 * N) |
191 | return "String too short" ; |
192 | if (Scalar.size() > 2 * N) |
193 | return "String too long" ; |
194 | copy(fromHex(Input: Scalar), Fixed.Storage); |
195 | return "" ; |
196 | } |
197 | |
198 | static QuotingType mustQuote(StringRef S) { return QuotingType::None; } |
199 | }; |
200 | } // namespace yaml |
201 | } // namespace llvm |
202 | void yaml::MappingTraits<CPUInfo::OtherInfo>::mapping( |
203 | IO &IO, CPUInfo::OtherInfo &Info) { |
204 | FixedSizeHex<sizeof(Info.ProcessorFeatures)> Features(Info.ProcessorFeatures); |
205 | IO.mapRequired(Key: "Features" , Val&: Features); |
206 | } |
207 | |
208 | namespace { |
209 | /// A type which only accepts strings of a fixed size for yaml conversion. |
210 | template <std::size_t N> struct FixedSizeString { |
211 | FixedSizeString(char (&Storage)[N]) : Storage(Storage) {} |
212 | |
213 | char (&Storage)[N]; |
214 | }; |
215 | } // namespace |
216 | |
217 | namespace llvm { |
218 | namespace yaml { |
219 | template <std::size_t N> struct ScalarTraits<FixedSizeString<N>> { |
220 | static void output(const FixedSizeString<N> &Fixed, void *, raw_ostream &OS) { |
221 | OS << StringRef(Fixed.Storage, N); |
222 | } |
223 | |
224 | static StringRef input(StringRef Scalar, void *, FixedSizeString<N> &Fixed) { |
225 | if (Scalar.size() < N) |
226 | return "String too short" ; |
227 | if (Scalar.size() > N) |
228 | return "String too long" ; |
229 | copy(Scalar, Fixed.Storage); |
230 | return "" ; |
231 | } |
232 | |
233 | static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } |
234 | }; |
235 | } // namespace yaml |
236 | } // namespace llvm |
237 | |
238 | void yaml::MappingTraits<CPUInfo::X86Info>::mapping(IO &IO, |
239 | CPUInfo::X86Info &Info) { |
240 | FixedSizeString<sizeof(Info.VendorID)> VendorID(Info.VendorID); |
241 | IO.mapRequired(Key: "Vendor ID" , Val&: VendorID); |
242 | |
243 | mapRequiredHex(IO, Key: "Version Info" , Val&: Info.VersionInfo); |
244 | mapRequiredHex(IO, Key: "Feature Info" , Val&: Info.FeatureInfo); |
245 | mapOptionalHex(IO, Key: "AMD Extended Features" , Val&: Info.AMDExtendedFeatures, Default: 0); |
246 | } |
247 | |
248 | void yaml::MappingTraits<MemoryInfo>::mapping(IO &IO, MemoryInfo &Info) { |
249 | mapRequiredHex(IO, Key: "Base Address" , Val&: Info.BaseAddress); |
250 | mapOptionalHex(IO, Key: "Allocation Base" , Val&: Info.AllocationBase, Default: Info.BaseAddress); |
251 | mapRequiredAs<MemoryProtection>(IO, Key: "Allocation Protect" , |
252 | Val&: Info.AllocationProtect); |
253 | mapOptionalHex(IO, Key: "Reserved0" , Val&: Info.Reserved0, Default: 0); |
254 | mapRequiredHex(IO, Key: "Region Size" , Val&: Info.RegionSize); |
255 | mapRequiredAs<MemoryState>(IO, Key: "State" , Val&: Info.State); |
256 | mapOptionalAs<MemoryProtection>(IO, Key: "Protect" , Val&: Info.Protect, |
257 | Default: Info.AllocationProtect); |
258 | mapRequiredAs<MemoryType>(IO, Key: "Type" , Val&: Info.Type); |
259 | mapOptionalHex(IO, Key: "Reserved1" , Val&: Info.Reserved1, Default: 0); |
260 | } |
261 | |
262 | void yaml::MappingTraits<Memory64ListStream::entry_type>::mapping( |
263 | IO &IO, Memory64ListStream::entry_type &Mem) { |
264 | MappingContextTraits<MemoryDescriptor_64, yaml::BinaryRef>::mapping( |
265 | IO, Memory&: Mem.Entry, Content&: Mem.Content); |
266 | } |
267 | |
268 | void yaml::MappingTraits<VSFixedFileInfo>::mapping(IO &IO, |
269 | VSFixedFileInfo &Info) { |
270 | mapOptionalHex(IO, Key: "Signature" , Val&: Info.Signature, Default: 0); |
271 | mapOptionalHex(IO, Key: "Struct Version" , Val&: Info.StructVersion, Default: 0); |
272 | mapOptionalHex(IO, Key: "File Version High" , Val&: Info.FileVersionHigh, Default: 0); |
273 | mapOptionalHex(IO, Key: "File Version Low" , Val&: Info.FileVersionLow, Default: 0); |
274 | mapOptionalHex(IO, Key: "Product Version High" , Val&: Info.ProductVersionHigh, Default: 0); |
275 | mapOptionalHex(IO, Key: "Product Version Low" , Val&: Info.ProductVersionLow, Default: 0); |
276 | mapOptionalHex(IO, Key: "File Flags Mask" , Val&: Info.FileFlagsMask, Default: 0); |
277 | mapOptionalHex(IO, Key: "File Flags" , Val&: Info.FileFlags, Default: 0); |
278 | mapOptionalHex(IO, Key: "File OS" , Val&: Info.FileOS, Default: 0); |
279 | mapOptionalHex(IO, Key: "File Type" , Val&: Info.FileType, Default: 0); |
280 | mapOptionalHex(IO, Key: "File Subtype" , Val&: Info.FileSubtype, Default: 0); |
281 | mapOptionalHex(IO, Key: "File Date High" , Val&: Info.FileDateHigh, Default: 0); |
282 | mapOptionalHex(IO, Key: "File Date Low" , Val&: Info.FileDateLow, Default: 0); |
283 | } |
284 | |
285 | void yaml::MappingTraits<ModuleListStream::entry_type>::mapping( |
286 | IO &IO, ModuleListStream::entry_type &M) { |
287 | mapRequiredHex(IO, Key: "Base of Image" , Val&: M.Entry.BaseOfImage); |
288 | mapRequiredHex(IO, Key: "Size of Image" , Val&: M.Entry.SizeOfImage); |
289 | mapOptionalHex(IO, Key: "Checksum" , Val&: M.Entry.Checksum, Default: 0); |
290 | mapOptional(IO, Key: "Time Date Stamp" , Val&: M.Entry.TimeDateStamp, Default: 0); |
291 | IO.mapRequired(Key: "Module Name" , Val&: M.Name); |
292 | IO.mapOptional(Key: "Version Info" , Val&: M.Entry.VersionInfo, Default: VSFixedFileInfo()); |
293 | IO.mapRequired(Key: "CodeView Record" , Val&: M.CvRecord); |
294 | IO.mapOptional(Key: "Misc Record" , Val&: M.MiscRecord, Default: yaml::BinaryRef()); |
295 | mapOptionalHex(IO, Key: "Reserved0" , Val&: M.Entry.Reserved0, Default: 0); |
296 | mapOptionalHex(IO, Key: "Reserved1" , Val&: M.Entry.Reserved1, Default: 0); |
297 | } |
298 | |
299 | static void streamMapping(yaml::IO &IO, RawContentStream &Stream) { |
300 | IO.mapOptional(Key: "Content" , Val&: Stream.Content); |
301 | IO.mapOptional(Key: "Size" , Val&: Stream.Size, Default: Stream.Content.binary_size()); |
302 | } |
303 | |
304 | static std::string streamValidate(RawContentStream &Stream) { |
305 | if (Stream.Size.value < Stream.Content.binary_size()) |
306 | return "Stream size must be greater or equal to the content size" ; |
307 | return "" ; |
308 | } |
309 | |
310 | void yaml::MappingTraits<MemoryListStream::entry_type>::mapping( |
311 | IO &IO, MemoryListStream::entry_type &Range) { |
312 | MappingContextTraits<MemoryDescriptor, yaml::BinaryRef>::mapping( |
313 | IO, Memory&: Range.Entry, Content&: Range.Content); |
314 | } |
315 | |
316 | static void streamMapping(yaml::IO &IO, MemoryInfoListStream &Stream) { |
317 | IO.mapRequired(Key: "Memory Ranges" , Val&: Stream.Infos); |
318 | } |
319 | |
320 | static void streamMapping(yaml::IO &IO, MemoryListStream &Stream) { |
321 | IO.mapRequired(Key: "Memory Ranges" , Val&: Stream.Entries); |
322 | } |
323 | |
324 | static void streamMapping(yaml::IO &IO, Memory64ListStream &Stream) { |
325 | IO.mapRequired(Key: "Memory Ranges" , Val&: Stream.Entries); |
326 | } |
327 | |
328 | static std::string streamValidate(Memory64ListStream &Stream) { |
329 | for (auto &Entry : Stream.Entries) { |
330 | if (Entry.Entry.DataSize < Entry.Content.binary_size()) |
331 | return "Memory region size must be greater or equal to the content size" ; |
332 | } |
333 | return "" ; |
334 | } |
335 | |
336 | static void streamMapping(yaml::IO &IO, ModuleListStream &Stream) { |
337 | IO.mapRequired(Key: "Modules" , Val&: Stream.Entries); |
338 | } |
339 | |
340 | static void streamMapping(yaml::IO &IO, SystemInfoStream &Stream) { |
341 | SystemInfo &Info = Stream.Info; |
342 | IO.mapRequired(Key: "Processor Arch" , Val&: Info.ProcessorArch); |
343 | mapOptional(IO, Key: "Processor Level" , Val&: Info.ProcessorLevel, Default: 0); |
344 | mapOptional(IO, Key: "Processor Revision" , Val&: Info.ProcessorRevision, Default: 0); |
345 | IO.mapOptional(Key: "Number of Processors" , Val&: Info.NumberOfProcessors, Default: 0); |
346 | IO.mapOptional(Key: "Product type" , Val&: Info.ProductType, Default: 0); |
347 | mapOptional(IO, Key: "Major Version" , Val&: Info.MajorVersion, Default: 0); |
348 | mapOptional(IO, Key: "Minor Version" , Val&: Info.MinorVersion, Default: 0); |
349 | mapOptional(IO, Key: "Build Number" , Val&: Info.BuildNumber, Default: 0); |
350 | IO.mapRequired(Key: "Platform ID" , Val&: Info.PlatformId); |
351 | IO.mapOptional(Key: "CSD Version" , Val&: Stream.CSDVersion, Default: "" ); |
352 | mapOptionalHex(IO, Key: "Suite Mask" , Val&: Info.SuiteMask, Default: 0); |
353 | mapOptionalHex(IO, Key: "Reserved" , Val&: Info.Reserved, Default: 0); |
354 | switch (static_cast<ProcessorArchitecture>(Info.ProcessorArch)) { |
355 | case ProcessorArchitecture::X86: |
356 | case ProcessorArchitecture::AMD64: |
357 | IO.mapOptional(Key: "CPU" , Val&: Info.CPU.X86); |
358 | break; |
359 | case ProcessorArchitecture::ARM: |
360 | case ProcessorArchitecture::ARM64: |
361 | case ProcessorArchitecture::BP_ARM64: |
362 | IO.mapOptional(Key: "CPU" , Val&: Info.CPU.Arm); |
363 | break; |
364 | default: |
365 | IO.mapOptional(Key: "CPU" , Val&: Info.CPU.Other); |
366 | break; |
367 | } |
368 | } |
369 | |
370 | static void streamMapping(yaml::IO &IO, TextContentStream &Stream) { |
371 | IO.mapOptional(Key: "Text" , Val&: Stream.Text); |
372 | } |
373 | |
374 | void yaml::MappingContextTraits<MemoryDescriptor, yaml::BinaryRef>::mapping( |
375 | IO &IO, MemoryDescriptor &Memory, BinaryRef &Content) { |
376 | mapRequiredHex(IO, Key: "Start of Memory Range" , Val&: Memory.StartOfMemoryRange); |
377 | IO.mapRequired(Key: "Content" , Val&: Content); |
378 | } |
379 | |
380 | void yaml::MappingContextTraits<MemoryDescriptor_64, yaml::BinaryRef>::mapping( |
381 | IO &IO, MemoryDescriptor_64 &Memory, BinaryRef &Content) { |
382 | mapRequiredHex(IO, Key: "Start of Memory Range" , Val&: Memory.StartOfMemoryRange); |
383 | IO.mapRequired(Key: "Content" , Val&: Content); |
384 | mapOptional(IO, Key: "Data Size" , Val&: Memory.DataSize, Default: Content.binary_size()); |
385 | } |
386 | |
387 | void yaml::MappingTraits<ThreadListStream::entry_type>::mapping( |
388 | IO &IO, ThreadListStream::entry_type &T) { |
389 | mapRequiredHex(IO, Key: "Thread Id" , Val&: T.Entry.ThreadId); |
390 | mapOptionalHex(IO, Key: "Suspend Count" , Val&: T.Entry.SuspendCount, Default: 0); |
391 | mapOptionalHex(IO, Key: "Priority Class" , Val&: T.Entry.PriorityClass, Default: 0); |
392 | mapOptionalHex(IO, Key: "Priority" , Val&: T.Entry.Priority, Default: 0); |
393 | mapOptionalHex(IO, Key: "Environment Block" , Val&: T.Entry.EnvironmentBlock, Default: 0); |
394 | IO.mapRequired(Key: "Context" , Val&: T.Context); |
395 | IO.mapRequired(Key: "Stack" , Val&: T.Entry.Stack, Ctx&: T.Stack); |
396 | } |
397 | |
398 | static void streamMapping(yaml::IO &IO, ThreadListStream &Stream) { |
399 | IO.mapRequired(Key: "Threads" , Val&: Stream.Entries); |
400 | } |
401 | |
402 | static void streamMapping(yaml::IO &IO, MinidumpYAML::ExceptionStream &Stream) { |
403 | mapRequiredHex(IO, Key: "Thread ID" , Val&: Stream.MDExceptionStream.ThreadId); |
404 | IO.mapRequired(Key: "Exception Record" , Val&: Stream.MDExceptionStream.ExceptionRecord); |
405 | IO.mapRequired(Key: "Thread Context" , Val&: Stream.ThreadContext); |
406 | } |
407 | |
408 | void yaml::MappingTraits<minidump::Exception>::mapping( |
409 | yaml::IO &IO, minidump::Exception &Exception) { |
410 | mapRequiredHex(IO, Key: "Exception Code" , Val&: Exception.ExceptionCode); |
411 | mapOptionalHex(IO, Key: "Exception Flags" , Val&: Exception.ExceptionFlags, Default: 0); |
412 | mapOptionalHex(IO, Key: "Exception Record" , Val&: Exception.ExceptionRecord, Default: 0); |
413 | mapOptionalHex(IO, Key: "Exception Address" , Val&: Exception.ExceptionAddress, Default: 0); |
414 | mapOptional(IO, Key: "Number of Parameters" , Val&: Exception.NumberParameters, Default: 0); |
415 | |
416 | for (size_t Index = 0; Index < Exception.MaxParameters; ++Index) { |
417 | SmallString<16> Name("Parameter " ); |
418 | Twine(Index).toVector(Out&: Name); |
419 | support::ulittle64_t &Field = Exception.ExceptionInformation[Index]; |
420 | |
421 | if (Index < Exception.NumberParameters) |
422 | mapRequiredHex(IO, Key: Name.c_str(), Val&: Field); |
423 | else |
424 | mapOptionalHex(IO, Key: Name.c_str(), Val&: Field, Default: 0); |
425 | } |
426 | } |
427 | |
428 | void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping( |
429 | yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) { |
430 | StreamType Type; |
431 | if (IO.outputting()) |
432 | Type = S->Type; |
433 | IO.mapRequired(Key: "Type" , Val&: Type); |
434 | |
435 | if (!IO.outputting()) |
436 | S = MinidumpYAML::Stream::create(Type); |
437 | switch (S->Kind) { |
438 | case MinidumpYAML::Stream::StreamKind::Exception: |
439 | streamMapping(IO, Stream&: llvm::cast<MinidumpYAML::ExceptionStream>(Val&: *S)); |
440 | break; |
441 | case MinidumpYAML::Stream::StreamKind::MemoryInfoList: |
442 | streamMapping(IO, Stream&: llvm::cast<MemoryInfoListStream>(Val&: *S)); |
443 | break; |
444 | case MinidumpYAML::Stream::StreamKind::MemoryList: |
445 | streamMapping(IO, Stream&: llvm::cast<MemoryListStream>(Val&: *S)); |
446 | break; |
447 | case MinidumpYAML::Stream::StreamKind::Memory64List: |
448 | streamMapping(IO, Stream&: llvm::cast<Memory64ListStream>(Val&: *S)); |
449 | break; |
450 | case MinidumpYAML::Stream::StreamKind::ModuleList: |
451 | streamMapping(IO, Stream&: llvm::cast<ModuleListStream>(Val&: *S)); |
452 | break; |
453 | case MinidumpYAML::Stream::StreamKind::RawContent: |
454 | streamMapping(IO, Stream&: llvm::cast<RawContentStream>(Val&: *S)); |
455 | break; |
456 | case MinidumpYAML::Stream::StreamKind::SystemInfo: |
457 | streamMapping(IO, Stream&: llvm::cast<SystemInfoStream>(Val&: *S)); |
458 | break; |
459 | case MinidumpYAML::Stream::StreamKind::TextContent: |
460 | streamMapping(IO, Stream&: llvm::cast<TextContentStream>(Val&: *S)); |
461 | break; |
462 | case MinidumpYAML::Stream::StreamKind::ThreadList: |
463 | streamMapping(IO, Stream&: llvm::cast<ThreadListStream>(Val&: *S)); |
464 | break; |
465 | } |
466 | } |
467 | |
468 | std::string yaml::MappingTraits<std::unique_ptr<Stream>>::validate( |
469 | yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) { |
470 | switch (S->Kind) { |
471 | case MinidumpYAML::Stream::StreamKind::RawContent: |
472 | return streamValidate(Stream&: cast<RawContentStream>(Val&: *S)); |
473 | case MinidumpYAML::Stream::StreamKind::Memory64List: |
474 | return streamValidate(Stream&: cast<Memory64ListStream>(Val&: *S)); |
475 | case MinidumpYAML::Stream::StreamKind::Exception: |
476 | case MinidumpYAML::Stream::StreamKind::MemoryInfoList: |
477 | case MinidumpYAML::Stream::StreamKind::MemoryList: |
478 | case MinidumpYAML::Stream::StreamKind::ModuleList: |
479 | case MinidumpYAML::Stream::StreamKind::SystemInfo: |
480 | case MinidumpYAML::Stream::StreamKind::TextContent: |
481 | case MinidumpYAML::Stream::StreamKind::ThreadList: |
482 | return "" ; |
483 | } |
484 | llvm_unreachable("Fully covered switch above!" ); |
485 | } |
486 | |
487 | void yaml::MappingTraits<Object>::mapping(IO &IO, Object &O) { |
488 | IO.mapTag(Tag: "!minidump" , Default: true); |
489 | mapOptionalHex(IO, Key: "Signature" , Val&: O.Header.Signature, Default: Header::MagicSignature); |
490 | mapOptionalHex(IO, Key: "Version" , Val&: O.Header.Version, Default: Header::MagicVersion); |
491 | mapOptionalHex(IO, Key: "Flags" , Val&: O.Header.Flags, Default: 0); |
492 | IO.mapRequired(Key: "Streams" , Val&: O.Streams); |
493 | } |
494 | |
495 | Expected<std::unique_ptr<Stream>> |
496 | Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) { |
497 | StreamKind Kind = getKind(Type: StreamDesc.Type); |
498 | switch (Kind) { |
499 | case StreamKind::Exception: { |
500 | Expected<const minidump::ExceptionStream &> ExpectedExceptionStream = |
501 | File.getExceptionStream(Directory: StreamDesc); |
502 | if (!ExpectedExceptionStream) |
503 | return ExpectedExceptionStream.takeError(); |
504 | Expected<ArrayRef<uint8_t>> ExpectedThreadContext = |
505 | File.getRawData(Desc: ExpectedExceptionStream->ThreadContext); |
506 | if (!ExpectedThreadContext) |
507 | return ExpectedThreadContext.takeError(); |
508 | return std::make_unique<ExceptionStream>(args: *ExpectedExceptionStream, |
509 | args&: *ExpectedThreadContext); |
510 | } |
511 | case StreamKind::MemoryInfoList: { |
512 | if (auto ExpectedList = File.getMemoryInfoList()) |
513 | return std::make_unique<MemoryInfoListStream>(args&: *ExpectedList); |
514 | else |
515 | return ExpectedList.takeError(); |
516 | } |
517 | case StreamKind::MemoryList: { |
518 | auto ExpectedList = File.getMemoryList(); |
519 | if (!ExpectedList) |
520 | return ExpectedList.takeError(); |
521 | std::vector<MemoryListStream::entry_type> Ranges; |
522 | for (const MemoryDescriptor &MD : *ExpectedList) { |
523 | auto ExpectedContent = File.getRawData(Desc: MD.Memory); |
524 | if (!ExpectedContent) |
525 | return ExpectedContent.takeError(); |
526 | Ranges.push_back(x: {.Entry: MD, .Content: *ExpectedContent}); |
527 | } |
528 | return std::make_unique<MemoryListStream>(args: std::move(Ranges)); |
529 | } |
530 | case StreamKind::Memory64List: { |
531 | Error Err = Error::success(); |
532 | auto Memory64List = File.getMemory64List(Err); |
533 | std::vector<Memory64ListStream::entry_type> Ranges; |
534 | for (const auto &Pair : Memory64List) { |
535 | Ranges.push_back(x: {.Entry: Pair.first, .Content: Pair.second}); |
536 | } |
537 | |
538 | if (Err) |
539 | return Err; |
540 | return std::make_unique<Memory64ListStream>(args: std::move(Ranges)); |
541 | } |
542 | case StreamKind::ModuleList: { |
543 | auto ExpectedList = File.getModuleList(); |
544 | if (!ExpectedList) |
545 | return ExpectedList.takeError(); |
546 | std::vector<ModuleListStream::entry_type> Modules; |
547 | for (const Module &M : *ExpectedList) { |
548 | auto ExpectedName = File.getString(Offset: M.ModuleNameRVA); |
549 | if (!ExpectedName) |
550 | return ExpectedName.takeError(); |
551 | auto ExpectedCv = File.getRawData(Desc: M.CvRecord); |
552 | if (!ExpectedCv) |
553 | return ExpectedCv.takeError(); |
554 | auto ExpectedMisc = File.getRawData(Desc: M.MiscRecord); |
555 | if (!ExpectedMisc) |
556 | return ExpectedMisc.takeError(); |
557 | Modules.push_back( |
558 | x: {.Entry: M, .Name: std::move(*ExpectedName), .CvRecord: *ExpectedCv, .MiscRecord: *ExpectedMisc}); |
559 | } |
560 | return std::make_unique<ModuleListStream>(args: std::move(Modules)); |
561 | } |
562 | case StreamKind::RawContent: |
563 | return std::make_unique<RawContentStream>(args: StreamDesc.Type, |
564 | args: File.getRawStream(Stream: StreamDesc)); |
565 | case StreamKind::SystemInfo: { |
566 | auto ExpectedInfo = File.getSystemInfo(); |
567 | if (!ExpectedInfo) |
568 | return ExpectedInfo.takeError(); |
569 | auto ExpectedCSDVersion = File.getString(Offset: ExpectedInfo->CSDVersionRVA); |
570 | if (!ExpectedCSDVersion) |
571 | return ExpectedInfo.takeError(); |
572 | return std::make_unique<SystemInfoStream>(args: *ExpectedInfo, |
573 | args: std::move(*ExpectedCSDVersion)); |
574 | } |
575 | case StreamKind::TextContent: |
576 | return std::make_unique<TextContentStream>( |
577 | args: StreamDesc.Type, args: toStringRef(Input: File.getRawStream(Stream: StreamDesc))); |
578 | case StreamKind::ThreadList: { |
579 | auto ExpectedList = File.getThreadList(); |
580 | if (!ExpectedList) |
581 | return ExpectedList.takeError(); |
582 | std::vector<ThreadListStream::entry_type> Threads; |
583 | for (const Thread &T : *ExpectedList) { |
584 | auto ExpectedStack = File.getRawData(Desc: T.Stack.Memory); |
585 | if (!ExpectedStack) |
586 | return ExpectedStack.takeError(); |
587 | auto ExpectedContext = File.getRawData(Desc: T.Context); |
588 | if (!ExpectedContext) |
589 | return ExpectedContext.takeError(); |
590 | Threads.push_back(x: {.Entry: T, .Stack: *ExpectedStack, .Context: *ExpectedContext}); |
591 | } |
592 | return std::make_unique<ThreadListStream>(args: std::move(Threads)); |
593 | } |
594 | } |
595 | llvm_unreachable("Unhandled stream kind!" ); |
596 | } |
597 | |
598 | Expected<Object> Object::create(const object::MinidumpFile &File) { |
599 | std::vector<std::unique_ptr<Stream>> Streams; |
600 | Streams.reserve(n: File.streams().size()); |
601 | for (const Directory &StreamDesc : File.streams()) { |
602 | auto ExpectedStream = Stream::create(StreamDesc, File); |
603 | if (!ExpectedStream) |
604 | return ExpectedStream.takeError(); |
605 | Streams.push_back(x: std::move(*ExpectedStream)); |
606 | } |
607 | return Object(File.header(), std::move(Streams)); |
608 | } |
609 | |