//===- MinidumpTest.cpp - Tests for Minidump.cpp --------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Object/Minidump.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::object; using namespace minidump; static Expected> create(ArrayRef Data) { return MinidumpFile::create( MemoryBufferRef(toStringRef(Data), "Test buffer")); } TEST(MinidumpFile, BasicInterface) { std::vector Data{ // Header 'M', 'D', 'M', 'P', // Signature 0x93, 0xa7, 0, 0, // Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; // A very simple minidump file which contains just a single stream. auto ExpectedFile = create(Data); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; const Header &H = File.header(); EXPECT_EQ(Header::MagicSignature, H.Signature); EXPECT_EQ(Header::MagicVersion, H.Version); EXPECT_EQ(1u, H.NumberOfStreams); EXPECT_EQ(0x20u, H.StreamDirectoryRVA); EXPECT_EQ(0x03020100u, H.Checksum); EXPECT_EQ(0x07060504u, H.TimeDateStamp); EXPECT_EQ(uint64_t(0x0504030201000908), H.Flags); ASSERT_EQ(1u, File.streams().size()); const Directory &Stream0 = File.streams()[0]; EXPECT_EQ(StreamType::LinuxCPUInfo, Stream0.Type); EXPECT_EQ(7u, Stream0.Location.DataSize); EXPECT_EQ(0x2cu, Stream0.Location.RVA); EXPECT_EQ("CPUINFO", toStringRef(File.getRawStream(Stream0))); EXPECT_EQ("CPUINFO", toStringRef(*File.getRawStream(StreamType::LinuxCPUInfo))); EXPECT_THAT_EXPECTED(File.getSystemInfo(), Failed()); } // Use the input from the previous test, but corrupt it in various ways TEST(MinidumpFile, create_ErrorCases) { std::vector FileTooShort{'M', 'D', 'M', 'P'}; EXPECT_THAT_EXPECTED(create(FileTooShort), Failed()); std::vector WrongSignature{ // Header '!', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; EXPECT_THAT_EXPECTED(create(WrongSignature), Failed()); std::vector WrongVersion{ // Header 'M', 'D', 'M', 'P', 0x39, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; EXPECT_THAT_EXPECTED(create(WrongVersion), Failed()); std::vector DirectoryAfterEOF{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 1, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; EXPECT_THAT_EXPECTED(create(DirectoryAfterEOF), Failed()); std::vector TruncatedDirectory{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 1, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; EXPECT_THAT_EXPECTED(create(TruncatedDirectory), Failed()); std::vector Stream0AfterEOF{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 0x2c, 1, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; EXPECT_THAT_EXPECTED(create(Stream0AfterEOF), Failed()); std::vector Stream0Truncated{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 3, 0, 0x67, 0x47, 8, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; EXPECT_THAT_EXPECTED(create(Stream0Truncated), Failed()); std::vector DuplicateStream{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 2, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 0x40, 0, 0, 0, // RVA // Stream 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 0x40, 0, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; EXPECT_THAT_EXPECTED(create(DuplicateStream), Failed()); std::vector DenseMapInfoConflict{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 0xff, 0xff, 0xff, 0xff, 7, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // Stream 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; EXPECT_THAT_EXPECTED(create(DenseMapInfoConflict), Failed()); } TEST(MinidumpFile, IngoresDummyStreams) { std::vector TwoDummyStreams{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 2, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize, 0x20, 0, 0, 0, // RVA 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize, 0x20, 0, 0, 0, // RVA }; auto ExpectedFile = create(TwoDummyStreams); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; ASSERT_EQ(2u, File.streams().size()); EXPECT_EQ(StreamType::Unused, File.streams()[0].Type); EXPECT_EQ(StreamType::Unused, File.streams()[1].Type); EXPECT_EQ(None, File.getRawStream(StreamType::Unused)); } TEST(MinidumpFile, getSystemInfo) { std::vector Data{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 7, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // SystemInfo 0, 0, 1, 2, // ProcessorArch, ProcessorLevel 3, 4, 5, 6, // ProcessorRevision, NumberOfProcessors, ProductType 7, 8, 9, 0, 1, 2, 3, 4, // MajorVersion, MinorVersion 5, 6, 7, 8, 2, 0, 0, 0, // BuildNumber, PlatformId 1, 2, 3, 4, 5, 6, 7, 8, // CSDVersionRVA, SuiteMask, Reserved 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', // VendorID 1, 2, 3, 4, 5, 6, 7, 8, // VersionInfo, FeatureInfo 9, 0, 1, 2, // AMDExtendedFeatures }; auto ExpectedFile = create(Data); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; auto ExpectedInfo = File.getSystemInfo(); ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded()); const SystemInfo &Info = *ExpectedInfo; EXPECT_EQ(ProcessorArchitecture::X86, Info.ProcessorArch); EXPECT_EQ(0x0201, Info.ProcessorLevel); EXPECT_EQ(0x0403, Info.ProcessorRevision); EXPECT_EQ(5, Info.NumberOfProcessors); EXPECT_EQ(6, Info.ProductType); EXPECT_EQ(0x00090807u, Info.MajorVersion); EXPECT_EQ(0x04030201u, Info.MinorVersion); EXPECT_EQ(0x08070605u, Info.BuildNumber); EXPECT_EQ(OSPlatform::Win32NT, Info.PlatformId); EXPECT_EQ(0x04030201u, Info.CSDVersionRVA); EXPECT_EQ(0x0605u, Info.SuiteMask); EXPECT_EQ(0x0807u, Info.Reserved); EXPECT_EQ("LLVMLLVMLLVM", llvm::StringRef(Info.CPU.X86.VendorID, sizeof(Info.CPU.X86.VendorID))); EXPECT_EQ(0x04030201u, Info.CPU.X86.VersionInfo); EXPECT_EQ(0x08070605u, Info.CPU.X86.FeatureInfo); EXPECT_EQ(0x02010009u, Info.CPU.X86.AMDExtendedFeatures); } TEST(MinidumpFile, getString) { std::vector ManyStrings{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 2, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize, 0x20, 0, 0, 0, // RVA 1, 0, 0, 0, 0, 0, // String1 - odd length 0, 0, 1, 0, 0, 0, // String2 - too long 2, 0, 0, 0, 0, 0xd8, // String3 - invalid utf16 0, 0, 0, 0, 0, 0, // String4 - "" 2, 0, 0, 0, 'a', 0, // String5 - "a" 0, // Mis-align next string 2, 0, 0, 0, 'a', 0, // String6 - "a" }; auto ExpectedFile = create(ManyStrings); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; EXPECT_THAT_EXPECTED(File.getString(44), Failed()); EXPECT_THAT_EXPECTED(File.getString(50), Failed()); EXPECT_THAT_EXPECTED(File.getString(56), Failed()); EXPECT_THAT_EXPECTED(File.getString(62), HasValue("")); EXPECT_THAT_EXPECTED(File.getString(68), HasValue("a")); EXPECT_THAT_EXPECTED(File.getString(75), HasValue("a")); // Check the case when the size field does not fit into the remaining data. EXPECT_THAT_EXPECTED(File.getString(ManyStrings.size() - 2), Failed()); } TEST(MinidumpFile, getModuleList) { std::vector OneModule{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 4, 0, 0, 0, 112, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // ModuleList 1, 0, 0, 0, // NumberOfModules 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags 0, 0, 0, 0, // FileOS 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType 0, 0, 0, 0, 0, 0, 0, 0, // FileDate 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 }; // Same as before, but with a padded module list. std::vector PaddedModule{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 4, 0, 0, 0, 116, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // ModuleList 1, 0, 0, 0, // NumberOfModules 0, 0, 0, 0, // Padding 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags 0, 0, 0, 0, // FileOS 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType 0, 0, 0, 0, 0, 0, 0, 0, // FileDate 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 }; for (ArrayRef Data : {OneModule, PaddedModule}) { auto ExpectedFile = create(Data); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; Expected> ExpectedModule = File.getModuleList(); ASSERT_THAT_EXPECTED(ExpectedModule, Succeeded()); ASSERT_EQ(1u, ExpectedModule->size()); const Module &M = ExpectedModule.get()[0]; EXPECT_EQ(0x0807060504030201u, M.BaseOfImage); EXPECT_EQ(0x02010009u, M.SizeOfImage); EXPECT_EQ(0x06050403u, M.Checksum); EXPECT_EQ(0x00090807u, M.TimeDateStamp); EXPECT_EQ(0x04030201u, M.ModuleNameRVA); EXPECT_EQ(0x04030201u, M.CvRecord.DataSize); EXPECT_EQ(0x08070605u, M.CvRecord.RVA); EXPECT_EQ(0x02010009u, M.MiscRecord.DataSize); EXPECT_EQ(0x06050403u, M.MiscRecord.RVA); EXPECT_EQ(0x0403020100090807u, M.Reserved0); EXPECT_EQ(0x0201000908070605u, M.Reserved1); } std::vector StreamTooShort{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 4, 0, 0, 0, 111, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // ModuleList 1, 0, 0, 0, // NumberOfModules 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags 0, 0, 0, 0, // FileOS 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType 0, 0, 0, 0, 0, 0, 0, 0, // FileDate 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 }; auto ExpectedFile = create(StreamTooShort); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; EXPECT_THAT_EXPECTED(File.getModuleList(), Failed()); } TEST(MinidumpFile, getThreadList) { std::vector OneThread{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 3, 0, 0, 0, 52, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // ThreadList 1, 0, 0, 0, // NumberOfThreads 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock // Stack 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA // Context 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA }; // Same as before, but with a padded thread list. std::vector PaddedThread{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 3, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // ThreadList 1, 0, 0, 0, // NumberOfThreads 0, 0, 0, 0, // Padding 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock // Stack 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA // Context 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA }; for (ArrayRef Data : {OneThread, PaddedThread}) { auto ExpectedFile = create(Data); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; Expected> ExpectedThread = File.getThreadList(); ASSERT_THAT_EXPECTED(ExpectedThread, Succeeded()); ASSERT_EQ(1u, ExpectedThread->size()); const Thread &T = ExpectedThread.get()[0]; EXPECT_EQ(0x04030201u, T.ThreadId); EXPECT_EQ(0x08070605u, T.SuspendCount); EXPECT_EQ(0x02010009u, T.PriorityClass); EXPECT_EQ(0x06050403u, T.Priority); EXPECT_EQ(0x0403020100090807u, T.EnvironmentBlock); EXPECT_EQ(0x0201000908070605u, T.Stack.StartOfMemoryRange); EXPECT_EQ(0x06050403u, T.Stack.Memory.DataSize); EXPECT_EQ(0x00090807u, T.Stack.Memory.RVA); EXPECT_EQ(0x04030201u, T.Context.DataSize); EXPECT_EQ(0x08070605u, T.Context.RVA); } } TEST(MinidumpFile, getMemoryList) { std::vector OneRange{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 5, 0, 0, 0, 20, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryDescriptor 1, 0, 0, 0, // NumberOfMemoryRanges 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA }; // Same as before, but with a padded memory list. std::vector PaddedRange{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 5, 0, 0, 0, 24, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryDescriptor 1, 0, 0, 0, // NumberOfMemoryRanges 0, 0, 0, 0, // Padding 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA }; for (ArrayRef Data : {OneRange, PaddedRange}) { auto ExpectedFile = create(Data); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; Expected> ExpectedRanges = File.getMemoryList(); ASSERT_THAT_EXPECTED(ExpectedRanges, Succeeded()); ASSERT_EQ(1u, ExpectedRanges->size()); const MemoryDescriptor &MD = ExpectedRanges.get()[0]; EXPECT_EQ(0x0201000908070605u, MD.StartOfMemoryRange); EXPECT_EQ(0x06050403u, MD.Memory.DataSize); EXPECT_EQ(0x00090807u, MD.Memory.RVA); } } TEST(MinidumpFile, getMemoryInfoList) { std::vector OneEntry{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryInfoListHeader 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries // MemoryInfo 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1 }; // Same as before, but the list header is larger. std::vector BiggerHeader{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryInfoListHeader 20, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries 0, 0, 0, 0, // ??? // MemoryInfo 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1 }; // Same as before, but the entry is larger. std::vector BiggerEntry{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryInfoListHeader 16, 0, 0, 0, 52, 0, 0, 0, // SizeOfHeader, SizeOfEntry 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries // MemoryInfo 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1 0, 0, 0, 0, // ??? }; for (ArrayRef Data : {OneEntry, BiggerHeader, BiggerEntry}) { auto ExpectedFile = create(Data); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; auto ExpectedInfo = File.getMemoryInfoList(); ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded()); ASSERT_EQ(1, std::distance(ExpectedInfo->begin(), ExpectedInfo->end())); const MemoryInfo &Info = *ExpectedInfo.get().begin(); EXPECT_EQ(0x0706050403020100u, Info.BaseAddress); EXPECT_EQ(0x0504030201000908u, Info.AllocationBase); EXPECT_EQ(MemoryProtection::Execute, Info.AllocationProtect); EXPECT_EQ(0x09080706u, Info.Reserved0); EXPECT_EQ(0x0706050403020100u, Info.RegionSize); EXPECT_EQ(MemoryState::Commit, Info.State); EXPECT_EQ(MemoryProtection::ExecuteRead, Info.Protect); EXPECT_EQ(MemoryType::Private, Info.Type); EXPECT_EQ(0x01000908u, Info.Reserved1); } // Header does not fit into the stream. std::vector HeaderTooBig{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryInfoListHeader 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 1, 0, 0, 0, 0, 0, 0, // ??? }; Expected> File = create(HeaderTooBig); ASSERT_THAT_EXPECTED(File, Succeeded()); EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed()); // Header fits into the stream, but it is too small to contain the required // entries. std::vector HeaderTooSmall{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryInfoListHeader 15, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 1, 0, 0, 0, 0, 0, 0, // ??? }; File = create(HeaderTooSmall); ASSERT_THAT_EXPECTED(File, Succeeded()); EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed()); std::vector EntryTooBig{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryInfoListHeader 16, 0, 0, 0, 49, 0, 0, 0, // SizeOfHeader, SizeOfEntry 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries // MemoryInfo 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1 }; File = create(EntryTooBig); ASSERT_THAT_EXPECTED(File, Succeeded()); EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed()); std::vector ThreeEntries{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 32, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 0, 0, 0, 0, 0, 0, 0, 0, // Flags // Stream Directory 16, 0, 0, 0, 160, 0, 0, 0, // Type, DataSize, 44, 0, 0, 0, // RVA // MemoryInfoListHeader 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 3, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries // MemoryInfo 0, 1, 2, 3, 0, 0, 0, 0, // BaseAddress 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1 0, 0, 4, 5, 6, 7, 0, 0, // BaseAddress 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1 0, 0, 0, 8, 9, 0, 1, 0, // BaseAddress 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1 }; File = create(ThreeEntries); ASSERT_THAT_EXPECTED(File, Succeeded()); auto ExpectedInfo = File.get()->getMemoryInfoList(); ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded()); EXPECT_THAT(to_vector<3>(map_range(*ExpectedInfo, [](const MemoryInfo &Info) -> uint64_t { return Info.BaseAddress; })), testing::ElementsAre(0x0000000003020100u, 0x0000070605040000u, 0x0001000908000000u)); } TEST(MinidumpFile, getExceptionStream) { std::vector Data{ // Header 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 1, 0, 0, 0, // NumberOfStreams, 0x20, 0, 0, 0, // StreamDirectoryRVA 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 8, 9, 0, 1, 2, 3, 4, 5, // Flags // Stream Directory 6, 0, 0, 0, 168, 0, 0, 0, // Type, DataSize, 0x2c, 0, 0, 0, // RVA // Exception Stream 1, 2, 3, 4, // Thread ID 0, 0, 0, 0, // Padding // Exception Record 2, 3, 4, 2, 7, 8, 8, 9, // Code, Flags 3, 4, 5, 6, 7, 8, 9, 10, // Inner exception record address 8, 7, 6, 5, 4, 3, 2, 1, // Exception address 4, 0, 0, 0, 0, 0, 0, 0, // Parameter count, padding 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // Parameter 0 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // Parameter 1 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Parameter 2 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // Parameter 3 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // Parameter 4 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // Parameter 5 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // Parameter 6 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // Parameter 7 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // Parameter 8 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, // Parameter 9 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, // Parameter 10 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, // Parameter 11 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, // Parameter 12 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, // Parameter 13 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, // Parameter 14 // Thread Context 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, // DataSize, RVA }; auto ExpectedFile = create(Data); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); const MinidumpFile &File = **ExpectedFile; Expected ExpectedStream = File.getExceptionStream(); ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded()); EXPECT_EQ(0x04030201u, ExpectedStream->ThreadId); const minidump::Exception &Exception = ExpectedStream->ExceptionRecord; EXPECT_EQ(0x02040302u, Exception.ExceptionCode); EXPECT_EQ(0x09080807u, Exception.ExceptionFlags); EXPECT_EQ(0x0a09080706050403u, Exception.ExceptionRecord); EXPECT_EQ(0x0102030405060708u, Exception.ExceptionAddress); EXPECT_EQ(4u, Exception.NumberParameters); for (uint64_t index = 0; index < Exception.MaxParameters; ++index) { EXPECT_EQ(0x1716151413121110u + index * 0x1010101010101010u, Exception.ExceptionInformation[index]); } EXPECT_EQ(0x84838281, ExpectedStream->ThreadContext.DataSize); EXPECT_EQ(0x88878685, ExpectedStream->ThreadContext.RVA); }