776 lines · cpp
1//===- MinidumpTest.cpp - Tests for Minidump.cpp --------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Object/Minidump.h"10#include "llvm/Support/MemoryBuffer.h"11#include "llvm/Testing/Support/Error.h"12#include "gtest/gtest.h"13 14using namespace llvm;15using namespace llvm::object;16using namespace minidump;17 18static Expected<std::unique_ptr<MinidumpFile>> create(ArrayRef<uint8_t> Data) {19 return MinidumpFile::create(20 MemoryBufferRef(toStringRef(Data), "Test buffer"));21}22 23TEST(MinidumpFile, BasicInterface) {24 std::vector<uint8_t> Data{ // Header25 'M', 'D', 'M', 'P', // Signature26 0x93, 0xa7, 0, 0, // Version27 1, 0, 0, 0, // NumberOfStreams,28 0x20, 0, 0, 0, // StreamDirectoryRVA29 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp30 8, 9, 0, 1, 2, 3, 4, 5, // Flags31 // Stream Directory32 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,33 0x2c, 0, 0, 0, // RVA34 // Stream35 '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);48 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);54 55 EXPECT_EQ("CPUINFO", toStringRef(File.getRawStream(Stream0)));56 EXPECT_EQ("CPUINFO",57 toStringRef(*File.getRawStream(StreamType::LinuxCPUInfo)));58 59 EXPECT_THAT_EXPECTED(File.getSystemInfo(), Failed<BinaryError>());60}61 62// Use the input from the previous test, but corrupt it in various ways63TEST(MinidumpFile, create_ErrorCases) {64 std::vector<uint8_t> FileTooShort{'M', 'D', 'M', 'P'};65 EXPECT_THAT_EXPECTED(create(FileTooShort), Failed<BinaryError>());66 67 std::vector<uint8_t> WrongSignature{68 // Header69 '!', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version70 1, 0, 0, 0, // NumberOfStreams,71 0x20, 0, 0, 0, // StreamDirectoryRVA72 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp73 8, 9, 0, 1, 2, 3, 4, 5, // Flags74 // Stream Directory75 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,76 0x2c, 0, 0, 0, // RVA77 // Stream78 'C', 'P', 'U', 'I', 'N', 'F', 'O'};79 EXPECT_THAT_EXPECTED(create(WrongSignature), Failed<BinaryError>());80 81 std::vector<uint8_t> WrongVersion{82 // Header83 'M', 'D', 'M', 'P', 0x39, 0xa7, 0, 0, // Signature, Version84 1, 0, 0, 0, // NumberOfStreams,85 0x20, 0, 0, 0, // StreamDirectoryRVA86 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp87 8, 9, 0, 1, 2, 3, 4, 5, // Flags88 // Stream Directory89 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,90 0x2c, 0, 0, 0, // RVA91 // Stream92 'C', 'P', 'U', 'I', 'N', 'F', 'O'};93 EXPECT_THAT_EXPECTED(create(WrongVersion), Failed<BinaryError>());94 95 std::vector<uint8_t> DirectoryAfterEOF{96 // Header97 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version98 1, 0, 0, 0, // NumberOfStreams,99 0x20, 1, 0, 0, // StreamDirectoryRVA100 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp101 8, 9, 0, 1, 2, 3, 4, 5, // Flags102 // Stream Directory103 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,104 0x2c, 0, 0, 0, // RVA105 // Stream106 'C', 'P', 'U', 'I', 'N', 'F', 'O'};107 EXPECT_THAT_EXPECTED(create(DirectoryAfterEOF), Failed<BinaryError>());108 109 std::vector<uint8_t> TruncatedDirectory{110 // Header111 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version112 1, 1, 0, 0, // NumberOfStreams,113 0x20, 0, 0, 0, // StreamDirectoryRVA114 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp115 8, 9, 0, 1, 2, 3, 4, 5, // Flags116 // Stream Directory117 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,118 0x2c, 0, 0, 0, // RVA119 // Stream120 'C', 'P', 'U', 'I', 'N', 'F', 'O'};121 EXPECT_THAT_EXPECTED(create(TruncatedDirectory), Failed<BinaryError>());122 123 std::vector<uint8_t> Stream0AfterEOF{124 // Header125 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version126 1, 0, 0, 0, // NumberOfStreams,127 0x20, 0, 0, 0, // StreamDirectoryRVA128 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp129 8, 9, 0, 1, 2, 3, 4, 5, // Flags130 // Stream Directory131 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,132 0x2c, 1, 0, 0, // RVA133 // Stream134 'C', 'P', 'U', 'I', 'N', 'F', 'O'};135 EXPECT_THAT_EXPECTED(create(Stream0AfterEOF), Failed<BinaryError>());136 137 std::vector<uint8_t> Stream0Truncated{138 // Header139 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version140 1, 0, 0, 0, // NumberOfStreams,141 0x20, 0, 0, 0, // StreamDirectoryRVA142 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp143 8, 9, 0, 1, 2, 3, 4, 5, // Flags144 // Stream Directory145 3, 0, 0x67, 0x47, 8, 0, 0, 0, // Type, DataSize,146 0x2c, 0, 0, 0, // RVA147 // Stream148 'C', 'P', 'U', 'I', 'N', 'F', 'O'};149 EXPECT_THAT_EXPECTED(create(Stream0Truncated), Failed<BinaryError>());150 151 std::vector<uint8_t> DuplicateStream{152 // Header153 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version154 2, 0, 0, 0, // NumberOfStreams,155 0x20, 0, 0, 0, // StreamDirectoryRVA156 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp157 8, 9, 0, 1, 2, 3, 4, 5, // Flags158 // Stream Directory159 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,160 0x40, 0, 0, 0, // RVA161 // Stream162 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize,163 0x40, 0, 0, 0, // RVA164 // Stream165 'C', 'P', 'U', 'I', 'N', 'F', 'O'};166 EXPECT_THAT_EXPECTED(create(DuplicateStream), Failed<BinaryError>());167 168 std::vector<uint8_t> DenseMapInfoConflict{169 // Header170 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version171 1, 0, 0, 0, // NumberOfStreams,172 0x20, 0, 0, 0, // StreamDirectoryRVA173 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp174 8, 9, 0, 1, 2, 3, 4, 5, // Flags175 // Stream Directory176 0xff, 0xff, 0xff, 0xff, 7, 0, 0, 0, // Type, DataSize,177 0x2c, 0, 0, 0, // RVA178 // Stream179 'C', 'P', 'U', 'I', 'N', 'F', 'O'};180 EXPECT_THAT_EXPECTED(create(DenseMapInfoConflict), Failed<BinaryError>());181}182 183TEST(MinidumpFile, IngoresDummyStreams) {184 std::vector<uint8_t> TwoDummyStreams{185 // Header186 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version187 2, 0, 0, 0, // NumberOfStreams,188 0x20, 0, 0, 0, // StreamDirectoryRVA189 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp190 8, 9, 0, 1, 2, 3, 4, 5, // Flags191 // Stream Directory192 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,193 0x20, 0, 0, 0, // RVA194 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,195 0x20, 0, 0, 0, // RVA196 };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(std::nullopt, File.getRawStream(StreamType::Unused));204}205 206TEST(MinidumpFile, getSystemInfo) {207 std::vector<uint8_t> Data{208 // Header209 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version210 1, 0, 0, 0, // NumberOfStreams,211 0x20, 0, 0, 0, // StreamDirectoryRVA212 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp213 8, 9, 0, 1, 2, 3, 4, 5, // Flags214 // Stream Directory215 7, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize,216 0x2c, 0, 0, 0, // RVA217 // SystemInfo218 0, 0, 1, 2, // ProcessorArch, ProcessorLevel219 3, 4, 5, 6, // ProcessorRevision, NumberOfProcessors, ProductType220 7, 8, 9, 0, 1, 2, 3, 4, // MajorVersion, MinorVersion221 5, 6, 7, 8, 2, 0, 0, 0, // BuildNumber, PlatformId222 1, 2, 3, 4, 5, 6, 7, 8, // CSDVersionRVA, SuiteMask, Reserved223 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', // VendorID224 1, 2, 3, 4, 5, 6, 7, 8, // VersionInfo, FeatureInfo225 9, 0, 1, 2, // AMDExtendedFeatures226 };227 auto ExpectedFile = create(Data);228 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());229 const MinidumpFile &File = **ExpectedFile;230 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);251}252 253TEST(MinidumpFile, getString) {254 std::vector<uint8_t> ManyStrings{255 // Header256 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version257 2, 0, 0, 0, // NumberOfStreams,258 0x20, 0, 0, 0, // StreamDirectoryRVA259 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp260 8, 9, 0, 1, 2, 3, 4, 5, // Flags261 // Stream Directory262 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize,263 0x20, 0, 0, 0, // RVA264 1, 0, 0, 0, 0, 0, // String1 - odd length265 0, 0, 1, 0, 0, 0, // String2 - too long266 2, 0, 0, 0, 0, 0xd8, // String3 - invalid utf16267 0, 0, 0, 0, 0, 0, // String4 - ""268 2, 0, 0, 0, 'a', 0, // String5 - "a"269 0, // Mis-align next string270 2, 0, 0, 0, 'a', 0, // String6 - "a"271 272 };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"));282 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>());286}287 288TEST(MinidumpFile, getModuleList) {289 std::vector<uint8_t> OneModule{290 // Header291 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version292 1, 0, 0, 0, // NumberOfStreams,293 32, 0, 0, 0, // StreamDirectoryRVA294 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp295 0, 0, 0, 0, 0, 0, 0, 0, // Flags296 // Stream Directory297 4, 0, 0, 0, 112, 0, 0, 0, // Type, DataSize,298 44, 0, 0, 0, // RVA299 // ModuleList300 1, 0, 0, 0, // NumberOfModules301 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage302 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum303 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA304 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion305 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion306 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion307 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags308 0, 0, 0, 0, // FileOS309 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType310 0, 0, 0, 0, 0, 0, 0, 0, // FileDate311 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord312 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord313 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0314 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1315 };316 // Same as before, but with a padded module list.317 std::vector<uint8_t> PaddedModule{318 // Header319 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version320 1, 0, 0, 0, // NumberOfStreams,321 32, 0, 0, 0, // StreamDirectoryRVA322 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp323 0, 0, 0, 0, 0, 0, 0, 0, // Flags324 // Stream Directory325 4, 0, 0, 0, 116, 0, 0, 0, // Type, DataSize,326 44, 0, 0, 0, // RVA327 // ModuleList328 1, 0, 0, 0, // NumberOfModules329 0, 0, 0, 0, // Padding330 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage331 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum332 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA333 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion334 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion335 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion336 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags337 0, 0, 0, 0, // FileOS338 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType339 0, 0, 0, 0, 0, 0, 0, 0, // FileDate340 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord341 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord342 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0343 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1344 };345 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);365 }366 367 std::vector<uint8_t> StreamTooShort{368 // Header369 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version370 1, 0, 0, 0, // NumberOfStreams,371 32, 0, 0, 0, // StreamDirectoryRVA372 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp373 0, 0, 0, 0, 0, 0, 0, 0, // Flags374 // Stream Directory375 4, 0, 0, 0, 111, 0, 0, 0, // Type, DataSize,376 44, 0, 0, 0, // RVA377 // ModuleList378 1, 0, 0, 0, // NumberOfModules379 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage380 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum381 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA382 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion383 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion384 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion385 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags386 0, 0, 0, 0, // FileOS387 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType388 0, 0, 0, 0, 0, 0, 0, 0, // FileDate389 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord390 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord391 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0392 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1393 };394 auto ExpectedFile = create(StreamTooShort);395 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());396 const MinidumpFile &File = **ExpectedFile;397 EXPECT_THAT_EXPECTED(File.getModuleList(), Failed<BinaryError>());398}399 400TEST(MinidumpFile, getThreadList) {401 std::vector<uint8_t> OneThread{402 // Header403 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version404 1, 0, 0, 0, // NumberOfStreams,405 32, 0, 0, 0, // StreamDirectoryRVA406 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp407 0, 0, 0, 0, 0, 0, 0, 0, // Flags408 // Stream Directory409 3, 0, 0, 0, 52, 0, 0, 0, // Type, DataSize,410 44, 0, 0, 0, // RVA411 // ThreadList412 1, 0, 0, 0, // NumberOfThreads413 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount414 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority415 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock416 // Stack417 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange418 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA419 // Context420 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA421 };422 // Same as before, but with a padded thread list.423 std::vector<uint8_t> PaddedThread{424 // Header425 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version426 1, 0, 0, 0, // NumberOfStreams,427 32, 0, 0, 0, // StreamDirectoryRVA428 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp429 0, 0, 0, 0, 0, 0, 0, 0, // Flags430 // Stream Directory431 3, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize,432 44, 0, 0, 0, // RVA433 // ThreadList434 1, 0, 0, 0, // NumberOfThreads435 0, 0, 0, 0, // Padding436 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount437 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority438 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock439 // Stack440 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange441 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA442 // Context443 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA444 };445 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);464 }465}466 467TEST(MinidumpFile, getMemoryList) {468 std::vector<uint8_t> OneRange{469 // Header470 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version471 1, 0, 0, 0, // NumberOfStreams,472 32, 0, 0, 0, // StreamDirectoryRVA473 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp474 0, 0, 0, 0, 0, 0, 0, 0, // Flags475 // Stream Directory476 5, 0, 0, 0, 20, 0, 0, 0, // Type, DataSize,477 44, 0, 0, 0, // RVA478 // MemoryDescriptor479 1, 0, 0, 0, // NumberOfMemoryRanges480 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange481 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA482 };483 // Same as before, but with a padded memory list.484 std::vector<uint8_t> PaddedRange{485 // Header486 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version487 1, 0, 0, 0, // NumberOfStreams,488 32, 0, 0, 0, // StreamDirectoryRVA489 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp490 0, 0, 0, 0, 0, 0, 0, 0, // Flags491 // Stream Directory492 5, 0, 0, 0, 24, 0, 0, 0, // Type, DataSize,493 44, 0, 0, 0, // RVA494 // MemoryDescriptor495 1, 0, 0, 0, // NumberOfMemoryRanges496 0, 0, 0, 0, // Padding497 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange498 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA499 };500 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);512 }513}514 515TEST(MinidumpFile, getMemoryInfoList) {516 std::vector<uint8_t> OneEntry{517 // Header518 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version519 1, 0, 0, 0, // NumberOfStreams,520 32, 0, 0, 0, // StreamDirectoryRVA521 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp522 0, 0, 0, 0, 0, 0, 0, 0, // Flags523 // Stream Directory524 16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize,525 44, 0, 0, 0, // RVA526 // MemoryInfoListHeader527 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry528 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries529 // MemoryInfo530 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress531 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase532 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0533 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize534 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect535 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1536 };537 538 // Same as before, but the list header is larger.539 std::vector<uint8_t> BiggerHeader{540 // Header541 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version542 1, 0, 0, 0, // NumberOfStreams,543 32, 0, 0, 0, // StreamDirectoryRVA544 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp545 0, 0, 0, 0, 0, 0, 0, 0, // Flags546 // Stream Directory547 16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize,548 44, 0, 0, 0, // RVA549 // MemoryInfoListHeader550 20, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry551 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries552 0, 0, 0, 0, // ???553 // MemoryInfo554 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress555 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase556 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0557 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize558 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect559 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1560 };561 562 // Same as before, but the entry is larger.563 std::vector<uint8_t> BiggerEntry{564 // Header565 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version566 1, 0, 0, 0, // NumberOfStreams,567 32, 0, 0, 0, // StreamDirectoryRVA568 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp569 0, 0, 0, 0, 0, 0, 0, 0, // Flags570 // Stream Directory571 16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize,572 44, 0, 0, 0, // RVA573 // MemoryInfoListHeader574 16, 0, 0, 0, 52, 0, 0, 0, // SizeOfHeader, SizeOfEntry575 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries576 // MemoryInfo577 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress578 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase579 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0580 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize581 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect582 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1583 0, 0, 0, 0, // ???584 };585 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(1, 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);603 }604 605 // Header does not fit into the stream.606 std::vector<uint8_t> HeaderTooBig{607 // Header608 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version609 1, 0, 0, 0, // NumberOfStreams,610 32, 0, 0, 0, // StreamDirectoryRVA611 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp612 0, 0, 0, 0, 0, 0, 0, 0, // Flags613 // Stream Directory614 16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize,615 44, 0, 0, 0, // RVA616 // MemoryInfoListHeader617 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry618 1, 0, 0, 0, 0, 0, 0, // ???619 };620 Expected<std::unique_ptr<MinidumpFile>> File = create(HeaderTooBig);621 ASSERT_THAT_EXPECTED(File, Succeeded());622 EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>());623 624 // Header fits into the stream, but it is too small to contain the required625 // entries.626 std::vector<uint8_t> HeaderTooSmall{627 // Header628 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version629 1, 0, 0, 0, // NumberOfStreams,630 32, 0, 0, 0, // StreamDirectoryRVA631 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp632 0, 0, 0, 0, 0, 0, 0, 0, // Flags633 // Stream Directory634 16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize,635 44, 0, 0, 0, // RVA636 // MemoryInfoListHeader637 15, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry638 1, 0, 0, 0, 0, 0, 0, // ???639 };640 File = create(HeaderTooSmall);641 ASSERT_THAT_EXPECTED(File, Succeeded());642 EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>());643 644 std::vector<uint8_t> EntryTooBig{645 // Header646 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version647 1, 0, 0, 0, // NumberOfStreams,648 32, 0, 0, 0, // StreamDirectoryRVA649 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp650 0, 0, 0, 0, 0, 0, 0, 0, // Flags651 // Stream Directory652 16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize,653 44, 0, 0, 0, // RVA654 // MemoryInfoListHeader655 16, 0, 0, 0, 49, 0, 0, 0, // SizeOfHeader, SizeOfEntry656 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries657 // MemoryInfo658 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress659 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase660 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0661 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize662 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect663 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1664 };665 File = create(EntryTooBig);666 ASSERT_THAT_EXPECTED(File, Succeeded());667 EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>());668 669 std::vector<uint8_t> ThreeEntries{670 // Header671 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version672 1, 0, 0, 0, // NumberOfStreams,673 32, 0, 0, 0, // StreamDirectoryRVA674 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp675 0, 0, 0, 0, 0, 0, 0, 0, // Flags676 // Stream Directory677 16, 0, 0, 0, 160, 0, 0, 0, // Type, DataSize,678 44, 0, 0, 0, // RVA679 // MemoryInfoListHeader680 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry681 3, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries682 // MemoryInfo683 0, 1, 2, 3, 0, 0, 0, 0, // BaseAddress684 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase685 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0686 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize687 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect688 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1689 0, 0, 4, 5, 6, 7, 0, 0, // BaseAddress690 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase691 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0692 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize693 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect694 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1695 0, 0, 0, 8, 9, 0, 1, 0, // BaseAddress696 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase697 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0698 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize699 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect700 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1701 };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;709 })),710 testing::ElementsAre(0x0000000003020100u, 0x0000070605040000u,711 0x0001000908000000u));712}713 714TEST(MinidumpFile, getExceptionStreams) {715 std::vector<uint8_t> Data{716 // Header717 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version718 1, 0, 0, 0, // NumberOfStreams,719 0x20, 0, 0, 0, // StreamDirectoryRVA720 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp721 8, 9, 0, 1, 2, 3, 4, 5, // Flags722 // Stream Directory723 6, 0, 0, 0, 168, 0, 0, 0, // Type, DataSize,724 0x2c, 0, 0, 0, // RVA725 // Exception Stream726 1, 2, 3, 4, // Thread ID727 0, 0, 0, 0, // Padding728 // Exception Record729 2, 3, 4, 2, 7, 8, 8, 9, // Code, Flags730 3, 4, 5, 6, 7, 8, 9, 10, // Inner exception record address731 8, 7, 6, 5, 4, 3, 2, 1, // Exception address732 4, 0, 0, 0, 0, 0, 0, 0, // Parameter count, padding733 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // Parameter 0734 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // Parameter 1735 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Parameter 2736 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // Parameter 3737 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // Parameter 4738 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // Parameter 5739 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // Parameter 6740 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // Parameter 7741 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // Parameter 8742 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, // Parameter 9743 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, // Parameter 10744 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, // Parameter 11745 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, // Parameter 12746 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, // Parameter 13747 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, // Parameter 14748 // Thread Context749 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, // DataSize, RVA750 };751 auto ExpectedFile = create(Data);752 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());753 const MinidumpFile &File = **ExpectedFile;754 755 auto ExceptionStreams = File.getExceptionStreams();756 ASSERT_NE(ExceptionStreams.begin(), ExceptionStreams.end());757 auto ExceptionIterator = ExceptionStreams.begin();758 Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;759 ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());760 EXPECT_EQ(0x04030201u, ExpectedStream->ThreadId);761 const minidump::Exception &Exception = ExpectedStream->ExceptionRecord;762 EXPECT_EQ(0x02040302u, Exception.ExceptionCode);763 EXPECT_EQ(0x09080807u, Exception.ExceptionFlags);764 EXPECT_EQ(0x0a09080706050403u, Exception.ExceptionRecord);765 EXPECT_EQ(0x0102030405060708u, Exception.ExceptionAddress);766 EXPECT_EQ(4u, Exception.NumberParameters);767 for (uint64_t index = 0; index < Exception.MaxParameters; ++index) {768 EXPECT_EQ(0x1716151413121110u + index * 0x1010101010101010u,769 Exception.ExceptionInformation[index]);770 }771 EXPECT_EQ(0x84838281, ExpectedStream->ThreadContext.DataSize);772 EXPECT_EQ(0x88878685, ExpectedStream->ThreadContext.RVA);773 ++ExceptionIterator;774 ASSERT_EQ(ExceptionIterator, ExceptionStreams.end());775}776