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>>
toBinary(SmallVectorImpl<char> & Storage,StringRef Yaml)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 
TEST(MinidumpYAML,Basic)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 
TEST(MinidumpYAML,RawContent)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 
TEST(MinidumpYAML,X86SystemInfo)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 
TEST(MinidumpYAML,OtherSystemInfo)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       makeArrayRef(SysInfo.CPU.Other.ProcessorFeatures));
141 }
142 
143 // Test that we can parse a normal-looking ExceptionStream.
TEST(MinidumpYAML,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   Expected<const minidump::ExceptionStream &> ExpectedStream =
166       File.getExceptionStream();
167 
168   ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());
169 
170   const minidump::ExceptionStream &Stream = *ExpectedStream;
171   EXPECT_EQ(0x7u, Stream.ThreadId);
172   const minidump::Exception &Exception = Stream.ExceptionRecord;
173   EXPECT_EQ(0x23u, Exception.ExceptionCode);
174   EXPECT_EQ(0x5u, Exception.ExceptionFlags);
175   EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);
176   EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);
177   EXPECT_EQ(2u, Exception.NumberParameters);
178   EXPECT_EQ(0x22u, Exception.ExceptionInformation[0]);
179   EXPECT_EQ(0x24u, Exception.ExceptionInformation[1]);
180 
181   Expected<ArrayRef<uint8_t>> ExpectedContext =
182       File.getRawData(Stream.ThreadContext);
183   ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());
184   EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,
185                                0xab, 0xad, 0xca, 0xfe}),
186             *ExpectedContext);
187 }
188 
189 // Test that we can parse an exception stream with no ExceptionInformation.
TEST(MinidumpYAML,ExceptionStream_NoParameters)190 TEST(MinidumpYAML, ExceptionStream_NoParameters) {
191   SmallString<0> Storage;
192   auto ExpectedFile = toBinary(Storage, R"(
193 --- !minidump
194 Streams:
195   - Type:            Exception
196     Thread ID:  0x7
197     Exception Record:
198       Exception Code:  0x23
199       Exception Flags: 0x5
200       Exception Record: 0x0102030405060708
201       Exception Address: 0x0a0b0c0d0e0f1011
202     Thread Context:  3DeadBeefDefacedABadCafe)");
203   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
204   object::MinidumpFile &File = **ExpectedFile;
205 
206   ASSERT_EQ(1u, File.streams().size());
207 
208   Expected<const minidump::ExceptionStream &> ExpectedStream =
209       File.getExceptionStream();
210 
211   ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());
212 
213   const minidump::ExceptionStream &Stream = *ExpectedStream;
214   EXPECT_EQ(0x7u, Stream.ThreadId);
215   const minidump::Exception &Exception = Stream.ExceptionRecord;
216   EXPECT_EQ(0x23u, Exception.ExceptionCode);
217   EXPECT_EQ(0x5u, Exception.ExceptionFlags);
218   EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);
219   EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);
220   EXPECT_EQ(0u, Exception.NumberParameters);
221 
222   Expected<ArrayRef<uint8_t>> ExpectedContext =
223       File.getRawData(Stream.ThreadContext);
224   ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());
225   EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,
226                                0xab, 0xad, 0xca, 0xfe}),
227             *ExpectedContext);
228 }
229 
230 // Test that we can parse an ExceptionStream where the stated number of
231 // parameters is greater than the actual size of the ExceptionInformation
232 // array.
TEST(MinidumpYAML,ExceptionStream_TooManyParameters)233 TEST(MinidumpYAML, ExceptionStream_TooManyParameters) {
234   SmallString<0> Storage;
235   auto ExpectedFile = toBinary(Storage, R"(
236 --- !minidump
237 Streams:
238   - Type:            Exception
239     Thread ID:  0x8
240     Exception Record:
241       Exception Code: 0
242       Number of Parameters: 16
243       Parameter 0: 0x0
244       Parameter 1: 0xff
245       Parameter 2: 0xee
246       Parameter 3: 0xdd
247       Parameter 4: 0xcc
248       Parameter 5: 0xbb
249       Parameter 6: 0xaa
250       Parameter 7: 0x99
251       Parameter 8: 0x88
252       Parameter 9: 0x77
253       Parameter 10: 0x66
254       Parameter 11: 0x55
255       Parameter 12: 0x44
256       Parameter 13: 0x33
257       Parameter 14: 0x22
258     Thread Context:  3DeadBeefDefacedABadCafe)");
259   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
260   object::MinidumpFile &File = **ExpectedFile;
261 
262   ASSERT_EQ(1u, File.streams().size());
263 
264   Expected<const minidump::ExceptionStream &> ExpectedStream =
265       File.getExceptionStream();
266 
267   ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());
268 
269   const minidump::ExceptionStream &Stream = *ExpectedStream;
270   EXPECT_EQ(0x8u, Stream.ThreadId);
271   const minidump::Exception &Exception = Stream.ExceptionRecord;
272   EXPECT_EQ(0x0u, Exception.ExceptionCode);
273   EXPECT_EQ(0x0u, Exception.ExceptionFlags);
274   EXPECT_EQ(0x00u, Exception.ExceptionRecord);
275   EXPECT_EQ(0x0u, Exception.ExceptionAddress);
276   EXPECT_EQ(16u, Exception.NumberParameters);
277   EXPECT_EQ(0x0u, Exception.ExceptionInformation[0]);
278   for (int Index = 1; Index < 15; ++Index) {
279     EXPECT_EQ(0x110u - Index * 0x11, Exception.ExceptionInformation[Index]);
280   }
281 
282   Expected<ArrayRef<uint8_t>> ExpectedContext =
283       File.getRawData(Stream.ThreadContext);
284   ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());
285   EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,
286                                0xab, 0xad, 0xca, 0xfe}),
287             *ExpectedContext);
288 }
289 
290 // Test that we can parse an ExceptionStream where the number of
291 // ExceptionInformation parameters provided is greater than the
292 // specified Number of Parameters.
TEST(MinidumpYAML,ExceptionStream_ExtraParameter)293 TEST(MinidumpYAML, ExceptionStream_ExtraParameter) {
294   SmallString<0> Storage;
295   auto ExpectedFile = toBinary(Storage, R"(
296 --- !minidump
297 Streams:
298   - Type:            Exception
299     Thread ID:  0x7
300     Exception Record:
301       Exception Code:  0x23
302       Exception Flags: 0x5
303       Exception Record: 0x0102030405060708
304       Exception Address: 0x0a0b0c0d0e0f1011
305       Number of Parameters: 2
306       Parameter 0: 0x99
307       Parameter 1: 0x23
308       Parameter 2: 0x42
309     Thread Context:  3DeadBeefDefacedABadCafe)");
310   ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());
311   object::MinidumpFile &File = **ExpectedFile;
312 
313   ASSERT_EQ(1u, File.streams().size());
314 
315   Expected<const minidump::ExceptionStream &> ExpectedStream =
316       File.getExceptionStream();
317 
318   ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());
319 
320   const minidump::ExceptionStream &Stream = *ExpectedStream;
321   EXPECT_EQ(0x7u, Stream.ThreadId);
322   const minidump::Exception &Exception = Stream.ExceptionRecord;
323   EXPECT_EQ(0x23u, Exception.ExceptionCode);
324   EXPECT_EQ(0x5u, Exception.ExceptionFlags);
325   EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);
326   EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);
327   EXPECT_EQ(2u, Exception.NumberParameters);
328   EXPECT_EQ(0x99u, Exception.ExceptionInformation[0]);
329   EXPECT_EQ(0x23u, Exception.ExceptionInformation[1]);
330   EXPECT_EQ(0x42u, Exception.ExceptionInformation[2]);
331 
332   Expected<ArrayRef<uint8_t>> ExpectedContext =
333       File.getRawData(Stream.ThreadContext);
334   ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());
335   EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,
336                                0xab, 0xad, 0xca, 0xfe}),
337             *ExpectedContext);
338 }
339