1 //===- MinidumpTest.cpp - Tests for Minidump.cpp --------------------------===//
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/Support/MemoryBuffer.h"
11 #include "llvm/Testing/Support/Error.h"
12 #include "gtest/gtest.h"
15 using namespace llvm::object
;
16 using namespace minidump
;
18 static Expected
<std::unique_ptr
<MinidumpFile
>> create(ArrayRef
<uint8_t> Data
) {
19 return MinidumpFile::create(
20 MemoryBufferRef(toStringRef(Data
), "Test buffer"));
23 TEST(MinidumpFile
, BasicInterface
) {
24 std::vector
<uint8_t> Data
{ // Header
25 'M', 'D', 'M', 'P', // Signature
26 0x93, 0xa7, 0, 0, // Version
27 1, 0, 0, 0, // NumberOfStreams,
28 0x20, 0, 0, 0, // StreamDirectoryRVA
29 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
30 8, 9, 0, 1, 2, 3, 4, 5, // Flags
32 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,
35 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
36 // A very simple minidump file which contains just a single stream.
37 auto ExpectedFile
= create(Data
);
38 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
39 const MinidumpFile
&File
= **ExpectedFile
;
40 const Header
&H
= File
.header();
41 EXPECT_EQ(Header::MagicSignature
, H
.Signature
);
42 EXPECT_EQ(Header::MagicVersion
, H
.Version
);
43 EXPECT_EQ(1u, H
.NumberOfStreams
);
44 EXPECT_EQ(0x20u
, H
.StreamDirectoryRVA
);
45 EXPECT_EQ(0x03020100u
, H
.Checksum
);
46 EXPECT_EQ(0x07060504u
, H
.TimeDateStamp
);
47 EXPECT_EQ(uint64_t(0x0504030201000908), H
.Flags
);
49 ASSERT_EQ(1u, File
.streams().size());
50 const Directory
&Stream0
= File
.streams()[0];
51 EXPECT_EQ(StreamType::LinuxCPUInfo
, Stream0
.Type
);
52 EXPECT_EQ(7u, Stream0
.Location
.DataSize
);
53 EXPECT_EQ(0x2cu
, Stream0
.Location
.RVA
);
55 EXPECT_EQ("CPUINFO", toStringRef(File
.getRawStream(Stream0
)));
57 toStringRef(*File
.getRawStream(StreamType::LinuxCPUInfo
)));
59 EXPECT_THAT_EXPECTED(File
.getSystemInfo(), Failed
<BinaryError
>());
62 // Use the input from the previous test, but corrupt it in various ways
63 TEST(MinidumpFile
, create_ErrorCases
) {
64 std::vector
<uint8_t> FileTooShort
{'M', 'D', 'M', 'P'};
65 EXPECT_THAT_EXPECTED(create(FileTooShort
), Failed
<BinaryError
>());
67 std::vector
<uint8_t> WrongSignature
{
69 '!', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
70 1, 0, 0, 0, // NumberOfStreams,
71 0x20, 0, 0, 0, // StreamDirectoryRVA
72 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
73 8, 9, 0, 1, 2, 3, 4, 5, // Flags
75 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,
78 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
79 EXPECT_THAT_EXPECTED(create(WrongSignature
), Failed
<BinaryError
>());
81 std::vector
<uint8_t> WrongVersion
{
83 'M', 'D', 'M', 'P', 0x39, 0xa7, 0, 0, // Signature, Version
84 1, 0, 0, 0, // NumberOfStreams,
85 0x20, 0, 0, 0, // StreamDirectoryRVA
86 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
87 8, 9, 0, 1, 2, 3, 4, 5, // Flags
89 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,
92 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
93 EXPECT_THAT_EXPECTED(create(WrongVersion
), Failed
<BinaryError
>());
95 std::vector
<uint8_t> DirectoryAfterEOF
{
97 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
98 1, 0, 0, 0, // NumberOfStreams,
99 0x20, 1, 0, 0, // StreamDirectoryRVA
100 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
101 8, 9, 0, 1, 2, 3, 4, 5, // Flags
103 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,
104 0x2c, 0, 0, 0, // RVA
106 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
107 EXPECT_THAT_EXPECTED(create(DirectoryAfterEOF
), Failed
<BinaryError
>());
109 std::vector
<uint8_t> TruncatedDirectory
{
111 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
112 1, 1, 0, 0, // NumberOfStreams,
113 0x20, 0, 0, 0, // StreamDirectoryRVA
114 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
115 8, 9, 0, 1, 2, 3, 4, 5, // Flags
117 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,
118 0x2c, 0, 0, 0, // RVA
120 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
121 EXPECT_THAT_EXPECTED(create(TruncatedDirectory
), Failed
<BinaryError
>());
123 std::vector
<uint8_t> Stream0AfterEOF
{
125 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
126 1, 0, 0, 0, // NumberOfStreams,
127 0x20, 0, 0, 0, // StreamDirectoryRVA
128 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
129 8, 9, 0, 1, 2, 3, 4, 5, // Flags
131 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,
132 0x2c, 1, 0, 0, // RVA
134 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
135 EXPECT_THAT_EXPECTED(create(Stream0AfterEOF
), Failed
<BinaryError
>());
137 std::vector
<uint8_t> Stream0Truncated
{
139 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
140 1, 0, 0, 0, // NumberOfStreams,
141 0x20, 0, 0, 0, // StreamDirectoryRVA
142 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
143 8, 9, 0, 1, 2, 3, 4, 5, // Flags
145 3, 0, 0x67, 0x47, 8, 0, 0, 0, // Type, DataSize,
146 0x2c, 0, 0, 0, // RVA
148 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
149 EXPECT_THAT_EXPECTED(create(Stream0Truncated
), Failed
<BinaryError
>());
151 std::vector
<uint8_t> DuplicateStream
{
153 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
154 2, 0, 0, 0, // NumberOfStreams,
155 0x20, 0, 0, 0, // StreamDirectoryRVA
156 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
157 8, 9, 0, 1, 2, 3, 4, 5, // Flags
159 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,
160 0x40, 0, 0, 0, // RVA
162 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,
163 0x40, 0, 0, 0, // RVA
165 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
166 EXPECT_THAT_EXPECTED(create(DuplicateStream
), Failed
<BinaryError
>());
168 std::vector
<uint8_t> DenseMapInfoConflict
{
170 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
171 1, 0, 0, 0, // NumberOfStreams,
172 0x20, 0, 0, 0, // StreamDirectoryRVA
173 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
174 8, 9, 0, 1, 2, 3, 4, 5, // Flags
176 0xff, 0xff, 0xff, 0xff, 7, 0, 0, 0, // Type, DataSize,
177 0x2c, 0, 0, 0, // RVA
179 'C', 'P', 'U', 'I', 'N', 'F', 'O'};
180 EXPECT_THAT_EXPECTED(create(DenseMapInfoConflict
), Failed
<BinaryError
>());
183 TEST(MinidumpFile
, IngoresDummyStreams
) {
184 std::vector
<uint8_t> TwoDummyStreams
{
186 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
187 2, 0, 0, 0, // NumberOfStreams,
188 0x20, 0, 0, 0, // StreamDirectoryRVA
189 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
190 8, 9, 0, 1, 2, 3, 4, 5, // Flags
192 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,
193 0x20, 0, 0, 0, // RVA
194 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,
195 0x20, 0, 0, 0, // RVA
197 auto ExpectedFile
= create(TwoDummyStreams
);
198 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
199 const MinidumpFile
&File
= **ExpectedFile
;
200 ASSERT_EQ(2u, File
.streams().size());
201 EXPECT_EQ(StreamType::Unused
, File
.streams()[0].Type
);
202 EXPECT_EQ(StreamType::Unused
, File
.streams()[1].Type
);
203 EXPECT_EQ(None
, File
.getRawStream(StreamType::Unused
));
206 TEST(MinidumpFile
, getSystemInfo
) {
207 std::vector
<uint8_t> Data
{
209 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
210 1, 0, 0, 0, // NumberOfStreams,
211 0x20, 0, 0, 0, // StreamDirectoryRVA
212 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
213 8, 9, 0, 1, 2, 3, 4, 5, // Flags
215 7, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize,
216 0x2c, 0, 0, 0, // RVA
218 0, 0, 1, 2, // ProcessorArch, ProcessorLevel
219 3, 4, 5, 6, // ProcessorRevision, NumberOfProcessors, ProductType
220 7, 8, 9, 0, 1, 2, 3, 4, // MajorVersion, MinorVersion
221 5, 6, 7, 8, 2, 0, 0, 0, // BuildNumber, PlatformId
222 1, 2, 3, 4, 5, 6, 7, 8, // CSDVersionRVA, SuiteMask, Reserved
223 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', // VendorID
224 1, 2, 3, 4, 5, 6, 7, 8, // VersionInfo, FeatureInfo
225 9, 0, 1, 2, // AMDExtendedFeatures
227 auto ExpectedFile
= create(Data
);
228 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
229 const MinidumpFile
&File
= **ExpectedFile
;
231 auto ExpectedInfo
= File
.getSystemInfo();
232 ASSERT_THAT_EXPECTED(ExpectedInfo
, Succeeded());
233 const SystemInfo
&Info
= *ExpectedInfo
;
234 EXPECT_EQ(ProcessorArchitecture::X86
, Info
.ProcessorArch
);
235 EXPECT_EQ(0x0201, Info
.ProcessorLevel
);
236 EXPECT_EQ(0x0403, Info
.ProcessorRevision
);
237 EXPECT_EQ(5, Info
.NumberOfProcessors
);
238 EXPECT_EQ(6, Info
.ProductType
);
239 EXPECT_EQ(0x00090807u
, Info
.MajorVersion
);
240 EXPECT_EQ(0x04030201u
, Info
.MinorVersion
);
241 EXPECT_EQ(0x08070605u
, Info
.BuildNumber
);
242 EXPECT_EQ(OSPlatform::Win32NT
, Info
.PlatformId
);
243 EXPECT_EQ(0x04030201u
, Info
.CSDVersionRVA
);
244 EXPECT_EQ(0x0605u
, Info
.SuiteMask
);
245 EXPECT_EQ(0x0807u
, Info
.Reserved
);
246 EXPECT_EQ("LLVMLLVMLLVM", llvm::StringRef(Info
.CPU
.X86
.VendorID
,
247 sizeof(Info
.CPU
.X86
.VendorID
)));
248 EXPECT_EQ(0x04030201u
, Info
.CPU
.X86
.VersionInfo
);
249 EXPECT_EQ(0x08070605u
, Info
.CPU
.X86
.FeatureInfo
);
250 EXPECT_EQ(0x02010009u
, Info
.CPU
.X86
.AMDExtendedFeatures
);
253 TEST(MinidumpFile
, getString
) {
254 std::vector
<uint8_t> ManyStrings
{
256 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
257 2, 0, 0, 0, // NumberOfStreams,
258 0x20, 0, 0, 0, // StreamDirectoryRVA
259 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
260 8, 9, 0, 1, 2, 3, 4, 5, // Flags
262 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,
263 0x20, 0, 0, 0, // RVA
264 1, 0, 0, 0, 0, 0, // String1 - odd length
265 0, 0, 1, 0, 0, 0, // String2 - too long
266 2, 0, 0, 0, 0, 0xd8, // String3 - invalid utf16
267 0, 0, 0, 0, 0, 0, // String4 - ""
268 2, 0, 0, 0, 'a', 0, // String5 - "a"
269 0, // Mis-align next string
270 2, 0, 0, 0, 'a', 0, // String6 - "a"
273 auto ExpectedFile
= create(ManyStrings
);
274 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
275 const MinidumpFile
&File
= **ExpectedFile
;
276 EXPECT_THAT_EXPECTED(File
.getString(44), Failed
<BinaryError
>());
277 EXPECT_THAT_EXPECTED(File
.getString(50), Failed
<BinaryError
>());
278 EXPECT_THAT_EXPECTED(File
.getString(56), Failed
<BinaryError
>());
279 EXPECT_THAT_EXPECTED(File
.getString(62), HasValue(""));
280 EXPECT_THAT_EXPECTED(File
.getString(68), HasValue("a"));
281 EXPECT_THAT_EXPECTED(File
.getString(75), HasValue("a"));
283 // Check the case when the size field does not fit into the remaining data.
284 EXPECT_THAT_EXPECTED(File
.getString(ManyStrings
.size() - 2),
285 Failed
<BinaryError
>());
288 TEST(MinidumpFile
, getModuleList
) {
289 std::vector
<uint8_t> OneModule
{
291 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
292 1, 0, 0, 0, // NumberOfStreams,
293 32, 0, 0, 0, // StreamDirectoryRVA
294 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
295 0, 0, 0, 0, 0, 0, 0, 0, // Flags
297 4, 0, 0, 0, 112, 0, 0, 0, // Type, DataSize,
300 1, 0, 0, 0, // NumberOfModules
301 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage
302 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum
303 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA
304 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion
305 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion
306 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion
307 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags
308 0, 0, 0, 0, // FileOS
309 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType
310 0, 0, 0, 0, 0, 0, 0, 0, // FileDate
311 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord
312 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord
313 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0
314 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1
316 // Same as before, but with a padded module list.
317 std::vector
<uint8_t> PaddedModule
{
319 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
320 1, 0, 0, 0, // NumberOfStreams,
321 32, 0, 0, 0, // StreamDirectoryRVA
322 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
323 0, 0, 0, 0, 0, 0, 0, 0, // Flags
325 4, 0, 0, 0, 116, 0, 0, 0, // Type, DataSize,
328 1, 0, 0, 0, // NumberOfModules
329 0, 0, 0, 0, // Padding
330 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage
331 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum
332 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA
333 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion
334 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion
335 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion
336 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags
337 0, 0, 0, 0, // FileOS
338 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType
339 0, 0, 0, 0, 0, 0, 0, 0, // FileDate
340 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord
341 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord
342 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0
343 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1
346 for (ArrayRef
<uint8_t> Data
: {OneModule
, PaddedModule
}) {
347 auto ExpectedFile
= create(Data
);
348 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
349 const MinidumpFile
&File
= **ExpectedFile
;
350 Expected
<ArrayRef
<Module
>> ExpectedModule
= File
.getModuleList();
351 ASSERT_THAT_EXPECTED(ExpectedModule
, Succeeded());
352 ASSERT_EQ(1u, ExpectedModule
->size());
353 const Module
&M
= ExpectedModule
.get()[0];
354 EXPECT_EQ(0x0807060504030201u
, M
.BaseOfImage
);
355 EXPECT_EQ(0x02010009u
, M
.SizeOfImage
);
356 EXPECT_EQ(0x06050403u
, M
.Checksum
);
357 EXPECT_EQ(0x00090807u
, M
.TimeDateStamp
);
358 EXPECT_EQ(0x04030201u
, M
.ModuleNameRVA
);
359 EXPECT_EQ(0x04030201u
, M
.CvRecord
.DataSize
);
360 EXPECT_EQ(0x08070605u
, M
.CvRecord
.RVA
);
361 EXPECT_EQ(0x02010009u
, M
.MiscRecord
.DataSize
);
362 EXPECT_EQ(0x06050403u
, M
.MiscRecord
.RVA
);
363 EXPECT_EQ(0x0403020100090807u
, M
.Reserved0
);
364 EXPECT_EQ(0x0201000908070605u
, M
.Reserved1
);
367 std::vector
<uint8_t> StreamTooShort
{
369 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
370 1, 0, 0, 0, // NumberOfStreams,
371 32, 0, 0, 0, // StreamDirectoryRVA
372 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
373 0, 0, 0, 0, 0, 0, 0, 0, // Flags
375 4, 0, 0, 0, 111, 0, 0, 0, // Type, DataSize,
378 1, 0, 0, 0, // NumberOfModules
379 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage
380 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum
381 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA
382 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion
383 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion
384 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion
385 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags
386 0, 0, 0, 0, // FileOS
387 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType
388 0, 0, 0, 0, 0, 0, 0, 0, // FileDate
389 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord
390 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord
391 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0
392 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1
394 auto ExpectedFile
= create(StreamTooShort
);
395 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
396 const MinidumpFile
&File
= **ExpectedFile
;
397 EXPECT_THAT_EXPECTED(File
.getModuleList(), Failed
<BinaryError
>());
400 TEST(MinidumpFile
, getThreadList
) {
401 std::vector
<uint8_t> OneThread
{
403 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
404 1, 0, 0, 0, // NumberOfStreams,
405 32, 0, 0, 0, // StreamDirectoryRVA
406 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
407 0, 0, 0, 0, 0, 0, 0, 0, // Flags
409 3, 0, 0, 0, 52, 0, 0, 0, // Type, DataSize,
412 1, 0, 0, 0, // NumberOfThreads
413 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount
414 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority
415 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock
417 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
418 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
420 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA
422 // Same as before, but with a padded thread list.
423 std::vector
<uint8_t> PaddedThread
{
425 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
426 1, 0, 0, 0, // NumberOfStreams,
427 32, 0, 0, 0, // StreamDirectoryRVA
428 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
429 0, 0, 0, 0, 0, 0, 0, 0, // Flags
431 3, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize,
434 1, 0, 0, 0, // NumberOfThreads
435 0, 0, 0, 0, // Padding
436 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount
437 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority
438 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock
440 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
441 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
443 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA
446 for (ArrayRef
<uint8_t> Data
: {OneThread
, PaddedThread
}) {
447 auto ExpectedFile
= create(Data
);
448 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
449 const MinidumpFile
&File
= **ExpectedFile
;
450 Expected
<ArrayRef
<Thread
>> ExpectedThread
= File
.getThreadList();
451 ASSERT_THAT_EXPECTED(ExpectedThread
, Succeeded());
452 ASSERT_EQ(1u, ExpectedThread
->size());
453 const Thread
&T
= ExpectedThread
.get()[0];
454 EXPECT_EQ(0x04030201u
, T
.ThreadId
);
455 EXPECT_EQ(0x08070605u
, T
.SuspendCount
);
456 EXPECT_EQ(0x02010009u
, T
.PriorityClass
);
457 EXPECT_EQ(0x06050403u
, T
.Priority
);
458 EXPECT_EQ(0x0403020100090807u
, T
.EnvironmentBlock
);
459 EXPECT_EQ(0x0201000908070605u
, T
.Stack
.StartOfMemoryRange
);
460 EXPECT_EQ(0x06050403u
, T
.Stack
.Memory
.DataSize
);
461 EXPECT_EQ(0x00090807u
, T
.Stack
.Memory
.RVA
);
462 EXPECT_EQ(0x04030201u
, T
.Context
.DataSize
);
463 EXPECT_EQ(0x08070605u
, T
.Context
.RVA
);
467 TEST(MinidumpFile
, getMemoryList
) {
468 std::vector
<uint8_t> OneRange
{
470 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
471 1, 0, 0, 0, // NumberOfStreams,
472 32, 0, 0, 0, // StreamDirectoryRVA
473 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
474 0, 0, 0, 0, 0, 0, 0, 0, // Flags
476 5, 0, 0, 0, 20, 0, 0, 0, // Type, DataSize,
479 1, 0, 0, 0, // NumberOfMemoryRanges
480 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
481 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
483 // Same as before, but with a padded memory list.
484 std::vector
<uint8_t> PaddedRange
{
486 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
487 1, 0, 0, 0, // NumberOfStreams,
488 32, 0, 0, 0, // StreamDirectoryRVA
489 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
490 0, 0, 0, 0, 0, 0, 0, 0, // Flags
492 5, 0, 0, 0, 24, 0, 0, 0, // Type, DataSize,
495 1, 0, 0, 0, // NumberOfMemoryRanges
496 0, 0, 0, 0, // Padding
497 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange
498 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA
501 for (ArrayRef
<uint8_t> Data
: {OneRange
, PaddedRange
}) {
502 auto ExpectedFile
= create(Data
);
503 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
504 const MinidumpFile
&File
= **ExpectedFile
;
505 Expected
<ArrayRef
<MemoryDescriptor
>> ExpectedRanges
= File
.getMemoryList();
506 ASSERT_THAT_EXPECTED(ExpectedRanges
, Succeeded());
507 ASSERT_EQ(1u, ExpectedRanges
->size());
508 const MemoryDescriptor
&MD
= ExpectedRanges
.get()[0];
509 EXPECT_EQ(0x0201000908070605u
, MD
.StartOfMemoryRange
);
510 EXPECT_EQ(0x06050403u
, MD
.Memory
.DataSize
);
511 EXPECT_EQ(0x00090807u
, MD
.Memory
.RVA
);
515 TEST(MinidumpFile
, getMemoryInfoList
) {
516 std::vector
<uint8_t> OneEntry
{
518 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
519 1, 0, 0, 0, // NumberOfStreams,
520 32, 0, 0, 0, // StreamDirectoryRVA
521 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
522 0, 0, 0, 0, 0, 0, 0, 0, // Flags
524 16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize,
526 // MemoryInfoListHeader
527 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
528 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries
530 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress
531 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase
532 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0
533 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize
534 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect
535 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1
538 // Same as before, but the list header is larger.
539 std::vector
<uint8_t> BiggerHeader
{
541 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
542 1, 0, 0, 0, // NumberOfStreams,
543 32, 0, 0, 0, // StreamDirectoryRVA
544 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
545 0, 0, 0, 0, 0, 0, 0, 0, // Flags
547 16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize,
549 // MemoryInfoListHeader
550 20, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
551 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries
554 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress
555 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase
556 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0
557 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize
558 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect
559 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1
562 // Same as before, but the entry is larger.
563 std::vector
<uint8_t> BiggerEntry
{
565 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
566 1, 0, 0, 0, // NumberOfStreams,
567 32, 0, 0, 0, // StreamDirectoryRVA
568 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
569 0, 0, 0, 0, 0, 0, 0, 0, // Flags
571 16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize,
573 // MemoryInfoListHeader
574 16, 0, 0, 0, 52, 0, 0, 0, // SizeOfHeader, SizeOfEntry
575 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries
577 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress
578 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase
579 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0
580 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize
581 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect
582 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1
586 for (ArrayRef
<uint8_t> Data
: {OneEntry
, BiggerHeader
, BiggerEntry
}) {
587 auto ExpectedFile
= create(Data
);
588 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
589 const MinidumpFile
&File
= **ExpectedFile
;
590 auto ExpectedInfo
= File
.getMemoryInfoList();
591 ASSERT_THAT_EXPECTED(ExpectedInfo
, Succeeded());
592 ASSERT_EQ(1u, std::distance(ExpectedInfo
->begin(), ExpectedInfo
->end()));
593 const MemoryInfo
&Info
= *ExpectedInfo
.get().begin();
594 EXPECT_EQ(0x0706050403020100u
, Info
.BaseAddress
);
595 EXPECT_EQ(0x0504030201000908u
, Info
.AllocationBase
);
596 EXPECT_EQ(MemoryProtection::Execute
, Info
.AllocationProtect
);
597 EXPECT_EQ(0x09080706u
, Info
.Reserved0
);
598 EXPECT_EQ(0x0706050403020100u
, Info
.RegionSize
);
599 EXPECT_EQ(MemoryState::Commit
, Info
.State
);
600 EXPECT_EQ(MemoryProtection::ExecuteRead
, Info
.Protect
);
601 EXPECT_EQ(MemoryType::Private
, Info
.Type
);
602 EXPECT_EQ(0x01000908u
, Info
.Reserved1
);
605 // Header does not fit into the stream.
606 std::vector
<uint8_t> HeaderTooBig
{
608 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
609 1, 0, 0, 0, // NumberOfStreams,
610 32, 0, 0, 0, // StreamDirectoryRVA
611 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
612 0, 0, 0, 0, 0, 0, 0, 0, // Flags
614 16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize,
616 // MemoryInfoListHeader
617 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
618 1, 0, 0, 0, 0, 0, 0, // ???
620 Expected
<std::unique_ptr
<MinidumpFile
>> File
= create(HeaderTooBig
);
621 ASSERT_THAT_EXPECTED(File
, Succeeded());
622 EXPECT_THAT_EXPECTED(File
.get()->getMemoryInfoList(), Failed
<BinaryError
>());
624 // Header fits into the stream, but it is too small to contain the required
626 std::vector
<uint8_t> HeaderTooSmall
{
628 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
629 1, 0, 0, 0, // NumberOfStreams,
630 32, 0, 0, 0, // StreamDirectoryRVA
631 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
632 0, 0, 0, 0, 0, 0, 0, 0, // Flags
634 16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize,
636 // MemoryInfoListHeader
637 15, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
638 1, 0, 0, 0, 0, 0, 0, // ???
640 File
= create(HeaderTooSmall
);
641 ASSERT_THAT_EXPECTED(File
, Succeeded());
642 EXPECT_THAT_EXPECTED(File
.get()->getMemoryInfoList(), Failed
<BinaryError
>());
644 std::vector
<uint8_t> EntryTooBig
{
646 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
647 1, 0, 0, 0, // NumberOfStreams,
648 32, 0, 0, 0, // StreamDirectoryRVA
649 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
650 0, 0, 0, 0, 0, 0, 0, 0, // Flags
652 16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize,
654 // MemoryInfoListHeader
655 16, 0, 0, 0, 49, 0, 0, 0, // SizeOfHeader, SizeOfEntry
656 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries
658 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress
659 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase
660 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0
661 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize
662 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect
663 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1
665 File
= create(EntryTooBig
);
666 ASSERT_THAT_EXPECTED(File
, Succeeded());
667 EXPECT_THAT_EXPECTED(File
.get()->getMemoryInfoList(), Failed
<BinaryError
>());
669 std::vector
<uint8_t> ThreeEntries
{
671 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
672 1, 0, 0, 0, // NumberOfStreams,
673 32, 0, 0, 0, // StreamDirectoryRVA
674 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
675 0, 0, 0, 0, 0, 0, 0, 0, // Flags
677 16, 0, 0, 0, 160, 0, 0, 0, // Type, DataSize,
679 // MemoryInfoListHeader
680 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry
681 3, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries
683 0, 1, 2, 3, 0, 0, 0, 0, // BaseAddress
684 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase
685 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0
686 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize
687 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect
688 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1
689 0, 0, 4, 5, 6, 7, 0, 0, // BaseAddress
690 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase
691 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0
692 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize
693 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect
694 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1
695 0, 0, 0, 8, 9, 0, 1, 0, // BaseAddress
696 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase
697 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0
698 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize
699 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect
700 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1
702 File
= create(ThreeEntries
);
703 ASSERT_THAT_EXPECTED(File
, Succeeded());
704 auto ExpectedInfo
= File
.get()->getMemoryInfoList();
705 ASSERT_THAT_EXPECTED(ExpectedInfo
, Succeeded());
706 EXPECT_THAT(to_vector
<3>(map_range(*ExpectedInfo
,
707 [](const MemoryInfo
&Info
) -> uint64_t {
708 return Info
.BaseAddress
;
710 testing::ElementsAre(0x0000000003020100u
, 0x0000070605040000u
,
711 0x0001000908000000u
));
714 TEST(MinidumpFile
, getExceptionStream
) {
715 std::vector
<uint8_t> Data
{
717 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version
718 1, 0, 0, 0, // NumberOfStreams,
719 0x20, 0, 0, 0, // StreamDirectoryRVA
720 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp
721 8, 9, 0, 1, 2, 3, 4, 5, // Flags
723 6, 0, 0, 0, 168, 0, 0, 0, // Type, DataSize,
724 0x2c, 0, 0, 0, // RVA
726 1, 2, 3, 4, // Thread ID
727 0, 0, 0, 0, // Padding
729 2, 3, 4, 2, 7, 8, 8, 9, // Code, Flags
730 3, 4, 5, 6, 7, 8, 9, 10, // Inner exception record address
731 8, 7, 6, 5, 4, 3, 2, 1, // Exception address
732 4, 0, 0, 0, 0, 0, 0, 0, // Parameter count, padding
733 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // Parameter 0
734 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // Parameter 1
735 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Parameter 2
736 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // Parameter 3
737 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // Parameter 4
738 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // Parameter 5
739 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // Parameter 6
740 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // Parameter 7
741 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // Parameter 8
742 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, // Parameter 9
743 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, // Parameter 10
744 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, // Parameter 11
745 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, // Parameter 12
746 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, // Parameter 13
747 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, // Parameter 14
749 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, // DataSize, RVA
751 auto ExpectedFile
= create(Data
);
752 ASSERT_THAT_EXPECTED(ExpectedFile
, Succeeded());
753 const MinidumpFile
&File
= **ExpectedFile
;
754 Expected
<const minidump::ExceptionStream
&> ExpectedStream
=
755 File
.getExceptionStream();
756 ASSERT_THAT_EXPECTED(ExpectedStream
, Succeeded());
757 EXPECT_EQ(0x04030201u
, ExpectedStream
->ThreadId
);
758 const minidump::Exception
&Exception
= ExpectedStream
->ExceptionRecord
;
759 EXPECT_EQ(0x02040302u
, Exception
.ExceptionCode
);
760 EXPECT_EQ(0x09080807u
, Exception
.ExceptionFlags
);
761 EXPECT_EQ(0x0a09080706050403u
, Exception
.ExceptionRecord
);
762 EXPECT_EQ(0x0102030405060708u
, Exception
.ExceptionAddress
);
763 EXPECT_EQ(4u, Exception
.NumberParameters
);
764 for (uint64_t index
= 0; index
< Exception
.MaxParameters
; ++index
) {
765 EXPECT_EQ(0x1716151413121110u
+ index
* 0x1010101010101010u
,
766 Exception
.ExceptionInformation
[index
]);
768 EXPECT_EQ(0x84838281, ExpectedStream
->ThreadContext
.DataSize
);
769 EXPECT_EQ(0x88878685, ExpectedStream
->ThreadContext
.RVA
);