xref: /llvm-project/llvm/unittests/ObjectYAML/MinidumpYAMLTest.cpp (revision deba13409245aabf3fda8b82a873336ea5238d3a)
1 //===- MinidumpYAMLTest.cpp - Tests for Minidump<->YAML code --------------===//
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/Object/Minidump.h"
10 #include "llvm/ObjectYAML/yaml2obj.h"
11 #include "llvm/Support/YAMLTraits.h"
12 #include "llvm/Testing/Support/Error.h"
13 #include "gtest/gtest.h"
14 
15 using namespace llvm;
16 using namespace llvm::minidump;
17 
18 static Expected<std::unique_ptr<object::MinidumpFile>>
19 toBinary(SmallVectorImpl<char> &Storage, StringRef Yaml) {
20   Storage.clear();
21   raw_svector_ostream OS(Storage);
22   yaml::Input YIn(Yaml);
23   if (!yaml::convertYAML(YIn, OS, [](const Twine &Msg) {}))
24     return createStringError(std::errc::invalid_argument,
25                              "unable to convert YAML");
26 
27   return object::MinidumpFile::create(MemoryBufferRef(OS.str(), "Binary"));
28 }
29 
30 TEST(MinidumpYAML, Basic) {
31   SmallString<0> Storage;
32   auto ExpectedFile = toBinary(Storage, R"(
33 --- !minidump
34 Streams:
35   - Type:            SystemInfo
36     Processor Arch:  ARM64
37     Platform ID:     Linux
38     CPU:
39       CPUID:           0x05060708
40   - Type:            LinuxMaps
41     Text:             |
42       400d9000-400db000 r-xp 00000000 b3:04 227        /system/bin/app_process
43       400db000-400dc000 r--p 00001000 b3:04 227        /system/bin/app_process
44 
45   - Type:            LinuxAuxv
46     Content:         DEADBEEFBAADF00D)");
47   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
48   object::MinidumpFile &File = **ExpectedFile;
49 
50   ASSERT_EQ(3u, File.streams().size());
51 
52   EXPECT_EQ(StreamType::SystemInfo, File.streams()[0].Type);
53   auto ExpectedSysInfo = File.getSystemInfo();
54   ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded());
55   const SystemInfo &SysInfo = *ExpectedSysInfo;
56   EXPECT_EQ(ProcessorArchitecture::ARM64, SysInfo.ProcessorArch);
57   EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId);
58   EXPECT_EQ(0x05060708u, SysInfo.CPU.Arm.CPUID);
59 
60   EXPECT_EQ(StreamType::LinuxMaps, File.streams()[1].Type);
61   EXPECT_EQ("400d9000-400db000 r-xp 00000000 b3:04 227        "
62             "/system/bin/app_process\n"
63             "400db000-400dc000 r--p 00001000 b3:04 227        "
64             "/system/bin/app_process\n",
65             toStringRef(*File.getRawStream(StreamType::LinuxMaps)));
66 
67   EXPECT_EQ(StreamType::LinuxAuxv, File.streams()[2].Type);
68   EXPECT_EQ((ArrayRef<uint8_t>{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D}),
69             File.getRawStream(StreamType::LinuxAuxv));
70 }
71 
72 TEST(MinidumpYAML, RawContent) {
73   SmallString<0> Storage;
74   auto ExpectedFile = toBinary(Storage, R"(
75 --- !minidump
76 Streams:
77   - Type:            LinuxAuxv
78     Size:            9
79     Content:         DEADBEEFBAADF00D)");
80   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
81   object::MinidumpFile &File = **ExpectedFile;
82 
83   EXPECT_EQ(
84       (ArrayRef<uint8_t>{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D, 0x00}),
85       File.getRawStream(StreamType::LinuxAuxv));
86 }
87 
88 TEST(MinidumpYAML, X86SystemInfo) {
89   SmallString<0> Storage;
90   auto ExpectedFile = toBinary(Storage, R"(
91 --- !minidump
92 Streams:
93   - Type:            SystemInfo
94     Processor Arch:  X86
95     Platform ID:     Linux
96     CPU:
97       Vendor ID:       LLVMLLVMLLVM
98       Version Info:    0x01020304
99       Feature Info:    0x05060708
100       AMD Extended Features: 0x09000102)");
101   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
102   object::MinidumpFile &File = **ExpectedFile;
103 
104   ASSERT_EQ(1u, File.streams().size());
105 
106   auto ExpectedSysInfo = File.getSystemInfo();
107   ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded());
108   const SystemInfo &SysInfo = *ExpectedSysInfo;
109   EXPECT_EQ(ProcessorArchitecture::X86, SysInfo.ProcessorArch);
110   EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId);
111   EXPECT_EQ("LLVMLLVMLLVM", StringRef(SysInfo.CPU.X86.VendorID,
112                                       sizeof(SysInfo.CPU.X86.VendorID)));
113   EXPECT_EQ(0x01020304u, SysInfo.CPU.X86.VersionInfo);
114   EXPECT_EQ(0x05060708u, SysInfo.CPU.X86.FeatureInfo);
115   EXPECT_EQ(0x09000102u, SysInfo.CPU.X86.AMDExtendedFeatures);
116 }
117 
118 TEST(MinidumpYAML, OtherSystemInfo) {
119   SmallString<0> Storage;
120   auto ExpectedFile = toBinary(Storage, R"(
121 --- !minidump
122 Streams:
123   - Type:            SystemInfo
124     Processor Arch:  PPC
125     Platform ID:     Linux
126     CPU:
127       Features:        000102030405060708090a0b0c0d0e0f)");
128   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
129   object::MinidumpFile &File = **ExpectedFile;
130 
131   ASSERT_EQ(1u, File.streams().size());
132 
133   auto ExpectedSysInfo = File.getSystemInfo();
134   ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded());
135   const SystemInfo &SysInfo = *ExpectedSysInfo;
136   EXPECT_EQ(ProcessorArchitecture::PPC, SysInfo.ProcessorArch);
137   EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId);
138   EXPECT_EQ(
139       (ArrayRef<uint8_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
140       ArrayRef(SysInfo.CPU.Other.ProcessorFeatures));
141 }
142 
143 // Test that we can parse a normal-looking ExceptionStream.
144 TEST(MinidumpYAML, ExceptionStream) {
145   SmallString<0> Storage;
146   auto ExpectedFile = toBinary(Storage, R"(
147 --- !minidump
148 Streams:
149   - Type:            Exception
150     Thread ID:  0x7
151     Exception Record:
152       Exception Code:  0x23
153       Exception Flags: 0x5
154       Exception Record: 0x0102030405060708
155       Exception Address: 0x0a0b0c0d0e0f1011
156       Number of Parameters: 2
157       Parameter 0: 0x22
158       Parameter 1: 0x24
159     Thread Context:  3DeadBeefDefacedABadCafe)");
160   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
161   object::MinidumpFile &File = **ExpectedFile;
162 
163   ASSERT_EQ(1u, File.streams().size());
164 
165   auto ExceptionIterator = File.getExceptionStreams().begin();
166 
167   Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;
168 
169   ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());
170 
171   const minidump::ExceptionStream &Stream = *ExpectedStream;
172   EXPECT_EQ(0x7u, Stream.ThreadId);
173   const minidump::Exception &Exception = Stream.ExceptionRecord;
174   EXPECT_EQ(0x23u, Exception.ExceptionCode);
175   EXPECT_EQ(0x5u, Exception.ExceptionFlags);
176   EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);
177   EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);
178   EXPECT_EQ(2u, Exception.NumberParameters);
179   EXPECT_EQ(0x22u, Exception.ExceptionInformation[0]);
180   EXPECT_EQ(0x24u, Exception.ExceptionInformation[1]);
181 
182   Expected<ArrayRef<uint8_t>> ExpectedContext =
183       File.getRawData(Stream.ThreadContext);
184   ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());
185   EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,
186                                0xab, 0xad, 0xca, 0xfe}),
187             *ExpectedContext);
188 }
189 
190 // Test that we can parse an exception stream with no ExceptionInformation.
191 TEST(MinidumpYAML, ExceptionStream_NoParameters) {
192   SmallString<0> Storage;
193   auto ExpectedFile = toBinary(Storage, R"(
194 --- !minidump
195 Streams:
196   - Type:            Exception
197     Thread ID:  0x7
198     Exception Record:
199       Exception Code:  0x23
200       Exception Flags: 0x5
201       Exception Record: 0x0102030405060708
202       Exception Address: 0x0a0b0c0d0e0f1011
203     Thread Context:  3DeadBeefDefacedABadCafe)");
204   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
205   object::MinidumpFile &File = **ExpectedFile;
206 
207   ASSERT_EQ(1u, File.streams().size());
208 
209   auto ExceptionIterator = File.getExceptionStreams().begin();
210 
211   Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;
212   ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());
213 
214   const minidump::ExceptionStream &Stream = *ExpectedStream;
215   EXPECT_EQ(0x7u, Stream.ThreadId);
216   const minidump::Exception &Exception = Stream.ExceptionRecord;
217   EXPECT_EQ(0x23u, Exception.ExceptionCode);
218   EXPECT_EQ(0x5u, Exception.ExceptionFlags);
219   EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);
220   EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);
221   EXPECT_EQ(0u, Exception.NumberParameters);
222 
223   Expected<ArrayRef<uint8_t>> ExpectedContext =
224       File.getRawData(Stream.ThreadContext);
225   ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());
226   EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,
227                                0xab, 0xad, 0xca, 0xfe}),
228             *ExpectedContext);
229 }
230 
231 // Test that we can parse an ExceptionStream where the stated number of
232 // parameters is greater than the actual size of the ExceptionInformation
233 // array.
234 TEST(MinidumpYAML, ExceptionStream_TooManyParameters) {
235   SmallString<0> Storage;
236   auto ExpectedFile = toBinary(Storage, R"(
237 --- !minidump
238 Streams:
239   - Type:            Exception
240     Thread ID:  0x8
241     Exception Record:
242       Exception Code: 0
243       Number of Parameters: 16
244       Parameter 0: 0x0
245       Parameter 1: 0xff
246       Parameter 2: 0xee
247       Parameter 3: 0xdd
248       Parameter 4: 0xcc
249       Parameter 5: 0xbb
250       Parameter 6: 0xaa
251       Parameter 7: 0x99
252       Parameter 8: 0x88
253       Parameter 9: 0x77
254       Parameter 10: 0x66
255       Parameter 11: 0x55
256       Parameter 12: 0x44
257       Parameter 13: 0x33
258       Parameter 14: 0x22
259     Thread Context:  3DeadBeefDefacedABadCafe)");
260   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
261   object::MinidumpFile &File = **ExpectedFile;
262 
263   ASSERT_EQ(1u, File.streams().size());
264 
265   auto ExceptionIterator = File.getExceptionStreams().begin();
266 
267   Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;
268 
269   ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());
270 
271   const minidump::ExceptionStream &Stream = *ExpectedStream;
272   EXPECT_EQ(0x8u, Stream.ThreadId);
273   const minidump::Exception &Exception = Stream.ExceptionRecord;
274   EXPECT_EQ(0x0u, Exception.ExceptionCode);
275   EXPECT_EQ(0x0u, Exception.ExceptionFlags);
276   EXPECT_EQ(0x00u, Exception.ExceptionRecord);
277   EXPECT_EQ(0x0u, Exception.ExceptionAddress);
278   EXPECT_EQ(16u, Exception.NumberParameters);
279   EXPECT_EQ(0x0u, Exception.ExceptionInformation[0]);
280   for (int Index = 1; Index < 15; ++Index) {
281     EXPECT_EQ(0x110u - Index * 0x11, Exception.ExceptionInformation[Index]);
282   }
283 
284   Expected<ArrayRef<uint8_t>> ExpectedContext =
285       File.getRawData(Stream.ThreadContext);
286   ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());
287   EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,
288                                0xab, 0xad, 0xca, 0xfe}),
289             *ExpectedContext);
290 }
291 
292 // Test that we can parse an ExceptionStream where the number of
293 // ExceptionInformation parameters provided is greater than the
294 // specified Number of Parameters.
295 TEST(MinidumpYAML, ExceptionStream_ExtraParameter) {
296   SmallString<0> Storage;
297   auto ExpectedFile = toBinary(Storage, R"(
298 --- !minidump
299 Streams:
300   - Type:            Exception
301     Thread ID:  0x7
302     Exception Record:
303       Exception Code:  0x23
304       Exception Flags: 0x5
305       Exception Record: 0x0102030405060708
306       Exception Address: 0x0a0b0c0d0e0f1011
307       Number of Parameters: 2
308       Parameter 0: 0x99
309       Parameter 1: 0x23
310       Parameter 2: 0x42
311     Thread Context:  3DeadBeefDefacedABadCafe)");
312   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
313   object::MinidumpFile &File = **ExpectedFile;
314 
315   ASSERT_EQ(1u, File.streams().size());
316 
317   auto ExceptionIterator = File.getExceptionStreams().begin();
318 
319   Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;
320 
321   ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());
322 
323   const minidump::ExceptionStream &Stream = *ExpectedStream;
324   EXPECT_EQ(0x7u, Stream.ThreadId);
325   const minidump::Exception &Exception = Stream.ExceptionRecord;
326   EXPECT_EQ(0x23u, Exception.ExceptionCode);
327   EXPECT_EQ(0x5u, Exception.ExceptionFlags);
328   EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);
329   EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);
330   EXPECT_EQ(2u, Exception.NumberParameters);
331   EXPECT_EQ(0x99u, Exception.ExceptionInformation[0]);
332   EXPECT_EQ(0x23u, Exception.ExceptionInformation[1]);
333   EXPECT_EQ(0x42u, Exception.ExceptionInformation[2]);
334 
335   Expected<ArrayRef<uint8_t>> ExpectedContext =
336       File.getRawData(Stream.ThreadContext);
337   ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());
338   EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,
339                                0xab, 0xad, 0xca, 0xfe}),
340             *ExpectedContext);
341 }
342 
343 TEST(MinidumpYAML, MemoryRegion_64bit) {
344   SmallString<0> Storage;
345   auto ExpectedFile = toBinary(Storage, R"(
346 --- !minidump
347 Streams:
348   - Type:            Memory64List
349     Memory Ranges:
350       - Start of Memory Range: 0x7FFFFFCF0818283
351         Content:               '68656c6c6f'
352       - Start of Memory Range: 0x7FFFFFFF0818283
353         Content:               '776f726c64'
354         )");
355 
356   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
357   object::MinidumpFile &File = **ExpectedFile;
358 
359   ASSERT_THAT(File.streams().size(), 1u);
360 
361   Error Err = Error::success();
362   iterator_range<object::MinidumpFile::FallibleMemory64Iterator> MemoryList =
363       File.getMemory64List(Err);
364 
365   ASSERT_THAT_ERROR(std::move(Err), Succeeded());
366   auto Iterator = MemoryList.begin();
367 
368   auto DescOnePair = *Iterator;
369   const minidump::MemoryDescriptor_64 &DescOne = DescOnePair.first;
370   ASSERT_THAT(DescOne.StartOfMemoryRange, 0x7FFFFFCF0818283u);
371   ASSERT_THAT(DescOne.DataSize, 5u);
372   ++Iterator;
373   ASSERT_THAT_ERROR(std::move(Err), Succeeded());
374 
375   auto DescTwoPair = *Iterator;
376   const minidump::MemoryDescriptor_64 &DescTwo = DescTwoPair.first;
377   ASSERT_THAT(DescTwo.StartOfMemoryRange, 0x7FFFFFFF0818283u);
378   ASSERT_THAT(DescTwo.DataSize, 5u);
379   ++Iterator;
380   ASSERT_THAT_ERROR(std::move(Err), Succeeded());
381 
382   const std::optional<ArrayRef<uint8_t>> ExpectedContent =
383       File.getRawStream(StreamType::Memory64List);
384   ASSERT_TRUE(ExpectedContent);
385   const size_t ExpectedStreamSize =
386       sizeof(Memory64ListHeader) + (sizeof(MemoryDescriptor_64) * 2);
387   ASSERT_THAT(ExpectedContent->size(), ExpectedStreamSize);
388 
389   Expected<minidump::Memory64ListHeader> ExpectedHeader =
390       File.getMemoryList64Header();
391   ASSERT_THAT_EXPECTED(ExpectedHeader, Succeeded());
392   ASSERT_THAT(ExpectedHeader->BaseRVA, 92u);
393 
394   Expected<ArrayRef<uint8_t>> DescOneExpectedContentSlice = DescOnePair.second;
395   ASSERT_THAT_EXPECTED(DescOneExpectedContentSlice, Succeeded());
396   ASSERT_THAT(DescOneExpectedContentSlice->size(), 5u);
397   ASSERT_THAT(*DescOneExpectedContentSlice, arrayRefFromStringRef("hello"));
398 
399   Expected<ArrayRef<uint8_t>> DescTwoExpectedContentSlice = DescTwoPair.second;
400   ASSERT_THAT_EXPECTED(DescTwoExpectedContentSlice, Succeeded());
401   ASSERT_THAT(*DescTwoExpectedContentSlice, arrayRefFromStringRef("world"));
402 
403   ASSERT_EQ(Iterator, MemoryList.end());
404 }
405 
406 // Test that we can parse multiple exception streams.
407 TEST(MinidumpYAML, ExceptionStream_MultipleExceptions) {
408   SmallString<0> Storage;
409   auto ExpectedFile = toBinary(Storage, R"(
410 --- !minidump
411 Streams:
412   - Type:            Exception
413     Thread ID:  0x7
414     Exception Record:
415       Exception Code:  0x23
416       Exception Flags: 0x5
417       Exception Record: 0x0102030405060708
418       Exception Address: 0x0a0b0c0d0e0f1011
419       Number of Parameters: 2
420       Parameter 0: 0x99
421       Parameter 1: 0x23
422       Parameter 2: 0x42
423     Thread Context:  3DeadBeefDefacedABadCafe
424   - Type:            Exception
425     Thread ID:  0x5
426     Exception Record:
427       Exception Code:  0x23
428       Exception Flags: 0x5
429       Exception Record: 0x0102030405060708
430       Exception Address: 0x0a0b0c0d0e0f1011
431     Thread Context:  3DeadBeefDefacedABadCafe)");
432 
433   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
434   object::MinidumpFile &File = **ExpectedFile;
435 
436   ASSERT_EQ(2u, File.streams().size());
437 
438   size_t count = 0;
439   for (auto exception_stream : File.getExceptionStreams()) {
440     count++;
441     ASSERT_THAT_EXPECTED(exception_stream, Succeeded());
442     ASSERT_THAT(0x23u, exception_stream->ExceptionRecord.ExceptionCode);
443   }
444 
445   ASSERT_THAT(2u, count);
446 }
447