1 //===- MinidumpYAMLTest.cpp - Tests for Minidump<->YAML code --------------===//
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/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"
16 using namespace llvm::minidump
;
18 static Expected
<std::unique_ptr
<object::MinidumpFile
>>
19 toBinary(SmallVectorImpl
<char> &Storage
, StringRef Yaml
) {
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");
27 return object::MinidumpFile::create(MemoryBufferRef(OS
.str(), "Binary"));
30 TEST(MinidumpYAML
, Basic
) {
31 SmallString
<0> Storage
;
32 auto ExpectedFile
= toBinary(Storage
, R
"(
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
46 Content: DEADBEEFBAADF00D)");
47 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
48 object::MinidumpFile
&File
= **ExpectedFile
;
50 ASSERT_EQ(3u, File
.streams().size());
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
);
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
)));
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
));
72 TEST(MinidumpYAML
, RawContent
) {
73 SmallString
<0> Storage
;
74 auto ExpectedFile
= toBinary(Storage
, R
"(
79 Content: DEADBEEFBAADF00D)");
80 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
81 object::MinidumpFile
&File
= **ExpectedFile
;
84 (ArrayRef
<uint8_t>{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D, 0x00}),
85 File
.getRawStream(StreamType::LinuxAuxv
));
88 TEST(MinidumpYAML
, X86SystemInfo
) {
89 SmallString
<0> Storage
;
90 auto ExpectedFile
= toBinary(Storage
, R
"(
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
;
104 ASSERT_EQ(1u, File
.streams().size());
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
);
118 TEST(MinidumpYAML
, OtherSystemInfo
) {
119 SmallString
<0> Storage
;
120 auto ExpectedFile
= toBinary(Storage
, R
"(
127 Features: 000102030405060708090a0b0c0d0e0f)");
128 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
129 object::MinidumpFile
&File
= **ExpectedFile
;
131 ASSERT_EQ(1u, File
.streams().size());
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
);
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
));
143 // Test that we can parse a normal-looking ExceptionStream.
144 TEST(MinidumpYAML
, ExceptionStream
) {
145 SmallString
<0> Storage
;
146 auto ExpectedFile
= toBinary(Storage
, R
"(
154 Exception Record: 0x0102030405060708
155 Exception Address: 0x0a0b0c0d0e0f1011
156 Number of Parameters: 2
159 Thread Context: 3DeadBeefDefacedABadCafe)");
160 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
161 object::MinidumpFile
&File
= **ExpectedFile
;
163 ASSERT_EQ(1u, File
.streams().size());
165 auto ExceptionIterator
= File
.getExceptionStreams().begin();
167 Expected
<const ExceptionStream
&> ExpectedStream
= *ExceptionIterator
;
169 ASSERT_THAT_EXPECTED(ExpectedStream
, Succeeded());
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]);
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}),
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
"(
201 Exception Record: 0x0102030405060708
202 Exception Address: 0x0a0b0c0d0e0f1011
203 Thread Context: 3DeadBeefDefacedABadCafe)");
204 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
205 object::MinidumpFile
&File
= **ExpectedFile
;
207 ASSERT_EQ(1u, File
.streams().size());
209 auto ExceptionIterator
= File
.getExceptionStreams().begin();
211 Expected
<const ExceptionStream
&> ExpectedStream
= *ExceptionIterator
;
212 ASSERT_THAT_EXPECTED(ExpectedStream
, Succeeded());
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
);
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}),
231 // Test that we can parse an ExceptionStream where the stated number of
232 // parameters is greater than the actual size of the ExceptionInformation
234 TEST(MinidumpYAML
, ExceptionStream_TooManyParameters
) {
235 SmallString
<0> Storage
;
236 auto ExpectedFile
= toBinary(Storage
, R
"(
243 Number of Parameters: 16
259 Thread Context: 3DeadBeefDefacedABadCafe)");
260 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
261 object::MinidumpFile
&File
= **ExpectedFile
;
263 ASSERT_EQ(1u, File
.streams().size());
265 auto ExceptionIterator
= File
.getExceptionStreams().begin();
267 Expected
<const ExceptionStream
&> ExpectedStream
= *ExceptionIterator
;
269 ASSERT_THAT_EXPECTED(ExpectedStream
, Succeeded());
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
]);
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}),
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
"(
305 Exception Record: 0x0102030405060708
306 Exception Address: 0x0a0b0c0d0e0f1011
307 Number of Parameters: 2
311 Thread Context: 3DeadBeefDefacedABadCafe)");
312 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
313 object::MinidumpFile
&File
= **ExpectedFile
;
315 ASSERT_EQ(1u, File
.streams().size());
317 auto ExceptionIterator
= File
.getExceptionStreams().begin();
319 Expected
<const ExceptionStream
&> ExpectedStream
= *ExceptionIterator
;
321 ASSERT_THAT_EXPECTED(ExpectedStream
, Succeeded());
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]);
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}),
343 TEST(MinidumpYAML
, MemoryRegion_64bit
) {
344 SmallString
<0> Storage
;
345 auto ExpectedFile
= toBinary(Storage
, R
"(
350 - Start of Memory Range: 0x7FFFFFCF0818283
351 Content: '68656c6c6f'
352 - Start of Memory Range: 0x7FFFFFFF0818283
353 Content: '776f726c64'
356 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
357 object::MinidumpFile
&File
= **ExpectedFile
;
359 ASSERT_THAT(File
.streams().size(), 1u);
361 Error Err
= Error::success();
362 iterator_range
<object::MinidumpFile::FallibleMemory64Iterator
> MemoryList
=
363 File
.getMemory64List(Err
);
365 ASSERT_THAT_ERROR(std::move(Err
), Succeeded());
366 auto Iterator
= MemoryList
.begin();
368 auto DescOnePair
= *Iterator
;
369 const minidump::MemoryDescriptor_64
&DescOne
= DescOnePair
.first
;
370 ASSERT_THAT(DescOne
.StartOfMemoryRange
, 0x7FFFFFCF0818283u
);
371 ASSERT_THAT(DescOne
.DataSize
, 5u);
373 ASSERT_THAT_ERROR(std::move(Err
), Succeeded());
375 auto DescTwoPair
= *Iterator
;
376 const minidump::MemoryDescriptor_64
&DescTwo
= DescTwoPair
.first
;
377 ASSERT_THAT(DescTwo
.StartOfMemoryRange
, 0x7FFFFFFF0818283u
);
378 ASSERT_THAT(DescTwo
.DataSize
, 5u);
380 ASSERT_THAT_ERROR(std::move(Err
), Succeeded());
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
);
389 Expected
<minidump::Memory64ListHeader
> ExpectedHeader
=
390 File
.getMemoryList64Header();
391 ASSERT_THAT_EXPECTED(ExpectedHeader
, Succeeded());
392 ASSERT_THAT(ExpectedHeader
->BaseRVA
, 92u);
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"));
399 Expected
<ArrayRef
<uint8_t>> DescTwoExpectedContentSlice
= DescTwoPair
.second
;
400 ASSERT_THAT_EXPECTED(DescTwoExpectedContentSlice
, Succeeded());
401 ASSERT_THAT(*DescTwoExpectedContentSlice
, arrayRefFromStringRef("world"));
403 ASSERT_EQ(Iterator
, MemoryList
.end());
406 // Test that we can parse multiple exception streams.
407 TEST(MinidumpYAML
, ExceptionStream_MultipleExceptions
) {
408 SmallString
<0> Storage
;
409 auto ExpectedFile
= toBinary(Storage
, R
"(
417 Exception Record: 0x0102030405060708
418 Exception Address: 0x0a0b0c0d0e0f1011
419 Number of Parameters: 2
423 Thread Context: 3DeadBeefDefacedABadCafe
429 Exception Record: 0x0102030405060708
430 Exception Address: 0x0a0b0c0d0e0f1011
431 Thread Context: 3DeadBeefDefacedABadCafe)");
433 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
434 object::MinidumpFile
&File
= **ExpectedFile
;
436 ASSERT_EQ(2u, File
.streams().size());
439 for (auto exception_stream
: File
.getExceptionStreams()) {
441 ASSERT_THAT_EXPECTED(exception_stream
, Succeeded());
442 ASSERT_THAT(0x23u
, exception_stream
->ExceptionRecord
.ExceptionCode
);
445 ASSERT_THAT(2u, count
);