breakpad/src/processor/minidump_unittest.cc
Joshua Peraza c2d969cb10 Added classes to support reading unloaded module lists in minidumps.
The implementations of Module/UnloadedModule and
ModuleList/UnloadedModuleList are very similar. They have been made
separate classes because they operate on different structs, complicating
factoring code into a base class and have sufficiently different
implementation that templates would not be suitable.

When unloaded modules have partially overlapping ranges, the module
shrink down feature is used to move the start of the higher range to the
end of the lower range. If two unloaded modules overlap identically, the
second module will not be added to the range map and the failure
ignored.

Places where MinidumpUnloadedModule differs from MinidumpModule:
  code_identifier: the android/linux case is deleted since cv_records
    never exist.
  debug_file/debug_identifier/version: always return empty strings.
  Read: an expected size is provided as opposed to MD_MODULE_SIZE. A
    seek is used if there are extra, unused bytes.

Places where MinidumpUnloadedModuleList differs from
  MinidumpModuleList:
  Read: entry and header size is provided in the header in
    addition to count. This changes the checks and handling of padding.
    Failures from StoreRange are ignored.
  GetMainModule: always returns NULL.

BUG=

Change-Id: I52e93d3ccc38483f50a6418fede8b506ec879aaa
Reviewed-on: https://chromium-review.googlesource.com/421566
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2016-12-16 20:15:04 +00:00

1629 lines
60 KiB
C++

// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Unit test for Minidump. Uses a pre-generated minidump and
// verifies that certain streams are correct.
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/minidump.h"
#include "processor/logging.h"
#include "processor/synth_minidump.h"
namespace {
using google_breakpad::Minidump;
using google_breakpad::MinidumpContext;
using google_breakpad::MinidumpException;
using google_breakpad::MinidumpMemoryInfo;
using google_breakpad::MinidumpMemoryInfoList;
using google_breakpad::MinidumpMemoryList;
using google_breakpad::MinidumpMemoryRegion;
using google_breakpad::MinidumpModule;
using google_breakpad::MinidumpModuleList;
using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpUnloadedModule;
using google_breakpad::MinidumpUnloadedModuleList;
using google_breakpad::MinidumpThread;
using google_breakpad::MinidumpThreadList;
using google_breakpad::SynthMinidump::Context;
using google_breakpad::SynthMinidump::Dump;
using google_breakpad::SynthMinidump::Exception;
using google_breakpad::SynthMinidump::Memory;
using google_breakpad::SynthMinidump::Module;
using google_breakpad::SynthMinidump::UnloadedModule;
using google_breakpad::SynthMinidump::Section;
using google_breakpad::SynthMinidump::Stream;
using google_breakpad::SynthMinidump::String;
using google_breakpad::SynthMinidump::SystemInfo;
using google_breakpad::SynthMinidump::Thread;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using std::ifstream;
using std::istringstream;
using std::vector;
using ::testing::Return;
class MinidumpTest : public ::testing::Test {
public:
void SetUp() {
minidump_file_ = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/minidump2.dmp";
}
string minidump_file_;
};
TEST_F(MinidumpTest, TestMinidumpFromFile) {
Minidump minidump(minidump_file_);
ASSERT_EQ(minidump.path(), minidump_file_);
ASSERT_TRUE(minidump.Read());
const MDRawHeader* header = minidump.header();
ASSERT_NE(header, (MDRawHeader*)NULL);
ASSERT_EQ(header->signature, uint32_t(MD_HEADER_SIGNATURE));
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
ASSERT_TRUE(md_module != NULL);
ASSERT_EQ("c:\\test_app.exe", md_module->code_file());
ASSERT_EQ("c:\\test_app.pdb", md_module->debug_file());
ASSERT_EQ("45D35F6C2d000", md_module->code_identifier());
ASSERT_EQ("5A9832E5287241C1838ED98914E9B7FF1", md_module->debug_identifier());
}
TEST_F(MinidumpTest, TestMinidumpFromStream) {
// read minidump contents into memory, construct a stringstream around them
ifstream file_stream(minidump_file_.c_str(), std::ios::in);
ASSERT_TRUE(file_stream.good());
vector<char> bytes;
file_stream.seekg(0, std::ios_base::end);
ASSERT_TRUE(file_stream.good());
bytes.resize(file_stream.tellg());
file_stream.seekg(0, std::ios_base::beg);
ASSERT_TRUE(file_stream.good());
file_stream.read(&bytes[0], bytes.size());
ASSERT_TRUE(file_stream.good());
string str(&bytes[0], bytes.size());
istringstream stream(str);
ASSERT_TRUE(stream.good());
// now read minidump from stringstream
Minidump minidump(stream);
ASSERT_EQ(minidump.path(), "");
ASSERT_TRUE(minidump.Read());
const MDRawHeader* header = minidump.header();
ASSERT_NE(header, (MDRawHeader*)NULL);
ASSERT_EQ(header->signature, uint32_t(MD_HEADER_SIGNATURE));
//TODO: add more checks here
}
TEST(Dump, ReadBackEmpty) {
Dump dump(0);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream stream(contents);
Minidump minidump(stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
}
TEST(Dump, ReadBackEmptyBigEndian) {
Dump big_minidump(0, kBigEndian);
big_minidump.Finish();
string contents;
ASSERT_TRUE(big_minidump.GetContents(&contents));
istringstream stream(contents);
Minidump minidump(stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
}
TEST(Dump, OneStream) {
Dump dump(0, kBigEndian);
Stream stream(dump, 0xfbb7fa2bU);
stream.Append("stream contents");
dump.Add(&stream);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ(0xfbb7fa2bU, dir->stream_type);
uint32_t stream_length;
ASSERT_TRUE(minidump.SeekToStreamType(0xfbb7fa2bU, &stream_length));
ASSERT_EQ(15U, stream_length);
char stream_contents[15];
ASSERT_TRUE(minidump.ReadBytes(stream_contents, sizeof(stream_contents)));
EXPECT_EQ(string("stream contents"),
string(stream_contents, sizeof(stream_contents)));
EXPECT_FALSE(minidump.GetThreadList());
EXPECT_FALSE(minidump.GetModuleList());
EXPECT_FALSE(minidump.GetMemoryList());
EXPECT_FALSE(minidump.GetException());
EXPECT_FALSE(minidump.GetAssertion());
EXPECT_FALSE(minidump.GetSystemInfo());
EXPECT_FALSE(minidump.GetMiscInfo());
EXPECT_FALSE(minidump.GetBreakpadInfo());
}
TEST(Dump, OneMemory) {
Dump dump(0, kBigEndian);
Memory memory(dump, 0x309d68010bd21b2cULL);
memory.Append("memory contents");
dump.Add(&memory);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((uint32_t) MD_MEMORY_LIST_STREAM, dir->stream_type);
MinidumpMemoryList *memory_list = minidump.GetMemoryList();
ASSERT_TRUE(memory_list != NULL);
ASSERT_EQ(1U, memory_list->region_count());
MinidumpMemoryRegion *region1 = memory_list->GetMemoryRegionAtIndex(0);
ASSERT_EQ(0x309d68010bd21b2cULL, region1->GetBase());
ASSERT_EQ(15U, region1->GetSize());
const uint8_t *region1_bytes = region1->GetMemory();
ASSERT_TRUE(memcmp("memory contents", region1_bytes, 15) == 0);
}
// One thread --- and its requisite entourage.
TEST(Dump, OneThread) {
Dump dump(0, kLittleEndian);
Memory stack(dump, 0x2326a0fa);
stack.Append("stack for thread");
MDRawContextX86 raw_context;
const uint32_t kExpectedEIP = 0x6913f540;
raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = kExpectedEIP;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Thread thread(dump, 0xa898f11b, stack, context,
0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
dump.Add(&stack);
dump.Add(&context);
dump.Add(&thread);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
MinidumpMemoryList *md_memory_list = minidump.GetMemoryList();
ASSERT_TRUE(md_memory_list != NULL);
ASSERT_EQ(1U, md_memory_list->region_count());
MinidumpMemoryRegion *md_region = md_memory_list->GetMemoryRegionAtIndex(0);
ASSERT_EQ(0x2326a0faU, md_region->GetBase());
ASSERT_EQ(16U, md_region->GetSize());
const uint8_t *region_bytes = md_region->GetMemory();
ASSERT_TRUE(memcmp("stack for thread", region_bytes, 16) == 0);
MinidumpThreadList *thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list != NULL);
ASSERT_EQ(1U, thread_list->thread_count());
MinidumpThread *md_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(md_thread != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
ASSERT_EQ(0xa898f11bU, thread_id);
MinidumpMemoryRegion *md_stack = md_thread->GetMemory();
ASSERT_TRUE(md_stack != NULL);
ASSERT_EQ(0x2326a0faU, md_stack->GetBase());
ASSERT_EQ(16U, md_stack->GetSize());
const uint8_t *md_stack_bytes = md_stack->GetMemory();
ASSERT_TRUE(memcmp("stack for thread", md_stack_bytes, 16) == 0);
MinidumpContext *md_context = md_thread->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
uint64_t eip;
ASSERT_TRUE(md_context->GetInstructionPointer(&eip));
EXPECT_EQ(kExpectedEIP, eip);
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((uint32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
(md_raw_context->context_flags
& (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
EXPECT_EQ(0x382583b9U, raw_context.esi);
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
EXPECT_EQ(kExpectedEIP, raw_context.eip);
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
EXPECT_EQ(0x659caaa4U, raw_context.esp);
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
TEST(Dump, ThreadMissingMemory) {
Dump dump(0, kLittleEndian);
Memory stack(dump, 0x2326a0fa);
// Stack has no contents.
MDRawContextX86 raw_context;
memset(&raw_context, 0, sizeof(raw_context));
raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
Context context(dump, raw_context);
Thread thread(dump, 0xa898f11b, stack, context,
0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
dump.Add(&stack);
dump.Add(&context);
dump.Add(&thread);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
// This should succeed even though the thread has no stack memory.
MinidumpThreadList* thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list != NULL);
ASSERT_EQ(1U, thread_list->thread_count());
MinidumpThread* md_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(md_thread != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
ASSERT_EQ(0xa898f11bU, thread_id);
MinidumpContext* md_context = md_thread->GetContext();
ASSERT_NE(reinterpret_cast<MinidumpContext*>(NULL), md_context);
MinidumpMemoryRegion* md_stack = md_thread->GetMemory();
ASSERT_EQ(reinterpret_cast<MinidumpMemoryRegion*>(NULL), md_stack);
}
TEST(Dump, ThreadMissingContext) {
Dump dump(0, kLittleEndian);
Memory stack(dump, 0x2326a0fa);
stack.Append("stack for thread");
// Context is empty.
Context context(dump);
Thread thread(dump, 0xa898f11b, stack, context,
0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
dump.Add(&stack);
dump.Add(&context);
dump.Add(&thread);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
// This should succeed even though the thread has no stack memory.
MinidumpThreadList* thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list != NULL);
ASSERT_EQ(1U, thread_list->thread_count());
MinidumpThread* md_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(md_thread != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
ASSERT_EQ(0xa898f11bU, thread_id);
MinidumpMemoryRegion* md_stack = md_thread->GetMemory();
ASSERT_NE(reinterpret_cast<MinidumpMemoryRegion*>(NULL), md_stack);
MinidumpContext* md_context = md_thread->GetContext();
ASSERT_EQ(reinterpret_cast<MinidumpContext*>(NULL), md_context);
}
TEST(Dump, OneUnloadedModule) {
Dump dump(0, kBigEndian);
String module_name(dump, "unloaded module");
String csd_version(dump, "Windows 9000");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
UnloadedModule unloaded_module(
dump,
0xa90206ca83eb2852ULL,
0xada542bd,
module_name,
0x34571371,
0xb1054d2a);
dump.Add(&unloaded_module);
dump.Add(&module_name);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(1);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((uint32_t) MD_UNLOADED_MODULE_LIST_STREAM, dir->stream_type);
MinidumpUnloadedModuleList *md_unloaded_module_list =
minidump.GetUnloadedModuleList();
ASSERT_TRUE(md_unloaded_module_list != NULL);
ASSERT_EQ(1U, md_unloaded_module_list->module_count());
const MinidumpUnloadedModule *md_unloaded_module =
md_unloaded_module_list->GetModuleAtIndex(0);
ASSERT_TRUE(md_unloaded_module != NULL);
ASSERT_EQ(0xa90206ca83eb2852ULL, md_unloaded_module->base_address());
ASSERT_EQ(0xada542bd, md_unloaded_module->size());
ASSERT_EQ("unloaded module", md_unloaded_module->code_file());
ASSERT_EQ("", md_unloaded_module->debug_file());
// time_date_stamp and size_of_image concatenated
ASSERT_EQ("B1054D2Aada542bd", md_unloaded_module->code_identifier());
ASSERT_EQ("", md_unloaded_module->debug_identifier());
const MDRawUnloadedModule *md_raw_unloaded_module =
md_unloaded_module->module();
ASSERT_TRUE(md_raw_unloaded_module != NULL);
ASSERT_EQ(0xb1054d2aU, md_raw_unloaded_module->time_date_stamp);
ASSERT_EQ(0x34571371U, md_raw_unloaded_module->checksum);
}
static const MDVSFixedFileInfo fixed_file_info = {
0xb2fba33a, // signature
0x33d7a728, // struct_version
0x31afcb20, // file_version_hi
0xe51cdab1, // file_version_lo
0xd1ea6907, // product_version_hi
0x03032857, // product_version_lo
0x11bf71d7, // file_flags_mask
0x5fb8cdbf, // file_flags
0xe45d0d5d, // file_os
0x107d9562, // file_type
0x5a8844d4, // file_subtype
0xa8d30b20, // file_date_hi
0x651c3e4e // file_date_lo
};
TEST(Dump, OneModule) {
Dump dump(0, kBigEndian);
String module_name(dump, "single module");
Section cv_info(dump);
cv_info
.D32(MD_CVINFOPDB70_SIGNATURE) // signature
// signature, a MDGUID
.D32(0xabcd1234)
.D16(0xf00d)
.D16(0xbeef)
.Append("\x01\x02\x03\x04\x05\x06\x07\x08")
.D32(1) // age
.AppendCString("c:\\foo\\file.pdb"); // pdb_file_name
String csd_version(dump, "Windows 9000");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
module_name,
0xb1054d2a,
0x34571371,
fixed_file_info, // from synth_minidump_unittest_data.h
&cv_info, nullptr);
dump.Add(&module);
dump.Add(&module_name);
dump.Add(&cv_info);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(1);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((uint32_t) MD_MODULE_LIST_STREAM, dir->stream_type);
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(1U, md_module_list->module_count());
const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
ASSERT_TRUE(md_module != NULL);
ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address());
ASSERT_EQ(0xada542bd, md_module->size());
ASSERT_EQ("single module", md_module->code_file());
ASSERT_EQ("c:\\foo\\file.pdb", md_module->debug_file());
// time_date_stamp and size_of_image concatenated
ASSERT_EQ("B1054D2Aada542bd", md_module->code_identifier());
ASSERT_EQ("ABCD1234F00DBEEF01020304050607081", md_module->debug_identifier());
const MDRawModule *md_raw_module = md_module->module();
ASSERT_TRUE(md_raw_module != NULL);
ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp);
ASSERT_EQ(0x34571371U, md_raw_module->checksum);
ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info,
sizeof(fixed_file_info)) == 0);
}
// Test that a module with a MDCVInfoELF CV record is handled properly.
TEST(Dump, OneModuleCVELF) {
Dump dump(0, kLittleEndian);
String module_name(dump, "elf module");
Section cv_info(dump);
cv_info
.D32(MD_CVINFOELF_SIGNATURE) // signature
// build_id
.Append("\x5f\xa9\xcd\xb4\x10\x53\xdf\x1b\x86\xfa\xb7\x33\xb4\xdf"
"\x37\x38\xce\xa3\x4a\x87");
const MDRawSystemInfo linux_x86 = {
MD_CPU_ARCHITECTURE_X86, // processor_architecture
6, // processor_level
0xd08, // processor_revision
1, // number_of_processors
0, // product_type
0, // major_version
0, // minor_version
0, // build_number
MD_OS_LINUX, // platform_id
0xdeadbeef, // csd_version_rva
0x100, // suite_mask
0, // reserved2
{ // cpu
{ // x86_cpu_info
{ 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id
0x6d8, // version_information
0xafe9fbff, // feature_information
0xffffffff // amd_extended_cpu_features
}
}
};
String csd_version(dump, "Literally Linux");
SystemInfo system_info(dump, linux_x86, csd_version);
Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
module_name,
0xb1054d2a,
0x34571371,
fixed_file_info, // from synth_minidump_unittest_data.h
&cv_info, nullptr);
dump.Add(&module);
dump.Add(&module_name);
dump.Add(&cv_info);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(1U, md_module_list->module_count());
const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
ASSERT_TRUE(md_module != NULL);
ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address());
ASSERT_EQ(0xada542bd, md_module->size());
ASSERT_EQ("elf module", md_module->code_file());
// debug_file == code_file
ASSERT_EQ("elf module", md_module->debug_file());
// just the build_id, directly
ASSERT_EQ("5fa9cdb41053df1b86fab733b4df3738cea34a87",
md_module->code_identifier());
// build_id truncted to GUID length and treated as such, with zero
// age appended
ASSERT_EQ("B4CDA95F53101BDF86FAB733B4DF37380", md_module->debug_identifier());
const MDRawModule *md_raw_module = md_module->module();
ASSERT_TRUE(md_raw_module != NULL);
ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp);
ASSERT_EQ(0x34571371U, md_raw_module->checksum);
ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info,
sizeof(fixed_file_info)) == 0);
}
// Test that a build_id that's shorter than a GUID is handled properly.
TEST(Dump, CVELFShort) {
Dump dump(0, kLittleEndian);
String module_name(dump, "elf module");
Section cv_info(dump);
cv_info
.D32(MD_CVINFOELF_SIGNATURE) // signature
// build_id, shorter than a GUID
.Append("\x5f\xa9\xcd\xb4");
const MDRawSystemInfo linux_x86 = {
MD_CPU_ARCHITECTURE_X86, // processor_architecture
6, // processor_level
0xd08, // processor_revision
1, // number_of_processors
0, // product_type
0, // major_version
0, // minor_version
0, // build_number
MD_OS_LINUX, // platform_id
0xdeadbeef, // csd_version_rva
0x100, // suite_mask
0, // reserved2
{ // cpu
{ // x86_cpu_info
{ 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id
0x6d8, // version_information
0xafe9fbff, // feature_information
0xffffffff // amd_extended_cpu_features
}
}
};
String csd_version(dump, "Literally Linux");
SystemInfo system_info(dump, linux_x86, csd_version);
Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
module_name,
0xb1054d2a,
0x34571371,
fixed_file_info, // from synth_minidump_unittest_data.h
&cv_info, nullptr);
dump.Add(&module);
dump.Add(&module_name);
dump.Add(&cv_info);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(1U, md_module_list->module_count());
const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
ASSERT_TRUE(md_module != NULL);
// just the build_id, directly
ASSERT_EQ("5fa9cdb4", md_module->code_identifier());
// build_id expanded to GUID length and treated as such, with zero
// age appended
ASSERT_EQ("B4CDA95F0000000000000000000000000", md_module->debug_identifier());
}
// Test that a build_id that's very long is handled properly.
TEST(Dump, CVELFLong) {
Dump dump(0, kLittleEndian);
String module_name(dump, "elf module");
Section cv_info(dump);
cv_info
.D32(MD_CVINFOELF_SIGNATURE) // signature
// build_id, lots of bytes
.Append("\x5f\xa9\xcd\xb4\x10\x53\xdf\x1b\x86\xfa\xb7\x33\xb4\xdf"
"\x37\x38\xce\xa3\x4a\x87\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\x0c\x0d\x0e\x0f");
const MDRawSystemInfo linux_x86 = {
MD_CPU_ARCHITECTURE_X86, // processor_architecture
6, // processor_level
0xd08, // processor_revision
1, // number_of_processors
0, // product_type
0, // major_version
0, // minor_version
0, // build_number
MD_OS_LINUX, // platform_id
0xdeadbeef, // csd_version_rva
0x100, // suite_mask
0, // reserved2
{ // cpu
{ // x86_cpu_info
{ 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id
0x6d8, // version_information
0xafe9fbff, // feature_information
0xffffffff // amd_extended_cpu_features
}
}
};
String csd_version(dump, "Literally Linux");
SystemInfo system_info(dump, linux_x86, csd_version);
Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
module_name,
0xb1054d2a,
0x34571371,
fixed_file_info, // from synth_minidump_unittest_data.h
&cv_info, nullptr);
dump.Add(&module);
dump.Add(&module_name);
dump.Add(&cv_info);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(1U, md_module_list->module_count());
const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
ASSERT_TRUE(md_module != NULL);
// just the build_id, directly
ASSERT_EQ(
"5fa9cdb41053df1b86fab733b4df3738cea34a870102030405060708090a0b0c0d0e0f",
md_module->code_identifier());
// build_id truncated to GUID length and treated as such, with zero
// age appended.
ASSERT_EQ("B4CDA95F53101BDF86FAB733B4DF37380", md_module->debug_identifier());
}
TEST(Dump, OneSystemInfo) {
Dump dump(0, kLittleEndian);
String csd_version(dump, "Petulant Pierogi");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((uint32_t) MD_SYSTEM_INFO_STREAM, dir->stream_type);
MinidumpSystemInfo *md_system_info = minidump.GetSystemInfo();
ASSERT_TRUE(md_system_info != NULL);
ASSERT_EQ("windows", md_system_info->GetOS());
ASSERT_EQ("x86", md_system_info->GetCPU());
ASSERT_EQ("Petulant Pierogi", *md_system_info->GetCSDVersion());
ASSERT_EQ("GenuineIntel", *md_system_info->GetCPUVendor());
}
TEST(Dump, BigDump) {
Dump dump(0, kLittleEndian);
// A SystemInfo stream.
String csd_version(dump, "Munificent Macaque");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
dump.Add(&csd_version);
dump.Add(&system_info);
// Five threads!
Memory stack0(dump, 0x70b9ebfc);
stack0.Append("stack for thread zero");
MDRawContextX86 raw_context0;
raw_context0.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context0.eip = 0xaf0709e4;
Context context0(dump, raw_context0);
Thread thread0(dump, 0xbbef4432, stack0, context0,
0xd0377e7b, 0xdb8eb0cf, 0xd73bc314, 0x09d357bac7f9a163ULL);
dump.Add(&stack0);
dump.Add(&context0);
dump.Add(&thread0);
Memory stack1(dump, 0xf988cc45);
stack1.Append("stack for thread one");
MDRawContextX86 raw_context1;
raw_context1.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context1.eip = 0xe4f56f81;
Context context1(dump, raw_context1);
Thread thread1(dump, 0x657c3f58, stack1, context1,
0xa68fa182, 0x6f3cf8dd, 0xe3a78ccf, 0x78cc84775e4534bbULL);
dump.Add(&stack1);
dump.Add(&context1);
dump.Add(&thread1);
Memory stack2(dump, 0xc8a92e7c);
stack2.Append("stack for thread two");
MDRawContextX86 raw_context2;
raw_context2.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context2.eip = 0xb336a438;
Context context2(dump, raw_context2);
Thread thread2(dump, 0xdf4b8a71, stack2, context2,
0x674c26b6, 0x445d7120, 0x7e700c56, 0xd89bf778e7793e17ULL);
dump.Add(&stack2);
dump.Add(&context2);
dump.Add(&thread2);
Memory stack3(dump, 0x36d08e08);
stack3.Append("stack for thread three");
MDRawContextX86 raw_context3;
raw_context3.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context3.eip = 0xdf99a60c;
Context context3(dump, raw_context3);
Thread thread3(dump, 0x86e6c341, stack3, context3,
0x32dc5c55, 0x17a2aba8, 0xe0cc75e7, 0xa46393994dae83aeULL);
dump.Add(&stack3);
dump.Add(&context3);
dump.Add(&thread3);
Memory stack4(dump, 0x1e0ab4fa);
stack4.Append("stack for thread four");
MDRawContextX86 raw_context4;
raw_context4.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context4.eip = 0xaa646267;
Context context4(dump, raw_context4);
Thread thread4(dump, 0x261a28d4, stack4, context4,
0x6ebd389e, 0xa0cd4759, 0x30168846, 0x164f650a0cf39d35ULL);
dump.Add(&stack4);
dump.Add(&context4);
dump.Add(&thread4);
// Three modules!
String module1_name(dump, "module one");
Module module1(dump, 0xeb77da57b5d4cbdaULL, 0x83cd5a37, module1_name);
dump.Add(&module1_name);
dump.Add(&module1);
String module2_name(dump, "module two");
Module module2(dump, 0x8675884adfe5ac90ULL, 0xb11e4ea3, module2_name);
dump.Add(&module2_name);
dump.Add(&module2);
String module3_name(dump, "module three");
Module module3(dump, 0x95fc1544da321b6cULL, 0x7c2bf081, module3_name);
dump.Add(&module3_name);
dump.Add(&module3);
// Unloaded modules!
uint64_t umodule1_base = 0xeb77da57b5d4cbdaULL;
uint32_t umodule1_size = 0x83cd5a37;
String umodule1_name(dump, "unloaded module one");
UnloadedModule unloaded_module1(dump, umodule1_base, umodule1_size,
umodule1_name);
dump.Add(&umodule1_name);
dump.Add(&unloaded_module1);
uint64_t umodule2_base = 0xeb77da57b5d4cbdaULL;
uint32_t umodule2_size = 0x83cd5a37;
String umodule2_name(dump, "unloaded module two");
UnloadedModule unloaded_module2(dump, umodule2_base, umodule2_size,
umodule2_name);
dump.Add(&umodule2_name);
dump.Add(&unloaded_module2);
uint64_t umodule3_base = 0xeb77da5839a20000ULL;
uint32_t umodule3_size = 0x83cd5a37;
String umodule3_name(dump, "unloaded module three");
UnloadedModule unloaded_module3(dump, umodule3_base, umodule3_size,
umodule3_name);
dump.Add(&umodule3_name);
dump.Add(&unloaded_module3);
// Add one more memory region, on top of the five stacks.
Memory memory5(dump, 0x61979e828040e564ULL);
memory5.Append("contents of memory 5");
dump.Add(&memory5);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(5U, minidump.GetDirectoryEntryCount());
// Check the threads.
MinidumpThreadList *thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list != NULL);
ASSERT_EQ(5U, thread_list->thread_count());
uint32_t thread_id;
ASSERT_TRUE(thread_list->GetThreadAtIndex(0)->GetThreadID(&thread_id));
ASSERT_EQ(0xbbef4432U, thread_id);
ASSERT_EQ(0x70b9ebfcU,
thread_list->GetThreadAtIndex(0)->GetMemory()->GetBase());
ASSERT_EQ(0xaf0709e4U,
thread_list->GetThreadAtIndex(0)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(1)->GetThreadID(&thread_id));
ASSERT_EQ(0x657c3f58U, thread_id);
ASSERT_EQ(0xf988cc45U,
thread_list->GetThreadAtIndex(1)->GetMemory()->GetBase());
ASSERT_EQ(0xe4f56f81U,
thread_list->GetThreadAtIndex(1)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(2)->GetThreadID(&thread_id));
ASSERT_EQ(0xdf4b8a71U, thread_id);
ASSERT_EQ(0xc8a92e7cU,
thread_list->GetThreadAtIndex(2)->GetMemory()->GetBase());
ASSERT_EQ(0xb336a438U,
thread_list->GetThreadAtIndex(2)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(3)->GetThreadID(&thread_id));
ASSERT_EQ(0x86e6c341U, thread_id);
ASSERT_EQ(0x36d08e08U,
thread_list->GetThreadAtIndex(3)->GetMemory()->GetBase());
ASSERT_EQ(0xdf99a60cU,
thread_list->GetThreadAtIndex(3)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(4)->GetThreadID(&thread_id));
ASSERT_EQ(0x261a28d4U, thread_id);
ASSERT_EQ(0x1e0ab4faU,
thread_list->GetThreadAtIndex(4)->GetMemory()->GetBase());
ASSERT_EQ(0xaa646267U,
thread_list->GetThreadAtIndex(4)->GetContext()->GetContextX86()
->eip);
// Check the modules.
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(3U, md_module_list->module_count());
EXPECT_EQ(0xeb77da57b5d4cbdaULL,
md_module_list->GetModuleAtIndex(0)->base_address());
EXPECT_EQ(0x8675884adfe5ac90ULL,
md_module_list->GetModuleAtIndex(1)->base_address());
EXPECT_EQ(0x95fc1544da321b6cULL,
md_module_list->GetModuleAtIndex(2)->base_address());
// Check unloaded modules
MinidumpUnloadedModuleList *md_unloaded_module_list =
minidump.GetUnloadedModuleList();
ASSERT_TRUE(md_unloaded_module_list != NULL);
ASSERT_EQ(3U, md_unloaded_module_list->module_count());
EXPECT_EQ(umodule1_base,
md_unloaded_module_list->GetModuleAtIndex(0)->base_address());
EXPECT_EQ(umodule2_base,
md_unloaded_module_list->GetModuleAtIndex(1)->base_address());
EXPECT_EQ(umodule3_base,
md_unloaded_module_list->GetModuleAtIndex(2)->base_address());
const MinidumpUnloadedModule *umodule =
md_unloaded_module_list->GetModuleForAddress(
umodule1_base + umodule1_size / 2);
EXPECT_EQ(umodule1_base, umodule->base_address());
umodule = md_unloaded_module_list->GetModuleAtSequence(0);
EXPECT_EQ(umodule1_base, umodule->base_address());
EXPECT_EQ(NULL, md_unloaded_module_list->GetMainModule());
}
TEST(Dump, OneMemoryInfo) {
Dump dump(0, kBigEndian);
Stream stream(dump, MD_MEMORY_INFO_LIST_STREAM);
// Add the MDRawMemoryInfoList header.
const uint64_t kNumberOfEntries = 1;
stream.D32(sizeof(MDRawMemoryInfoList)) // size_of_header
.D32(sizeof(MDRawMemoryInfo)) // size_of_entry
.D64(kNumberOfEntries); // number_of_entries
// Now add a MDRawMemoryInfo entry.
const uint64_t kBaseAddress = 0x1000;
const uint64_t kRegionSize = 0x2000;
stream.D64(kBaseAddress) // base_address
.D64(kBaseAddress) // allocation_base
.D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // allocation_protection
.D32(0) // __alignment1
.D64(kRegionSize) // region_size
.D32(MD_MEMORY_STATE_COMMIT) // state
.D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // protection
.D32(MD_MEMORY_TYPE_PRIVATE) // type
.D32(0); // __alignment2
dump.Add(&stream);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((uint32_t) MD_MEMORY_INFO_LIST_STREAM, dir->stream_type);
MinidumpMemoryInfoList *info_list = minidump.GetMemoryInfoList();
ASSERT_TRUE(info_list != NULL);
ASSERT_EQ(1U, info_list->info_count());
const MinidumpMemoryInfo *info1 = info_list->GetMemoryInfoAtIndex(0);
ASSERT_EQ(kBaseAddress, info1->GetBase());
ASSERT_EQ(kRegionSize, info1->GetSize());
ASSERT_TRUE(info1->IsExecutable());
ASSERT_TRUE(info1->IsWritable());
// Should get back the same memory region here.
const MinidumpMemoryInfo *info2 =
info_list->GetMemoryInfoForAddress(kBaseAddress + kRegionSize / 2);
ASSERT_EQ(kBaseAddress, info2->GetBase());
ASSERT_EQ(kRegionSize, info2->GetSize());
}
TEST(Dump, OneExceptionX86) {
Dump dump(0, kLittleEndian);
MDRawContextX86 raw_context;
raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = 0x6913f540;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcdU, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((uint32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
(md_raw_context->context_flags
& (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
EXPECT_EQ(0x382583b9U, raw_context.esi);
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
EXPECT_EQ(0x6913f540U, raw_context.eip);
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
EXPECT_EQ(0x659caaa4U, raw_context.esp);
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
TEST(Dump, OneExceptionX86XState) {
Dump dump(0, kLittleEndian);
MDRawContextX86 raw_context;
raw_context.context_flags = MD_CONTEXT_X86_INTEGER |
MD_CONTEXT_X86_CONTROL | MD_CONTEXT_X86_XSTATE;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = 0x6913f540;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcdU, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((uint32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
(md_raw_context->context_flags
& (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
EXPECT_EQ(0x382583b9U, raw_context.esi);
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
EXPECT_EQ(0x6913f540U, raw_context.eip);
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
EXPECT_EQ(0x659caaa4U, raw_context.esp);
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
// Testing that the CPU type can be loaded from a system info stream when
// the CPU flags are missing from the context_flags of an exception record
TEST(Dump, OneExceptionX86NoCPUFlags) {
Dump dump(0, kLittleEndian);
MDRawContextX86 raw_context;
// Intentionally not setting CPU type in the context_flags
raw_context.context_flags = 0;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = 0x6913f540;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
// Add system info. This is needed as an alternative source for CPU type
// information. Note, that the CPU flags were intentionally skipped from
// the context_flags and this alternative source is required.
String csd_version(dump, "Service Pack 2");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcdU, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
ASSERT_TRUE(md_raw_context != NULL);
// Even though the CPU flags were missing from the context_flags, the
// GetContext call above is expected to load the missing CPU flags from the
// system info stream and set the CPU type bits in context_flags.
ASSERT_EQ((uint32_t) (MD_CONTEXT_X86), md_raw_context->context_flags);
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
EXPECT_EQ(0x382583b9U, raw_context.esi);
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
EXPECT_EQ(0x6913f540U, raw_context.eip);
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
EXPECT_EQ(0x659caaa4U, raw_context.esp);
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
// This test covers a scenario where a dump contains an exception but the
// context record of the exception is missing the CPU type information in its
// context_flags. The dump has no system info stream so it is imposible to
// deduce the CPU type, hence the context record is unusable.
TEST(Dump, OneExceptionX86NoCPUFlagsNoSystemInfo) {
Dump dump(0, kLittleEndian);
MDRawContextX86 raw_context;
// Intentionally not setting CPU type in the context_flags
raw_context.context_flags = 0;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = 0x6913f540;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcdU, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
// The context record of the exception is unusable because the context_flags
// don't have CPU type information and at the same time the minidump lacks
// system info stream so it is impossible to deduce the CPU type.
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_EQ(NULL, md_context);
}
TEST(Dump, OneExceptionARM) {
Dump dump(0, kLittleEndian);
MDRawContextARM raw_context;
raw_context.context_flags = MD_CONTEXT_ARM_INTEGER;
raw_context.iregs[0] = 0x3ecba80d;
raw_context.iregs[1] = 0x382583b9;
raw_context.iregs[2] = 0x7fccc03f;
raw_context.iregs[3] = 0xf62f8ec2;
raw_context.iregs[4] = 0x46a6a6a8;
raw_context.iregs[5] = 0x6a5025e2;
raw_context.iregs[6] = 0xd9fabb4a;
raw_context.iregs[7] = 0x6913f540;
raw_context.iregs[8] = 0xbffe6eda;
raw_context.iregs[9] = 0xb2ce1e2d;
raw_context.iregs[10] = 0x659caaa4;
raw_context.iregs[11] = 0xf0e0d0c0;
raw_context.iregs[12] = 0xa9b8c7d6;
raw_context.iregs[13] = 0x12345678;
raw_context.iregs[14] = 0xabcd1234;
raw_context.iregs[15] = 0x10203040;
raw_context.cpsr = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcdU, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_ARM, md_context->GetContextCPU());
const MDRawContextARM *md_raw_context = md_context->GetContextARM();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_ARM_INTEGER,
(md_raw_context->context_flags
& MD_CONTEXT_ARM_INTEGER));
EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
EXPECT_EQ(0x2e951ef7U, raw_context.cpsr);
}
TEST(Dump, OneExceptionARMOldFlags) {
Dump dump(0, kLittleEndian);
MDRawContextARM raw_context;
// MD_CONTEXT_ARM_INTEGER, but with _OLD
raw_context.context_flags = MD_CONTEXT_ARM_OLD | 0x00000002;
raw_context.iregs[0] = 0x3ecba80d;
raw_context.iregs[1] = 0x382583b9;
raw_context.iregs[2] = 0x7fccc03f;
raw_context.iregs[3] = 0xf62f8ec2;
raw_context.iregs[4] = 0x46a6a6a8;
raw_context.iregs[5] = 0x6a5025e2;
raw_context.iregs[6] = 0xd9fabb4a;
raw_context.iregs[7] = 0x6913f540;
raw_context.iregs[8] = 0xbffe6eda;
raw_context.iregs[9] = 0xb2ce1e2d;
raw_context.iregs[10] = 0x659caaa4;
raw_context.iregs[11] = 0xf0e0d0c0;
raw_context.iregs[12] = 0xa9b8c7d6;
raw_context.iregs[13] = 0x12345678;
raw_context.iregs[14] = 0xabcd1234;
raw_context.iregs[15] = 0x10203040;
raw_context.cpsr = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcdU, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_ARM, md_context->GetContextCPU());
const MDRawContextARM *md_raw_context = md_context->GetContextARM();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_ARM_INTEGER,
(md_raw_context->context_flags
& MD_CONTEXT_ARM_INTEGER));
EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
EXPECT_EQ(0x2e951ef7U, raw_context.cpsr);
}
TEST(Dump, OneExceptionMIPS) {
Dump dump(0, kLittleEndian);
MDRawContextMIPS raw_context;
raw_context.context_flags = MD_CONTEXT_MIPS_INTEGER;
raw_context.iregs[0] = 0x3ecba80d;
raw_context.iregs[1] = 0x382583b9;
raw_context.iregs[2] = 0x7fccc03f;
raw_context.iregs[3] = 0xf62f8ec2;
raw_context.iregs[4] = 0x46a6a6a8;
raw_context.iregs[5] = 0x6a5025e2;
raw_context.iregs[6] = 0xd9fabb4a;
raw_context.iregs[7] = 0x6913f540;
raw_context.iregs[8] = 0xbffe6eda;
raw_context.iregs[9] = 0xb2ce1e2d;
raw_context.iregs[10] = 0x659caaa4;
raw_context.iregs[11] = 0xf0e0d0c0;
raw_context.iregs[12] = 0xa9b8c7d6;
raw_context.iregs[13] = 0x12345678;
raw_context.iregs[14] = 0xabcd1234;
raw_context.iregs[15] = 0x10203040;
raw_context.iregs[16] = 0xa80d3ecb;
raw_context.iregs[17] = 0x83b93825;
raw_context.iregs[18] = 0xc03f7fcc;
raw_context.iregs[19] = 0x8ec2f62f;
raw_context.iregs[20] = 0xa6a846a6;
raw_context.iregs[21] = 0x25e26a50;
raw_context.iregs[22] = 0xbb4ad9fa;
raw_context.iregs[23] = 0xf5406913;
raw_context.iregs[24] = 0x6edabffe;
raw_context.iregs[25] = 0x1e2db2ce;
raw_context.iregs[26] = 0xaaa4659c;
raw_context.iregs[27] = 0xd0c0f0e0;
raw_context.iregs[28] = 0xc7d6a9b8;
raw_context.iregs[29] = 0x56781234;
raw_context.iregs[30] = 0x1234abcd;
raw_context.iregs[31] = 0x30401020;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // Thread id.
0xdcba4321, // Exception code.
0xf0e0d0c0, // Exception flags.
0x0919a9b9); // Exception address.
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
uint32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcdU, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9U,
raw_exception->exception_record.exception_address);
MinidumpContext* md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_MIPS, md_context->GetContextCPU());
const MDRawContextMIPS* md_raw_context = md_context->GetContextMIPS();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((uint32_t) MD_CONTEXT_MIPS_INTEGER,
(md_raw_context->context_flags & MD_CONTEXT_MIPS_INTEGER));
EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
EXPECT_EQ(0xa80d3ecbU, raw_context.iregs[16]);
EXPECT_EQ(0x83b93825U, raw_context.iregs[17]);
EXPECT_EQ(0xc03f7fccU, raw_context.iregs[18]);
EXPECT_EQ(0x8ec2f62fU, raw_context.iregs[19]);
EXPECT_EQ(0xa6a846a6U, raw_context.iregs[20]);
EXPECT_EQ(0x25e26a50U, raw_context.iregs[21]);
EXPECT_EQ(0xbb4ad9faU, raw_context.iregs[22]);
EXPECT_EQ(0xf5406913U, raw_context.iregs[23]);
EXPECT_EQ(0x6edabffeU, raw_context.iregs[24]);
EXPECT_EQ(0x1e2db2ceU, raw_context.iregs[25]);
EXPECT_EQ(0xaaa4659cU, raw_context.iregs[26]);
EXPECT_EQ(0xd0c0f0e0U, raw_context.iregs[27]);
EXPECT_EQ(0xc7d6a9b8U, raw_context.iregs[28]);
EXPECT_EQ(0x56781234U, raw_context.iregs[29]);
EXPECT_EQ(0x1234abcdU, raw_context.iregs[30]);
EXPECT_EQ(0x30401020U, raw_context.iregs[31]);
}
} // namespace