//===- MinidumpYAMLTest.cpp - Tests for Minidump<->YAML code --------------===// // // 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/ObjectYAML/yaml2obj.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::minidump; static Expected> toBinary(SmallVectorImpl &Storage, StringRef Yaml) { Storage.clear(); raw_svector_ostream OS(Storage); yaml::Input YIn(Yaml); if (!yaml::convertYAML(YIn, OS, [](const Twine &Msg) {})) return createStringError(std::errc::invalid_argument, "unable to convert YAML"); return object::MinidumpFile::create(MemoryBufferRef(OS.str(), "Binary")); } TEST(MinidumpYAML, Basic) { SmallString<0> Storage; auto ExpectedFile = toBinary(Storage, R"( --- !minidump Streams: - Type: SystemInfo Processor Arch: ARM64 Platform ID: Linux CPU: CPUID: 0x05060708 - Type: LinuxMaps Text: | 400d9000-400db000 r-xp 00000000 b3:04 227 /system/bin/app_process 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process - Type: LinuxAuxv Content: DEADBEEFBAADF00D)"); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); object::MinidumpFile &File = **ExpectedFile; ASSERT_EQ(3u, File.streams().size()); EXPECT_EQ(StreamType::SystemInfo, File.streams()[0].Type); auto ExpectedSysInfo = File.getSystemInfo(); ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded()); const SystemInfo &SysInfo = *ExpectedSysInfo; EXPECT_EQ(ProcessorArchitecture::ARM64, SysInfo.ProcessorArch); EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId); EXPECT_EQ(0x05060708u, SysInfo.CPU.Arm.CPUID); EXPECT_EQ(StreamType::LinuxMaps, File.streams()[1].Type); EXPECT_EQ("400d9000-400db000 r-xp 00000000 b3:04 227 " "/system/bin/app_process\n" "400db000-400dc000 r--p 00001000 b3:04 227 " "/system/bin/app_process\n", toStringRef(*File.getRawStream(StreamType::LinuxMaps))); EXPECT_EQ(StreamType::LinuxAuxv, File.streams()[2].Type); EXPECT_EQ((ArrayRef{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D}), File.getRawStream(StreamType::LinuxAuxv)); } TEST(MinidumpYAML, RawContent) { SmallString<0> Storage; auto ExpectedFile = toBinary(Storage, R"( --- !minidump Streams: - Type: LinuxAuxv Size: 9 Content: DEADBEEFBAADF00D)"); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); object::MinidumpFile &File = **ExpectedFile; EXPECT_EQ( (ArrayRef{0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D, 0x00}), File.getRawStream(StreamType::LinuxAuxv)); } TEST(MinidumpYAML, X86SystemInfo) { SmallString<0> Storage; auto ExpectedFile = toBinary(Storage, R"( --- !minidump Streams: - Type: SystemInfo Processor Arch: X86 Platform ID: Linux CPU: Vendor ID: LLVMLLVMLLVM Version Info: 0x01020304 Feature Info: 0x05060708 AMD Extended Features: 0x09000102)"); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); object::MinidumpFile &File = **ExpectedFile; ASSERT_EQ(1u, File.streams().size()); auto ExpectedSysInfo = File.getSystemInfo(); ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded()); const SystemInfo &SysInfo = *ExpectedSysInfo; EXPECT_EQ(ProcessorArchitecture::X86, SysInfo.ProcessorArch); EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId); EXPECT_EQ("LLVMLLVMLLVM", StringRef(SysInfo.CPU.X86.VendorID, sizeof(SysInfo.CPU.X86.VendorID))); EXPECT_EQ(0x01020304u, SysInfo.CPU.X86.VersionInfo); EXPECT_EQ(0x05060708u, SysInfo.CPU.X86.FeatureInfo); EXPECT_EQ(0x09000102u, SysInfo.CPU.X86.AMDExtendedFeatures); } TEST(MinidumpYAML, OtherSystemInfo) { SmallString<0> Storage; auto ExpectedFile = toBinary(Storage, R"( --- !minidump Streams: - Type: SystemInfo Processor Arch: PPC Platform ID: Linux CPU: Features: 000102030405060708090a0b0c0d0e0f)"); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); object::MinidumpFile &File = **ExpectedFile; ASSERT_EQ(1u, File.streams().size()); auto ExpectedSysInfo = File.getSystemInfo(); ASSERT_THAT_EXPECTED(ExpectedSysInfo, Succeeded()); const SystemInfo &SysInfo = *ExpectedSysInfo; EXPECT_EQ(ProcessorArchitecture::PPC, SysInfo.ProcessorArch); EXPECT_EQ(OSPlatform::Linux, SysInfo.PlatformId); EXPECT_EQ( (ArrayRef{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}), makeArrayRef(SysInfo.CPU.Other.ProcessorFeatures)); } // Test that we can parse a normal-looking ExceptionStream. TEST(MinidumpYAML, ExceptionStream) { SmallString<0> Storage; auto ExpectedFile = toBinary(Storage, R"( --- !minidump Streams: - Type: Exception Thread ID: 0x7 Exception Record: Exception Code: 0x23 Exception Flags: 0x5 Exception Record: 0x0102030405060708 Exception Address: 0x0a0b0c0d0e0f1011 Number of Parameters: 2 Parameter 0: 0x22 Parameter 1: 0x24 Thread Context: 3DeadBeefDefacedABadCafe)"); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); object::MinidumpFile &File = **ExpectedFile; ASSERT_EQ(1u, File.streams().size()); Expected ExpectedStream = File.getExceptionStream(); ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded()); const minidump::ExceptionStream &Stream = *ExpectedStream; EXPECT_EQ(0x7u, Stream.ThreadId); const minidump::Exception &Exception = Stream.ExceptionRecord; EXPECT_EQ(0x23u, Exception.ExceptionCode); EXPECT_EQ(0x5u, Exception.ExceptionFlags); EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord); EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress); EXPECT_EQ(2u, Exception.NumberParameters); EXPECT_EQ(0x22u, Exception.ExceptionInformation[0]); EXPECT_EQ(0x24u, Exception.ExceptionInformation[1]); Expected> ExpectedContext = File.getRawData(Stream.ThreadContext); ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded()); EXPECT_EQ((ArrayRef{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed, 0xab, 0xad, 0xca, 0xfe}), *ExpectedContext); } // Test that we can parse an exception stream with no ExceptionInformation. TEST(MinidumpYAML, ExceptionStream_NoParameters) { SmallString<0> Storage; auto ExpectedFile = toBinary(Storage, R"( --- !minidump Streams: - Type: Exception Thread ID: 0x7 Exception Record: Exception Code: 0x23 Exception Flags: 0x5 Exception Record: 0x0102030405060708 Exception Address: 0x0a0b0c0d0e0f1011 Thread Context: 3DeadBeefDefacedABadCafe)"); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); object::MinidumpFile &File = **ExpectedFile; ASSERT_EQ(1u, File.streams().size()); Expected ExpectedStream = File.getExceptionStream(); ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded()); const minidump::ExceptionStream &Stream = *ExpectedStream; EXPECT_EQ(0x7u, Stream.ThreadId); const minidump::Exception &Exception = Stream.ExceptionRecord; EXPECT_EQ(0x23u, Exception.ExceptionCode); EXPECT_EQ(0x5u, Exception.ExceptionFlags); EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord); EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress); EXPECT_EQ(0u, Exception.NumberParameters); Expected> ExpectedContext = File.getRawData(Stream.ThreadContext); ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded()); EXPECT_EQ((ArrayRef{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed, 0xab, 0xad, 0xca, 0xfe}), *ExpectedContext); } // Test that we can parse an ExceptionStream where the stated number of // parameters is greater than the actual size of the ExceptionInformation // array. TEST(MinidumpYAML, ExceptionStream_TooManyParameters) { SmallString<0> Storage; auto ExpectedFile = toBinary(Storage, R"( --- !minidump Streams: - Type: Exception Thread ID: 0x8 Exception Record: Exception Code: 0 Number of Parameters: 16 Parameter 0: 0x0 Parameter 1: 0xff Parameter 2: 0xee Parameter 3: 0xdd Parameter 4: 0xcc Parameter 5: 0xbb Parameter 6: 0xaa Parameter 7: 0x99 Parameter 8: 0x88 Parameter 9: 0x77 Parameter 10: 0x66 Parameter 11: 0x55 Parameter 12: 0x44 Parameter 13: 0x33 Parameter 14: 0x22 Thread Context: 3DeadBeefDefacedABadCafe)"); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); object::MinidumpFile &File = **ExpectedFile; ASSERT_EQ(1u, File.streams().size()); Expected ExpectedStream = File.getExceptionStream(); ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded()); const minidump::ExceptionStream &Stream = *ExpectedStream; EXPECT_EQ(0x8u, Stream.ThreadId); const minidump::Exception &Exception = Stream.ExceptionRecord; EXPECT_EQ(0x0u, Exception.ExceptionCode); EXPECT_EQ(0x0u, Exception.ExceptionFlags); EXPECT_EQ(0x00u, Exception.ExceptionRecord); EXPECT_EQ(0x0u, Exception.ExceptionAddress); EXPECT_EQ(16u, Exception.NumberParameters); EXPECT_EQ(0x0u, Exception.ExceptionInformation[0]); for (int Index = 1; Index < 15; ++Index) { EXPECT_EQ(0x110u - Index * 0x11, Exception.ExceptionInformation[Index]); } Expected> ExpectedContext = File.getRawData(Stream.ThreadContext); ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded()); EXPECT_EQ((ArrayRef{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed, 0xab, 0xad, 0xca, 0xfe}), *ExpectedContext); } // Test that we can parse an ExceptionStream where the number of // ExceptionInformation parameters provided is greater than the // specified Number of Parameters. TEST(MinidumpYAML, ExceptionStream_ExtraParameter) { SmallString<0> Storage; auto ExpectedFile = toBinary(Storage, R"( --- !minidump Streams: - Type: Exception Thread ID: 0x7 Exception Record: Exception Code: 0x23 Exception Flags: 0x5 Exception Record: 0x0102030405060708 Exception Address: 0x0a0b0c0d0e0f1011 Number of Parameters: 2 Parameter 0: 0x99 Parameter 1: 0x23 Parameter 2: 0x42 Thread Context: 3DeadBeefDefacedABadCafe)"); ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); object::MinidumpFile &File = **ExpectedFile; ASSERT_EQ(1u, File.streams().size()); Expected ExpectedStream = File.getExceptionStream(); ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded()); const minidump::ExceptionStream &Stream = *ExpectedStream; EXPECT_EQ(0x7u, Stream.ThreadId); const minidump::Exception &Exception = Stream.ExceptionRecord; EXPECT_EQ(0x23u, Exception.ExceptionCode); EXPECT_EQ(0x5u, Exception.ExceptionFlags); EXPECT_EQ(0x0102030405060708u, Exception.ExceptionRecord); EXPECT_EQ(0x0a0b0c0d0e0f1011u, Exception.ExceptionAddress); EXPECT_EQ(2u, Exception.NumberParameters); EXPECT_EQ(0x99u, Exception.ExceptionInformation[0]); EXPECT_EQ(0x23u, Exception.ExceptionInformation[1]); EXPECT_EQ(0x42u, Exception.ExceptionInformation[2]); Expected> ExpectedContext = File.getRawData(Stream.ThreadContext); ASSERT_THAT_EXPECTED(ExpectedContext, Succeeded()); EXPECT_EQ((ArrayRef{0x3d, 0xea, 0xdb, 0xee, 0xfd, 0xef, 0xac, 0xed, 0xab, 0xad, 0xca, 0xfe}), *ExpectedContext); }