diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:26:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:26:05 +0000 |
commit | 14f1b3e8826ce43b978db93a62d1166055db5394 (patch) | |
tree | 0a00ad8d3498783fe0193f3b656bca17c4c8697d /unittests/Process/minidump/MinidumpParserTest.cpp | |
parent | 4ee8c119c71a06dcad1e0fecc8c675e480e59337 (diff) |
Notes
Diffstat (limited to 'unittests/Process/minidump/MinidumpParserTest.cpp')
-rw-r--r-- | unittests/Process/minidump/MinidumpParserTest.cpp | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/unittests/Process/minidump/MinidumpParserTest.cpp b/unittests/Process/minidump/MinidumpParserTest.cpp new file mode 100644 index 0000000000000..83225e88ee03c --- /dev/null +++ b/unittests/Process/minidump/MinidumpParserTest.cpp @@ -0,0 +1,453 @@ +//===-- MinidumpTypesTest.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Project includes +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/minidump/MinidumpParser.h" +#include "Plugins/Process/minidump/MinidumpTypes.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_32.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" + +// Other libraries and framework includes +#include "gtest/gtest.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Target/MemoryRegionInfo.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +// C includes + +// C++ includes +#include <memory> + +extern const char *TestMainArgv0; + +using namespace lldb_private; +using namespace minidump; + +class MinidumpParserTest : public testing::Test { +public: + void SetUp() override { + llvm::StringRef dmp_folder = llvm::sys::path::parent_path(TestMainArgv0); + inputs_folder = dmp_folder; + llvm::sys::path::append(inputs_folder, "Inputs"); + } + + void SetUpData(const char *minidump_filename, size_t load_size = SIZE_MAX) { + llvm::SmallString<128> filename = inputs_folder; + llvm::sys::path::append(filename, minidump_filename); + FileSpec minidump_file(filename.c_str(), false); + lldb::DataBufferSP data_sp( + minidump_file.MemoryMapFileContents(0, load_size)); + llvm::Optional<MinidumpParser> optional_parser = + MinidumpParser::Create(data_sp); + ASSERT_TRUE(optional_parser.hasValue()); + parser.reset(new MinidumpParser(optional_parser.getValue())); + ASSERT_GT(parser->GetData().size(), 0UL); + } + + llvm::SmallString<128> inputs_folder; + std::unique_ptr<MinidumpParser> parser; +}; + +TEST_F(MinidumpParserTest, GetThreadsAndGetThreadContext) { + SetUpData("linux-x86_64.dmp"); + llvm::ArrayRef<MinidumpThread> thread_list; + + thread_list = parser->GetThreads(); + ASSERT_EQ(1UL, thread_list.size()); + + const MinidumpThread thread = thread_list[0]; + + EXPECT_EQ(16001UL, thread.thread_id); + + llvm::ArrayRef<uint8_t> context = parser->GetThreadContext(thread); + EXPECT_EQ(1232UL, context.size()); +} + +TEST_F(MinidumpParserTest, GetThreadsTruncatedFile) { + SetUpData("linux-x86_64.dmp", 200); + llvm::ArrayRef<MinidumpThread> thread_list; + + thread_list = parser->GetThreads(); + ASSERT_EQ(0UL, thread_list.size()); +} + +TEST_F(MinidumpParserTest, GetArchitecture) { + SetUpData("linux-x86_64.dmp"); + ASSERT_EQ(llvm::Triple::ArchType::x86_64, + parser->GetArchitecture().GetMachine()); + ASSERT_EQ(llvm::Triple::OSType::Linux, + parser->GetArchitecture().GetTriple().getOS()); +} + +TEST_F(MinidumpParserTest, GetMiscInfo) { + SetUpData("linux-x86_64.dmp"); + const MinidumpMiscInfo *misc_info = parser->GetMiscInfo(); + ASSERT_EQ(nullptr, misc_info); +} + +TEST_F(MinidumpParserTest, GetLinuxProcStatus) { + SetUpData("linux-x86_64.dmp"); + llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus(); + ASSERT_TRUE(proc_status.hasValue()); + lldb::pid_t pid = proc_status->GetPid(); + ASSERT_EQ(16001UL, pid); +} + +TEST_F(MinidumpParserTest, GetPid) { + SetUpData("linux-x86_64.dmp"); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(16001UL, pid.getValue()); +} + +TEST_F(MinidumpParserTest, GetModuleList) { + SetUpData("linux-x86_64.dmp"); + llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList(); + ASSERT_EQ(8UL, modules.size()); + std::string module_names[8] = { + "/usr/local/google/home/dvlahovski/projects/test_breakpad/a.out", + "/lib/x86_64-linux-gnu/libm-2.19.so", + "/lib/x86_64-linux-gnu/libc-2.19.so", + "/lib/x86_64-linux-gnu/libgcc_s.so.1", + "/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19", + "/lib/x86_64-linux-gnu/libpthread-2.19.so", + "/lib/x86_64-linux-gnu/ld-2.19.so", + "linux-gate.so", + }; + + for (int i = 0; i < 8; ++i) { + llvm::Optional<std::string> name = + parser->GetMinidumpString(modules[i].module_name_rva); + ASSERT_TRUE(name.hasValue()); + EXPECT_EQ(module_names[i], name.getValue()); + } +} + +TEST_F(MinidumpParserTest, GetFilteredModuleList) { + SetUpData("linux-x86_64_not_crashed.dmp"); + llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList(); + std::vector<const MinidumpModule *> filtered_modules = + parser->GetFilteredModuleList(); + EXPECT_EQ(10UL, modules.size()); + EXPECT_EQ(9UL, filtered_modules.size()); + // EXPECT_GT(modules.size(), filtered_modules.size()); + bool found = false; + for (size_t i = 0; i < filtered_modules.size(); ++i) { + llvm::Optional<std::string> name = + parser->GetMinidumpString(filtered_modules[i]->module_name_rva); + ASSERT_TRUE(name.hasValue()); + if (name.getValue() == "/tmp/test/linux-x86_64_not_crashed") { + ASSERT_FALSE(found) << "There should be only one module with this name " + "in the filtered module list"; + found = true; + ASSERT_EQ(0x400000UL, filtered_modules[i]->base_of_image); + } + } +} + +TEST_F(MinidumpParserTest, GetExceptionStream) { + SetUpData("linux-x86_64.dmp"); + const MinidumpExceptionStream *exception_stream = + parser->GetExceptionStream(); + ASSERT_NE(nullptr, exception_stream); + ASSERT_EQ(11UL, exception_stream->exception_record.exception_code); +} + +void check_mem_range_exists(std::unique_ptr<MinidumpParser> &parser, + const uint64_t range_start, + const uint64_t range_size) { + llvm::Optional<minidump::Range> range = parser->FindMemoryRange(range_start); + ASSERT_TRUE(range.hasValue()) << "There is no range containing this address"; + EXPECT_EQ(range_start, range->start); + EXPECT_EQ(range_start + range_size, range->start + range->range_ref.size()); +} + +TEST_F(MinidumpParserTest, FindMemoryRange) { + SetUpData("linux-x86_64.dmp"); + // There are two memory ranges in the file (size is in bytes, decimal): + // 1) 0x401d46 256 + // 2) 0x7ffceb34a000 12288 + EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue()); + + check_mem_range_exists(parser, 0x401d46, 256); + EXPECT_FALSE(parser->FindMemoryRange(0x401d46 + 256).hasValue()); + + check_mem_range_exists(parser, 0x7ffceb34a000, 12288); + EXPECT_FALSE(parser->FindMemoryRange(0x7ffceb34a000 + 12288).hasValue()); +} + +TEST_F(MinidumpParserTest, GetMemory) { + SetUpData("linux-x86_64.dmp"); + + EXPECT_EQ(128UL, parser->GetMemory(0x401d46, 128).size()); + EXPECT_EQ(256UL, parser->GetMemory(0x401d46, 512).size()); + + EXPECT_EQ(12288UL, parser->GetMemory(0x7ffceb34a000, 12288).size()); + EXPECT_EQ(1024UL, parser->GetMemory(0x7ffceb34a000, 1024).size()); + + EXPECT_TRUE(parser->GetMemory(0x500000, 512).empty()); +} + +TEST_F(MinidumpParserTest, FindMemoryRangeWithFullMemoryMinidump) { + SetUpData("fizzbuzz_wow64.dmp"); + + // There are a lot of ranges in the file, just testing with some of them + EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue()); + check_mem_range_exists(parser, 0x10000, 65536); // first range + check_mem_range_exists(parser, 0x40000, 4096); + EXPECT_FALSE(parser->FindMemoryRange(0x40000 + 4096).hasValue()); + check_mem_range_exists(parser, 0x77c12000, 8192); + check_mem_range_exists(parser, 0x7ffe0000, 4096); // last range + EXPECT_FALSE(parser->FindMemoryRange(0x7ffe0000 + 4096).hasValue()); +} + +void check_region_info(std::unique_ptr<MinidumpParser> &parser, + const uint64_t addr, MemoryRegionInfo::OptionalBool read, + MemoryRegionInfo::OptionalBool write, + MemoryRegionInfo::OptionalBool exec) { + auto range_info = parser->GetMemoryRegionInfo(addr); + ASSERT_TRUE(range_info.hasValue()); + EXPECT_EQ(read, range_info->GetReadable()); + EXPECT_EQ(write, range_info->GetWritable()); + EXPECT_EQ(exec, range_info->GetExecutable()); +} + +TEST_F(MinidumpParserTest, GetMemoryRegionInfo) { + SetUpData("fizzbuzz_wow64.dmp"); + + const auto yes = MemoryRegionInfo::eYes; + const auto no = MemoryRegionInfo::eNo; + + check_region_info(parser, 0x00000, no, no, no); + check_region_info(parser, 0x10000, yes, yes, no); + check_region_info(parser, 0x20000, yes, yes, no); + check_region_info(parser, 0x30000, yes, yes, no); + check_region_info(parser, 0x31000, no, no, no); + check_region_info(parser, 0x40000, yes, no, no); +} + +// Windows Minidump tests +// fizzbuzz_no_heap.dmp is copied from the WinMiniDump tests +TEST_F(MinidumpParserTest, GetArchitectureWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + ASSERT_EQ(llvm::Triple::ArchType::x86, + parser->GetArchitecture().GetMachine()); + ASSERT_EQ(llvm::Triple::OSType::Win32, + parser->GetArchitecture().GetTriple().getOS()); +} + +TEST_F(MinidumpParserTest, GetLinuxProcStatusWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus(); + ASSERT_FALSE(proc_status.hasValue()); +} + +TEST_F(MinidumpParserTest, GetMiscInfoWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + const MinidumpMiscInfo *misc_info = parser->GetMiscInfo(); + ASSERT_NE(nullptr, misc_info); + llvm::Optional<lldb::pid_t> pid = misc_info->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(4440UL, pid.getValue()); +} + +TEST_F(MinidumpParserTest, GetPidWindows) { + SetUpData("fizzbuzz_no_heap.dmp"); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(4440UL, pid.getValue()); +} + +// wow64 +TEST_F(MinidumpParserTest, GetPidWow64) { + SetUpData("fizzbuzz_wow64.dmp"); + llvm::Optional<lldb::pid_t> pid = parser->GetPid(); + ASSERT_TRUE(pid.hasValue()); + ASSERT_EQ(7836UL, pid.getValue()); +} + +TEST_F(MinidumpParserTest, GetModuleListWow64) { + SetUpData("fizzbuzz_wow64.dmp"); + llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList(); + ASSERT_EQ(16UL, modules.size()); + std::string module_names[16] = { + R"(D:\src\llvm\llvm\tools\lldb\packages\Python\lldbsuite\test\functionalities\postmortem\wow64_minidump\fizzbuzz.exe)", + R"(C:\Windows\System32\ntdll.dll)", + R"(C:\Windows\System32\wow64.dll)", + R"(C:\Windows\System32\wow64win.dll)", + R"(C:\Windows\System32\wow64cpu.dll)", + R"(D:\src\llvm\llvm\tools\lldb\packages\Python\lldbsuite\test\functionalities\postmortem\wow64_minidump\fizzbuzz.exe)", + R"(C:\Windows\SysWOW64\ntdll.dll)", + R"(C:\Windows\SysWOW64\kernel32.dll)", + R"(C:\Windows\SysWOW64\KERNELBASE.dll)", + R"(C:\Windows\SysWOW64\advapi32.dll)", + R"(C:\Windows\SysWOW64\msvcrt.dll)", + R"(C:\Windows\SysWOW64\sechost.dll)", + R"(C:\Windows\SysWOW64\rpcrt4.dll)", + R"(C:\Windows\SysWOW64\sspicli.dll)", + R"(C:\Windows\SysWOW64\CRYPTBASE.dll)", + R"(C:\Windows\System32\api-ms-win-core-synch-l1-2-0.DLL)", + }; + + for (int i = 0; i < 16; ++i) { + llvm::Optional<std::string> name = + parser->GetMinidumpString(modules[i].module_name_rva); + ASSERT_TRUE(name.hasValue()); + EXPECT_EQ(module_names[i], name.getValue()); + } +} + +// Register tests +#define REG_VAL32(x) *(reinterpret_cast<uint32_t *>(x)) +#define REG_VAL64(x) *(reinterpret_cast<uint64_t *>(x)) + +TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32) { + SetUpData("linux-i386.dmp"); + llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread)); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_i386(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_32(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map<uint64_t, uint32_t> reg_values; + + reg_values[lldb_eax_i386] = 0x00000000; + reg_values[lldb_ebx_i386] = 0xf7778000; + reg_values[lldb_ecx_i386] = 0x00000001; + reg_values[lldb_edx_i386] = 0xff9dd4a3; + reg_values[lldb_edi_i386] = 0x080482a8; + reg_values[lldb_esi_i386] = 0xff9dd55c; + reg_values[lldb_ebp_i386] = 0xff9dd53c; + reg_values[lldb_esp_i386] = 0xff9dd52c; + reg_values[lldb_eip_i386] = 0x080482a0; + reg_values[lldb_eflags_i386] = 0x00010282; + reg_values[lldb_cs_i386] = 0x00000023; + reg_values[lldb_fs_i386] = 0x00000000; + reg_values[lldb_gs_i386] = 0x00000063; + reg_values[lldb_ss_i386] = 0x0000002b; + reg_values[lldb_ds_i386] = 0x0000002b; + reg_values[lldb_es_i386] = 0x0000002b; + + for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); + ++reg_index) { + if (reg_values.find(reg_index) != reg_values.end()) { + EXPECT_EQ(reg_values[reg_index], + REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +} + +TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_64) { + SetUpData("linux-x86_64.dmp"); + llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread)); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_x86_64(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_64(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map<uint64_t, uint64_t> reg_values; + + reg_values[lldb_rax_x86_64] = 0x0000000000000000; + reg_values[lldb_rbx_x86_64] = 0x0000000000000000; + reg_values[lldb_rcx_x86_64] = 0x0000000000000010; + reg_values[lldb_rdx_x86_64] = 0x0000000000000000; + reg_values[lldb_rdi_x86_64] = 0x00007ffceb349cf0; + reg_values[lldb_rsi_x86_64] = 0x0000000000000000; + reg_values[lldb_rbp_x86_64] = 0x00007ffceb34a210; + reg_values[lldb_rsp_x86_64] = 0x00007ffceb34a210; + reg_values[lldb_r8_x86_64] = 0x00007fe9bc1aa9c0; + reg_values[lldb_r9_x86_64] = 0x0000000000000000; + reg_values[lldb_r10_x86_64] = 0x00007fe9bc3f16a0; + reg_values[lldb_r11_x86_64] = 0x0000000000000246; + reg_values[lldb_r12_x86_64] = 0x0000000000401c92; + reg_values[lldb_r13_x86_64] = 0x00007ffceb34a430; + reg_values[lldb_r14_x86_64] = 0x0000000000000000; + reg_values[lldb_r15_x86_64] = 0x0000000000000000; + reg_values[lldb_rip_x86_64] = 0x0000000000401dc6; + reg_values[lldb_rflags_x86_64] = 0x0000000000010206; + reg_values[lldb_cs_x86_64] = 0x0000000000000033; + reg_values[lldb_fs_x86_64] = 0x0000000000000000; + reg_values[lldb_gs_x86_64] = 0x0000000000000000; + reg_values[lldb_ss_x86_64] = 0x0000000000000000; + reg_values[lldb_ds_x86_64] = 0x0000000000000000; + reg_values[lldb_es_x86_64] = 0x0000000000000000; + + for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); + ++reg_index) { + if (reg_values.find(reg_index) != reg_values.end()) { + EXPECT_EQ(reg_values[reg_index], + REG_VAL64(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +} + +TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32_wow64) { + SetUpData("fizzbuzz_wow64.dmp"); + llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef<uint8_t> registers(parser->GetThreadContextWow64(thread)); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_i386(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_32(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map<uint64_t, uint32_t> reg_values; + + reg_values[lldb_eax_i386] = 0x00000000; + reg_values[lldb_ebx_i386] = 0x0037f608; + reg_values[lldb_ecx_i386] = 0x00e61578; + reg_values[lldb_edx_i386] = 0x00000008; + reg_values[lldb_edi_i386] = 0x00000000; + reg_values[lldb_esi_i386] = 0x00000002; + reg_values[lldb_ebp_i386] = 0x0037f654; + reg_values[lldb_esp_i386] = 0x0037f5b8; + reg_values[lldb_eip_i386] = 0x77ce01fd; + reg_values[lldb_eflags_i386] = 0x00000246; + reg_values[lldb_cs_i386] = 0x00000023; + reg_values[lldb_fs_i386] = 0x00000053; + reg_values[lldb_gs_i386] = 0x0000002b; + reg_values[lldb_ss_i386] = 0x0000002b; + reg_values[lldb_ds_i386] = 0x0000002b; + reg_values[lldb_es_i386] = 0x0000002b; + + for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); + ++reg_index) { + if (reg_values.find(reg_index) != reg_values.end()) { + EXPECT_EQ(reg_values[reg_index], + REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +}
\ No newline at end of file |