text: // 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::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::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);
  }
  
  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);
  
    // 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(4U, 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());
  }
  
  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
  