447 lines · cpp
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-exception6//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 15using namespace llvm;16using namespace llvm::minidump;17 18static Expected<std::unique_ptr<object::MinidumpFile>>19toBinary(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 30TEST(MinidumpYAML, Basic) {31 SmallString<0> Storage;32 auto ExpectedFile = toBinary(Storage, R"(33--- !minidump34Streams:35 - Type: SystemInfo36 Processor Arch: ARM6437 Platform ID: Linux38 CPU:39 CPUID: 0x0506070840 - Type: LinuxMaps41 Text: |42 400d9000-400db000 r-xp 00000000 b3:04 227 /system/bin/app_process43 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process44 45 - Type: LinuxAuxv46 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 72TEST(MinidumpYAML, RawContent) {73 SmallString<0> Storage;74 auto ExpectedFile = toBinary(Storage, R"(75--- !minidump76Streams:77 - Type: LinuxAuxv78 Size: 979 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 88TEST(MinidumpYAML, X86SystemInfo) {89 SmallString<0> Storage;90 auto ExpectedFile = toBinary(Storage, R"(91--- !minidump92Streams:93 - Type: SystemInfo94 Processor Arch: X8695 Platform ID: Linux96 CPU:97 Vendor ID: LLVMLLVMLLVM98 Version Info: 0x0102030499 Feature Info: 0x05060708100 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 118TEST(MinidumpYAML, OtherSystemInfo) {119 SmallString<0> Storage;120 auto ExpectedFile = toBinary(Storage, R"(121--- !minidump122Streams:123 - Type: SystemInfo124 Processor Arch: PPC125 Platform ID: Linux126 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 ArrayRef(SysInfo.CPU.Other.ProcessorFeatures));141}142 143// Test that we can parse a normal-looking ExceptionStream.144TEST(MinidumpYAML, ExceptionStream) {145 SmallString<0> Storage;146 auto ExpectedFile = toBinary(Storage, R"(147--- !minidump148Streams:149 - Type: Exception150 Thread ID: 0x7151 Exception Record:152 Exception Code: 0x23153 Exception Flags: 0x5154 Exception Record: 0x0102030405060708155 Exception Address: 0x0a0b0c0d0e0f1011156 Number of Parameters: 2157 Parameter 0: 0x22158 Parameter 1: 0x24159 Thread Context: 3DeadBeefDefacedABadCafe)");160 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());161 object::MinidumpFile &File = **ExpectedFile;162 163 ASSERT_EQ(1u, File.streams().size());164 165 auto ExceptionIterator = File.getExceptionStreams().begin();166 167 Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;168 169 ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());170 171 const minidump::ExceptionStream &Stream = *ExpectedStream;172 EXPECT_EQ(0x7u, Stream.ThreadId);173 const minidump::Exception &Exception = Stream.ExceptionRecord;174 EXPECT_EQ(0x23u, Exception.ExceptionCode);175 EXPECT_EQ(0x5u, Exception.ExceptionFlags);176 EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);177 EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);178 EXPECT_EQ(2u, Exception.NumberParameters);179 EXPECT_EQ(0x22u, Exception.ExceptionInformation[0]);180 EXPECT_EQ(0x24u, Exception.ExceptionInformation[1]);181 182 Expected<ArrayRef<uint8_t>> ExpectedContext =183 File.getRawData(Stream.ThreadContext);184 ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());185 EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,186 0xab, 0xad, 0xca, 0xfe}),187 *ExpectedContext);188}189 190// Test that we can parse an exception stream with no ExceptionInformation.191TEST(MinidumpYAML, ExceptionStream_NoParameters) {192 SmallString<0> Storage;193 auto ExpectedFile = toBinary(Storage, R"(194--- !minidump195Streams:196 - Type: Exception197 Thread ID: 0x7198 Exception Record:199 Exception Code: 0x23200 Exception Flags: 0x5201 Exception Record: 0x0102030405060708202 Exception Address: 0x0a0b0c0d0e0f1011203 Thread Context: 3DeadBeefDefacedABadCafe)");204 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());205 object::MinidumpFile &File = **ExpectedFile;206 207 ASSERT_EQ(1u, File.streams().size());208 209 auto ExceptionIterator = File.getExceptionStreams().begin();210 211 Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;212 ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());213 214 const minidump::ExceptionStream &Stream = *ExpectedStream;215 EXPECT_EQ(0x7u, Stream.ThreadId);216 const minidump::Exception &Exception = Stream.ExceptionRecord;217 EXPECT_EQ(0x23u, Exception.ExceptionCode);218 EXPECT_EQ(0x5u, Exception.ExceptionFlags);219 EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);220 EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);221 EXPECT_EQ(0u, Exception.NumberParameters);222 223 Expected<ArrayRef<uint8_t>> ExpectedContext =224 File.getRawData(Stream.ThreadContext);225 ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());226 EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,227 0xab, 0xad, 0xca, 0xfe}),228 *ExpectedContext);229}230 231// Test that we can parse an ExceptionStream where the stated number of232// parameters is greater than the actual size of the ExceptionInformation233// array.234TEST(MinidumpYAML, ExceptionStream_TooManyParameters) {235 SmallString<0> Storage;236 auto ExpectedFile = toBinary(Storage, R"(237--- !minidump238Streams:239 - Type: Exception240 Thread ID: 0x8241 Exception Record:242 Exception Code: 0243 Number of Parameters: 16244 Parameter 0: 0x0245 Parameter 1: 0xff246 Parameter 2: 0xee247 Parameter 3: 0xdd248 Parameter 4: 0xcc249 Parameter 5: 0xbb250 Parameter 6: 0xaa251 Parameter 7: 0x99252 Parameter 8: 0x88253 Parameter 9: 0x77254 Parameter 10: 0x66255 Parameter 11: 0x55256 Parameter 12: 0x44257 Parameter 13: 0x33258 Parameter 14: 0x22259 Thread Context: 3DeadBeefDefacedABadCafe)");260 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());261 object::MinidumpFile &File = **ExpectedFile;262 263 ASSERT_EQ(1u, File.streams().size());264 265 auto ExceptionIterator = File.getExceptionStreams().begin();266 267 Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;268 269 ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());270 271 const minidump::ExceptionStream &Stream = *ExpectedStream;272 EXPECT_EQ(0x8u, Stream.ThreadId);273 const minidump::Exception &Exception = Stream.ExceptionRecord;274 EXPECT_EQ(0x0u, Exception.ExceptionCode);275 EXPECT_EQ(0x0u, Exception.ExceptionFlags);276 EXPECT_EQ(0x00u, Exception.ExceptionRecord);277 EXPECT_EQ(0x0u, Exception.ExceptionAddress);278 EXPECT_EQ(16u, Exception.NumberParameters);279 EXPECT_EQ(0x0u, Exception.ExceptionInformation[0]);280 for (int Index = 1; Index < 15; ++Index) {281 EXPECT_EQ(0x110u - Index * 0x11, Exception.ExceptionInformation[Index]);282 }283 284 Expected<ArrayRef<uint8_t>> ExpectedContext =285 File.getRawData(Stream.ThreadContext);286 ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());287 EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,288 0xab, 0xad, 0xca, 0xfe}),289 *ExpectedContext);290}291 292// Test that we can parse an ExceptionStream where the number of293// ExceptionInformation parameters provided is greater than the294// specified Number of Parameters.295TEST(MinidumpYAML, ExceptionStream_ExtraParameter) {296 SmallString<0> Storage;297 auto ExpectedFile = toBinary(Storage, R"(298--- !minidump299Streams:300 - Type: Exception301 Thread ID: 0x7302 Exception Record:303 Exception Code: 0x23304 Exception Flags: 0x5305 Exception Record: 0x0102030405060708306 Exception Address: 0x0a0b0c0d0e0f1011307 Number of Parameters: 2308 Parameter 0: 0x99309 Parameter 1: 0x23310 Parameter 2: 0x42311 Thread Context: 3DeadBeefDefacedABadCafe)");312 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());313 object::MinidumpFile &File = **ExpectedFile;314 315 ASSERT_EQ(1u, File.streams().size());316 317 auto ExceptionIterator = File.getExceptionStreams().begin();318 319 Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator;320 321 ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded());322 323 const minidump::ExceptionStream &Stream = *ExpectedStream;324 EXPECT_EQ(0x7u, Stream.ThreadId);325 const minidump::Exception &Exception = Stream.ExceptionRecord;326 EXPECT_EQ(0x23u, Exception.ExceptionCode);327 EXPECT_EQ(0x5u, Exception.ExceptionFlags);328 EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord);329 EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress);330 EXPECT_EQ(2u, Exception.NumberParameters);331 EXPECT_EQ(0x99u, Exception.ExceptionInformation[0]);332 EXPECT_EQ(0x23u, Exception.ExceptionInformation[1]);333 EXPECT_EQ(0x42u, Exception.ExceptionInformation[2]);334 335 Expected<ArrayRef<uint8_t>> ExpectedContext =336 File.getRawData(Stream.ThreadContext);337 ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded());338 EXPECT_EQ((ArrayRef<uint8_t>{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed,339 0xab, 0xad, 0xca, 0xfe}),340 *ExpectedContext);341}342 343TEST(MinidumpYAML, MemoryRegion_64bit) {344 SmallString<0> Storage;345 auto ExpectedFile = toBinary(Storage, R"(346--- !minidump347Streams:348 - Type: Memory64List349 Memory Ranges:350 - Start of Memory Range: 0x7FFFFFCF0818283351 Content: '68656c6c6f'352 - Start of Memory Range: 0x7FFFFFFF0818283353 Content: '776f726c64'354 )");355 356 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());357 object::MinidumpFile &File = **ExpectedFile;358 359 ASSERT_THAT(File.streams().size(), 1u);360 361 Error Err = Error::success();362 iterator_range<object::MinidumpFile::FallibleMemory64Iterator> MemoryList =363 File.getMemory64List(Err);364 365 ASSERT_THAT_ERROR(std::move(Err), Succeeded());366 auto Iterator = MemoryList.begin();367 368 auto DescOnePair = *Iterator;369 const minidump::MemoryDescriptor_64 &DescOne = DescOnePair.first;370 ASSERT_THAT(DescOne.StartOfMemoryRange, 0x7FFFFFCF0818283u);371 ASSERT_THAT(DescOne.DataSize, 5u);372 ++Iterator;373 ASSERT_THAT_ERROR(std::move(Err), Succeeded());374 375 auto DescTwoPair = *Iterator;376 const minidump::MemoryDescriptor_64 &DescTwo = DescTwoPair.first;377 ASSERT_THAT(DescTwo.StartOfMemoryRange, 0x7FFFFFFF0818283u);378 ASSERT_THAT(DescTwo.DataSize, 5u);379 ++Iterator;380 ASSERT_THAT_ERROR(std::move(Err), Succeeded());381 382 const std::optional<ArrayRef<uint8_t>> ExpectedContent =383 File.getRawStream(StreamType::Memory64List);384 ASSERT_TRUE(ExpectedContent);385 const size_t ExpectedStreamSize =386 sizeof(Memory64ListHeader) + (sizeof(MemoryDescriptor_64) * 2);387 ASSERT_THAT(ExpectedContent->size(), ExpectedStreamSize);388 389 Expected<minidump::Memory64ListHeader> ExpectedHeader =390 File.getMemoryList64Header();391 ASSERT_THAT_EXPECTED(ExpectedHeader, Succeeded());392 ASSERT_THAT(ExpectedHeader->BaseRVA, 92u);393 394 Expected<ArrayRef<uint8_t>> DescOneExpectedContentSlice = DescOnePair.second;395 ASSERT_THAT_EXPECTED(DescOneExpectedContentSlice, Succeeded());396 ASSERT_THAT(DescOneExpectedContentSlice->size(), 5u);397 ASSERT_THAT(*DescOneExpectedContentSlice, arrayRefFromStringRef("hello"));398 399 Expected<ArrayRef<uint8_t>> DescTwoExpectedContentSlice = DescTwoPair.second;400 ASSERT_THAT_EXPECTED(DescTwoExpectedContentSlice, Succeeded());401 ASSERT_THAT(*DescTwoExpectedContentSlice, arrayRefFromStringRef("world"));402 403 ASSERT_EQ(Iterator, MemoryList.end());404}405 406// Test that we can parse multiple exception streams.407TEST(MinidumpYAML, ExceptionStream_MultipleExceptions) {408 SmallString<0> Storage;409 auto ExpectedFile = toBinary(Storage, R"(410--- !minidump411Streams:412 - Type: Exception413 Thread ID: 0x7414 Exception Record:415 Exception Code: 0x23416 Exception Flags: 0x5417 Exception Record: 0x0102030405060708418 Exception Address: 0x0a0b0c0d0e0f1011419 Number of Parameters: 2420 Parameter 0: 0x99421 Parameter 1: 0x23422 Parameter 2: 0x42423 Thread Context: 3DeadBeefDefacedABadCafe424 - Type: Exception425 Thread ID: 0x5426 Exception Record:427 Exception Code: 0x23428 Exception Flags: 0x5429 Exception Record: 0x0102030405060708430 Exception Address: 0x0a0b0c0d0e0f1011431 Thread Context: 3DeadBeefDefacedABadCafe)");432 433 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded());434 object::MinidumpFile &File = **ExpectedFile;435 436 ASSERT_EQ(2u, File.streams().size());437 438 size_t count = 0;439 for (auto exception_stream : File.getExceptionStreams()) {440 count++;441 ASSERT_THAT_EXPECTED(exception_stream, Succeeded());442 ASSERT_THAT(0x23u, exception_stream->ExceptionRecord.ExceptionCode);443 }444 445 ASSERT_THAT(2u, count);446}447