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.
  
  // minidump.cc: A minidump reader.
  //
  // See minidump.h for documentation.
  //
  // Author: Mark Mentovai
  
  #include "google_breakpad/processor/minidump.h"
  
  #include <assert.h>
  #include <fcntl.h>
  #include <stddef.h>
  #include <string.h>
  #include <time.h>
  
  #ifdef _WIN32
  #include <io.h>
  #else  // _WIN32
  #include <unistd.h>
  #endif  // _WIN32
  
  #include <algorithm>
  #include <fstream>
  #include <limits>
  #include <utility>
  
  #include "processor/range_map-inl.h"
  
  #include "common/macros.h"
  #include "common/scoped_ptr.h"
  #include "common/stdio_wrapper.h"
  #include "google_breakpad/processor/dump_context.h"
  #include "processor/basic_code_module.h"
  #include "processor/basic_code_modules.h"
  #include "processor/convert_old_arm64_context.h"
  #include "processor/logging.h"
  
  namespace google_breakpad {
  
  using std::istream;
  using std::ifstream;
  using std::numeric_limits;
  using std::vector;
  
  namespace {
  
  // Returns true iff |context_size| matches exactly one of the sizes of the
  // various MDRawContext* types.
  // TODO(blundell): This function can be removed once
  // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550 is fixed.
  bool IsContextSizeUnique(uint32_t context_size) {
    int num_matching_contexts = 0;
    if (context_size == sizeof(MDRawContextX86))
      num_matching_contexts++;
    if (context_size == sizeof(MDRawContextPPC))
      num_matching_contexts++;
    if (context_size == sizeof(MDRawContextPPC64))
      num_matching_contexts++;
    if (context_size == sizeof(MDRawContextAMD64))
      num_matching_contexts++;
    if (context_size == sizeof(MDRawContextSPARC))
      num_matching_contexts++;
    if (context_size == sizeof(MDRawContextARM))
      num_matching_contexts++;
    if (context_size == sizeof(MDRawContextARM64))
      num_matching_contexts++;
    if (context_size == sizeof(MDRawContextARM64_Old))
      num_matching_contexts++;
    if (context_size == sizeof(MDRawContextMIPS))
      num_matching_contexts++;
    return num_matching_contexts == 1;
  }
  
  //
  // Swapping routines
  //
  // Inlining these doesn't increase code size significantly, and it saves
  // a whole lot of unnecessary jumping back and forth.
  //
  
  
  // Swapping an 8-bit quantity is a no-op.  This function is only provided
  // to account for certain templatized operations that require swapping for
  // wider types but handle uint8_t too
  // (MinidumpMemoryRegion::GetMemoryAtAddressInternal).
  inline void Swap(uint8_t* value) {}
  
  // Optimization: don't need to AND the furthest right shift, because we're
  // shifting an unsigned quantity.  The standard requires zero-filling in this
  // case.  If the quantities were signed, a bitmask whould be needed for this
  // right shift to avoid an arithmetic shift (which retains the sign bit).
  // The furthest left shift never needs to be ANDed bitmask.
  
  inline void Swap(uint16_t* value) {
    *value = (*value >> 8) | (*value << 8);
  }
  
  inline void Swap(uint32_t* value) {
    *value =  (*value >> 24) |
             ((*value >> 8)  & 0x0000ff00) |
             ((*value << 8)  & 0x00ff0000) |
              (*value << 24);
  }
  
  inline void Swap(uint64_t* value) {
    uint32_t* value32 = reinterpret_cast<uint32_t*>(value);
    Swap(&value32[0]);
    Swap(&value32[1]);
    uint32_t temp = value32[0];
    value32[0] = value32[1];
    value32[1] = temp;
  }
  
  
  // Given a pointer to a 128-bit int in the minidump data, set the "low"
  // and "high" fields appropriately.
  void Normalize128(uint128_struct* value, bool is_big_endian) {
    // The struct format is [high, low], so if the format is big-endian,
    // the most significant bytes will already be in the high field.
    if (!is_big_endian) {
      uint64_t temp = value->low;
      value->low = value->high;
      value->high = temp;
    }
  }
  
  // This just swaps each int64 half of the 128-bit value.
  // The value should also be normalized by calling Normalize128().
  void Swap(uint128_struct* value) {
    Swap(&value->low);
    Swap(&value->high);
  }
  
  // Swapping signed integers
  inline void Swap(int32_t* value) {
    Swap(reinterpret_cast<uint32_t*>(value));
  }
  
  inline void Swap(MDLocationDescriptor* location_descriptor) {
    Swap(&location_descriptor->data_size);
    Swap(&location_descriptor->rva);
  }
  
  inline void Swap(MDMemoryDescriptor* memory_descriptor) {
    Swap(&memory_descriptor->start_of_memory_range);
    Swap(&memory_descriptor->memory);
  }
  
  inline void Swap(MDGUID* guid) {
    Swap(&guid->data1);
    Swap(&guid->data2);
    Swap(&guid->data3);
    // Don't swap guid->data4[] because it contains 8-bit quantities.
  }
  
  inline void Swap(MDSystemTime* system_time) {
    Swap(&system_time->year);
    Swap(&system_time->month);
    Swap(&system_time->day_of_week);
    Swap(&system_time->day);
    Swap(&system_time->hour);
    Swap(&system_time->minute);
    Swap(&system_time->second);
    Swap(&system_time->milliseconds);
  }
  
  inline void Swap(MDXStateFeature* xstate_feature) {
    Swap(&xstate_feature->offset);
    Swap(&xstate_feature->size);
  }
  
  inline void Swap(MDXStateConfigFeatureMscInfo* xstate_feature_info) {
    Swap(&xstate_feature_info->size_of_info);
    Swap(&xstate_feature_info->context_size);
    Swap(&xstate_feature_info->enabled_features);
  
    for (size_t i = 0; i < MD_MAXIMUM_XSTATE_FEATURES; i++) {
      Swap(&xstate_feature_info->features[i]);
    }
  }
  
  inline void Swap(MDRawSimpleStringDictionaryEntry* entry) {
    Swap(&entry->key);
    Swap(&entry->value);
  }
  
  inline void Swap(uint16_t* data, size_t size_in_bytes) {
    size_t data_length = size_in_bytes / sizeof(data[0]);
    for (size_t i = 0; i < data_length; i++) {
      Swap(&data[i]);
    }
  }
  
  //
  // Character conversion routines
  //
  
  
  // Standard wide-character conversion routines depend on the system's own
  // idea of what width a wide character should be: some use 16 bits, and
  // some use 32 bits.  For the purposes of a minidump, wide strings are
  // always represented with 16-bit UTF-16 chracters.  iconv isn't available
  // everywhere, and its interface varies where it is available.  iconv also
  // deals purely with char* pointers, so in addition to considering the swap
  // parameter, a converter that uses iconv would also need to take the host
  // CPU's endianness into consideration.  It doesn't seems worth the trouble
  // of making it a dependency when we don't care about anything but UTF-16.
  string* UTF16ToUTF8(const vector<uint16_t>& in, bool swap) {
    scoped_ptr<string> out(new string());
  
    // Set the string's initial capacity to the number of UTF-16 characters,
    // because the UTF-8 representation will always be at least this long.
    // If the UTF-8 representation is longer, the string will grow dynamically.
    out->reserve(in.size());
  
    for (vector<uint16_t>::const_iterator iterator = in.begin();
         iterator != in.end();
         ++iterator) {
      // Get a 16-bit value from the input
      uint16_t in_word = *iterator;
      if (swap)
        Swap(&in_word);
  
      // Convert the input value (in_word) into a Unicode code point (unichar).
      uint32_t unichar;
      if (in_word >= 0xdc00 && in_word <= 0xdcff) {
        BPLOG(ERROR) << "UTF16ToUTF8 found low surrogate " <<
                        HexString(in_word) << " without high";
        return NULL;
      } else if (in_word >= 0xd800 && in_word <= 0xdbff) {
        // High surrogate.
        unichar = (in_word - 0xd7c0) << 10;
        if (++iterator == in.end()) {
          BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " <<
                          HexString(in_word) << " at end of string";
          return NULL;
        }
        uint32_t high_word = in_word;
        in_word = *iterator;
        if (in_word < 0xdc00 || in_word > 0xdcff) {
          BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " <<
                          HexString(high_word) << " without low " <<
                          HexString(in_word);
          return NULL;
        }
        unichar |= in_word & 0x03ff;
      } else {
        // The ordinary case, a single non-surrogate Unicode character encoded
        // as a single 16-bit value.
        unichar = in_word;
      }
  
      // Convert the Unicode code point (unichar) into its UTF-8 representation,
      // appending it to the out string.
      if (unichar < 0x80) {
        (*out) += static_cast<char>(unichar);
      } else if (unichar < 0x800) {
        (*out) += 0xc0 | static_cast<char>(unichar >> 6);
        (*out) += 0x80 | static_cast<char>(unichar & 0x3f);
      } else if (unichar < 0x10000) {
        (*out) += 0xe0 | static_cast<char>(unichar >> 12);
        (*out) += 0x80 | static_cast<char>((unichar >> 6) & 0x3f);
        (*out) += 0x80 | static_cast<char>(unichar & 0x3f);
      } else if (unichar < 0x200000) {
        (*out) += 0xf0 | static_cast<char>(unichar >> 18);
        (*out) += 0x80 | static_cast<char>((unichar >> 12) & 0x3f);
        (*out) += 0x80 | static_cast<char>((unichar >> 6) & 0x3f);
        (*out) += 0x80 | static_cast<char>(unichar & 0x3f);
      } else {
        BPLOG(ERROR) << "UTF16ToUTF8 cannot represent high value " <<
                        HexString(unichar) << " in UTF-8";
        return NULL;
      }
    }
  
    return out.release();
  }
  
  // Return the smaller of the number of code units in the UTF-16 string,
  // not including the terminating null word, or maxlen.
  size_t UTF16codeunits(const uint16_t* string, size_t maxlen) {
    size_t count = 0;
    while (count < maxlen && string[count] != 0)
      count++;
    return count;
  }
  
  inline void Swap(MDTimeZoneInformation* time_zone) {
    Swap(&time_zone->bias);
    // Skip time_zone->standard_name.  No need to swap UTF-16 fields.
    // The swap will be done as part of the conversion to UTF-8.
    Swap(&time_zone->standard_date);
    Swap(&time_zone->standard_bias);
    // Skip time_zone->daylight_name.  No need to swap UTF-16 fields.
    // The swap will be done as part of the conversion to UTF-8.
    Swap(&time_zone->daylight_date);
    Swap(&time_zone->daylight_bias);
  }
  
  void ConvertUTF16BufferToUTF8String(const uint16_t* utf16_data,
                                      size_t max_length_in_bytes,
                                      string* utf8_result,
                                      bool swap) {
    // Since there is no explicit byte length for each string, use
    // UTF16codeunits to calculate word length, then derive byte
    // length from that.
    size_t max_word_length = max_length_in_bytes / sizeof(utf16_data[0]);
    size_t word_length = UTF16codeunits(utf16_data, max_word_length);
    if (word_length > 0) {
      size_t byte_length = word_length * sizeof(utf16_data[0]);
      vector<uint16_t> utf16_vector(word_length);
      memcpy(&utf16_vector[0], &utf16_data[0], byte_length);
      scoped_ptr<string> temp(UTF16ToUTF8(utf16_vector, swap));
      if (temp.get()) {
        utf8_result->assign(*temp);
      }
    } else {
      utf8_result->clear();
    }
  }
  
  
  // For fields that may or may not be valid, PrintValueOrInvalid will print the
  // string "(invalid)" if the field is not valid, and will print the value if
  // the field is valid. The value is printed as hexadecimal or decimal.
  
  enum NumberFormat {
    kNumberFormatDecimal,
    kNumberFormatHexadecimal,
  };
  
  void PrintValueOrInvalid(bool valid,
                           NumberFormat number_format,
                           uint32_t value) {
    if (!valid) {
      printf("(invalid)\n");
    } else if (number_format == kNumberFormatDecimal) {
      printf("%d\n", value);
    } else {
      printf("0x%x\n", value);
    }
  }
  
  // Converts a time_t to a string showing the time in UTC.
  string TimeTToUTCString(time_t tt) {
    struct tm timestruct;
  #ifdef _MSC_VER
    gmtime_s(&timestruct, &tt);
  #else
    gmtime_r(&tt, &timestruct);
  #endif
  
    char timestr[20];
    size_t rv = strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", &timestruct);
    if (rv == 0) {
      return string();
    }
  
    return string(timestr);
  }
  
  string MDGUIDToString(const MDGUID& uuid) {
    char buf[37];
    snprintf(buf, sizeof(buf), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
             uuid.data1,
             uuid.data2,
             uuid.data3,
             uuid.data4[0],
             uuid.data4[1],
             uuid.data4[2],
             uuid.data4[3],
             uuid.data4[4],
             uuid.data4[5],
             uuid.data4[6],
             uuid.data4[7]);
    return std::string(buf);
  }
  
  bool IsDevAshmem(const string& filename) {
    const string kDevAshmem("/dev/ashmem/");
    return filename.compare(0, kDevAshmem.length(), kDevAshmem) == 0;
  }
  
  }  // namespace
  
  //
  // MinidumpObject
  //
  
  
  MinidumpObject::MinidumpObject(Minidump* minidump)
      : DumpObject(),
        minidump_(minidump) {
  }
  
  
  //
  // MinidumpStream
  //
  
  
  MinidumpStream::MinidumpStream(Minidump* minidump)
      : MinidumpObject(minidump) {
  }
  
  
  //
  // MinidumpContext
  //
  
  
  MinidumpContext::MinidumpContext(Minidump* minidump)
      : DumpContext(),
        minidump_(minidump) {
  }
  
  MinidumpContext::~MinidumpContext() {
  }
  
  bool MinidumpContext::Read(uint32_t expected_size) {
    valid_ = false;
  
    // Certain raw context types are currently assumed to have unique sizes.
    if (!IsContextSizeUnique(sizeof(MDRawContextAMD64))) {
      BPLOG(ERROR) << "sizeof(MDRawContextAMD64) cannot match the size of any "
                   << "other raw context";
      return false;
    }
    if (!IsContextSizeUnique(sizeof(MDRawContextPPC64))) {
      BPLOG(ERROR) << "sizeof(MDRawContextPPC64) cannot match the size of any "
                   << "other raw context";
      return false;
    }
    if (!IsContextSizeUnique(sizeof(MDRawContextARM64_Old))) {
      BPLOG(ERROR) << "sizeof(MDRawContextARM64_Old) cannot match the size of any "
                   << "other raw context";
      return false;
    }
  
    FreeContext();
  
    // First, figure out what type of CPU this context structure is for.
    // For some reason, the AMD64 Context doesn't have context_flags
    // at the beginning of the structure, so special case it here.
  
    uint32_t sysinfo_cpu_type = 0;
    if (!minidump_->GetContextCPUFlagsFromSystemInfo(&sysinfo_cpu_type)) {
      BPLOG(ERROR) << "Failed to preserve the current stream position";
      return false;
    }
  
    if (expected_size == sizeof(MDRawContextAMD64) ||
        (sysinfo_cpu_type == MD_CONTEXT_AMD64 &&
         expected_size >= sizeof(MDRawContextAMD64))) {
      BPLOG(INFO) << "MinidumpContext: looks like AMD64 context";
  
      scoped_ptr<MDRawContextAMD64> context_amd64(new MDRawContextAMD64());
      if (!minidump_->ReadBytes(context_amd64.get(),
                                sizeof(MDRawContextAMD64))) {
        BPLOG(ERROR) << "MinidumpContext could not read amd64 context";
        return false;
      }
  
      // Context may include xsave registers and so be larger than
      // sizeof(MDRawContextAMD64). For now we skip this extended data.
      if (expected_size > sizeof(MDRawContextAMD64)) {
        size_t bytes_left = expected_size - sizeof(MDRawContextAMD64);
        std::vector<uint8_t> xstate(bytes_left);
        if (!minidump_->ReadBytes(xstate.data(),
                                  bytes_left)) {
          BPLOG(ERROR) << "MinidumpContext could not skip amd64 xstate";
          return false;
        }
      }
  
      if (minidump_->swap())
        Swap(&context_amd64->context_flags);
  
      uint32_t cpu_type = context_amd64->context_flags & MD_CONTEXT_CPU_MASK;
      if (cpu_type == 0) {
        context_amd64->context_flags |= sysinfo_cpu_type;
      }
  
      if (cpu_type != MD_CONTEXT_AMD64) {
        // TODO: Fall through to switch below.
        // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550
        BPLOG(ERROR) << "MinidumpContext not actually amd64 context";
        return false;
      }
  
      // Do this after reading the entire MDRawContext structure because
      // GetSystemInfo may seek minidump to a new position.
      if (!CheckAgainstSystemInfo(cpu_type)) {
        BPLOG(ERROR) << "MinidumpContext amd64 does not match system info";
        return false;
      }
  
      // Normalize the 128-bit types in the dump.
      // Since this is AMD64, by definition, the values are little-endian.
      for (unsigned int vr_index = 0;
           vr_index < MD_CONTEXT_AMD64_VR_COUNT;
           ++vr_index)
        Normalize128(&context_amd64->vector_register[vr_index], false);
  
      if (minidump_->swap()) {
        Swap(&context_amd64->p1_home);
        Swap(&context_amd64->p2_home);
        Swap(&context_amd64->p3_home);
        Swap(&context_amd64->p4_home);
        Swap(&context_amd64->p5_home);
        Swap(&context_amd64->p6_home);
        // context_flags is already swapped
        Swap(&context_amd64->mx_csr);
        Swap(&context_amd64->cs);
        Swap(&context_amd64->ds);
        Swap(&context_amd64->es);
        Swap(&context_amd64->fs);
        Swap(&context_amd64->ss);
        Swap(&context_amd64->eflags);
        Swap(&context_amd64->dr0);
        Swap(&context_amd64->dr1);
        Swap(&context_amd64->dr2);
        Swap(&context_amd64->dr3);
        Swap(&context_amd64->dr6);
        Swap(&context_amd64->dr7);
        Swap(&context_amd64->rax);
        Swap(&context_amd64->rcx);
        Swap(&context_amd64->rdx);
        Swap(&context_amd64->rbx);
        Swap(&context_amd64->rsp);
        Swap(&context_amd64->rbp);
        Swap(&context_amd64->rsi);
        Swap(&context_amd64->rdi);
        Swap(&context_amd64->r8);
        Swap(&context_amd64->r9);
        Swap(&context_amd64->r10);
        Swap(&context_amd64->r11);
        Swap(&context_amd64->r12);
        Swap(&context_amd64->r13);
        Swap(&context_amd64->r14);
        Swap(&context_amd64->r15);
        Swap(&context_amd64->rip);
        // FIXME: I'm not sure what actually determines
        // which member of the union {flt_save, sse_registers}
        // is valid.  We're not currently using either,
        // but it would be good to have them swapped properly.
  
        for (unsigned int vr_index = 0;
             vr_index < MD_CONTEXT_AMD64_VR_COUNT;
             ++vr_index)
          Swap(&context_amd64->vector_register[vr_index]);
        Swap(&context_amd64->vector_control);
        Swap(&context_amd64->debug_control);
        Swap(&context_amd64->last_branch_to_rip);
        Swap(&context_amd64->last_branch_from_rip);
        Swap(&context_amd64->last_exception_to_rip);
        Swap(&context_amd64->last_exception_from_rip);
      }
  
      SetContextFlags(context_amd64->context_flags);
  
      SetContextAMD64(context_amd64.release());
    } else if (expected_size == sizeof(MDRawContextPPC64)) {
      // |context_flags| of MDRawContextPPC64 is 64 bits, but other MDRawContext
      // in the else case have 32 bits |context_flags|, so special case it here.
      uint64_t context_flags;
      if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
        BPLOG(ERROR) << "MinidumpContext could not read context flags";
        return false;
      }
      if (minidump_->swap())
        Swap(&context_flags);
  
      uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
      scoped_ptr<MDRawContextPPC64> context_ppc64(new MDRawContextPPC64());
  
      if (cpu_type == 0) {
        if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
          context_ppc64->context_flags |= cpu_type;
        } else {
          BPLOG(ERROR) << "Failed to preserve the current stream position";
          return false;
        }
      }
  
      if (cpu_type != MD_CONTEXT_PPC64) {
        // TODO: Fall through to switch below.
        // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550
        BPLOG(ERROR) << "MinidumpContext not actually ppc64 context";
        return false;
      }
  
      // Set the context_flags member, which has already been read, and
      // read the rest of the structure beginning with the first member
      // after context_flags.
      context_ppc64->context_flags = context_flags;
  
      size_t flags_size = sizeof(context_ppc64->context_flags);
      uint8_t* context_after_flags =
            reinterpret_cast<uint8_t*>(context_ppc64.get()) + flags_size;
      if (!minidump_->ReadBytes(context_after_flags,
                                sizeof(MDRawContextPPC64) - flags_size)) {
        BPLOG(ERROR) << "MinidumpContext could not read ppc64 context";
        return false;
      }
  
      // Do this after reading the entire MDRawContext structure because
      // GetSystemInfo may seek minidump to a new position.
      if (!CheckAgainstSystemInfo(cpu_type)) {
        BPLOG(ERROR) << "MinidumpContext ppc64 does not match system info";
        return false;
      }
      if (minidump_->swap()) {
        // context_ppc64->context_flags was already swapped.
        Swap(&context_ppc64->srr0);
        Swap(&context_ppc64->srr1);
        for (unsigned int gpr_index = 0;
             gpr_index < MD_CONTEXT_PPC64_GPR_COUNT;
             ++gpr_index) {
          Swap(&context_ppc64->gpr[gpr_index]);
        }
        Swap(&context_ppc64->cr);
        Swap(&context_ppc64->xer);
        Swap(&context_ppc64->lr);
        Swap(&context_ppc64->ctr);
        Swap(&context_ppc64->vrsave);
        for (unsigned int fpr_index = 0;
             fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
             ++fpr_index) {
          Swap(&context_ppc64->float_save.fpregs[fpr_index]);
        }
        // Don't swap context_ppc64->float_save.fpscr_pad because it is only
        // used for padding.
        Swap(&context_ppc64->float_save.fpscr);
        for (unsigned int vr_index = 0;
             vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
             ++vr_index) {
          Normalize128(&context_ppc64->vector_save.save_vr[vr_index], true);
          Swap(&context_ppc64->vector_save.save_vr[vr_index]);
        }
        Swap(&context_ppc64->vector_save.save_vscr);
        // Don't swap the padding fields in vector_save.
        Swap(&context_ppc64->vector_save.save_vrvalid);
      }
  
      SetContextFlags(static_cast<uint32_t>(context_ppc64->context_flags));
  
      // Check for data loss when converting context flags from uint64_t into
      // uint32_t
      if (static_cast<uint64_t>(GetContextFlags()) !=
          context_ppc64->context_flags) {
        BPLOG(ERROR) << "Data loss detected when converting PPC64 context_flags";
        return false;
      }
  
      SetContextPPC64(context_ppc64.release());
    } else if (expected_size == sizeof(MDRawContextARM64_Old)) {
      // |context_flags| of MDRawContextARM64_Old is 64 bits, but other MDRawContext
      // in the else case have 32 bits |context_flags|, so special case it here.
      uint64_t context_flags;
  
      BPLOG(INFO) << "MinidumpContext: looks like ARM64 context";
  
      if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
        BPLOG(ERROR) << "MinidumpContext could not read context flags";
        return false;
      }
      if (minidump_->swap())
        Swap(&context_flags);
  
      scoped_ptr<MDRawContextARM64_Old> context_arm64(new MDRawContextARM64_Old());
  
      uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
      if (cpu_type == 0) {
        if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
          context_arm64->context_flags |= cpu_type;
        } else {
          BPLOG(ERROR) << "Failed to preserve the current stream position";
          return false;
        }
      }
  
      if (cpu_type != MD_CONTEXT_ARM64_OLD) {
        // TODO: Fall through to switch below.
        // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550
        BPLOG(ERROR) << "MinidumpContext not actually arm64 context";
        return false;
      }
  
      // Set the context_flags member, which has already been read, and
      // read the rest of the structure beginning with the first member
      // after context_flags.
      context_arm64->context_flags = context_flags;
  
      size_t flags_size = sizeof(context_arm64->context_flags);
      uint8_t* context_after_flags =
          reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size;
      if (!minidump_->ReadBytes(context_after_flags,
                                sizeof(MDRawContextARM64_Old) - flags_size)) {
        BPLOG(ERROR) << "MinidumpContext could not read arm64 context";
        return false;
      }
  
      // Do this after reading the entire MDRawContext structure because
      // GetSystemInfo may seek minidump to a new position.
      if (!CheckAgainstSystemInfo(cpu_type)) {
        BPLOG(ERROR) << "MinidumpContext arm64 does not match system info";
        return false;
      }
  
      if (minidump_->swap()) {
        // context_arm64->context_flags was already swapped.
        for (unsigned int ireg_index = 0;
             ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
             ++ireg_index) {
          Swap(&context_arm64->iregs[ireg_index]);
        }
        Swap(&context_arm64->cpsr);
        Swap(&context_arm64->float_save.fpsr);
        Swap(&context_arm64->float_save.fpcr);
        for (unsigned int fpr_index = 0;
             fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
             ++fpr_index) {
          Normalize128(&context_arm64->float_save.regs[fpr_index],
                       minidump_->is_big_endian());
          Swap(&context_arm64->float_save.regs[fpr_index]);
        }
      }
  
      scoped_ptr<MDRawContextARM64> new_context(new MDRawContextARM64());
      ConvertOldARM64Context(*context_arm64.get(), new_context.get());
      SetContextFlags(new_context->context_flags);
      SetContextARM64(new_context.release());
    } else {
      uint32_t context_flags;
      if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
        BPLOG(ERROR) << "MinidumpContext could not read context flags";
        return false;
      }
      if (minidump_->swap())
        Swap(&context_flags);
  
      uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
      if (cpu_type == 0) {
        // Unfortunately the flag for MD_CONTEXT_ARM that was taken
        // from a Windows CE SDK header conflicts in practice with
        // the CONTEXT_XSTATE flag. MD_CONTEXT_ARM has been renumbered,
        // but handle dumps with the legacy value gracefully here.
        if (context_flags & MD_CONTEXT_ARM_OLD) {
          context_flags |= MD_CONTEXT_ARM;
          context_flags &= ~MD_CONTEXT_ARM_OLD;
          cpu_type = MD_CONTEXT_ARM;
        }
      }
  
      // Fixup if we were not provided a cpu type.
      if (cpu_type == 0) {
        cpu_type = sysinfo_cpu_type;
        context_flags |= cpu_type;
      }
  
      // Allocate the context structure for the correct CPU and fill it.  The
      // casts are slightly unorthodox, but it seems better to do that than to
      // maintain a separate pointer for each type of CPU context structure
      // when only one of them will be used.
      switch (cpu_type) {
        case MD_CONTEXT_X86: {
          if (expected_size != sizeof(MDRawContextX86)) {
            BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " <<
              expected_size << " != " << sizeof(MDRawContextX86);
            return false;
          }
  
          scoped_ptr<MDRawContextX86> context_x86(new MDRawContextX86());
  
          // Set the context_flags member, which has already been read, and
          // read the rest of the structure beginning with the first member
          // after context_flags.
          context_x86->context_flags = context_flags;
  
          size_t flags_size = sizeof(context_x86->context_flags);
          uint8_t* context_after_flags =
            reinterpret_cast<uint8_t*>(context_x86.get()) + flags_size;
          if (!minidump_->ReadBytes(context_after_flags,
                                    sizeof(MDRawContextX86) - flags_size)) {
            BPLOG(ERROR) << "MinidumpContext could not read x86 context";
            return false;
          }
  
          // Do this after reading the entire MDRawContext structure because
          // GetSystemInfo may seek minidump to a new position.
          if (!CheckAgainstSystemInfo(cpu_type)) {
            BPLOG(ERROR) << "MinidumpContext x86 does not match system info";
            return false;
          }
  
          if (minidump_->swap()) {
            // context_x86->context_flags was already swapped.
            Swap(&context_x86->dr0);
            Swap(&context_x86->dr1);
            Swap(&context_x86->dr2);
            Swap(&context_x86->dr3);
            Swap(&context_x86->dr6);
            Swap(&context_x86->dr7);
            Swap(&context_x86->float_save.control_word);
            Swap(&context_x86->float_save.status_word);
            Swap(&context_x86->float_save.tag_word);
            Swap(&context_x86->float_save.error_offset);
            Swap(&context_x86->float_save.error_selector);
            Swap(&context_x86->float_save.data_offset);
            Swap(&context_x86->float_save.data_selector);
            // context_x86->float_save.register_area[] contains 8-bit quantities
            // and does not need to be swapped.
            Swap(&context_x86->float_save.cr0_npx_state);
            Swap(&context_x86->gs);
            Swap(&context_x86->fs);
            Swap(&context_x86->es);
            Swap(&context_x86->ds);
            Swap(&context_x86->edi);
            Swap(&context_x86->esi);
            Swap(&context_x86->ebx);
            Swap(&context_x86->edx);
            Swap(&context_x86->ecx);
            Swap(&context_x86->eax);
            Swap(&context_x86->ebp);
            Swap(&context_x86->eip);
            Swap(&context_x86->cs);
            Swap(&context_x86->eflags);
            Swap(&context_x86->esp);
            Swap(&context_x86->ss);
            // context_x86->extended_registers[] contains 8-bit quantities and
            // does not need to be swapped.
          }
  
          SetContextX86(context_x86.release());
  
          break;
        }
  
        case MD_CONTEXT_PPC: {
          if (expected_size != sizeof(MDRawContextPPC)) {
            BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " <<
              expected_size << " != " << sizeof(MDRawContextPPC);
            return false;
          }
  
          scoped_ptr<MDRawContextPPC> context_ppc(new MDRawContextPPC());
  
          // Set the context_flags member, which has already been read, and
          // read the rest of the structure beginning with the first member
          // after context_flags.
          context_ppc->context_flags = context_flags;
  
          size_t flags_size = sizeof(context_ppc->context_flags);
          uint8_t* context_after_flags =
            reinterpret_cast<uint8_t*>(context_ppc.get()) + flags_size;
          if (!minidump_->ReadBytes(context_after_flags,
                                    sizeof(MDRawContextPPC) - flags_size)) {
            BPLOG(ERROR) << "MinidumpContext could not read ppc context";
            return false;
          }
  
          // Do this after reading the entire MDRawContext structure because
          // GetSystemInfo may seek minidump to a new position.
          if (!CheckAgainstSystemInfo(cpu_type)) {
            BPLOG(ERROR) << "MinidumpContext ppc does not match system info";
            return false;
          }
  
          // Normalize the 128-bit types in the dump.
          // Since this is PowerPC, by definition, the values are big-endian.
          for (unsigned int vr_index = 0;
               vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
               ++vr_index) {
            Normalize128(&context_ppc->vector_save.save_vr[vr_index], true);
          }
  
          if (minidump_->swap()) {
            // context_ppc->context_flags was already swapped.
            Swap(&context_ppc->srr0);
            Swap(&context_ppc->srr1);
            for (unsigned int gpr_index = 0;
                 gpr_index < MD_CONTEXT_PPC_GPR_COUNT;
                 ++gpr_index) {
              Swap(&context_ppc->gpr[gpr_index]);
            }
            Swap(&context_ppc->cr);
            Swap(&context_ppc->xer);
            Swap(&context_ppc->lr);
            Swap(&context_ppc->ctr);
            Swap(&context_ppc->mq);
            Swap(&context_ppc->vrsave);
            for (unsigned int fpr_index = 0;
                 fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
                 ++fpr_index) {
              Swap(&context_ppc->float_save.fpregs[fpr_index]);
            }
            // Don't swap context_ppc->float_save.fpscr_pad because it is only
            // used for padding.
            Swap(&context_ppc->float_save.fpscr);
            for (unsigned int vr_index = 0;
                 vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT;
                 ++vr_index) {
              Swap(&context_ppc->vector_save.save_vr[vr_index]);
            }
            Swap(&context_ppc->vector_save.save_vscr);
            // Don't swap the padding fields in vector_save.
            Swap(&context_ppc->vector_save.save_vrvalid);
          }
  
          SetContextPPC(context_ppc.release());
  
          break;
        }
  
        case MD_CONTEXT_SPARC: {
          if (expected_size != sizeof(MDRawContextSPARC)) {
            BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " <<
              expected_size << " != " << sizeof(MDRawContextSPARC);
            return false;
          }
  
          scoped_ptr<MDRawContextSPARC> context_sparc(new MDRawContextSPARC());
  
          // Set the context_flags member, which has already been read, and
          // read the rest of the structure beginning with the first member
          // after context_flags.
          context_sparc->context_flags = context_flags;
  
          size_t flags_size = sizeof(context_sparc->context_flags);
          uint8_t* context_after_flags =
              reinterpret_cast<uint8_t*>(context_sparc.get()) + flags_size;
          if (!minidump_->ReadBytes(context_after_flags,
                                    sizeof(MDRawContextSPARC) - flags_size)) {
            BPLOG(ERROR) << "MinidumpContext could not read sparc context";
            return false;
          }
  
          // Do this after reading the entire MDRawContext structure because
          // GetSystemInfo may seek minidump to a new position.
          if (!CheckAgainstSystemInfo(cpu_type)) {
            BPLOG(ERROR) << "MinidumpContext sparc does not match system info";
            return false;
          }
  
          if (minidump_->swap()) {
            // context_sparc->context_flags was already swapped.
            for (unsigned int gpr_index = 0;
                 gpr_index < MD_CONTEXT_SPARC_GPR_COUNT;
                 ++gpr_index) {
              Swap(&context_sparc->g_r[gpr_index]);
            }
            Swap(&context_sparc->ccr);
            Swap(&context_sparc->pc);
            Swap(&context_sparc->npc);
            Swap(&context_sparc->y);
            Swap(&context_sparc->asi);
            Swap(&context_sparc->fprs);
            for (unsigned int fpr_index = 0;
                 fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT;
                 ++fpr_index) {
              Swap(&context_sparc->float_save.regs[fpr_index]);
            }
            Swap(&context_sparc->float_save.filler);
            Swap(&context_sparc->float_save.fsr);
          }
          SetContextSPARC(context_sparc.release());
  
          break;
        }
  
        case MD_CONTEXT_ARM: {
          if (expected_size != sizeof(MDRawContextARM)) {
            BPLOG(ERROR) << "MinidumpContext arm size mismatch, " <<
              expected_size << " != " << sizeof(MDRawContextARM);
            return false;
          }
  
          scoped_ptr<MDRawContextARM> context_arm(new MDRawContextARM());
  
          // Set the context_flags member, which has already been read, and
          // read the rest of the structure beginning with the first member
          // after context_flags.
          context_arm->context_flags = context_flags;
  
          size_t flags_size = sizeof(context_arm->context_flags);
          uint8_t* context_after_flags =
              reinterpret_cast<uint8_t*>(context_arm.get()) + flags_size;
          if (!minidump_->ReadBytes(context_after_flags,
                                    sizeof(MDRawContextARM) - flags_size)) {
            BPLOG(ERROR) << "MinidumpContext could not read arm context";
            return false;
          }
  
          // Do this after reading the entire MDRawContext structure because
          // GetSystemInfo may seek minidump to a new position.
          if (!CheckAgainstSystemInfo(cpu_type)) {
            BPLOG(ERROR) << "MinidumpContext arm does not match system info";
            return false;
          }
  
          if (minidump_->swap()) {
            // context_arm->context_flags was already swapped.
            for (unsigned int ireg_index = 0;
                 ireg_index < MD_CONTEXT_ARM_GPR_COUNT;
                 ++ireg_index) {
              Swap(&context_arm->iregs[ireg_index]);
            }
            Swap(&context_arm->cpsr);
            Swap(&context_arm->float_save.fpscr);
            for (unsigned int fpr_index = 0;
                 fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT;
                 ++fpr_index) {
              Swap(&context_arm->float_save.regs[fpr_index]);
            }
            for (unsigned int fpe_index = 0;
                 fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT;
                 ++fpe_index) {
              Swap(&context_arm->float_save.extra[fpe_index]);
            }
          }
          SetContextARM(context_arm.release());
  
          break;
        }
  
        case MD_CONTEXT_ARM64: {
          if (expected_size != sizeof(MDRawContextARM64)) {
            BPLOG(ERROR) << "MinidumpContext arm64 size mismatch, " <<
                         expected_size << " != " << sizeof(MDRawContextARM64);
            return false;
          }
  
          scoped_ptr<MDRawContextARM64> context_arm64(new MDRawContextARM64());
  
          // Set the context_flags member, which has already been read, and
          // read the rest of the structure beginning with the first member
          // after context_flags.
          context_arm64->context_flags = context_flags;
  
          size_t flags_size = sizeof(context_arm64->context_flags);
          uint8_t* context_after_flags =
              reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size;
          if (!minidump_->ReadBytes(context_after_flags,
                                    sizeof(*context_arm64) - flags_size)) {
            BPLOG(ERROR) << "MinidumpContext could not read arm64 context";
            return false;
          }
  
          // Do this after reading the entire MDRawContext structure because
          // GetSystemInfo may seek minidump to a new position.
          if (!CheckAgainstSystemInfo(cpu_type)) {
            BPLOG(ERROR) << "MinidumpContext arm does not match system info";
            return false;
          }
  
          if (minidump_->swap()) {
            // context_arm64->context_flags was already swapped.
            for (unsigned int ireg_index = 0;
                 ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
                 ++ireg_index) {
              Swap(&context_arm64->iregs[ireg_index]);
            }
            Swap(&context_arm64->cpsr);
            Swap(&context_arm64->float_save.fpsr);
            Swap(&context_arm64->float_save.fpcr);
            for (unsigned int fpr_index = 0;
                 fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
                 ++fpr_index) {
              Normalize128(&context_arm64->float_save.regs[fpr_index],
                           minidump_->is_big_endian());
              Swap(&context_arm64->float_save.regs[fpr_index]);
            }
          }
          SetContextARM64(context_arm64.release());
          break;
        }
  
        case MD_CONTEXT_MIPS:
        case MD_CONTEXT_MIPS64: {
          if (expected_size != sizeof(MDRawContextMIPS)) {
            BPLOG(ERROR) << "MinidumpContext MIPS size mismatch, "
                         << expected_size
                         << " != "
                         << sizeof(MDRawContextMIPS);
            return false;
          }
  
          scoped_ptr<MDRawContextMIPS> context_mips(new MDRawContextMIPS());
  
          // Set the context_flags member, which has already been read, and
          // read the rest of the structure beginning with the first member
          // after context_flags.
          context_mips->context_flags = context_flags;
  
          size_t flags_size = sizeof(context_mips->context_flags);
          uint8_t* context_after_flags =
              reinterpret_cast<uint8_t*>(context_mips.get()) + flags_size;
          if (!minidump_->ReadBytes(context_after_flags,
                                    sizeof(MDRawContextMIPS) - flags_size)) {
            BPLOG(ERROR) << "MinidumpContext could not read MIPS context";
            return false;
          }
  
          // Do this after reading the entire MDRawContext structure because
          // GetSystemInfo may seek minidump to a new position.
          if (!CheckAgainstSystemInfo(cpu_type)) {
            BPLOG(ERROR) << "MinidumpContext MIPS does not match system info";
            return false;
          }
  
          if (minidump_->swap()) {
            // context_mips->context_flags was already swapped.
            for (int ireg_index = 0;
                 ireg_index < MD_CONTEXT_MIPS_GPR_COUNT;
                 ++ireg_index) {
              Swap(&context_mips->iregs[ireg_index]);
            }
  	  Swap(&context_mips->mdhi);
  	  Swap(&context_mips->mdlo);
            for (int dsp_index = 0;
                 dsp_index < MD_CONTEXT_MIPS_DSP_COUNT;
                 ++dsp_index) {
              Swap(&context_mips->hi[dsp_index]);
              Swap(&context_mips->lo[dsp_index]);
            }
  	  Swap(&context_mips->dsp_control);
            Swap(&context_mips->epc);
            Swap(&context_mips->badvaddr);
            Swap(&context_mips->status);
            Swap(&context_mips->cause);
            for (int fpr_index = 0;
                 fpr_index < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT;
                 ++fpr_index) {
              Swap(&context_mips->float_save.regs[fpr_index]);
            }
            Swap(&context_mips->float_save.fpcsr);
            Swap(&context_mips->float_save.fir);
          }
          SetContextMIPS(context_mips.release());
  
          break;
        }
  
        default: {
          // Unknown context type - Don't log as an error yet. Let the
          // caller work that out.
          BPLOG(INFO) << "MinidumpContext unknown context type " <<
            HexString(cpu_type);
          return false;
        }
      }
      SetContextFlags(context_flags);
    }
  
    valid_ = true;
    return true;
  }
  
  bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) {
    // It's OK if the minidump doesn't contain an MD_SYSTEM_INFO_STREAM,
    // as this function just implements a sanity check.
    MinidumpSystemInfo* system_info = minidump_->GetSystemInfo();
    if (!system_info) {
      BPLOG(INFO) << "MinidumpContext could not be compared against "
                     "MinidumpSystemInfo";
      return true;
    }
  
    // If there is an MD_SYSTEM_INFO_STREAM, it should contain valid system info.
    const MDRawSystemInfo* raw_system_info = system_info->system_info();
    if (!raw_system_info) {
      BPLOG(INFO) << "MinidumpContext could not be compared against "
                     "MDRawSystemInfo";
      return false;
    }
  
    MDCPUArchitecture system_info_cpu_type = static_cast<MDCPUArchitecture>(
        raw_system_info->processor_architecture);
  
    // Compare the CPU type of the context record to the CPU type in the
    // minidump's system info stream.
    bool return_value = false;
    switch (context_cpu_type) {
      case MD_CONTEXT_X86:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_X86 ||
            system_info_cpu_type == MD_CPU_ARCHITECTURE_X86_WIN64 ||
            system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) {
          return_value = true;
        }
        break;
  
      case MD_CONTEXT_PPC:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC)
          return_value = true;
        break;
  
      case MD_CONTEXT_PPC64:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC64)
          return_value = true;
        break;
  
      case MD_CONTEXT_AMD64:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64)
          return_value = true;
        break;
  
      case MD_CONTEXT_SPARC:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC)
          return_value = true;
        break;
  
      case MD_CONTEXT_ARM:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM)
          return_value = true;
        break;
  
      case MD_CONTEXT_ARM64:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64)
          return_value = true;
        break;
  
      case MD_CONTEXT_ARM64_OLD:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64_OLD)
          return_value = true;
        break;
  
      case MD_CONTEXT_MIPS:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS)
          return_value = true;
        break;
  
      case MD_CONTEXT_MIPS64:
        if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS64)
          return_value = true;
        break;
    }
  
    BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " <<
                                      HexString(context_cpu_type) <<
                                      " wrong for MinidumpSystemInfo CPU " <<
                                      HexString(system_info_cpu_type);
  
    return return_value;
  }
  
  
  //
  // MinidumpMemoryRegion
  //
  
  
  uint32_t MinidumpMemoryRegion::max_bytes_ = 64 * 1024 * 1024;  // 64MB
  
  
  MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump)
      : MinidumpObject(minidump),
        descriptor_(NULL),
        memory_(NULL) {
    hexdump_width_ = minidump_ ? minidump_->HexdumpMode() : 0;
    hexdump_ = hexdump_width_ != 0;
  }
  
  
  MinidumpMemoryRegion::~MinidumpMemoryRegion() {
    delete memory_;
  }
  
  
  void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) {
    descriptor_ = descriptor;
    valid_ = descriptor &&
             descriptor_->memory.data_size <=
                 numeric_limits<uint64_t>::max() -
                 descriptor_->start_of_memory_range;
  }
  
  
  const uint8_t* MinidumpMemoryRegion::GetMemory() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory";
      return NULL;
    }
  
    if (!memory_) {
      if (descriptor_->memory.data_size == 0) {
        BPLOG(ERROR) << "MinidumpMemoryRegion is empty";
        return NULL;
      }
  
      if (!minidump_->SeekSet(descriptor_->memory.rva)) {
        BPLOG(ERROR) << "MinidumpMemoryRegion could not seek to memory region";
        return NULL;
      }
  
      if (descriptor_->memory.data_size > max_bytes_) {
        BPLOG(ERROR) << "MinidumpMemoryRegion size " <<
                        descriptor_->memory.data_size << " exceeds maximum " <<
                        max_bytes_;
        return NULL;
      }
  
      scoped_ptr< vector<uint8_t> > memory(
          new vector<uint8_t>(descriptor_->memory.data_size));
  
      if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size)) {
        BPLOG(ERROR) << "MinidumpMemoryRegion could not read memory region";
        return NULL;
      }
  
      memory_ = memory.release();
    }
  
    return &(*memory_)[0];
  }
  
  
  uint64_t MinidumpMemoryRegion::GetBase() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase";
      return static_cast<uint64_t>(-1);
    }
  
    return descriptor_->start_of_memory_range;
  }
  
  
  uint32_t MinidumpMemoryRegion::GetSize() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize";
      return 0;
    }
  
    return descriptor_->memory.data_size;
  }
  
  
  void MinidumpMemoryRegion::FreeMemory() {
    delete memory_;
    memory_ = NULL;
  }
  
  
  template<typename T>
  bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(uint64_t address,
                                                        T*        value) const {
    BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal "
                               "requires |value|";
    assert(value);
    *value = 0;
  
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for "
                      "GetMemoryAtAddressInternal";
      return false;
    }
  
    // Common failure case
    if (address < descriptor_->start_of_memory_range ||
        sizeof(T) > numeric_limits<uint64_t>::max() - address ||
        address + sizeof(T) > descriptor_->start_of_memory_range +
                              descriptor_->memory.data_size) {
      BPLOG(INFO) << "MinidumpMemoryRegion request out of range: " <<
                      HexString(address) << "+" << sizeof(T) << "/" <<
                      HexString(descriptor_->start_of_memory_range) << "+" <<
                      HexString(descriptor_->memory.data_size);
      return false;
    }
  
    const uint8_t* memory = GetMemory();
    if (!memory) {
      // GetMemory already logged a perfectly good message.
      return false;
    }
  
    // If the CPU requires memory accesses to be aligned, this can crash.
    // x86 and ppc are able to cope, though.
    *value = *reinterpret_cast<const T*>(
        &memory[address - descriptor_->start_of_memory_range]);
  
    if (minidump_->swap())
      Swap(value);
  
    return true;
  }
  
  
  bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t  address,
                                                uint8_t*  value) const {
    return GetMemoryAtAddressInternal(address, value);
  }
  
  
  bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t  address,
                                                uint16_t* value) const {
    return GetMemoryAtAddressInternal(address, value);
  }
  
  
  bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t  address,
                                                uint32_t* value) const {
    return GetMemoryAtAddressInternal(address, value);
  }
  
  
  bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t  address,
                                                uint64_t* value) const {
    return GetMemoryAtAddressInternal(address, value);
  }
  
  
  void MinidumpMemoryRegion::Print() const {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpMemoryRegion cannot print invalid data";
      return;
    }
  
    const uint8_t* memory = GetMemory();
    if (memory) {
      if (hexdump_) {
        // Pretty hexdump view.
        for (unsigned int byte_index = 0;
             byte_index < descriptor_->memory.data_size;
             byte_index += hexdump_width_) {
          // In case the memory won't fill a whole line.
          unsigned int num_bytes = std::min(
              descriptor_->memory.data_size - byte_index, hexdump_width_);
  
          // Display the leading address.
          printf("%08x  ", byte_index);
  
          // Show the bytes in hex.
          for (unsigned int i = 0; i < hexdump_width_; ++i) {
            if (i < num_bytes) {
              // Show the single byte of memory in hex.
              printf("%02x ", memory[byte_index + i]);
            } else {
              // If this line doesn't fill up, pad it out.
              printf("   ");
            }
  
            // Insert a space every 8 bytes to make it more readable.
            if (((i + 1) % 8) == 0) {
              printf(" ");
            }
          }
  
          // Decode the line as ASCII.
          printf("|");
          for (unsigned int i = 0; i < hexdump_width_; ++i) {
            if (i < num_bytes) {
              uint8_t byte = memory[byte_index + i];
              printf("%c", isprint(byte) ? byte : '.');
            } else {
              // If this line doesn't fill up, pad it out.
              printf(" ");
            }
          }
          printf("|\n");
        }
      } else {
        // Ugly raw string view.
        printf("0x");
        for (unsigned int i = 0;
             i < descriptor_->memory.data_size;
             i++) {
          printf("%02x", memory[i]);
        }
        printf("\n");
      }
    } else {
      printf("No memory\n");
    }
  }
  
  
  void MinidumpMemoryRegion::SetPrintMode(bool hexdump,
                                          unsigned int hexdump_width) {
    // Require the width to be a multiple of 8 bytes.
    if (hexdump_width == 0 || (hexdump_width % 8) != 0) {
      BPLOG(ERROR) << "MinidumpMemoryRegion print hexdump_width must be "
                      "multiple of 8, not " << hexdump_width;
      return;
    }
  
    hexdump_ = hexdump;
    hexdump_width_ = hexdump_width;
  }
  
  
  //
  // MinidumpThread
  //
  
  
  MinidumpThread::MinidumpThread(Minidump* minidump)
      : MinidumpObject(minidump),
        thread_(),
        memory_(NULL),
        context_(NULL) {
  }
  
  
  MinidumpThread::~MinidumpThread() {
    delete memory_;
    delete context_;
  }
  
  
  bool MinidumpThread::Read() {
    // Invalidate cached data.
    delete memory_;
    memory_ = NULL;
    delete context_;
    context_ = NULL;
  
    valid_ = false;
  
    if (!minidump_->ReadBytes(&thread_, sizeof(thread_))) {
      BPLOG(ERROR) << "MinidumpThread cannot read thread";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&thread_.thread_id);
      Swap(&thread_.suspend_count);
      Swap(&thread_.priority_class);
      Swap(&thread_.priority);
      Swap(&thread_.teb);
      Swap(&thread_.stack);
      Swap(&thread_.thread_context);
    }
  
    // Check for base + size overflow or undersize.
    if (thread_.stack.memory.rva == 0 ||
        thread_.stack.memory.data_size == 0 ||
        thread_.stack.memory.data_size > numeric_limits<uint64_t>::max() -
                                         thread_.stack.start_of_memory_range) {
      // This is ok, but log an error anyway.
      BPLOG(ERROR) << "MinidumpThread has a memory region problem, " <<
                      HexString(thread_.stack.start_of_memory_range) << "+" <<
                      HexString(thread_.stack.memory.data_size) <<
                      ", RVA 0x" << HexString(thread_.stack.memory.rva);
    } else {
      memory_ = new MinidumpMemoryRegion(minidump_);
      memory_->SetDescriptor(&thread_.stack);
    }
  
    valid_ = true;
    return true;
  }
  
  uint64_t MinidumpThread::GetStartOfStackMemoryRange() const {
    if (!valid_) {
      BPLOG(ERROR) << "GetStartOfStackMemoryRange: Invalid MinidumpThread";
      return 0;
    }
  
    return thread_.stack.start_of_memory_range;
  }
  
  MinidumpMemoryRegion* MinidumpThread::GetMemory() {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpThread for GetMemory";
      return NULL;
    }
  
    return memory_;
  }
  
  
  MinidumpContext* MinidumpThread::GetContext() {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpThread for GetContext";
      return NULL;
    }
  
    if (!context_) {
      if (!minidump_->SeekSet(thread_.thread_context.rva)) {
        BPLOG(ERROR) << "MinidumpThread cannot seek to context";
        return NULL;
      }
  
      scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_));
  
      if (!context->Read(thread_.thread_context.data_size)) {
        BPLOG(ERROR) << "MinidumpThread cannot read context";
        return NULL;
      }
  
      context_ = context.release();
    }
  
    return context_;
  }
  
  
  bool MinidumpThread::GetThreadID(uint32_t* thread_id) const {
    BPLOG_IF(ERROR, !thread_id) << "MinidumpThread::GetThreadID requires "
                                   "|thread_id|";
    assert(thread_id);
    *thread_id = 0;
  
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpThread for GetThreadID";
      return false;
    }
  
    *thread_id = thread_.thread_id;
    return true;
  }
  
  
  void MinidumpThread::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpThread cannot print invalid data";
      return;
    }
  
    printf("MDRawThread\n");
    printf("  thread_id                   = 0x%x\n",   thread_.thread_id);
    printf("  suspend_count               = %d\n",     thread_.suspend_count);
    printf("  priority_class              = 0x%x\n",   thread_.priority_class);
    printf("  priority                    = 0x%x\n",   thread_.priority);
    printf("  teb                         = 0x%" PRIx64 "\n", thread_.teb);
    printf("  stack.start_of_memory_range = 0x%" PRIx64 "\n",
           thread_.stack.start_of_memory_range);
    printf("  stack.memory.data_size      = 0x%x\n",
           thread_.stack.memory.data_size);
    printf("  stack.memory.rva            = 0x%x\n",   thread_.stack.memory.rva);
    printf("  thread_context.data_size    = 0x%x\n",
           thread_.thread_context.data_size);
    printf("  thread_context.rva          = 0x%x\n",
           thread_.thread_context.rva);
  
    MinidumpContext* context = GetContext();
    if (context) {
      printf("\n");
      context->Print();
    } else {
      printf("  (no context)\n");
      printf("\n");
    }
  
    MinidumpMemoryRegion* memory = GetMemory();
    if (memory) {
      printf("Stack\n");
      memory->Print();
    } else {
      printf("No stack\n");
    }
    printf("\n");
  }
  
  
  //
  // MinidumpThreadList
  //
  
  
  uint32_t MinidumpThreadList::max_threads_ = 4096;
  
  
  MinidumpThreadList::MinidumpThreadList(Minidump* minidump)
      : MinidumpStream(minidump),
        id_to_thread_map_(),
        threads_(NULL),
        thread_count_(0) {
  }
  
  
  MinidumpThreadList::~MinidumpThreadList() {
    delete threads_;
  }
  
  
  bool MinidumpThreadList::Read(uint32_t expected_size) {
    // Invalidate cached data.
    id_to_thread_map_.clear();
    delete threads_;
    threads_ = NULL;
    thread_count_ = 0;
  
    valid_ = false;
  
    uint32_t thread_count;
    if (expected_size < sizeof(thread_count)) {
      BPLOG(ERROR) << "MinidumpThreadList count size mismatch, " <<
                      expected_size << " < " << sizeof(thread_count);
      return false;
    }
    if (!minidump_->ReadBytes(&thread_count, sizeof(thread_count))) {
      BPLOG(ERROR) << "MinidumpThreadList cannot read thread count";
      return false;
    }
  
    if (minidump_->swap())
      Swap(&thread_count);
  
    if (thread_count > numeric_limits<uint32_t>::max() / sizeof(MDRawThread)) {
      BPLOG(ERROR) << "MinidumpThreadList thread count " << thread_count <<
                      " would cause multiplication overflow";
      return false;
    }
  
    if (expected_size != sizeof(thread_count) +
                         thread_count * sizeof(MDRawThread)) {
      // may be padded with 4 bytes on 64bit ABIs for alignment
      if (expected_size == sizeof(thread_count) + 4 +
                           thread_count * sizeof(MDRawThread)) {
        uint32_t useless;
        if (!minidump_->ReadBytes(&useless, 4)) {
          BPLOG(ERROR) << "MinidumpThreadList cannot read threadlist padded "
                          "bytes";
          return false;
        }
      } else {
        BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size <<
                      " != " << sizeof(thread_count) +
                      thread_count * sizeof(MDRawThread);
        return false;
      }
    }
  
  
    if (thread_count > max_threads_) {
      BPLOG(ERROR) << "MinidumpThreadList count " << thread_count <<
                      " exceeds maximum " << max_threads_;
      return false;
    }
  
    if (thread_count != 0) {
      scoped_ptr<MinidumpThreads> threads(
          new MinidumpThreads(thread_count, MinidumpThread(minidump_)));
  
      for (unsigned int thread_index = 0;
           thread_index < thread_count;
           ++thread_index) {
        MinidumpThread* thread = &(*threads)[thread_index];
  
        // Assume that the file offset is correct after the last read.
        if (!thread->Read()) {
          BPLOG(ERROR) << "MinidumpThreadList cannot read thread " <<
                          thread_index << "/" << thread_count;
          return false;
        }
  
        uint32_t thread_id;
        if (!thread->GetThreadID(&thread_id)) {
          BPLOG(ERROR) << "MinidumpThreadList cannot get thread ID for thread " <<
                          thread_index << "/" << thread_count;
          return false;
        }
  
        if (GetThreadByID(thread_id)) {
          // Another thread with this ID is already in the list.  Data error.
          BPLOG(ERROR) << "MinidumpThreadList found multiple threads with ID " <<
                          HexString(thread_id) << " at thread " <<
                          thread_index << "/" << thread_count;
          return false;
        }
        id_to_thread_map_[thread_id] = thread;
      }
  
      threads_ = threads.release();
    }
  
    thread_count_ = thread_count;
  
    valid_ = true;
    return true;
  }
  
  
  MinidumpThread* MinidumpThreadList::GetThreadAtIndex(unsigned int index)
      const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpThreadList for GetThreadAtIndex";
      return NULL;
    }
  
    if (index >= thread_count_) {
      BPLOG(ERROR) << "MinidumpThreadList index out of range: " <<
                      index << "/" << thread_count_;
      return NULL;
    }
  
    return &(*threads_)[index];
  }
  
  
  MinidumpThread* MinidumpThreadList::GetThreadByID(uint32_t thread_id) {
    // Don't check valid_.  Read calls this method before everything is
    // validated.  It is safe to not check valid_ here.
    return id_to_thread_map_[thread_id];
  }
  
  
  void MinidumpThreadList::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpThreadList cannot print invalid data";
      return;
    }
  
    printf("MinidumpThreadList\n");
    printf("  thread_count = %d\n", thread_count_);
    printf("\n");
  
    for (unsigned int thread_index = 0;
         thread_index < thread_count_;
         ++thread_index) {
      printf("thread[%d]\n", thread_index);
  
      (*threads_)[thread_index].Print();
    }
  }
  
  
  //
  // MinidumpModule
  //
  
  
  uint32_t MinidumpModule::max_cv_bytes_ = 32768;
  uint32_t MinidumpModule::max_misc_bytes_ = 32768;
  
  
  MinidumpModule::MinidumpModule(Minidump* minidump)
      : MinidumpObject(minidump),
        module_valid_(false),
        has_debug_info_(false),
        module_(),
        name_(NULL),
        cv_record_(NULL),
        cv_record_signature_(MD_CVINFOUNKNOWN_SIGNATURE),
        misc_record_(NULL) {
  }
  
  
  MinidumpModule::~MinidumpModule() {
    delete name_;
    delete cv_record_;
    delete misc_record_;
  }
  
  
  bool MinidumpModule::Read() {
    // Invalidate cached data.
    delete name_;
    name_ = NULL;
    delete cv_record_;
    cv_record_ = NULL;
    cv_record_signature_ = MD_CVINFOUNKNOWN_SIGNATURE;
    delete misc_record_;
    misc_record_ = NULL;
  
    module_valid_ = false;
    has_debug_info_ = false;
    valid_ = false;
  
    if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) {
      BPLOG(ERROR) << "MinidumpModule cannot read module";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&module_.base_of_image);
      Swap(&module_.size_of_image);
      Swap(&module_.checksum);
      Swap(&module_.time_date_stamp);
      Swap(&module_.module_name_rva);
      Swap(&module_.version_info.signature);
      Swap(&module_.version_info.struct_version);
      Swap(&module_.version_info.file_version_hi);
      Swap(&module_.version_info.file_version_lo);
      Swap(&module_.version_info.product_version_hi);
      Swap(&module_.version_info.product_version_lo);
      Swap(&module_.version_info.file_flags_mask);
      Swap(&module_.version_info.file_flags);
      Swap(&module_.version_info.file_os);
      Swap(&module_.version_info.file_type);
      Swap(&module_.version_info.file_subtype);
      Swap(&module_.version_info.file_date_hi);
      Swap(&module_.version_info.file_date_lo);
      Swap(&module_.cv_record);
      Swap(&module_.misc_record);
      // Don't swap reserved fields because their contents are unknown (as
      // are their proper widths).
    }
  
    // Check for base + size overflow or undersize.
    if (module_.size_of_image == 0 ||
        module_.size_of_image >
            numeric_limits<uint64_t>::max() - module_.base_of_image) {
      BPLOG(ERROR) << "MinidumpModule has a module problem, " <<
                      HexString(module_.base_of_image) << "+" <<
                      HexString(module_.size_of_image);
      return false;
    }
  
    module_valid_ = true;
    return true;
  }
  
  
  bool MinidumpModule::ReadAuxiliaryData() {
    if (!module_valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModule for ReadAuxiliaryData";
      return false;
    }
  
    // Each module must have a name.
    name_ = minidump_->ReadString(module_.module_name_rva);
    if (!name_) {
      BPLOG(ERROR) << "MinidumpModule could not read name";
      return false;
    }
  
    // At this point, we have enough info for the module to be valid.
    valid_ = true;
  
    // CodeView and miscellaneous debug records are only required if the
    // module indicates that they exist.
    if (module_.cv_record.data_size && !GetCVRecord(NULL)) {
      BPLOG(ERROR) << "MinidumpModule has no CodeView record, "
                      "but one was expected";
      return false;
    }
  
    if (module_.misc_record.data_size && !GetMiscRecord(NULL)) {
      BPLOG(ERROR) << "MinidumpModule has no miscellaneous debug record, "
                      "but one was expected";
      return false;
    }
  
    has_debug_info_ = true;
    return true;
  }
  
  
  string MinidumpModule::code_file() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModule for code_file";
      return "";
    }
  
    return *name_;
  }
  
  
  string MinidumpModule::code_identifier() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModule for code_identifier";
      return "";
    }
  
    if (!has_debug_info_)
      return "";
  
    MinidumpSystemInfo* minidump_system_info = minidump_->GetSystemInfo();
    if (!minidump_system_info) {
      BPLOG(ERROR) << "MinidumpModule code_identifier requires "
                      "MinidumpSystemInfo";
      return "";
    }
  
    const MDRawSystemInfo* raw_system_info = minidump_system_info->system_info();
    if (!raw_system_info) {
      BPLOG(ERROR) << "MinidumpModule code_identifier requires MDRawSystemInfo";
      return "";
    }
  
    string identifier;
  
    switch (raw_system_info->platform_id) {
      case MD_OS_WIN32_NT:
      case MD_OS_WIN32_WINDOWS: {
        // Use the same format that the MS symbol server uses in filesystem
        // hierarchies.
        char identifier_string[17];
        snprintf(identifier_string, sizeof(identifier_string), "%08X%x",
                 module_.time_date_stamp, module_.size_of_image);
        identifier = identifier_string;
        break;
      }
  
      case MD_OS_ANDROID:
      case MD_OS_FUCHSIA:
      case MD_OS_LINUX: {
        // If ELF CodeView data is present, return the debug id.
        if (cv_record_ && cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
          const MDCVInfoELF* cv_record_elf =
              reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]);
          assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
  
          for (unsigned int build_id_index = 0;
               build_id_index < (cv_record_->size() - MDCVInfoELF_minsize);
               ++build_id_index) {
            char hexbyte[3];
            snprintf(hexbyte, sizeof(hexbyte), "%02x",
                     cv_record_elf->build_id[build_id_index]);
            identifier += hexbyte;
          }
          break;
        }
        // Otherwise fall through to the case below.
        BP_FALLTHROUGH;
      }
  
      case MD_OS_MAC_OS_X:
      case MD_OS_IOS:
      case MD_OS_SOLARIS:
      case MD_OS_NACL:
      case MD_OS_PS3: {
        // TODO(mmentovai): support uuid extension if present, otherwise fall
        // back to version (from LC_ID_DYLIB?), otherwise fall back to something
        // else.
        identifier = "id";
        break;
      }
  
      default: {
        // Without knowing what OS generated the dump, we can't generate a good
        // identifier.  Return an empty string, signalling failure.
        BPLOG(ERROR) << "MinidumpModule code_identifier requires known platform, "
                        "found " << HexString(raw_system_info->platform_id);
        break;
      }
    }
  
    return identifier;
  }
  
  
  string MinidumpModule::debug_file() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModule for debug_file";
      return "";
    }
  
    if (!has_debug_info_)
      return "";
  
    string file;
    // Prefer the CodeView record if present.
    if (cv_record_) {
      if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
        // It's actually an MDCVInfoPDB70 structure.
        const MDCVInfoPDB70* cv_record_70 =
            reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]);
        assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
  
        // GetCVRecord guarantees pdb_file_name is null-terminated.
        file = reinterpret_cast<const char*>(cv_record_70->pdb_file_name);
      } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
        // It's actually an MDCVInfoPDB20 structure.
        const MDCVInfoPDB20* cv_record_20 =
            reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]);
        assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
  
        // GetCVRecord guarantees pdb_file_name is null-terminated.
        file = reinterpret_cast<const char*>(cv_record_20->pdb_file_name);
      } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
        // It's actually an MDCVInfoELF structure.
        assert(reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0])->
            cv_signature == MD_CVINFOELF_SIGNATURE);
  
        // For MDCVInfoELF, the debug file is the code file.
        file = *name_;
      }
  
      // If there's a CodeView record but it doesn't match a known signature,
      // try the miscellaneous record.
    }
  
    if (file.empty()) {
      // No usable CodeView record.  Try the miscellaneous debug record.
      if (misc_record_) {
        const MDImageDebugMisc* misc_record =
            reinterpret_cast<const MDImageDebugMisc*>(&(*misc_record_)[0]);
        if (!misc_record->unicode) {
          // If it's not Unicode, just stuff it into the string.  It's unclear
          // if misc_record->data is 0-terminated, so use an explicit size.
          file = string(
              reinterpret_cast<const char*>(misc_record->data),
              module_.misc_record.data_size - MDImageDebugMisc_minsize);
        } else {
          // There's a misc_record but it encodes the debug filename in UTF-16.
          // (Actually, because miscellaneous records are so old, it's probably
          // UCS-2.)  Convert it to UTF-8 for congruity with the other strings
          // that this method (and all other methods in the Minidump family)
          // return.
  
          size_t bytes =
              module_.misc_record.data_size - MDImageDebugMisc_minsize;
          if (bytes % 2 == 0) {
            size_t utf16_words = bytes / 2;
  
            // UTF16ToUTF8 expects a vector<uint16_t>, so create a temporary one
            // and copy the UTF-16 data into it.
            vector<uint16_t> string_utf16(utf16_words);
            if (utf16_words)
              memcpy(&string_utf16[0], &misc_record->data, bytes);
  
            // GetMiscRecord already byte-swapped the data[] field if it contains
            // UTF-16, so pass false as the swap argument.
            scoped_ptr<string> new_file(UTF16ToUTF8(string_utf16, false));
            if (new_file.get() != nullptr) {
              file = *new_file;
            }
          }
        }
      }
    }
  
    // Manufacture debug-file from code-file
    if (file.empty()) {
      file = code_file();
  
      BPLOG(INFO) << "Generated debug_file '" << file << "' from code_file '" << *name_ << "'";
    }
  
    // This may be a windows-style pathname, so find the basename considering both
    // forward and back-slashes.
    const size_t last_slash_idx = file.find_last_of("\\/");
    if (std::string::npos != last_slash_idx) {
      file.erase(0, last_slash_idx + 1);
    }
  
    // Relatively common case
    BPLOG_IF(INFO, file.empty()) << "MinidumpModule could not determine "
                                    "debug_file for " << *name_;
  
    return file;
  }
  
  static string guid_and_age_to_debug_id(const MDGUID& guid,
                                         uint32_t age) {
    char identifier_string[41];
    snprintf(identifier_string, sizeof(identifier_string),
             "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x",
             guid.data1,
             guid.data2,
             guid.data3,
             guid.data4[0],
             guid.data4[1],
             guid.data4[2],
             guid.data4[3],
             guid.data4[4],
             guid.data4[5],
             guid.data4[6],
             guid.data4[7],
             age);
    return identifier_string;
  }
  
  string MinidumpModule::debug_identifier() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModule for debug_identifier";
      return "";
    }
  
    if (!has_debug_info_)
      return "";
  
    string identifier;
  
    // Use the CodeView record if present.
    if (cv_record_) {
      if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
        // It's actually an MDCVInfoPDB70 structure.
        const MDCVInfoPDB70* cv_record_70 =
            reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]);
        assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
  
        // Use the same format that the MS symbol server uses in filesystem
        // hierarchies.
        identifier = guid_and_age_to_debug_id(cv_record_70->signature,
                                              cv_record_70->age);
      } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
        // It's actually an MDCVInfoPDB20 structure.
        const MDCVInfoPDB20* cv_record_20 =
            reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]);
        assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
  
        // Use the same format that the MS symbol server uses in filesystem
        // hierarchies.
        char identifier_string[17];
        snprintf(identifier_string, sizeof(identifier_string),
                 "%08X%x", cv_record_20->signature, cv_record_20->age);
        identifier = identifier_string;
      } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
        // It's actually an MDCVInfoELF structure.
        const MDCVInfoELF* cv_record_elf =
            reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]);
        assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
  
        // For backwards-compatibility, stuff as many bytes as will fit into
        // a MDGUID and use the MS symbol server format as MDCVInfoPDB70 does
        // with age = 0. Historically Breakpad would do this during dump
        // writing to fit the build id data into a MDCVInfoPDB70 struct.
        // The full build id is available by calling code_identifier.
        MDGUID guid = {0};
        memcpy(&guid, &cv_record_elf->build_id,
               std::min(cv_record_->size() - MDCVInfoELF_minsize,
                        sizeof(MDGUID)));
        identifier = guid_and_age_to_debug_id(guid, 0);
      }
    }
  
    // TODO(mmentovai): if there's no usable CodeView record, there might be a
    // miscellaneous debug record.  It only carries a filename, though, and no
    // identifier.  I'm not sure what the right thing to do for the identifier
    // is in that case, but I don't expect to find many modules without a
    // CodeView record (or some other Breakpad extension structure in place of
    // a CodeView record).  Treat it as an error (empty identifier) for now.
  
    // TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier().
  
    // If possible, synthesize a debug_identifier from the version and
    // architecture.
    if (identifier.empty()) {
      std::string ver = version();
      if (ver.compare("") != 0) {
        identifier = "";
        for (std::string::const_iterator i = ver.begin(); i != ver.end(); i++) {
          if (isxdigit(*i)) {
            identifier += *i;
          }
        }
  
        MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo();
        if (minidump_system_info) {
          std::string cpu = minidump_system_info->GetCPU();
          for (std::string::const_iterator i = cpu.begin(); i != cpu.end(); i++) {
            char ashex[4];
            snprintf(ashex, sizeof(ashex), "%02x", *i);
            identifier += ashex;
          }
        }
  
        while (identifier.size() < 33) {
          identifier += "0";
        }
      }
    }
  
    // Fallback to a default debug_identifier
    if (identifier.empty())
    {
      identifier = "000000000000000000000000000000000";
    }
  
    // Relatively common case
    BPLOG_IF(INFO, identifier.empty()) << "MinidumpModule could not determine "
                                          "debug_identifier for " << *name_;
  
    return identifier;
  }
  
  
  string MinidumpModule::version() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModule for version";
      return "";
    }
  
    string version;
  
    if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE &&
        module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) {
      char version_string[24];
      snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u",
               module_.version_info.file_version_hi >> 16,
               module_.version_info.file_version_hi & 0xffff,
               module_.version_info.file_version_lo >> 16,
               module_.version_info.file_version_lo & 0xffff);
      version = version_string;
    }
  
    // TODO(mmentovai): possibly support other struct types in place of
    // the one used with MD_VSFIXEDFILEINFO_SIGNATURE.  We can possibly use
    // a different structure that better represents versioning facilities on
    // Mac OS X and Linux, instead of forcing them to adhere to the dotted
    // quad of 16-bit ints that Windows uses.
  
    BPLOG_IF(INFO, version.empty()) << "MinidumpModule could not determine "
                                       "version for " << *name_;
  
    return version;
  }
  
  
  CodeModule* MinidumpModule::Copy() const {
    return new BasicCodeModule(this);
  }
  
  
  uint64_t MinidumpModule::shrink_down_delta() const {
    return 0;
  }
  
  void MinidumpModule::SetShrinkDownDelta(uint64_t shrink_down_delta) {
    // Not implemented
    assert(false);
  }
  
  
  const uint8_t* MinidumpModule::GetCVRecord(uint32_t* size) {
    if (!module_valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModule for GetCVRecord";
      return NULL;
    }
  
    if (!cv_record_) {
      // This just guards against 0-sized CodeView records; more specific checks
      // are used when the signature is checked against various structure types.
      if (module_.cv_record.data_size == 0) {
        return NULL;
      }
  
      if (!minidump_->SeekSet(module_.cv_record.rva)) {
        BPLOG(ERROR) << "MinidumpModule could not seek to CodeView record";
        return NULL;
      }
  
      if (module_.cv_record.data_size > max_cv_bytes_) {
        BPLOG(ERROR) << "MinidumpModule CodeView record size " <<
                        module_.cv_record.data_size << " exceeds maximum " <<
                        max_cv_bytes_;
        return NULL;
      }
  
      // Allocating something that will be accessed as MDCVInfoPDB70 or
      // MDCVInfoPDB20 but is allocated as uint8_t[] can cause alignment
      // problems.  x86 and ppc are able to cope, though.  This allocation
      // style is needed because the MDCVInfoPDB70 or MDCVInfoPDB20 are
      // variable-sized due to their pdb_file_name fields; these structures
      // are not MDCVInfoPDB70_minsize or MDCVInfoPDB20_minsize and treating
      // them as such would result in incomplete structures or overruns.
      scoped_ptr< vector<uint8_t> > cv_record(
          new vector<uint8_t>(module_.cv_record.data_size));
  
      if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size)) {
        BPLOG(ERROR) << "MinidumpModule could not read CodeView record";
        return NULL;
      }
  
      uint32_t signature = MD_CVINFOUNKNOWN_SIGNATURE;
      if (module_.cv_record.data_size > sizeof(signature)) {
        MDCVInfoPDB70* cv_record_signature =
            reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]);
        signature = cv_record_signature->cv_signature;
        if (minidump_->swap())
          Swap(&signature);
      }
  
      if (signature == MD_CVINFOPDB70_SIGNATURE) {
        // Now that the structure type is known, recheck the size,
        // ensuring at least one byte for the null terminator.
        if (MDCVInfoPDB70_minsize + 1 > module_.cv_record.data_size) {
          BPLOG(ERROR) << "MinidumpModule CodeView7 record size mismatch, " <<
                          MDCVInfoPDB70_minsize << " > " <<
                          module_.cv_record.data_size;
          return NULL;
        }
  
        if (minidump_->swap()) {
          MDCVInfoPDB70* cv_record_70 =
              reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]);
          Swap(&cv_record_70->cv_signature);
          Swap(&cv_record_70->signature);
          Swap(&cv_record_70->age);
          // Don't swap cv_record_70.pdb_file_name because it's an array of 8-bit
          // quantities.  (It's a path, is it UTF-8?)
        }
  
        // The last field of either structure is null-terminated 8-bit character
        // data.  Ensure that it's null-terminated.
        if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') {
          BPLOG(ERROR) << "MinidumpModule CodeView7 record string is not "
                          "0-terminated";
          return NULL;
        }
      } else if (signature == MD_CVINFOPDB20_SIGNATURE) {
        // Now that the structure type is known, recheck the size,
        // ensuring at least one byte for the null terminator.
        if (MDCVInfoPDB20_minsize + 1 > module_.cv_record.data_size) {
          BPLOG(ERROR) << "MinidumpModule CodeView2 record size mismatch, " <<
                          MDCVInfoPDB20_minsize << " > " <<
                          module_.cv_record.data_size;
          return NULL;
        }
        if (minidump_->swap()) {
          MDCVInfoPDB20* cv_record_20 =
              reinterpret_cast<MDCVInfoPDB20*>(&(*cv_record)[0]);
          Swap(&cv_record_20->cv_header.signature);
          Swap(&cv_record_20->cv_header.offset);
          Swap(&cv_record_20->signature);
          Swap(&cv_record_20->age);
          // Don't swap cv_record_20.pdb_file_name because it's an array of 8-bit
          // quantities.  (It's a path, is it UTF-8?)
        }
  
        // The last field of either structure is null-terminated 8-bit character
        // data.  Ensure that it's null-terminated.
        if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') {
          BPLOG(ERROR) << "MindumpModule CodeView2 record string is not "
                          "0-terminated";
          return NULL;
        }
      } else if (signature == MD_CVINFOELF_SIGNATURE) {
        // Now that the structure type is known, recheck the size.
        if (MDCVInfoELF_minsize > module_.cv_record.data_size) {
          BPLOG(ERROR) << "MinidumpModule CodeViewELF record size mismatch, " <<
                          MDCVInfoELF_minsize << " > " <<
                          module_.cv_record.data_size;
          return NULL;
        }
        if (minidump_->swap()) {
          MDCVInfoELF* cv_record_elf =
              reinterpret_cast<MDCVInfoELF*>(&(*cv_record)[0]);
          Swap(&cv_record_elf->cv_signature);
        }
      }
  
      // If the signature doesn't match something above, it's not something
      // that Breakpad can presently handle directly.  Because some modules in
      // the wild contain such CodeView records as MD_CVINFOCV50_SIGNATURE,
      // don't bail out here - allow the data to be returned to the user,
      // although byte-swapping can't be done.
  
      // Store the vector type because that's how storage was allocated, but
      // return it casted to uint8_t*.
      cv_record_ = cv_record.release();
      cv_record_signature_ = signature;
    }
  
    if (size)
      *size = module_.cv_record.data_size;
  
    return &(*cv_record_)[0];
  }
  
  
  const MDImageDebugMisc* MinidumpModule::GetMiscRecord(uint32_t* size) {
    if (!module_valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModule for GetMiscRecord";
      return NULL;
    }
  
    if (!misc_record_) {
      if (module_.misc_record.data_size == 0) {
        return NULL;
      }
  
      if (MDImageDebugMisc_minsize > module_.misc_record.data_size) {
        BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record "
                        "size mismatch, " << MDImageDebugMisc_minsize << " > " <<
                        module_.misc_record.data_size;
        return NULL;
      }
  
      if (!minidump_->SeekSet(module_.misc_record.rva)) {
        BPLOG(ERROR) << "MinidumpModule could not seek to miscellaneous "
                        "debugging record";
        return NULL;
      }
  
      if (module_.misc_record.data_size > max_misc_bytes_) {
        BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record size " <<
                        module_.misc_record.data_size << " exceeds maximum " <<
                        max_misc_bytes_;
        return NULL;
      }
  
      // Allocating something that will be accessed as MDImageDebugMisc but
      // is allocated as uint8_t[] can cause alignment problems.  x86 and
      // ppc are able to cope, though.  This allocation style is needed
      // because the MDImageDebugMisc is variable-sized due to its data field;
      // this structure is not MDImageDebugMisc_minsize and treating it as such
      // would result in an incomplete structure or an overrun.
      scoped_ptr< vector<uint8_t> > misc_record_mem(
          new vector<uint8_t>(module_.misc_record.data_size));
      MDImageDebugMisc* misc_record =
          reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_mem)[0]);
  
      if (!minidump_->ReadBytes(misc_record, module_.misc_record.data_size)) {
        BPLOG(ERROR) << "MinidumpModule could not read miscellaneous debugging "
                        "record";
        return NULL;
      }
  
      if (minidump_->swap()) {
        Swap(&misc_record->data_type);
        Swap(&misc_record->length);
        // Don't swap misc_record.unicode because it's an 8-bit quantity.
        // Don't swap the reserved fields for the same reason, and because
        // they don't contain any valid data.
        if (misc_record->unicode) {
          // There is a potential alignment problem, but shouldn't be a problem
          // in practice due to the layout of MDImageDebugMisc.
          uint16_t* data16 = reinterpret_cast<uint16_t*>(&(misc_record->data));
          size_t dataBytes = module_.misc_record.data_size -
                             MDImageDebugMisc_minsize;
          Swap(data16, dataBytes);
        }
      }
  
      if (module_.misc_record.data_size != misc_record->length) {
        BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record data "
                        "size mismatch, " << module_.misc_record.data_size <<
                        " != " << misc_record->length;
        return NULL;
      }
  
      // Store the vector type because that's how storage was allocated, but
      // return it casted to MDImageDebugMisc*.
      misc_record_ = misc_record_mem.release();
    }
  
    if (size)
      *size = module_.misc_record.data_size;
  
    return reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_)[0]);
  }
  
  
  void MinidumpModule::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpModule cannot print invalid data";
      return;
    }
  
    printf("MDRawModule\n");
    printf("  base_of_image                   = 0x%" PRIx64 "\n",
           module_.base_of_image);
    printf("  size_of_image                   = 0x%x\n",
           module_.size_of_image);
    printf("  checksum                        = 0x%x\n",
           module_.checksum);
    printf("  time_date_stamp                 = 0x%x %s\n",
           module_.time_date_stamp,
           TimeTToUTCString(module_.time_date_stamp).c_str());
    printf("  module_name_rva                 = 0x%x\n",
           module_.module_name_rva);
    printf("  version_info.signature          = 0x%x\n",
           module_.version_info.signature);
    printf("  version_info.struct_version     = 0x%x\n",
           module_.version_info.struct_version);
    printf("  version_info.file_version       = 0x%x:0x%x\n",
           module_.version_info.file_version_hi,
           module_.version_info.file_version_lo);
    printf("  version_info.product_version    = 0x%x:0x%x\n",
           module_.version_info.product_version_hi,
           module_.version_info.product_version_lo);
    printf("  version_info.file_flags_mask    = 0x%x\n",
           module_.version_info.file_flags_mask);
    printf("  version_info.file_flags         = 0x%x\n",
           module_.version_info.file_flags);
    printf("  version_info.file_os            = 0x%x\n",
           module_.version_info.file_os);
    printf("  version_info.file_type          = 0x%x\n",
           module_.version_info.file_type);
    printf("  version_info.file_subtype       = 0x%x\n",
           module_.version_info.file_subtype);
    printf("  version_info.file_date          = 0x%x:0x%x\n",
           module_.version_info.file_date_hi,
           module_.version_info.file_date_lo);
    printf("  cv_record.data_size             = %d\n",
           module_.cv_record.data_size);
    printf("  cv_record.rva                   = 0x%x\n",
           module_.cv_record.rva);
    printf("  misc_record.data_size           = %d\n",
           module_.misc_record.data_size);
    printf("  misc_record.rva                 = 0x%x\n",
           module_.misc_record.rva);
  
    printf("  (code_file)                     = \"%s\"\n", code_file().c_str());
    printf("  (code_identifier)               = \"%s\"\n",
           code_identifier().c_str());
  
    uint32_t cv_record_size;
    const uint8_t* cv_record = GetCVRecord(&cv_record_size);
    if (cv_record) {
      if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) {
        const MDCVInfoPDB70* cv_record_70 =
            reinterpret_cast<const MDCVInfoPDB70*>(cv_record);
        assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE);
  
        printf("  (cv_record).cv_signature        = 0x%x\n",
               cv_record_70->cv_signature);
        printf("  (cv_record).signature           = %s\n",
               MDGUIDToString(cv_record_70->signature).c_str());
        printf("  (cv_record).age                 = %d\n",
               cv_record_70->age);
        printf("  (cv_record).pdb_file_name       = \"%s\"\n",
               cv_record_70->pdb_file_name);
      } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) {
        const MDCVInfoPDB20* cv_record_20 =
            reinterpret_cast<const MDCVInfoPDB20*>(cv_record);
        assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE);
  
        printf("  (cv_record).cv_header.signature = 0x%x\n",
               cv_record_20->cv_header.signature);
        printf("  (cv_record).cv_header.offset    = 0x%x\n",
               cv_record_20->cv_header.offset);
        printf("  (cv_record).signature           = 0x%x %s\n",
               cv_record_20->signature,
               TimeTToUTCString(cv_record_20->signature).c_str());
        printf("  (cv_record).age                 = %d\n",
               cv_record_20->age);
        printf("  (cv_record).pdb_file_name       = \"%s\"\n",
               cv_record_20->pdb_file_name);
      } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) {
        const MDCVInfoELF* cv_record_elf =
            reinterpret_cast<const MDCVInfoELF*>(cv_record);
        assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE);
  
        printf("  (cv_record).cv_signature        = 0x%x\n",
               cv_record_elf->cv_signature);
        printf("  (cv_record).build_id            = ");
        for (unsigned int build_id_index = 0;
             build_id_index < (cv_record_size - MDCVInfoELF_minsize);
             ++build_id_index) {
          printf("%02x", cv_record_elf->build_id[build_id_index]);
        }
        printf("\n");
      } else {
        printf("  (cv_record)                     = ");
        for (unsigned int cv_byte_index = 0;
             cv_byte_index < cv_record_size;
             ++cv_byte_index) {
          printf("%02x", cv_record[cv_byte_index]);
        }
        printf("\n");
      }
    } else {
      printf("  (cv_record)                     = (null)\n");
    }
  
    const MDImageDebugMisc* misc_record = GetMiscRecord(NULL);
    if (misc_record) {
      printf("  (misc_record).data_type         = 0x%x\n",
             misc_record->data_type);
      printf("  (misc_record).length            = 0x%x\n",
             misc_record->length);
      printf("  (misc_record).unicode           = %d\n",
             misc_record->unicode);
      if (misc_record->unicode) {
        string misc_record_data_utf8;
        ConvertUTF16BufferToUTF8String(
            reinterpret_cast<const uint16_t*>(misc_record->data),
            misc_record->length - offsetof(MDImageDebugMisc, data),
            &misc_record_data_utf8,
            false);  // already swapped
        printf("  (misc_record).data              = \"%s\"\n",
               misc_record_data_utf8.c_str());
      } else {
        printf("  (misc_record).data              = \"%s\"\n",
               misc_record->data);
      }
    } else {
      printf("  (misc_record)                   = (null)\n");
    }
  
    printf("  (debug_file)                    = \"%s\"\n", debug_file().c_str());
    printf("  (debug_identifier)              = \"%s\"\n",
           debug_identifier().c_str());
    printf("  (version)                       = \"%s\"\n", version().c_str());
    printf("\n");
  }
  
  
  //
  // MinidumpModuleList
  //
  
  
  uint32_t MinidumpModuleList::max_modules_ = 2048;
  
  
  MinidumpModuleList::MinidumpModuleList(Minidump* minidump)
      : MinidumpStream(minidump),
        range_map_(new RangeMap<uint64_t, unsigned int>()),
        modules_(NULL),
        module_count_(0) {
    MDOSPlatform platform;
    if (minidump_->GetPlatform(&platform) &&
        (platform == MD_OS_ANDROID || platform == MD_OS_LINUX)) {
      range_map_->SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
    }
  }
  
  
  MinidumpModuleList::~MinidumpModuleList() {
    delete range_map_;
    delete modules_;
  }
  
  
  bool MinidumpModuleList::Read(uint32_t expected_size) {
    // Invalidate cached data.
    range_map_->Clear();
    delete modules_;
    modules_ = NULL;
    module_count_ = 0;
  
    valid_ = false;
  
    uint32_t module_count;
    if (expected_size < sizeof(module_count)) {
      BPLOG(ERROR) << "MinidumpModuleList count size mismatch, " <<
                      expected_size << " < " << sizeof(module_count);
      return false;
    }
    if (!minidump_->ReadBytes(&module_count, sizeof(module_count))) {
      BPLOG(ERROR) << "MinidumpModuleList could not read module count";
      return false;
    }
  
    if (minidump_->swap())
      Swap(&module_count);
  
    if (module_count > numeric_limits<uint32_t>::max() / MD_MODULE_SIZE) {
      BPLOG(ERROR) << "MinidumpModuleList module count " << module_count <<
                      " would cause multiplication overflow";
      return false;
    }
  
    if (expected_size != sizeof(module_count) +
                         module_count * MD_MODULE_SIZE) {
      // may be padded with 4 bytes on 64bit ABIs for alignment
      if (expected_size == sizeof(module_count) + 4 +
                           module_count * MD_MODULE_SIZE) {
        uint32_t useless;
        if (!minidump_->ReadBytes(&useless, 4)) {
          BPLOG(ERROR) << "MinidumpModuleList cannot read modulelist padded "
                          "bytes";
          return false;
        }
      } else {
        BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size <<
                        " != " << sizeof(module_count) +
                        module_count * MD_MODULE_SIZE;
        return false;
      }
    }
  
    if (module_count > max_modules_) {
      BPLOG(ERROR) << "MinidumpModuleList count " << module_count <<
                      " exceeds maximum " << max_modules_;
      return false;
    }
  
    if (module_count != 0) {
      scoped_ptr<MinidumpModules> modules(
          new MinidumpModules(module_count, MinidumpModule(minidump_)));
  
      for (uint32_t module_index = 0; module_index < module_count;
           ++module_index) {
        MinidumpModule* module = &(*modules)[module_index];
  
        // Assume that the file offset is correct after the last read.
        if (!module->Read()) {
          BPLOG(ERROR) << "MinidumpModuleList could not read module " <<
                          module_index << "/" << module_count;
          return false;
        }
      }
  
      // Loop through the module list once more to read additional data and
      // build the range map.  This is done in a second pass because
      // MinidumpModule::ReadAuxiliaryData seeks around, and if it were
      // included in the loop above, additional seeks would be needed where
      // none are now to read contiguous data.
      uint64_t last_end_address = 0;
      for (uint32_t module_index = 0; module_index < module_count;
           ++module_index) {
        MinidumpModule& module = (*modules)[module_index];
  
        // ReadAuxiliaryData fails if any data that the module indicates should
        // exist is missing, but we treat some such cases as valid anyway.  See
        // issue #222: if a debugging record is of a format that's too large to
        // handle, it shouldn't render the entire dump invalid.  Check module
        // validity before giving up.
        if (!module.ReadAuxiliaryData() && !module.valid()) {
          BPLOG(ERROR) << "MinidumpModuleList could not read required module "
                          "auxiliary data for module " <<
                          module_index << "/" << module_count;
          return false;
        }
  
        // It is safe to use module->code_file() after successfully calling
        // module->ReadAuxiliaryData or noting that the module is valid.
  
        uint64_t base_address = module.base_address();
        uint64_t module_size = module.size();
        if (base_address == static_cast<uint64_t>(-1)) {
          BPLOG(ERROR) << "MinidumpModuleList found bad base address for module "
                       << module_index << "/" << module_count << ", "
                       << module.code_file();
          return false;
        }
  
        // Some minidumps have additional modules in the list that are duplicates.
        // Ignore them. See https://crbug.com/838322
        uint32_t existing_module_index;
        if (range_map_->RetrieveRange(base_address, &existing_module_index,
                                      nullptr, nullptr, nullptr) &&
            existing_module_index < module_count) {
          const MinidumpModule& existing_module =
              (*modules)[existing_module_index];
          if (existing_module.base_address() == module.base_address() &&
              existing_module.size() == module.size() &&
              existing_module.code_file() == module.code_file() &&
              existing_module.code_identifier() == module.code_identifier()) {
            continue;
          }
        }
  
        const bool is_android = minidump_->IsAndroid();
        if (!StoreRange(module, base_address, module_index, module_count,
                        is_android)) {
          if (!is_android || base_address >= last_end_address) {
            BPLOG(ERROR) << "MinidumpModuleList could not store module "
                         << module_index << "/" << module_count << ", "
                         << module.code_file() << ", " << HexString(base_address)
                         << "+" << HexString(module_size);
            return false;
          }
  
          // If failed due to apparent range overlap the cause may be the client
          // correction applied for Android packed relocations.  If this is the
          // case, back out the client correction and retry.
          assert(is_android);
          module_size -= last_end_address - base_address;
          base_address = last_end_address;
          if (!range_map_->StoreRange(base_address, module_size, module_index)) {
            BPLOG(ERROR) << "MinidumpModuleList could not store module "
                         << module_index << "/" << module_count << ", "
                         << module.code_file() << ", " << HexString(base_address)
                         << "+" << HexString(module_size) << ", after adjusting";
            return false;
          }
        }
        last_end_address = base_address + module_size;
      }
  
      modules_ = modules.release();
    }
  
    module_count_ = module_count;
  
    valid_ = true;
    return true;
  }
  
  bool MinidumpModuleList::StoreRange(const MinidumpModule& module,
                                      uint64_t base_address,
                                      uint32_t module_index,
                                      uint32_t module_count,
                                      bool is_android) {
    if (range_map_->StoreRange(base_address, module.size(), module_index))
      return true;
  
    // Android's shared memory implementation /dev/ashmem can contain duplicate
    // entries for JITted code, so ignore these.
    // TODO(wfh): Remove this code when Android is fixed.
    // See https://crbug.com/439531
    if (is_android && IsDevAshmem(module.code_file())) {
      BPLOG(INFO) << "MinidumpModuleList ignoring overlapping module "
                  << module_index << "/" << module_count << ", "
                  << module.code_file() << ", " << HexString(base_address) << "+"
                  << HexString(module.size());
      return true;
    }
  
    return false;
  }
  
  const MinidumpModule* MinidumpModuleList::GetModuleForAddress(
      uint64_t address) const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleForAddress";
      return NULL;
    }
  
    unsigned int module_index;
    if (!range_map_->RetrieveRange(address, &module_index, NULL /* base */,
                                   NULL /* delta */, NULL /* size */)) {
      BPLOG(INFO) << "MinidumpModuleList has no module at " <<
                     HexString(address);
      return NULL;
    }
  
    return GetModuleAtIndex(module_index);
  }
  
  
  const MinidumpModule* MinidumpModuleList::GetMainModule() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModuleList for GetMainModule";
      return NULL;
    }
  
    // The main code module is the first one present in a minidump file's
    // MDRawModuleList.
    return GetModuleAtIndex(0);
  }
  
  
  const MinidumpModule* MinidumpModuleList::GetModuleAtSequence(
      unsigned int sequence) const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtSequence";
      return NULL;
    }
  
    if (sequence >= module_count_) {
      BPLOG(ERROR) << "MinidumpModuleList sequence out of range: " <<
                      sequence << "/" << module_count_;
      return NULL;
    }
  
    unsigned int module_index;
    if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index,
                                          NULL /* base */, NULL /* delta */,
                                          NULL /* size */)) {
      BPLOG(ERROR) << "MinidumpModuleList has no module at sequence " << sequence;
      return NULL;
    }
  
    return GetModuleAtIndex(module_index);
  }
  
  
  const MinidumpModule* MinidumpModuleList::GetModuleAtIndex(
      unsigned int index) const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtIndex";
      return NULL;
    }
  
    if (index >= module_count_) {
      BPLOG(ERROR) << "MinidumpModuleList index out of range: " <<
                      index << "/" << module_count_;
      return NULL;
    }
  
    return &(*modules_)[index];
  }
  
  
  const CodeModules* MinidumpModuleList::Copy() const {
    return new BasicCodeModules(this, range_map_->GetMergeStrategy());
  }
  
  vector<linked_ptr<const CodeModule> >
  MinidumpModuleList::GetShrunkRangeModules() const {
    return vector<linked_ptr<const CodeModule> >();
  }
  
  void MinidumpModuleList::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpModuleList cannot print invalid data";
      return;
    }
  
    printf("MinidumpModuleList\n");
    printf("  module_count = %d\n", module_count_);
    printf("\n");
  
    for (unsigned int module_index = 0;
         module_index < module_count_;
         ++module_index) {
      printf("module[%d]\n", module_index);
  
      (*modules_)[module_index].Print();
    }
  }
  
  
  //
  // MinidumpMemoryList
  //
  
  
  uint32_t MinidumpMemoryList::max_regions_ = 4096;
  
  
  MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump)
      : MinidumpStream(minidump),
        range_map_(new RangeMap<uint64_t, unsigned int>()),
        descriptors_(NULL),
        regions_(NULL),
        region_count_(0) {
  }
  
  
  MinidumpMemoryList::~MinidumpMemoryList() {
    delete range_map_;
    delete descriptors_;
    delete regions_;
  }
  
  
  bool MinidumpMemoryList::Read(uint32_t expected_size) {
    // Invalidate cached data.
    delete descriptors_;
    descriptors_ = NULL;
    delete regions_;
    regions_ = NULL;
    range_map_->Clear();
    region_count_ = 0;
  
    valid_ = false;
  
    uint32_t region_count;
    if (expected_size < sizeof(region_count)) {
      BPLOG(ERROR) << "MinidumpMemoryList count size mismatch, " <<
                      expected_size << " < " << sizeof(region_count);
      return false;
    }
    if (!minidump_->ReadBytes(&region_count, sizeof(region_count))) {
      BPLOG(ERROR) << "MinidumpMemoryList could not read memory region count";
      return false;
    }
  
    if (minidump_->swap())
      Swap(&region_count);
  
    if (region_count >
            numeric_limits<uint32_t>::max() / sizeof(MDMemoryDescriptor)) {
      BPLOG(ERROR) << "MinidumpMemoryList region count " << region_count <<
                      " would cause multiplication overflow";
      return false;
    }
  
    if (expected_size != sizeof(region_count) +
                         region_count * sizeof(MDMemoryDescriptor)) {
      // may be padded with 4 bytes on 64bit ABIs for alignment
      if (expected_size == sizeof(region_count) + 4 +
                           region_count * sizeof(MDMemoryDescriptor)) {
        uint32_t useless;
        if (!minidump_->ReadBytes(&useless, 4)) {
          BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded "
                          "bytes";
          return false;
        }
      } else {
        BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size <<
                        " != " << sizeof(region_count) +
                        region_count * sizeof(MDMemoryDescriptor);
        return false;
      }
    }
  
    if (region_count > max_regions_) {
      BPLOG(ERROR) << "MinidumpMemoryList count " << region_count <<
                      " exceeds maximum " << max_regions_;
      return false;
    }
  
    if (region_count != 0) {
      scoped_ptr<MemoryDescriptors> descriptors(
          new MemoryDescriptors(region_count));
  
      // Read the entire array in one fell swoop, instead of reading one entry
      // at a time in the loop.
      if (!minidump_->ReadBytes(&(*descriptors)[0],
                                sizeof(MDMemoryDescriptor) * region_count)) {
        BPLOG(ERROR) << "MinidumpMemoryList could not read memory region list";
        return false;
      }
  
      scoped_ptr<MemoryRegions> regions(
          new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_)));
  
      for (unsigned int region_index = 0;
           region_index < region_count;
           ++region_index) {
        MDMemoryDescriptor* descriptor = &(*descriptors)[region_index];
  
        if (minidump_->swap())
          Swap(descriptor);
  
        uint64_t base_address = descriptor->start_of_memory_range;
        uint32_t region_size = descriptor->memory.data_size;
  
        // Check for base + size overflow or undersize.
        if (region_size == 0 ||
            region_size > numeric_limits<uint64_t>::max() - base_address) {
          BPLOG(ERROR) << "MinidumpMemoryList has a memory region problem, " <<
                          " region " << region_index << "/" << region_count <<
                          ", " << HexString(base_address) << "+" <<
                          HexString(region_size);
          return false;
        }
  
        if (!range_map_->StoreRange(base_address, region_size, region_index)) {
          BPLOG(ERROR) << "MinidumpMemoryList could not store memory region " <<
                          region_index << "/" << region_count << ", " <<
                          HexString(base_address) << "+" <<
                          HexString(region_size);
          return false;
        }
  
        (*regions)[region_index].SetDescriptor(descriptor);
      }
  
      descriptors_ = descriptors.release();
      regions_ = regions.release();
    }
  
    region_count_ = region_count;
  
    valid_ = true;
    return true;
  }
  
  
  MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionAtIndex(
        unsigned int index) {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionAtIndex";
      return NULL;
    }
  
    if (index >= region_count_) {
      BPLOG(ERROR) << "MinidumpMemoryList index out of range: " <<
                      index << "/" << region_count_;
      return NULL;
    }
  
    return &(*regions_)[index];
  }
  
  
  MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress(
      uint64_t address) {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionForAddress";
      return NULL;
    }
  
    unsigned int region_index;
    if (!range_map_->RetrieveRange(address, &region_index, NULL /* base */,
                                   NULL /* delta */, NULL /* size */)) {
      BPLOG(INFO) << "MinidumpMemoryList has no memory region at " <<
                     HexString(address);
      return NULL;
    }
  
    return GetMemoryRegionAtIndex(region_index);
  }
  
  
  void MinidumpMemoryList::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpMemoryList cannot print invalid data";
      return;
    }
  
    printf("MinidumpMemoryList\n");
    printf("  region_count = %d\n", region_count_);
    printf("\n");
  
    for (unsigned int region_index = 0;
         region_index < region_count_;
         ++region_index) {
      MDMemoryDescriptor* descriptor = &(*descriptors_)[region_index];
      printf("region[%d]\n", region_index);
      printf("MDMemoryDescriptor\n");
      printf("  start_of_memory_range = 0x%" PRIx64 "\n",
             descriptor->start_of_memory_range);
      printf("  memory.data_size      = 0x%x\n", descriptor->memory.data_size);
      printf("  memory.rva            = 0x%x\n", descriptor->memory.rva);
      MinidumpMemoryRegion* region = GetMemoryRegionAtIndex(region_index);
      if (region) {
        printf("Memory\n");
        region->Print();
      } else {
        printf("No memory\n");
      }
      printf("\n");
    }
  }
  
  
  //
  // MinidumpException
  //
  
  
  MinidumpException::MinidumpException(Minidump* minidump)
      : MinidumpStream(minidump),
        exception_(),
        context_(NULL) {
  }
  
  
  MinidumpException::~MinidumpException() {
    delete context_;
  }
  
  
  bool MinidumpException::Read(uint32_t expected_size) {
    // Invalidate cached data.
    delete context_;
    context_ = NULL;
  
    valid_ = false;
  
    if (expected_size != sizeof(exception_)) {
      BPLOG(ERROR) << "MinidumpException size mismatch, " << expected_size <<
                      " != " << sizeof(exception_);
      return false;
    }
  
    if (!minidump_->ReadBytes(&exception_, sizeof(exception_))) {
      BPLOG(ERROR) << "MinidumpException cannot read exception";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&exception_.thread_id);
      // exception_.__align is for alignment only and does not need to be
      // swapped.
      Swap(&exception_.exception_record.exception_code);
      Swap(&exception_.exception_record.exception_flags);
      Swap(&exception_.exception_record.exception_record);
      Swap(&exception_.exception_record.exception_address);
      Swap(&exception_.exception_record.number_parameters);
      // exception_.exception_record.__align is for alignment only and does not
      // need to be swapped.
      for (unsigned int parameter_index = 0;
           parameter_index < MD_EXCEPTION_MAXIMUM_PARAMETERS;
           ++parameter_index) {
        Swap(&exception_.exception_record.exception_information[parameter_index]);
      }
      Swap(&exception_.thread_context);
    }
  
    valid_ = true;
    return true;
  }
  
  
  bool MinidumpException::GetThreadID(uint32_t* thread_id) const {
    BPLOG_IF(ERROR, !thread_id) << "MinidumpException::GetThreadID requires "
                                   "|thread_id|";
    assert(thread_id);
    *thread_id = 0;
  
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpException for GetThreadID";
      return false;
    }
  
    *thread_id = exception_.thread_id;
    return true;
  }
  
  
  MinidumpContext* MinidumpException::GetContext() {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpException for GetContext";
      return NULL;
    }
  
    if (!context_) {
      if (!minidump_->SeekSet(exception_.thread_context.rva)) {
        BPLOG(ERROR) << "MinidumpException cannot seek to context";
        return NULL;
      }
  
      scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_));
  
      // Don't log as an error if we can still fall back on the thread's context
      // (which must be possible if we got this far.)
      if (!context->Read(exception_.thread_context.data_size)) {
        BPLOG(INFO) << "MinidumpException cannot read context";
        return NULL;
      }
  
      context_ = context.release();
    }
  
    return context_;
  }
  
  
  void MinidumpException::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpException cannot print invalid data";
      return;
    }
  
    printf("MDException\n");
    printf("  thread_id                                  = 0x%x\n",
           exception_.thread_id);
    printf("  exception_record.exception_code            = 0x%x\n",
           exception_.exception_record.exception_code);
    printf("  exception_record.exception_flags           = 0x%x\n",
           exception_.exception_record.exception_flags);
    printf("  exception_record.exception_record          = 0x%" PRIx64 "\n",
           exception_.exception_record.exception_record);
    printf("  exception_record.exception_address         = 0x%" PRIx64 "\n",
           exception_.exception_record.exception_address);
    printf("  exception_record.number_parameters         = %d\n",
           exception_.exception_record.number_parameters);
    for (unsigned int parameterIndex = 0;
         parameterIndex < exception_.exception_record.number_parameters;
         ++parameterIndex) {
      printf("  exception_record.exception_information[%2d] = 0x%" PRIx64 "\n",
             parameterIndex,
             exception_.exception_record.exception_information[parameterIndex]);
    }
    printf("  thread_context.data_size                   = %d\n",
           exception_.thread_context.data_size);
    printf("  thread_context.rva                         = 0x%x\n",
           exception_.thread_context.rva);
    MinidumpContext* context = GetContext();
    if (context) {
      printf("\n");
      context->Print();
    } else {
      printf("  (no context)\n");
      printf("\n");
    }
  }
  
  //
  // MinidumpAssertion
  //
  
  
  MinidumpAssertion::MinidumpAssertion(Minidump* minidump)
      : MinidumpStream(minidump),
        assertion_(),
        expression_(),
        function_(),
        file_() {
  }
  
  
  MinidumpAssertion::~MinidumpAssertion() {
  }
  
  
  bool MinidumpAssertion::Read(uint32_t expected_size) {
    // Invalidate cached data.
    valid_ = false;
  
    if (expected_size != sizeof(assertion_)) {
      BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size <<
                      " != " << sizeof(assertion_);
      return false;
    }
  
    if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) {
      BPLOG(ERROR) << "MinidumpAssertion cannot read assertion";
      return false;
    }
  
    // Each of {expression, function, file} is a UTF-16 string,
    // we'll convert them to UTF-8 for ease of use.
    ConvertUTF16BufferToUTF8String(assertion_.expression,
                                   sizeof(assertion_.expression), &expression_,
                                   minidump_->swap());
    ConvertUTF16BufferToUTF8String(assertion_.function,
                                   sizeof(assertion_.function), &function_,
                                   minidump_->swap());
    ConvertUTF16BufferToUTF8String(assertion_.file, sizeof(assertion_.file),
                                   &file_, minidump_->swap());
  
    if (minidump_->swap()) {
      Swap(&assertion_.line);
      Swap(&assertion_.type);
    }
  
    valid_ = true;
    return true;
  }
  
  void MinidumpAssertion::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data";
      return;
    }
  
    printf("MDAssertion\n");
    printf("  expression                                 = %s\n",
           expression_.c_str());
    printf("  function                                   = %s\n",
           function_.c_str());
    printf("  file                                       = %s\n",
           file_.c_str());
    printf("  line                                       = %u\n",
           assertion_.line);
    printf("  type                                       = %u\n",
           assertion_.type);
    printf("\n");
  }
  
  //
  // MinidumpSystemInfo
  //
  
  
  MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump)
      : MinidumpStream(minidump),
        system_info_(),
        csd_version_(NULL),
        cpu_vendor_(NULL) {
  }
  
  
  MinidumpSystemInfo::~MinidumpSystemInfo() {
    delete csd_version_;
    delete cpu_vendor_;
  }
  
  
  bool MinidumpSystemInfo::Read(uint32_t expected_size) {
    // Invalidate cached data.
    delete csd_version_;
    csd_version_ = NULL;
    delete cpu_vendor_;
    cpu_vendor_ = NULL;
  
    valid_ = false;
  
    if (expected_size != sizeof(system_info_)) {
      BPLOG(ERROR) << "MinidumpSystemInfo size mismatch, " << expected_size <<
                      " != " << sizeof(system_info_);
      return false;
    }
  
    if (!minidump_->ReadBytes(&system_info_, sizeof(system_info_))) {
      BPLOG(ERROR) << "MinidumpSystemInfo cannot read system info";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&system_info_.processor_architecture);
      Swap(&system_info_.processor_level);
      Swap(&system_info_.processor_revision);
      // number_of_processors and product_type are 8-bit quantities and need no
      // swapping.
      Swap(&system_info_.major_version);
      Swap(&system_info_.minor_version);
      Swap(&system_info_.build_number);
      Swap(&system_info_.platform_id);
      Swap(&system_info_.csd_version_rva);
      Swap(&system_info_.suite_mask);
      // Don't swap the reserved2 field because its contents are unknown.
  
      if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
          system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) {
        for (unsigned int i = 0; i < 3; ++i)
          Swap(&system_info_.cpu.x86_cpu_info.vendor_id[i]);
        Swap(&system_info_.cpu.x86_cpu_info.version_information);
        Swap(&system_info_.cpu.x86_cpu_info.feature_information);
        Swap(&system_info_.cpu.x86_cpu_info.amd_extended_cpu_features);
      } else {
        for (unsigned int i = 0; i < 2; ++i)
          Swap(&system_info_.cpu.other_cpu_info.processor_features[i]);
      }
    }
  
    valid_ = true;
    return true;
  }
  
  
  string MinidumpSystemInfo::GetOS() {
    string os;
  
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetOS";
      return os;
    }
  
    switch (system_info_.platform_id) {
      case MD_OS_WIN32_NT:
      case MD_OS_WIN32_WINDOWS:
        os = "windows";
        break;
  
      case MD_OS_MAC_OS_X:
        os = "mac";
        break;
  
      case MD_OS_IOS:
        os = "ios";
        break;
  
      case MD_OS_LINUX:
        os = "linux";
        break;
  
      case MD_OS_SOLARIS:
        os = "solaris";
        break;
  
      case MD_OS_ANDROID:
        os = "android";
        break;
  
      case MD_OS_PS3:
        os = "ps3";
        break;
  
      case MD_OS_NACL:
        os = "nacl";
        break;
  
      case MD_OS_FUCHSIA:
        os = "fuchsia";
        break;
  
      default:
        BPLOG(ERROR) << "MinidumpSystemInfo unknown OS for platform " <<
                        HexString(system_info_.platform_id);
        break;
    }
  
    return os;
  }
  
  
  string MinidumpSystemInfo::GetCPU() {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPU";
      return "";
    }
  
    string cpu;
  
    switch (system_info_.processor_architecture) {
      case MD_CPU_ARCHITECTURE_X86:
      case MD_CPU_ARCHITECTURE_X86_WIN64:
        cpu = "x86";
        break;
  
      case MD_CPU_ARCHITECTURE_AMD64:
        cpu = "x86-64";
        break;
  
      case MD_CPU_ARCHITECTURE_PPC:
        cpu = "ppc";
        break;
  
      case MD_CPU_ARCHITECTURE_PPC64:
        cpu = "ppc64";
        break;
  
      case MD_CPU_ARCHITECTURE_SPARC:
        cpu = "sparc";
        break;
  
      case MD_CPU_ARCHITECTURE_ARM:
        cpu = "arm";
        break;
  
      case MD_CPU_ARCHITECTURE_ARM64:
      case MD_CPU_ARCHITECTURE_ARM64_OLD:
        cpu = "arm64";
        break;
  
      default:
        BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " <<
                        HexString(system_info_.processor_architecture);
        break;
    }
  
    return cpu;
  }
  
  
  const string* MinidumpSystemInfo::GetCSDVersion() {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCSDVersion";
      return NULL;
    }
  
    if (!csd_version_)
      csd_version_ = minidump_->ReadString(system_info_.csd_version_rva);
  
    BPLOG_IF(ERROR, !csd_version_) << "MinidumpSystemInfo could not read "
                                      "CSD version";
  
    return csd_version_;
  }
  
  
  const string* MinidumpSystemInfo::GetCPUVendor() {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPUVendor";
      return NULL;
    }
  
    // CPU vendor information can only be determined from x86 minidumps.
    if (!cpu_vendor_ &&
        (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
         system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64)) {
      char cpu_vendor_string[13];
      snprintf(cpu_vendor_string, sizeof(cpu_vendor_string),
               "%c%c%c%c%c%c%c%c%c%c%c%c",
                system_info_.cpu.x86_cpu_info.vendor_id[0] & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 8) & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 16) & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 24) & 0xff,
                system_info_.cpu.x86_cpu_info.vendor_id[1] & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 8) & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 16) & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 24) & 0xff,
                system_info_.cpu.x86_cpu_info.vendor_id[2] & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 8) & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 16) & 0xff,
               (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 24) & 0xff);
      cpu_vendor_ = new string(cpu_vendor_string);
    }
  
    return cpu_vendor_;
  }
  
  
  void MinidumpSystemInfo::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpSystemInfo cannot print invalid data";
      return;
    }
  
    printf("MDRawSystemInfo\n");
    printf("  processor_architecture                     = 0x%x (%s)\n",
           system_info_.processor_architecture, GetCPU().c_str());
    printf("  processor_level                            = %d\n",
           system_info_.processor_level);
    printf("  processor_revision                         = 0x%x\n",
           system_info_.processor_revision);
    printf("  number_of_processors                       = %d\n",
           system_info_.number_of_processors);
    printf("  product_type                               = %d\n",
           system_info_.product_type);
    printf("  major_version                              = %d\n",
           system_info_.major_version);
    printf("  minor_version                              = %d\n",
           system_info_.minor_version);
    printf("  build_number                               = %d\n",
           system_info_.build_number);
    printf("  platform_id                                = 0x%x (%s)\n",
           system_info_.platform_id, GetOS().c_str());
    printf("  csd_version_rva                            = 0x%x\n",
           system_info_.csd_version_rva);
    printf("  suite_mask                                 = 0x%x\n",
           system_info_.suite_mask);
    if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
        system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) {
      printf("  cpu.x86_cpu_info (valid):\n");
    } else {
      printf("  cpu.x86_cpu_info (invalid):\n");
    }
    for (unsigned int i = 0; i < 3; ++i) {
      printf("  cpu.x86_cpu_info.vendor_id[%d]              = 0x%x\n",
             i, system_info_.cpu.x86_cpu_info.vendor_id[i]);
    }
    printf("  cpu.x86_cpu_info.version_information       = 0x%x\n",
           system_info_.cpu.x86_cpu_info.version_information);
    printf("  cpu.x86_cpu_info.feature_information       = 0x%x\n",
           system_info_.cpu.x86_cpu_info.feature_information);
    printf("  cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n",
           system_info_.cpu.x86_cpu_info.amd_extended_cpu_features);
    if (system_info_.processor_architecture != MD_CPU_ARCHITECTURE_X86 &&
        system_info_.processor_architecture != MD_CPU_ARCHITECTURE_X86_WIN64) {
      printf("  cpu.other_cpu_info (valid):\n");
      for (unsigned int i = 0; i < 2; ++i) {
        printf("  cpu.other_cpu_info.processor_features[%d]   = 0x%" PRIx64 "\n",
               i, system_info_.cpu.other_cpu_info.processor_features[i]);
      }
    }
    const string* csd_version = GetCSDVersion();
    if (csd_version) {
      printf("  (csd_version)                              = \"%s\"\n",
             csd_version->c_str());
    } else {
      printf("  (csd_version)                              = (null)\n");
    }
    const string* cpu_vendor = GetCPUVendor();
    if (cpu_vendor) {
      printf("  (cpu_vendor)                               = \"%s\"\n",
             cpu_vendor->c_str());
    } else {
      printf("  (cpu_vendor)                               = (null)\n");
    }
    printf("\n");
  }
  
  
  //
  // MinidumpUnloadedModule
  //
  
  
  MinidumpUnloadedModule::MinidumpUnloadedModule(Minidump* minidump)
      : MinidumpObject(minidump),
        module_valid_(false),
        unloaded_module_(),
        name_(NULL) {
  
  }
  
  MinidumpUnloadedModule::~MinidumpUnloadedModule() {
    delete name_;
  }
  
  string MinidumpUnloadedModule::code_file() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for code_file";
      return "";
    }
  
    return *name_;
  }
  
  string MinidumpUnloadedModule::code_identifier() const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for code_identifier";
      return "";
    }
  
    MinidumpSystemInfo* minidump_system_info = minidump_->GetSystemInfo();
    if (!minidump_system_info) {
      BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires "
                      "MinidumpSystemInfo";
      return "";
    }
  
    const MDRawSystemInfo* raw_system_info = minidump_system_info->system_info();
    if (!raw_system_info) {
      BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires "
                   << "MDRawSystemInfo";
      return "";
    }
  
    string identifier;
  
    switch (raw_system_info->platform_id) {
      case MD_OS_WIN32_NT:
      case MD_OS_WIN32_WINDOWS: {
        // Use the same format that the MS symbol server uses in filesystem
        // hierarchies.
        char identifier_string[17];
        snprintf(identifier_string, sizeof(identifier_string), "%08X%x",
                 unloaded_module_.time_date_stamp,
                 unloaded_module_.size_of_image);
        identifier = identifier_string;
        break;
      }
  
      case MD_OS_ANDROID:
      case MD_OS_LINUX:
      case MD_OS_MAC_OS_X:
      case MD_OS_IOS:
      case MD_OS_SOLARIS:
      case MD_OS_NACL:
      case MD_OS_PS3: {
        // TODO(mmentovai): support uuid extension if present, otherwise fall
        // back to version (from LC_ID_DYLIB?), otherwise fall back to something
        // else.
        identifier = "id";
        break;
      }
  
      default: {
        // Without knowing what OS generated the dump, we can't generate a good
        // identifier.  Return an empty string, signalling failure.
        BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires known "
                     << "platform, found "
                     << HexString(raw_system_info->platform_id);
        break;
      }
    }
  
    return identifier;
  }
  
  string MinidumpUnloadedModule::debug_file() const {
    return "";  // No debug info provided with unloaded modules
  }
  
  string MinidumpUnloadedModule::debug_identifier() const {
    return "";  // No debug info provided with unloaded modules
  }
  
  string MinidumpUnloadedModule::version() const {
    return "";  // No version info provided with unloaded modules
  }
  
  CodeModule* MinidumpUnloadedModule::Copy() const {
    return new BasicCodeModule(this);
  }
  
  uint64_t MinidumpUnloadedModule::shrink_down_delta() const {
    return 0;
  }
  
  void MinidumpUnloadedModule::SetShrinkDownDelta(uint64_t shrink_down_delta) {
    // Not implemented
    assert(false);
  }
  
  bool MinidumpUnloadedModule::Read(uint32_t expected_size) {
  
    delete name_;
    valid_ = false;
  
    if (expected_size < sizeof(unloaded_module_)) {
      BPLOG(ERROR) << "MinidumpUnloadedModule expected size is less than size "
                   << "of struct " << expected_size << " < "
                   << sizeof(unloaded_module_);
      return false;
    }
  
    if (!minidump_->ReadBytes(&unloaded_module_, sizeof(unloaded_module_))) {
      BPLOG(ERROR) << "MinidumpUnloadedModule cannot read module";
      return false;
    }
  
    if (expected_size > sizeof(unloaded_module_)) {
      uint32_t module_bytes_remaining = expected_size - sizeof(unloaded_module_);
      off_t pos = minidump_->Tell();
      if (!minidump_->SeekSet(pos + module_bytes_remaining)) {
        BPLOG(ERROR) << "MinidumpUnloadedModule unable to seek to end of module";
        return false;
      }
    }
  
    if (minidump_->swap()) {
      Swap(&unloaded_module_.base_of_image);
      Swap(&unloaded_module_.size_of_image);
      Swap(&unloaded_module_.checksum);
      Swap(&unloaded_module_.time_date_stamp);
      Swap(&unloaded_module_.module_name_rva);
    }
  
    // Check for base + size overflow or undersize.
    if (unloaded_module_.size_of_image == 0 ||
        unloaded_module_.size_of_image >
            numeric_limits<uint64_t>::max() - unloaded_module_.base_of_image) {
      BPLOG(ERROR) << "MinidumpUnloadedModule has a module problem, " <<
                      HexString(unloaded_module_.base_of_image) << "+" <<
                      HexString(unloaded_module_.size_of_image);
      return false;
    }
  
  
    module_valid_ = true;
    return true;
  }
  
  bool MinidumpUnloadedModule::ReadAuxiliaryData() {
    if (!module_valid_) {
      BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for ReadAuxiliaryData";
      return false;
    }
  
    // Each module must have a name.
    name_ = minidump_->ReadString(unloaded_module_.module_name_rva);
    if (!name_) {
      BPLOG(ERROR) << "MinidumpUnloadedModule could not read name";
      return false;
    }
  
    // At this point, we have enough info for the module to be valid.
    valid_ = true;
    return true;
  }
  
  //
  // MinidumpUnloadedModuleList
  //
  
  
  uint32_t MinidumpUnloadedModuleList::max_modules_ = 2048;
  
  
  MinidumpUnloadedModuleList::MinidumpUnloadedModuleList(Minidump* minidump)
    : MinidumpStream(minidump),
      range_map_(new RangeMap<uint64_t, unsigned int>()),
      unloaded_modules_(NULL),
      module_count_(0) {
    range_map_->SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
  }
  
  MinidumpUnloadedModuleList::~MinidumpUnloadedModuleList() {
    delete range_map_;
    delete unloaded_modules_;
  }
  
  
  bool MinidumpUnloadedModuleList::Read(uint32_t expected_size) {
    range_map_->Clear();
    delete unloaded_modules_;
    unloaded_modules_ = NULL;
    module_count_ = 0;
  
    valid_ = false;
  
    uint32_t size_of_header;
    if (!minidump_->ReadBytes(&size_of_header, sizeof(size_of_header))) {
      BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read header size";
      return false;
    }
  
    uint32_t size_of_entry;
    if (!minidump_->ReadBytes(&size_of_entry, sizeof(size_of_entry))) {
      BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read entry size";
      return false;
    }
  
    uint32_t number_of_entries;
    if (!minidump_->ReadBytes(&number_of_entries, sizeof(number_of_entries))) {
      BPLOG(ERROR) <<
                   "MinidumpUnloadedModuleList could not read number of entries";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&size_of_header);
      Swap(&size_of_entry);
      Swap(&number_of_entries);
    }
  
    uint32_t header_bytes_remaining = size_of_header - sizeof(size_of_header) -
        sizeof(size_of_entry) - sizeof(number_of_entries);
    if (header_bytes_remaining) {
      off_t pos = minidump_->Tell();
      if (!minidump_->SeekSet(pos + header_bytes_remaining)) {
        BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read header sized "
                     << size_of_header;
        return false;
      }
    }
  
    if (expected_size != size_of_header + (size_of_entry * number_of_entries)) {
      BPLOG(ERROR) << "MinidumpUnloadedModuleList expected_size mismatch " <<
                   expected_size << " != " << size_of_header << " + (" <<
                   size_of_entry << " * " << number_of_entries << ")";
      return false;
    }
  
    if (number_of_entries > max_modules_) {
      BPLOG(ERROR) << "MinidumpUnloadedModuleList count " <<
                   number_of_entries << " exceeds maximum " << max_modules_;
      return false;
    }
  
    if (number_of_entries != 0) {
      scoped_ptr<MinidumpUnloadedModules> modules(
          new MinidumpUnloadedModules(number_of_entries,
                                      MinidumpUnloadedModule(minidump_)));
  
      for (unsigned int module_index = 0;
           module_index < number_of_entries;
           ++module_index) {
        MinidumpUnloadedModule* module = &(*modules)[module_index];
  
        if (!module->Read(size_of_entry)) {
          BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read module " <<
                       module_index << "/" << number_of_entries;
          return false;
        }
      }
  
      for (unsigned int module_index = 0;
           module_index < number_of_entries;
           ++module_index) {
        MinidumpUnloadedModule* module = &(*modules)[module_index];
  
        if (!module->ReadAuxiliaryData()) {
          BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read required "
                       "module auxiliary data for module " <<
                       module_index << "/" << number_of_entries;
          return false;
        }
  
        uint64_t base_address = module->base_address();
        uint64_t module_size = module->size();
  
        // Ignore any failures for conflicting address ranges
        range_map_->StoreRange(base_address, module_size, module_index);
  
      }
      unloaded_modules_ = modules.release();
    }
  
    module_count_ = number_of_entries;
    valid_ = true;
    return true;
  }
  
  const MinidumpUnloadedModule* MinidumpUnloadedModuleList::GetModuleForAddress(
      uint64_t address) const {
    if (!valid_) {
      BPLOG(ERROR)
          << "Invalid MinidumpUnloadedModuleList for GetModuleForAddress";
      return NULL;
    }
  
    unsigned int module_index;
    if (!range_map_->RetrieveRange(address, &module_index, NULL /* base */,
                                   NULL /* delta */, NULL /* size */)) {
      BPLOG(INFO) << "MinidumpUnloadedModuleList has no module at "
                  << HexString(address);
      return NULL;
    }
  
    return GetModuleAtIndex(module_index);
  }
  
  const MinidumpUnloadedModule*
  MinidumpUnloadedModuleList::GetMainModule() const {
    return NULL;
  }
  
  const MinidumpUnloadedModule*
  MinidumpUnloadedModuleList::GetModuleAtSequence(unsigned int sequence) const {
    if (!valid_) {
      BPLOG(ERROR)
          << "Invalid MinidumpUnloadedModuleList for GetModuleAtSequence";
      return NULL;
    }
  
    if (sequence >= module_count_) {
      BPLOG(ERROR) << "MinidumpUnloadedModuleList sequence out of range: "
                   << sequence << "/" << module_count_;
      return NULL;
    }
  
    unsigned int module_index;
    if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index,
                                          NULL /* base */, NULL /* delta */,
                                          NULL /* size */)) {
      BPLOG(ERROR) << "MinidumpUnloadedModuleList has no module at sequence "
                   << sequence;
      return NULL;
    }
  
    return GetModuleAtIndex(module_index);
  }
  
  const MinidumpUnloadedModule*
  MinidumpUnloadedModuleList::GetModuleAtIndex(
      unsigned int index) const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpUnloadedModuleList for GetModuleAtIndex";
      return NULL;
    }
  
    if (index >= module_count_) {
      BPLOG(ERROR) << "MinidumpUnloadedModuleList index out of range: "
                   << index << "/" << module_count_;
      return NULL;
    }
  
    return &(*unloaded_modules_)[index];
  }
  
  const CodeModules* MinidumpUnloadedModuleList::Copy() const {
    return new BasicCodeModules(this, range_map_->GetMergeStrategy());
  }
  
  vector<linked_ptr<const CodeModule>>
  MinidumpUnloadedModuleList::GetShrunkRangeModules() const {
    return vector<linked_ptr<const CodeModule> >();
  }
  
  
  //
  // MinidumpMiscInfo
  //
  
  
  MinidumpMiscInfo::MinidumpMiscInfo(Minidump* minidump)
      : MinidumpStream(minidump),
        misc_info_() {
  }
  
  
  bool MinidumpMiscInfo::Read(uint32_t expected_size) {
    valid_ = false;
  
    size_t padding = 0;
    if (expected_size != MD_MISCINFO_SIZE &&
        expected_size != MD_MISCINFO2_SIZE &&
        expected_size != MD_MISCINFO3_SIZE &&
        expected_size != MD_MISCINFO4_SIZE &&
        expected_size != MD_MISCINFO5_SIZE) {
      if (expected_size > MD_MISCINFO5_SIZE) {
        // Only read the part of the misc info structure we know how to handle
        BPLOG(INFO) << "MinidumpMiscInfo size larger than expected "
                    << expected_size << ", skipping over the unknown part";
        padding = expected_size - MD_MISCINFO5_SIZE;
        expected_size = MD_MISCINFO5_SIZE;
      } else {
        BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size
                    << " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE
                    << ", " << MD_MISCINFO3_SIZE << ", " << MD_MISCINFO4_SIZE
                    << ", " << MD_MISCINFO5_SIZE << ")";
        return false;
      }
    }
  
    if (!minidump_->ReadBytes(&misc_info_, expected_size)) {
      BPLOG(ERROR) << "MinidumpMiscInfo cannot read miscellaneous info";
      return false;
    }
  
    if (padding != 0) {
      off_t saved_position = minidump_->Tell();
      if (saved_position == -1) {
        BPLOG(ERROR) << "MinidumpMiscInfo could not tell the current position";
        return false;
      }
  
      if (!minidump_->SeekSet(saved_position + static_cast<off_t>(padding))) {
        BPLOG(ERROR) << "MinidumpMiscInfo could not seek past the miscellaneous "
                     << "info structure";
        return false;
      }
    }
  
    if (minidump_->swap()) {
      // Swap version 1 fields
      Swap(&misc_info_.size_of_info);
      Swap(&misc_info_.flags1);
      Swap(&misc_info_.process_id);
      Swap(&misc_info_.process_create_time);
      Swap(&misc_info_.process_user_time);
      Swap(&misc_info_.process_kernel_time);
      if (misc_info_.size_of_info > MD_MISCINFO_SIZE) {
        // Swap version 2 fields
        Swap(&misc_info_.processor_max_mhz);
        Swap(&misc_info_.processor_current_mhz);
        Swap(&misc_info_.processor_mhz_limit);
        Swap(&misc_info_.processor_max_idle_state);
        Swap(&misc_info_.processor_current_idle_state);
      }
      if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
        // Swap version 3 fields
        Swap(&misc_info_.process_integrity_level);
        Swap(&misc_info_.process_execute_flags);
        Swap(&misc_info_.protected_process);
        Swap(&misc_info_.time_zone_id);
        Swap(&misc_info_.time_zone);
      }
      if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
        // Swap version 4 fields.
        // Do not swap UTF-16 strings.  The swap is done as part of the
        // conversion to UTF-8 (code follows below).
      }
      if (misc_info_.size_of_info > MD_MISCINFO4_SIZE) {
        // Swap version 5 fields
        Swap(&misc_info_.xstate_data);
        Swap(&misc_info_.process_cookie);
      }
    }
  
    if (expected_size + padding != misc_info_.size_of_info) {
      BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " <<
                      expected_size << " != " << misc_info_.size_of_info;
      return false;
    }
  
    // Convert UTF-16 strings
    if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
      // Convert UTF-16 strings in version 3 fields
      ConvertUTF16BufferToUTF8String(misc_info_.time_zone.standard_name,
                                     sizeof(misc_info_.time_zone.standard_name),
                                     &standard_name_, minidump_->swap());
      ConvertUTF16BufferToUTF8String(misc_info_.time_zone.daylight_name,
                                     sizeof(misc_info_.time_zone.daylight_name),
                                     &daylight_name_, minidump_->swap());
    }
    if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
      // Convert UTF-16 strings in version 4 fields
      ConvertUTF16BufferToUTF8String(misc_info_.build_string,
                                     sizeof(misc_info_.build_string),
                                     &build_string_, minidump_->swap());
      ConvertUTF16BufferToUTF8String(misc_info_.dbg_bld_str,
                                     sizeof(misc_info_.dbg_bld_str),
                                     &dbg_bld_str_, minidump_->swap());
    }
  
    valid_ = true;
    return true;
  }
  
  
  void MinidumpMiscInfo::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpMiscInfo cannot print invalid data";
      return;
    }
  
    printf("MDRawMiscInfo\n");
    // Print version 1 fields
    printf("  size_of_info                 = %d\n",   misc_info_.size_of_info);
    printf("  flags1                       = 0x%x\n", misc_info_.flags1);
    printf("  process_id                   = ");
    PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_ID,
                        kNumberFormatDecimal, misc_info_.process_id);
    if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES) {
      printf("  process_create_time          = 0x%x %s\n",
             misc_info_.process_create_time,
             TimeTToUTCString(misc_info_.process_create_time).c_str());
    } else {
      printf("  process_create_time          = (invalid)\n");
    }
    printf("  process_user_time            = ");
    PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES,
                        kNumberFormatDecimal, misc_info_.process_user_time);
    printf("  process_kernel_time          = ");
    PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES,
                        kNumberFormatDecimal, misc_info_.process_kernel_time);
    if (misc_info_.size_of_info > MD_MISCINFO_SIZE) {
      // Print version 2 fields
      printf("  processor_max_mhz            = ");
      PrintValueOrInvalid(misc_info_.flags1 &
                              MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
                          kNumberFormatDecimal, misc_info_.processor_max_mhz);
      printf("  processor_current_mhz        = ");
      PrintValueOrInvalid(misc_info_.flags1 &
                              MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
                          kNumberFormatDecimal, misc_info_.processor_current_mhz);
      printf("  processor_mhz_limit          = ");
      PrintValueOrInvalid(misc_info_.flags1 &
                              MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
                          kNumberFormatDecimal, misc_info_.processor_mhz_limit);
      printf("  processor_max_idle_state     = ");
      PrintValueOrInvalid(misc_info_.flags1 &
                              MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
                          kNumberFormatDecimal,
                          misc_info_.processor_max_idle_state);
      printf("  processor_current_idle_state = ");
      PrintValueOrInvalid(misc_info_.flags1 &
                              MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO,
                          kNumberFormatDecimal,
                          misc_info_.processor_current_idle_state);
    }
    if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) {
      // Print version 3 fields
      printf("  process_integrity_level      = ");
      PrintValueOrInvalid(misc_info_.flags1 &
                              MD_MISCINFO_FLAGS1_PROCESS_INTEGRITY,
                          kNumberFormatHexadecimal,
                          misc_info_.process_integrity_level);
      printf("  process_execute_flags        = ");
      PrintValueOrInvalid(misc_info_.flags1 &
                              MD_MISCINFO_FLAGS1_PROCESS_EXECUTE_FLAGS,
                          kNumberFormatHexadecimal,
                          misc_info_.process_execute_flags);
      printf("  protected_process            = ");
      PrintValueOrInvalid(misc_info_.flags1 &
                              MD_MISCINFO_FLAGS1_PROTECTED_PROCESS,
                          kNumberFormatDecimal, misc_info_.protected_process);
      printf("  time_zone_id                 = ");
      PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_TIMEZONE,
                          kNumberFormatDecimal, misc_info_.time_zone_id);
      if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_TIMEZONE) {
        printf("  time_zone.bias               = %d\n",
               misc_info_.time_zone.bias);
        printf("  time_zone.standard_name      = %s\n", standard_name_.c_str());
        printf("  time_zone.standard_date      = "
                   "%04d-%02d-%02d (%d) %02d:%02d:%02d.%03d\n",
               misc_info_.time_zone.standard_date.year,
               misc_info_.time_zone.standard_date.month,
               misc_info_.time_zone.standard_date.day,
               misc_info_.time_zone.standard_date.day_of_week,
               misc_info_.time_zone.standard_date.hour,
               misc_info_.time_zone.standard_date.minute,
               misc_info_.time_zone.standard_date.second,
               misc_info_.time_zone.standard_date.milliseconds);
        printf("  time_zone.standard_bias      = %d\n",
               misc_info_.time_zone.standard_bias);
        printf("  time_zone.daylight_name      = %s\n", daylight_name_.c_str());
        printf("  time_zone.daylight_date      = "
                   "%04d-%02d-%02d (%d) %02d:%02d:%02d.%03d\n",
               misc_info_.time_zone.daylight_date.year,
               misc_info_.time_zone.daylight_date.month,
               misc_info_.time_zone.daylight_date.day,
               misc_info_.time_zone.daylight_date.day_of_week,
               misc_info_.time_zone.daylight_date.hour,
               misc_info_.time_zone.daylight_date.minute,
               misc_info_.time_zone.daylight_date.second,
               misc_info_.time_zone.daylight_date.milliseconds);
        printf("  time_zone.daylight_bias      = %d\n",
               misc_info_.time_zone.daylight_bias);
      } else {
        printf("  time_zone.bias               = (invalid)\n");
        printf("  time_zone.standard_name      = (invalid)\n");
        printf("  time_zone.standard_date      = (invalid)\n");
        printf("  time_zone.standard_bias      = (invalid)\n");
        printf("  time_zone.daylight_name      = (invalid)\n");
        printf("  time_zone.daylight_date      = (invalid)\n");
        printf("  time_zone.daylight_bias      = (invalid)\n");
      }
    }
    if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) {
      // Print version 4 fields
      if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_BUILDSTRING) {
        printf("  build_string                 = %s\n", build_string_.c_str());
        printf("  dbg_bld_str                  = %s\n", dbg_bld_str_.c_str());
      } else {
        printf("  build_string                 = (invalid)\n");
        printf("  dbg_bld_str                  = (invalid)\n");
      }
    }
    if (misc_info_.size_of_info > MD_MISCINFO4_SIZE) {
      // Print version 5 fields
      if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_COOKIE) {
        printf("  xstate_data.size_of_info     = %d\n",
               misc_info_.xstate_data.size_of_info);
        printf("  xstate_data.context_size     = %d\n",
               misc_info_.xstate_data.context_size);
        printf("  xstate_data.enabled_features = 0x%" PRIx64 "\n",
               misc_info_.xstate_data.enabled_features);
        for (size_t i = 0; i < MD_MAXIMUM_XSTATE_FEATURES; i++) {
          if ((misc_info_.xstate_data.enabled_features >> i) & 1) {
            printf("  xstate_data.features[%02zu]     = { %d, %d }\n", i,
                   misc_info_.xstate_data.features[i].offset,
                   misc_info_.xstate_data.features[i].size);
          }
        }
        if (misc_info_.xstate_data.enabled_features == 0) {
          printf("  xstate_data.features[]       = (empty)\n");
        }
        printf("  process_cookie               = %d\n",
               misc_info_.process_cookie);
      } else {
        printf("  xstate_data.size_of_info     = (invalid)\n");
        printf("  xstate_data.context_size     = (invalid)\n");
        printf("  xstate_data.enabled_features = (invalid)\n");
        printf("  xstate_data.features[]       = (invalid)\n");
        printf("  process_cookie               = (invalid)\n");
      }
    }
    printf("\n");
  }
  
  
  //
  // MinidumpBreakpadInfo
  //
  
  
  MinidumpBreakpadInfo::MinidumpBreakpadInfo(Minidump* minidump)
      : MinidumpStream(minidump),
        breakpad_info_() {
  }
  
  
  bool MinidumpBreakpadInfo::Read(uint32_t expected_size) {
    valid_ = false;
  
    if (expected_size != sizeof(breakpad_info_)) {
      BPLOG(ERROR) << "MinidumpBreakpadInfo size mismatch, " << expected_size <<
                      " != " << sizeof(breakpad_info_);
      return false;
    }
  
    if (!minidump_->ReadBytes(&breakpad_info_, sizeof(breakpad_info_))) {
      BPLOG(ERROR) << "MinidumpBreakpadInfo cannot read Breakpad info";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&breakpad_info_.validity);
      Swap(&breakpad_info_.dump_thread_id);
      Swap(&breakpad_info_.requesting_thread_id);
    }
  
    valid_ = true;
    return true;
  }
  
  
  bool MinidumpBreakpadInfo::GetDumpThreadID(uint32_t* thread_id) const {
    BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetDumpThreadID "
                                   "requires |thread_id|";
    assert(thread_id);
    *thread_id = 0;
  
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetDumpThreadID";
      return false;
    }
  
    if (!(breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID)) {
      BPLOG(INFO) << "MinidumpBreakpadInfo has no dump thread";
      return false;
    }
  
    *thread_id = breakpad_info_.dump_thread_id;
    return true;
  }
  
  
  bool MinidumpBreakpadInfo::GetRequestingThreadID(uint32_t* thread_id)
      const {
    BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetRequestingThreadID "
                                   "requires |thread_id|";
    assert(thread_id);
    *thread_id = 0;
  
    if (!thread_id || !valid_) {
      BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetRequestingThreadID";
      return false;
    }
  
    if (!(breakpad_info_.validity &
              MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID)) {
      BPLOG(INFO) << "MinidumpBreakpadInfo has no requesting thread";
      return false;
    }
  
    *thread_id = breakpad_info_.requesting_thread_id;
    return true;
  }
  
  
  void MinidumpBreakpadInfo::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpBreakpadInfo cannot print invalid data";
      return;
    }
  
    printf("MDRawBreakpadInfo\n");
    printf("  validity             = 0x%x\n", breakpad_info_.validity);
    printf("  dump_thread_id       = ");
    PrintValueOrInvalid(breakpad_info_.validity &
                            MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID,
                        kNumberFormatHexadecimal, breakpad_info_.dump_thread_id);
    printf("  requesting_thread_id = ");
    PrintValueOrInvalid(breakpad_info_.validity &
                            MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID,
                        kNumberFormatHexadecimal,
                        breakpad_info_.requesting_thread_id);
  
    printf("\n");
  }
  
  
  //
  // MinidumpMemoryInfo
  //
  
  
  MinidumpMemoryInfo::MinidumpMemoryInfo(Minidump* minidump)
      : MinidumpObject(minidump),
        memory_info_() {
  }
  
  
  bool MinidumpMemoryInfo::IsExecutable() const {
    uint32_t protection =
        memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK;
    return protection == MD_MEMORY_PROTECT_EXECUTE ||
        protection == MD_MEMORY_PROTECT_EXECUTE_READ ||
        protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE;
  }
  
  
  bool MinidumpMemoryInfo::IsWritable() const {
    uint32_t protection =
        memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK;
    return protection == MD_MEMORY_PROTECT_READWRITE ||
      protection == MD_MEMORY_PROTECT_WRITECOPY ||
      protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE ||
      protection == MD_MEMORY_PROTECT_EXECUTE_WRITECOPY;
  }
  
  
  bool MinidumpMemoryInfo::Read() {
    valid_ = false;
  
    if (!minidump_->ReadBytes(&memory_info_, sizeof(memory_info_))) {
      BPLOG(ERROR) << "MinidumpMemoryInfo cannot read memory info";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&memory_info_.base_address);
      Swap(&memory_info_.allocation_base);
      Swap(&memory_info_.allocation_protection);
      Swap(&memory_info_.region_size);
      Swap(&memory_info_.state);
      Swap(&memory_info_.protection);
      Swap(&memory_info_.type);
    }
  
    // Check for base + size overflow or undersize.
    if (memory_info_.region_size == 0 ||
        memory_info_.region_size > numeric_limits<uint64_t>::max() -
                                       memory_info_.base_address) {
      BPLOG(ERROR) << "MinidumpMemoryInfo has a memory region problem, " <<
                      HexString(memory_info_.base_address) << "+" <<
                      HexString(memory_info_.region_size);
      return false;
    }
  
    valid_ = true;
    return true;
  }
  
  
  void MinidumpMemoryInfo::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpMemoryInfo cannot print invalid data";
      return;
    }
  
    printf("MDRawMemoryInfo\n");
    printf("  base_address          = 0x%" PRIx64 "\n",
           memory_info_.base_address);
    printf("  allocation_base       = 0x%" PRIx64 "\n",
           memory_info_.allocation_base);
    printf("  allocation_protection = 0x%x\n",
           memory_info_.allocation_protection);
    printf("  region_size           = 0x%" PRIx64 "\n", memory_info_.region_size);
    printf("  state                 = 0x%x\n", memory_info_.state);
    printf("  protection            = 0x%x\n", memory_info_.protection);
    printf("  type                  = 0x%x\n", memory_info_.type);
  }
  
  
  //
  // MinidumpMemoryInfoList
  //
  
  
  MinidumpMemoryInfoList::MinidumpMemoryInfoList(Minidump* minidump)
      : MinidumpStream(minidump),
        range_map_(new RangeMap<uint64_t, unsigned int>()),
        infos_(NULL),
        info_count_(0) {
  }
  
  
  MinidumpMemoryInfoList::~MinidumpMemoryInfoList() {
    delete range_map_;
    delete infos_;
  }
  
  
  bool MinidumpMemoryInfoList::Read(uint32_t expected_size) {
    // Invalidate cached data.
    delete infos_;
    infos_ = NULL;
    range_map_->Clear();
    info_count_ = 0;
  
    valid_ = false;
  
    MDRawMemoryInfoList header;
    if (expected_size < sizeof(MDRawMemoryInfoList)) {
      BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " <<
                      expected_size << " < " << sizeof(MDRawMemoryInfoList);
      return false;
    }
    if (!minidump_->ReadBytes(&header, sizeof(header))) {
      BPLOG(ERROR) << "MinidumpMemoryInfoList could not read header";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&header.size_of_header);
      Swap(&header.size_of_entry);
      Swap(&header.number_of_entries);
    }
  
    // Sanity check that the header is the expected size.
    // TODO(ted): could possibly handle this more gracefully, assuming
    // that future versions of the structs would be backwards-compatible.
    if (header.size_of_header != sizeof(MDRawMemoryInfoList)) {
      BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " <<
                      header.size_of_header << " != " <<
                      sizeof(MDRawMemoryInfoList);
      return false;
    }
  
    // Sanity check that the entries are the expected size.
    if (header.size_of_entry != sizeof(MDRawMemoryInfo)) {
      BPLOG(ERROR) << "MinidumpMemoryInfoList entry size mismatch, " <<
                      header.size_of_entry << " != " <<
                      sizeof(MDRawMemoryInfo);
      return false;
    }
  
    if (header.number_of_entries >
            numeric_limits<uint32_t>::max() / sizeof(MDRawMemoryInfo)) {
      BPLOG(ERROR) << "MinidumpMemoryInfoList info count " <<
                      header.number_of_entries <<
                      " would cause multiplication overflow";
      return false;
    }
  
    if (expected_size != sizeof(MDRawMemoryInfoList) +
                          header.number_of_entries * sizeof(MDRawMemoryInfo)) {
      BPLOG(ERROR) << "MinidumpMemoryInfoList size mismatch, " << expected_size <<
                      " != " << sizeof(MDRawMemoryInfoList) +
                          header.number_of_entries * sizeof(MDRawMemoryInfo);
      return false;
    }
  
    // Check for data loss when converting header.number_of_entries from
    // uint64_t into MinidumpMemoryInfos::size_type (uint32_t)
    MinidumpMemoryInfos::size_type header_number_of_entries =
        static_cast<unsigned int>(header.number_of_entries);
    if (static_cast<uint64_t>(header_number_of_entries) !=
        header.number_of_entries) {
      BPLOG(ERROR) << "Data loss detected when converting "
                      "the header's number_of_entries";
      return false;
    }
  
    if (header.number_of_entries != 0) {
      scoped_ptr<MinidumpMemoryInfos> infos(
          new MinidumpMemoryInfos(header_number_of_entries,
                                  MinidumpMemoryInfo(minidump_)));
  
      for (unsigned int index = 0;
           index < header.number_of_entries;
           ++index) {
        MinidumpMemoryInfo* info = &(*infos)[index];
  
        // Assume that the file offset is correct after the last read.
        if (!info->Read()) {
          BPLOG(ERROR) << "MinidumpMemoryInfoList cannot read info " <<
                          index << "/" << header.number_of_entries;
          return false;
        }
  
        uint64_t base_address = info->GetBase();
        uint64_t region_size = info->GetSize();
  
        if (!range_map_->StoreRange(base_address, region_size, index)) {
          BPLOG(ERROR) << "MinidumpMemoryInfoList could not store"
                          " memory region " <<
                          index << "/" << header.number_of_entries << ", " <<
                          HexString(base_address) << "+" <<
                          HexString(region_size);
          return false;
        }
      }
  
      infos_ = infos.release();
    }
  
    info_count_ = static_cast<uint32_t>(header_number_of_entries);
  
    valid_ = true;
    return true;
  }
  
  
  const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoAtIndex(
        unsigned int index) const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for GetMemoryInfoAtIndex";
      return NULL;
    }
  
    if (index >= info_count_) {
      BPLOG(ERROR) << "MinidumpMemoryInfoList index out of range: " <<
                      index << "/" << info_count_;
      return NULL;
    }
  
    return &(*infos_)[index];
  }
  
  
  const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoForAddress(
      uint64_t address) const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for"
                      " GetMemoryInfoForAddress";
      return NULL;
    }
  
    unsigned int info_index;
    if (!range_map_->RetrieveRange(address, &info_index, NULL /* base */,
                                   NULL /* delta */, NULL /* size */)) {
      BPLOG(INFO) << "MinidumpMemoryInfoList has no memory info at " <<
                     HexString(address);
      return NULL;
    }
  
    return GetMemoryInfoAtIndex(info_index);
  }
  
  
  void MinidumpMemoryInfoList::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpMemoryInfoList cannot print invalid data";
      return;
    }
  
    printf("MinidumpMemoryInfoList\n");
    printf("  info_count = %d\n", info_count_);
    printf("\n");
  
    for (unsigned int info_index = 0;
         info_index < info_count_;
         ++info_index) {
      printf("info[%d]\n", info_index);
      (*infos_)[info_index].Print();
      printf("\n");
    }
  }
  
  //
  // MinidumpLinuxMaps
  //
  
  MinidumpLinuxMaps::MinidumpLinuxMaps(Minidump* minidump)
      : MinidumpObject(minidump) {
  }
  
  void MinidumpLinuxMaps::Print() const {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpLinuxMaps cannot print invalid data";
      return;
    }
    std::cout << region_.line << std::endl;
  }
  
  //
  // MinidumpLinuxMapsList
  //
  
  MinidumpLinuxMapsList::MinidumpLinuxMapsList(Minidump* minidump)
      : MinidumpStream(minidump),
        maps_(NULL),
        maps_count_(0) {
  }
  
  MinidumpLinuxMapsList::~MinidumpLinuxMapsList() {
    if (maps_) {
      for (unsigned int i = 0; i < maps_->size(); i++) {
        delete (*maps_)[i];
      }
      delete maps_;
    }
  }
  
  const MinidumpLinuxMaps* MinidumpLinuxMapsList::GetLinuxMapsForAddress(
      uint64_t address) const {
    if (!valid_ || (maps_ == NULL)) {
      BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsForAddress";
      return NULL;
    }
  
    // Search every memory mapping.
    for (unsigned int index = 0; index < maps_count_; index++) {
      // Check if address is within bounds of the current memory region.
      if ((*maps_)[index]->GetBase() <= address &&
          (*maps_)[index]->GetBase() + (*maps_)[index]->GetSize() > address) {
        return (*maps_)[index];
      }
    }
  
    // No mapping encloses the memory address.
    BPLOG(ERROR) << "MinidumpLinuxMapsList has no mapping at "
                 << HexString(address);
    return NULL;
  }
  
  const MinidumpLinuxMaps* MinidumpLinuxMapsList::GetLinuxMapsAtIndex(
      unsigned int index) const {
    if (!valid_ || (maps_ == NULL)) {
      BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsAtIndex";
      return NULL;
    }
  
    // Index out of bounds.
    if (index >= maps_count_ || (maps_ == NULL)) {
      BPLOG(ERROR) << "MinidumpLinuxMapsList index of out range: "
                   << index
                   << "/"
                   << maps_count_;
      return NULL;
    }
    return (*maps_)[index];
  }
  
  bool MinidumpLinuxMapsList::Read(uint32_t expected_size) {
    // Invalidate cached data.
    if (maps_) {
      for (unsigned int i = 0; i < maps_->size(); i++) {
        delete (*maps_)[i];
      }
      delete maps_;
    }
    maps_ = NULL;
    maps_count_ = 0;
  
    valid_ = false;
  
    // Load and check expected stream length.
    uint32_t length = 0;
    if (!minidump_->SeekToStreamType(MD_LINUX_MAPS, &length)) {
      BPLOG(ERROR) << "MinidumpLinuxMapsList stream type not found";
      return false;
    }
    if (expected_size != length) {
      BPLOG(ERROR) << "MinidumpLinuxMapsList size mismatch: "
                   << expected_size
                   << " != "
                   << length;
      return false;
    }
  
    // Create a vector to read stream data. The vector needs to have
    // at least enough capacity to read all the data.
    vector<char> mapping_bytes(length);
    if (!minidump_->ReadBytes(&mapping_bytes[0], length)) {
      BPLOG(ERROR) << "MinidumpLinuxMapsList failed to read bytes";
      return false;
    }
    string map_string(mapping_bytes.begin(), mapping_bytes.end());
    vector<MappedMemoryRegion> all_regions;
  
    // Parse string into mapping data.
    if (!ParseProcMaps(map_string, &all_regions)) {
      return false;
    }
  
    scoped_ptr<MinidumpLinuxMappings> maps(new MinidumpLinuxMappings());
  
    // Push mapping data into wrapper classes.
    for (size_t i = 0; i < all_regions.size(); i++) {
      scoped_ptr<MinidumpLinuxMaps> ele(new MinidumpLinuxMaps(minidump_));
      ele->region_ = all_regions[i];
      ele->valid_ = true;
      maps->push_back(ele.release());
    }
  
    // Set instance variables.
    maps_ = maps.release();
    maps_count_ = static_cast<uint32_t>(maps_->size());
    valid_ = true;
    return true;
  }
  
  void MinidumpLinuxMapsList::Print() const {
    if (!valid_ || (maps_ == NULL)) {
      BPLOG(ERROR) << "MinidumpLinuxMapsList cannot print valid data";
      return;
    }
    for (size_t i = 0; i < maps_->size(); i++) {
      (*maps_)[i]->Print();
    }
  }
  
  //
  // MinidumpCrashpadInfo
  //
  
  
  MinidumpCrashpadInfo::MinidumpCrashpadInfo(Minidump* minidump)
      : MinidumpStream(minidump),
        crashpad_info_(),
        module_crashpad_info_links_(),
        module_crashpad_info_(),
        module_crashpad_info_list_annotations_(),
        module_crashpad_info_simple_annotations_(),
        simple_annotations_() {
  }
  
  
  bool MinidumpCrashpadInfo::Read(uint32_t expected_size) {
    valid_ = false;
  
    if (expected_size != sizeof(crashpad_info_)) {
      BPLOG(ERROR) << "MinidumpCrashpadInfo size mismatch, " << expected_size <<
                      " != " << sizeof(crashpad_info_);
      return false;
    }
  
    if (!minidump_->ReadBytes(&crashpad_info_, sizeof(crashpad_info_))) {
      BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad info";
      return false;
    }
  
    if (minidump_->swap()) {
      Swap(&crashpad_info_.version);
      Swap(&crashpad_info_.report_id);
      Swap(&crashpad_info_.client_id);
      Swap(&crashpad_info_.simple_annotations);
      Swap(&crashpad_info_.module_list);
    }
  
    if (crashpad_info_.simple_annotations.data_size) {
      if (!minidump_->ReadSimpleStringDictionary(
          crashpad_info_.simple_annotations.rva,
          &simple_annotations_)) {
        BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read simple_annotations";
        return false;
      }
    }
  
    if (crashpad_info_.module_list.data_size) {
      if (!minidump_->SeekSet(crashpad_info_.module_list.rva)) {
        BPLOG(ERROR) << "MinidumpCrashpadInfo cannot seek to module_list";
        return false;
      }
  
      uint32_t count;
      if (!minidump_->ReadBytes(&count, sizeof(count))) {
        BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read module_list count";
        return false;
      }
  
      if (minidump_->swap()) {
        Swap(&count);
      }
  
      scoped_array<MDRawModuleCrashpadInfoLink> module_crashpad_info_links(
          new MDRawModuleCrashpadInfoLink[count]);
  
      // Read the entire array in one fell swoop, instead of reading one entry
      // at a time in the loop.
      if (!minidump_->ReadBytes(
              &module_crashpad_info_links[0],
              sizeof(MDRawModuleCrashpadInfoLink) * count)) {
        BPLOG(ERROR)
            << "MinidumpCrashpadInfo could not read Crashpad module links";
        return false;
      }
  
      for (uint32_t index = 0; index < count; ++index) {
        if (minidump_->swap()) {
          Swap(&module_crashpad_info_links[index].minidump_module_list_index);
          Swap(&module_crashpad_info_links[index].location);
        }
  
        if (!minidump_->SeekSet(module_crashpad_info_links[index].location.rva)) {
          BPLOG(ERROR)
              << "MinidumpCrashpadInfo cannot seek to Crashpad module info";
          return false;
        }
  
        MDRawModuleCrashpadInfo module_crashpad_info;
        if (!minidump_->ReadBytes(&module_crashpad_info,
                                  sizeof(module_crashpad_info))) {
          BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module info";
          return false;
        }
  
        if (minidump_->swap()) {
          Swap(&module_crashpad_info.version);
          Swap(&module_crashpad_info.list_annotations);
          Swap(&module_crashpad_info.simple_annotations);
        }
  
        std::vector<std::string> list_annotations;
        if (module_crashpad_info.list_annotations.data_size) {
          if (!minidump_->ReadStringList(
                  module_crashpad_info.list_annotations.rva,
                  &list_annotations)) {
            BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module "
                "info list annotations";
            return false;
          }
        }
  
        std::map<std::string, std::string> simple_annotations;
        if (module_crashpad_info.simple_annotations.data_size) {
          if (!minidump_->ReadSimpleStringDictionary(
                  module_crashpad_info.simple_annotations.rva,
                  &simple_annotations)) {
            BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module "
                "info simple annotations";
            return false;
          }
        }
  
        module_crashpad_info_links_.push_back(
            module_crashpad_info_links[index].minidump_module_list_index);
        module_crashpad_info_.push_back(module_crashpad_info);
        module_crashpad_info_list_annotations_.push_back(list_annotations);
        module_crashpad_info_simple_annotations_.push_back(simple_annotations);
      }
    }
  
    valid_ = true;
    return true;
  }
  
  
  void MinidumpCrashpadInfo::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "MinidumpCrashpadInfo cannot print invalid data";
      return;
    }
  
    printf("MDRawCrashpadInfo\n");
    printf("  version = %d\n", crashpad_info_.version);
    printf("  report_id = %s\n",
           MDGUIDToString(crashpad_info_.report_id).c_str());
    printf("  client_id = %s\n",
           MDGUIDToString(crashpad_info_.client_id).c_str());
    for (const auto& annot : simple_annotations_) {
      printf("  simple_annotations[\"%s\"] = %s\n", annot.first.c_str(),
             annot.second.c_str());
    }
    for (uint32_t module_index = 0;
         module_index < module_crashpad_info_links_.size();
         ++module_index) {
      printf("  module_list[%d].minidump_module_list_index = %d\n",
             module_index, module_crashpad_info_links_[module_index]);
      printf("  module_list[%d].version = %d\n",
             module_index, module_crashpad_info_[module_index].version);
      const auto& list_annots =
          module_crashpad_info_list_annotations_[module_index];
      for (uint32_t annotation_index = 0; annotation_index < list_annots.size();
           ++annotation_index) {
        printf("  module_list[%d].list_annotations[%d] = %s\n", module_index,
               annotation_index, list_annots[annotation_index].c_str());
      }
      const auto& simple_annots =
          module_crashpad_info_simple_annotations_[module_index];
      for (const auto& annot : simple_annots) {
        printf("  module_list[%d].simple_annotations[\"%s\"] = %s\n",
               module_index, annot.first.c_str(), annot.second.c_str());
      }
    }
  
    printf("\n");
  }
  
  
  //
  // Minidump
  //
  
  
  uint32_t Minidump::max_streams_ = 128;
  unsigned int Minidump::max_string_length_ = 1024;
  
  
  Minidump::Minidump(const string& path, bool hexdump, unsigned int hexdump_width)
      : header_(),
        directory_(NULL),
        stream_map_(new MinidumpStreamMap()),
        path_(path),
        stream_(NULL),
        swap_(false),
        is_big_endian_(false),
        valid_(false),
        hexdump_(hexdump),
        hexdump_width_(hexdump_width) {
  }
  
  Minidump::Minidump(istream& stream)
      : header_(),
        directory_(NULL),
        stream_map_(new MinidumpStreamMap()),
        path_(),
        stream_(&stream),
        swap_(false),
        is_big_endian_(false),
        valid_(false),
        hexdump_(false),
        hexdump_width_(0) {
  }
  
  Minidump::~Minidump() {
    if (stream_) {
      BPLOG(INFO) << "Minidump closing minidump";
    }
    if (!path_.empty()) {
      delete stream_;
    }
    delete directory_;
    delete stream_map_;
  }
  
  
  bool Minidump::Open() {
    if (stream_ != NULL) {
      BPLOG(INFO) << "Minidump reopening minidump " << path_;
  
      // The file is already open.  Seek to the beginning, which is the position
      // the file would be at if it were opened anew.
      return SeekSet(0);
    }
  
    stream_ = new ifstream(path_.c_str(), std::ios::in | std::ios::binary);
    if (!stream_ || !stream_->good()) {
      string error_string;
      int error_code = ErrnoString(&error_string);
      BPLOG(ERROR) << "Minidump could not open minidump " << path_ <<
                      ", error " << error_code << ": " << error_string;
      return false;
    }
  
    BPLOG(INFO) << "Minidump opened minidump " << path_;
    return true;
  }
  
  bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t* context_cpu_flags) {
    // Initialize output parameters
    *context_cpu_flags = 0;
  
    // Save the current stream position
    off_t saved_position = Tell();
    if (saved_position == -1) {
      // Failed to save the current stream position.
      // Returns true because the current position of the stream is preserved.
      return true;
    }
  
    const MDRawSystemInfo* system_info =
      GetSystemInfo() ? GetSystemInfo()->system_info() : NULL;
  
    if (system_info != NULL) {
      switch (system_info->processor_architecture) {
        case MD_CPU_ARCHITECTURE_X86:
          *context_cpu_flags = MD_CONTEXT_X86;
          break;
        case MD_CPU_ARCHITECTURE_MIPS:
          *context_cpu_flags = MD_CONTEXT_MIPS;
          break;
        case MD_CPU_ARCHITECTURE_MIPS64:
          *context_cpu_flags = MD_CONTEXT_MIPS64;
          break;
        case MD_CPU_ARCHITECTURE_ALPHA:
          *context_cpu_flags = MD_CONTEXT_ALPHA;
          break;
        case MD_CPU_ARCHITECTURE_PPC:
          *context_cpu_flags = MD_CONTEXT_PPC;
          break;
        case MD_CPU_ARCHITECTURE_PPC64:
          *context_cpu_flags = MD_CONTEXT_PPC64;
          break;
        case MD_CPU_ARCHITECTURE_SHX:
          *context_cpu_flags = MD_CONTEXT_SHX;
          break;
        case MD_CPU_ARCHITECTURE_ARM:
          *context_cpu_flags = MD_CONTEXT_ARM;
          break;
        case MD_CPU_ARCHITECTURE_ARM64:
          *context_cpu_flags = MD_CONTEXT_ARM64;
          break;
        case MD_CPU_ARCHITECTURE_ARM64_OLD:
          *context_cpu_flags = MD_CONTEXT_ARM64_OLD;
          break;
        case MD_CPU_ARCHITECTURE_IA64:
          *context_cpu_flags = MD_CONTEXT_IA64;
          break;
        case MD_CPU_ARCHITECTURE_ALPHA64:
          *context_cpu_flags = 0;
          break;
        case MD_CPU_ARCHITECTURE_MSIL:
          *context_cpu_flags = 0;
          break;
        case MD_CPU_ARCHITECTURE_AMD64:
          *context_cpu_flags = MD_CONTEXT_AMD64;
          break;
        case MD_CPU_ARCHITECTURE_X86_WIN64:
          *context_cpu_flags = 0;
          break;
        case MD_CPU_ARCHITECTURE_SPARC:
          *context_cpu_flags = MD_CONTEXT_SPARC;
          break;
        case MD_CPU_ARCHITECTURE_UNKNOWN:
          *context_cpu_flags = 0;
          break;
        default:
          *context_cpu_flags = 0;
          break;
      }
    }
  
    // Restore position and return
    return SeekSet(saved_position);
  }
  
  
  bool Minidump::Read() {
    // Invalidate cached data.
    delete directory_;
    directory_ = NULL;
    stream_map_->clear();
  
    valid_ = false;
  
    if (!Open()) {
      BPLOG(ERROR) << "Minidump cannot open minidump";
      return false;
    }
  
    if (!ReadBytes(&header_, sizeof(MDRawHeader))) {
      BPLOG(ERROR) << "Minidump cannot read header";
      return false;
    }
  
    if (header_.signature != MD_HEADER_SIGNATURE) {
      // The file may be byte-swapped.  Under the present architecture, these
      // classes don't know or need to know what CPU (or endianness) the
      // minidump was produced on in order to parse it.  Use the signature as
      // a byte order marker.
      uint32_t signature_swapped = header_.signature;
      Swap(&signature_swapped);
      if (signature_swapped != MD_HEADER_SIGNATURE) {
        // This isn't a minidump or a byte-swapped minidump.
        BPLOG(ERROR) << "Minidump header signature mismatch: (" <<
                        HexString(header_.signature) << ", " <<
                        HexString(signature_swapped) << ") != " <<
                        HexString(MD_HEADER_SIGNATURE);
        return false;
      }
      swap_ = true;
    } else {
      // The file is not byte-swapped.  Set swap_ false (it may have been true
      // if the object is being reused?)
      swap_ = false;
    }
  
  #if defined(__BIG_ENDIAN__) || \
    (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
    is_big_endian_ = !swap_;
  #else
    is_big_endian_ = swap_;
  #endif
  
    BPLOG(INFO) << "Minidump " << (swap_ ? "" : "not ") <<
                   "byte-swapping minidump";
  
    if (swap_) {
      Swap(&header_.signature);
      Swap(&header_.version);
      Swap(&header_.stream_count);
      Swap(&header_.stream_directory_rva);
      Swap(&header_.checksum);
      Swap(&header_.time_date_stamp);
      Swap(&header_.flags);
    }
  
    // Version check.  The high 16 bits of header_.version contain something
    // else "implementation specific."
    if ((header_.version & 0x0000ffff) != MD_HEADER_VERSION) {
      BPLOG(ERROR) << "Minidump version mismatch: " <<
                      HexString(header_.version & 0x0000ffff) << " != " <<
                      HexString(MD_HEADER_VERSION);
      return false;
    }
  
    if (!SeekSet(header_.stream_directory_rva)) {
      BPLOG(ERROR) << "Minidump cannot seek to stream directory";
      return false;
    }
  
    if (header_.stream_count > max_streams_) {
      BPLOG(ERROR) << "Minidump stream count " << header_.stream_count <<
                      " exceeds maximum " << max_streams_;
      return false;
    }
  
    if (header_.stream_count != 0) {
      scoped_ptr<MinidumpDirectoryEntries> directory(
          new MinidumpDirectoryEntries(header_.stream_count));
  
      // Read the entire array in one fell swoop, instead of reading one entry
      // at a time in the loop.
      if (!ReadBytes(&(*directory)[0],
                     sizeof(MDRawDirectory) * header_.stream_count)) {
        BPLOG(ERROR) << "Minidump cannot read stream directory";
        return false;
      }
  
      for (unsigned int stream_index = 0;
           stream_index < header_.stream_count;
           ++stream_index) {
        MDRawDirectory* directory_entry = &(*directory)[stream_index];
  
        if (swap_) {
          Swap(&directory_entry->stream_type);
          Swap(&directory_entry->location);
        }
  
        // Initialize the stream_map_ map, which speeds locating a stream by
        // type.
        unsigned int stream_type = directory_entry->stream_type;
        switch (stream_type) {
          case MD_THREAD_LIST_STREAM:
          case MD_MODULE_LIST_STREAM:
          case MD_MEMORY_LIST_STREAM:
          case MD_EXCEPTION_STREAM:
          case MD_SYSTEM_INFO_STREAM:
          case MD_MISC_INFO_STREAM:
          case MD_BREAKPAD_INFO_STREAM:
          case MD_CRASHPAD_INFO_STREAM: {
            if (stream_map_->find(stream_type) != stream_map_->end()) {
              // Another stream with this type was already found.  A minidump
              // file should contain at most one of each of these stream types.
              BPLOG(ERROR) << "Minidump found multiple streams of type " <<
                              stream_type << ", but can only deal with one";
              return false;
            }
            BP_FALLTHROUGH;
          }
  
          default: {
            // Overwrites for stream types other than those above, but it's
            // expected to be the user's burden in that case.
            (*stream_map_)[stream_type].stream_index = stream_index;
          }
        }
      }
  
      directory_ = directory.release();
    }
  
    valid_ = true;
    return true;
  }
  
  
  MinidumpThreadList* Minidump::GetThreadList() {
    MinidumpThreadList* thread_list;
    return GetStream(&thread_list);
  }
  
  
  MinidumpModuleList* Minidump::GetModuleList() {
    MinidumpModuleList* module_list;
    return GetStream(&module_list);
  }
  
  
  MinidumpMemoryList* Minidump::GetMemoryList() {
    MinidumpMemoryList* memory_list;
    return GetStream(&memory_list);
  }
  
  
  MinidumpException* Minidump::GetException() {
    MinidumpException* exception;
    return GetStream(&exception);
  }
  
  MinidumpAssertion* Minidump::GetAssertion() {
    MinidumpAssertion* assertion;
    return GetStream(&assertion);
  }
  
  
  MinidumpSystemInfo* Minidump::GetSystemInfo() {
    MinidumpSystemInfo* system_info;
    return GetStream(&system_info);
  }
  
  
  MinidumpUnloadedModuleList* Minidump::GetUnloadedModuleList() {
    MinidumpUnloadedModuleList* unloaded_module_list;
    return GetStream(&unloaded_module_list);
  }
  
  
  MinidumpMiscInfo* Minidump::GetMiscInfo() {
    MinidumpMiscInfo* misc_info;
    return GetStream(&misc_info);
  }
  
  
  MinidumpBreakpadInfo* Minidump::GetBreakpadInfo() {
    MinidumpBreakpadInfo* breakpad_info;
    return GetStream(&breakpad_info);
  }
  
  MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() {
    MinidumpMemoryInfoList* memory_info_list;
    return GetStream(&memory_info_list);
  }
  
  MinidumpLinuxMapsList* Minidump::GetLinuxMapsList() {
    MinidumpLinuxMapsList* linux_maps_list;
    return GetStream(&linux_maps_list);
  }
  
  bool Minidump::IsAndroid() {
    MDOSPlatform platform;
    return GetPlatform(&platform) && platform == MD_OS_ANDROID;
  }
  
  bool Minidump::GetPlatform(MDOSPlatform* platform) {
    // Save the current stream position
    off_t saved_position = Tell();
    if (saved_position == -1) {
      return false;
    }
    const MDRawSystemInfo* system_info =
      GetSystemInfo() ? GetSystemInfo()->system_info() : NULL;
  
    // Restore position and return
    if (!SeekSet(saved_position)) {
      BPLOG(ERROR) << "Couldn't seek back to saved position";
      return false;
    }
  
    if (!system_info) {
      return false;
    }
    *platform = static_cast<MDOSPlatform>(system_info->platform_id);
    return true;
  }
  
  MinidumpCrashpadInfo* Minidump::GetCrashpadInfo() {
    MinidumpCrashpadInfo* crashpad_info;
    return GetStream(&crashpad_info);
  }
  
  static const char* get_stream_name(uint32_t stream_type) {
    switch (stream_type) {
    case MD_UNUSED_STREAM:
      return "MD_UNUSED_STREAM";
    case MD_RESERVED_STREAM_0:
      return "MD_RESERVED_STREAM_0";
    case MD_RESERVED_STREAM_1:
      return "MD_RESERVED_STREAM_1";
    case MD_THREAD_LIST_STREAM:
      return "MD_THREAD_LIST_STREAM";
    case MD_MODULE_LIST_STREAM:
      return "MD_MODULE_LIST_STREAM";
    case MD_MEMORY_LIST_STREAM:
      return "MD_MEMORY_LIST_STREAM";
    case MD_EXCEPTION_STREAM:
      return "MD_EXCEPTION_STREAM";
    case MD_SYSTEM_INFO_STREAM:
      return "MD_SYSTEM_INFO_STREAM";
    case MD_THREAD_EX_LIST_STREAM:
      return "MD_THREAD_EX_LIST_STREAM";
    case MD_MEMORY_64_LIST_STREAM:
      return "MD_MEMORY_64_LIST_STREAM";
    case MD_COMMENT_STREAM_A:
      return "MD_COMMENT_STREAM_A";
    case MD_COMMENT_STREAM_W:
      return "MD_COMMENT_STREAM_W";
    case MD_HANDLE_DATA_STREAM:
      return "MD_HANDLE_DATA_STREAM";
    case MD_FUNCTION_TABLE_STREAM:
      return "MD_FUNCTION_TABLE_STREAM";
    case MD_UNLOADED_MODULE_LIST_STREAM:
      return "MD_UNLOADED_MODULE_LIST_STREAM";
    case MD_MISC_INFO_STREAM:
      return "MD_MISC_INFO_STREAM";
    case MD_MEMORY_INFO_LIST_STREAM:
      return "MD_MEMORY_INFO_LIST_STREAM";
    case MD_THREAD_INFO_LIST_STREAM:
      return "MD_THREAD_INFO_LIST_STREAM";
    case MD_HANDLE_OPERATION_LIST_STREAM:
      return "MD_HANDLE_OPERATION_LIST_STREAM";
    case MD_TOKEN_STREAM:
      return "MD_TOKEN_STREAM";
    case MD_JAVASCRIPT_DATA_STREAM:
      return "MD_JAVASCRIPT_DATA_STREAM";
    case MD_SYSTEM_MEMORY_INFO_STREAM:
      return "MD_SYSTEM_MEMORY_INFO_STREAM";
    case MD_PROCESS_VM_COUNTERS_STREAM:
      return "MD_PROCESS_VM_COUNTERS_STREAM";
    case MD_LAST_RESERVED_STREAM:
      return "MD_LAST_RESERVED_STREAM";
    case MD_BREAKPAD_INFO_STREAM:
      return "MD_BREAKPAD_INFO_STREAM";
    case MD_ASSERTION_INFO_STREAM:
      return "MD_ASSERTION_INFO_STREAM";
    case MD_LINUX_CPU_INFO:
      return "MD_LINUX_CPU_INFO";
    case MD_LINUX_PROC_STATUS:
      return "MD_LINUX_PROC_STATUS";
    case MD_LINUX_LSB_RELEASE:
      return "MD_LINUX_LSB_RELEASE";
    case MD_LINUX_CMD_LINE:
      return "MD_LINUX_CMD_LINE";
    case MD_LINUX_ENVIRON:
      return "MD_LINUX_ENVIRON";
    case MD_LINUX_AUXV:
      return "MD_LINUX_AUXV";
    case MD_LINUX_MAPS:
      return "MD_LINUX_MAPS";
    case MD_LINUX_DSO_DEBUG:
      return "MD_LINUX_DSO_DEBUG";
    case MD_CRASHPAD_INFO_STREAM:
      return "MD_CRASHPAD_INFO_STREAM";
    default:
      return "unknown";
    }
  }
  
  void Minidump::Print() {
    if (!valid_) {
      BPLOG(ERROR) << "Minidump cannot print invalid data";
      return;
    }
  
    printf("MDRawHeader\n");
    printf("  signature            = 0x%x\n",    header_.signature);
    printf("  version              = 0x%x\n",    header_.version);
    printf("  stream_count         = %d\n",      header_.stream_count);
    printf("  stream_directory_rva = 0x%x\n",    header_.stream_directory_rva);
    printf("  checksum             = 0x%x\n",    header_.checksum);
    printf("  time_date_stamp      = 0x%x %s\n",
           header_.time_date_stamp,
           TimeTToUTCString(header_.time_date_stamp).c_str());
    printf("  flags                = 0x%" PRIx64 "\n",  header_.flags);
    printf("\n");
  
    for (unsigned int stream_index = 0;
         stream_index < header_.stream_count;
         ++stream_index) {
      MDRawDirectory* directory_entry = &(*directory_)[stream_index];
  
      printf("mDirectory[%d]\n", stream_index);
      printf("MDRawDirectory\n");
      printf("  stream_type        = 0x%x (%s)\n", directory_entry->stream_type,
             get_stream_name(directory_entry->stream_type));
      printf("  location.data_size = %d\n",
             directory_entry->location.data_size);
      printf("  location.rva       = 0x%x\n", directory_entry->location.rva);
      printf("\n");
    }
  
    printf("Streams:\n");
    for (MinidumpStreamMap::const_iterator iterator = stream_map_->begin();
         iterator != stream_map_->end();
         ++iterator) {
      uint32_t stream_type = iterator->first;
      const MinidumpStreamInfo& info = iterator->second;
      printf("  stream type 0x%x (%s) at index %d\n", stream_type,
             get_stream_name(stream_type),
             info.stream_index);
    }
    printf("\n");
  }
  
  
  const MDRawDirectory* Minidump::GetDirectoryEntryAtIndex(unsigned int index)
        const {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid Minidump for GetDirectoryEntryAtIndex";
      return NULL;
    }
  
    if (index >= header_.stream_count) {
      BPLOG(ERROR) << "Minidump stream directory index out of range: " <<
                      index << "/" << header_.stream_count;
      return NULL;
    }
  
    return &(*directory_)[index];
  }
  
  
  bool Minidump::ReadBytes(void* bytes, size_t count) {
    // Can't check valid_ because Read needs to call this method before
    // validity can be determined.
    if (!stream_) {
      return false;
    }
    stream_->read(static_cast<char*>(bytes), count);
    std::streamsize bytes_read = stream_->gcount();
    if (bytes_read == -1) {
      string error_string;
      int error_code = ErrnoString(&error_string);
      BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string;
      return false;
    }
  
    // Convert to size_t and check for data loss
    size_t bytes_read_converted = static_cast<size_t>(bytes_read);
    if (static_cast<std::streamsize>(bytes_read_converted) != bytes_read) {
      BPLOG(ERROR) << "ReadBytes: conversion data loss detected when converting "
                   << bytes_read << " to " << bytes_read_converted;
      return false;
    }
  
    if (bytes_read_converted != count) {
      BPLOG(ERROR) << "ReadBytes: read " << bytes_read_converted << "/" << count;
      return false;
    }
  
    return true;
  }
  
  
  bool Minidump::SeekSet(off_t offset) {
    // Can't check valid_ because Read needs to call this method before
    // validity can be determined.
    if (!stream_) {
      return false;
    }
    stream_->seekg(offset, std::ios_base::beg);
    if (!stream_->good()) {
      string error_string;
      int error_code = ErrnoString(&error_string);
      BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string;
      return false;
    }
    return true;
  }
  
  off_t Minidump::Tell() {
    if (!valid_ || !stream_) {
      return (off_t)-1;
    }
  
    // Check for conversion data loss
    std::streamoff std_streamoff = stream_->tellg();
    off_t rv = static_cast<off_t>(std_streamoff);
    if (static_cast<std::streamoff>(rv) == std_streamoff) {
      return rv;
    } else {
      BPLOG(ERROR) << "Data loss detected";
      return (off_t)-1;
    }
  }
  
  
  string* Minidump::ReadString(off_t offset) {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid Minidump for ReadString";
      return NULL;
    }
    if (!SeekSet(offset)) {
      BPLOG(ERROR) << "ReadString could not seek to string at offset " << offset;
      return NULL;
    }
  
    uint32_t bytes;
    if (!ReadBytes(&bytes, sizeof(bytes))) {
      BPLOG(ERROR) << "ReadString could not read string size at offset " <<
                      offset;
      return NULL;
    }
    if (swap_)
      Swap(&bytes);
  
    if (bytes % 2 != 0) {
      BPLOG(ERROR) << "ReadString found odd-sized " << bytes <<
                      "-byte string at offset " << offset;
      return NULL;
    }
    unsigned int utf16_words = bytes / 2;
  
    if (utf16_words > max_string_length_) {
      BPLOG(ERROR) << "ReadString string length " << utf16_words <<
                      " exceeds maximum " << max_string_length_ <<
                      " at offset " << offset;
      return NULL;
    }
  
    vector<uint16_t> string_utf16(utf16_words);
  
    if (utf16_words) {
      if (!ReadBytes(&string_utf16[0], bytes)) {
        BPLOG(ERROR) << "ReadString could not read " << bytes <<
                        "-byte string at offset " << offset;
        return NULL;
      }
    }
  
    return UTF16ToUTF8(string_utf16, swap_);
  }
  
  
  bool Minidump::ReadUTF8String(off_t offset, string* string_utf8) {
    if (!valid_) {
      BPLOG(ERROR) << "Invalid Minidump for ReadString";
      return false;
    }
    if (!SeekSet(offset)) {
      BPLOG(ERROR) << "ReadUTF8String could not seek to string at offset "
                   << offset;
      return false;
    }
  
    uint32_t bytes;
    if (!ReadBytes(&bytes, sizeof(bytes))) {
      BPLOG(ERROR) << "ReadUTF8String could not read string size at offset " <<
                      offset;
      return false;
    }
  
    if (swap_) {
      Swap(&bytes);
    }
  
    if (bytes > max_string_length_) {
      BPLOG(ERROR) << "ReadUTF8String string length " << bytes <<
                      " exceeds maximum " << max_string_length_ <<
                      " at offset " << offset;
      return false;
    }
  
    string_utf8->resize(bytes);
  
    if (!ReadBytes(&(*string_utf8)[0], bytes)) {
      BPLOG(ERROR) << "ReadUTF8String could not read " << bytes <<
                      "-byte string at offset " << offset;
      return false;
    }
  
    return true;
  }
  
  
  bool Minidump::ReadStringList(
      off_t offset,
      std::vector<std::string>* string_list) {
    string_list->clear();
  
    if (!SeekSet(offset)) {
      BPLOG(ERROR) << "Minidump cannot seek to string_list";
      return false;
    }
  
    uint32_t count;
    if (!ReadBytes(&count, sizeof(count))) {
      BPLOG(ERROR) << "Minidump cannot read string_list count";
      return false;
    }
  
    if (swap_) {
      Swap(&count);
    }
  
    scoped_array<MDRVA> rvas(new MDRVA[count]);
  
    // Read the entire array in one fell swoop, instead of reading one entry
    // at a time in the loop.
    if (!ReadBytes(&rvas[0], sizeof(MDRVA) * count)) {
      BPLOG(ERROR) << "Minidump could not read string_list";
      return false;
    }
  
    for (uint32_t index = 0; index < count; ++index) {
      if (swap()) {
        Swap(&rvas[index]);
      }
  
      string entry;
      if (!ReadUTF8String(rvas[index], &entry)) {
        BPLOG(ERROR) << "Minidump could not read string_list entry";
        return false;
      }
  
      string_list->push_back(entry);
    }
  
    return true;
  }
  
  
  bool Minidump::ReadSimpleStringDictionary(
      off_t offset,
      std::map<std::string, std::string>* simple_string_dictionary) {
    simple_string_dictionary->clear();
  
    if (!SeekSet(offset)) {
      BPLOG(ERROR) << "Minidump cannot seek to simple_string_dictionary";
      return false;
    }
  
    uint32_t count;
    if (!ReadBytes(&count, sizeof(count))) {
      BPLOG(ERROR)
          << "Minidump cannot read simple_string_dictionary count";
      return false;
    }
  
    if (swap()) {
      Swap(&count);
    }
  
    scoped_array<MDRawSimpleStringDictionaryEntry> entries(
        new MDRawSimpleStringDictionaryEntry[count]);
  
    // Read the entire array in one fell swoop, instead of reading one entry
    // at a time in the loop.
    if (!ReadBytes(
            &entries[0],
            sizeof(MDRawSimpleStringDictionaryEntry) * count)) {
      BPLOG(ERROR) << "Minidump could not read simple_string_dictionary";
      return false;
    }
  
    for (uint32_t index = 0; index < count; ++index) {
      if (swap()) {
        Swap(&entries[index]);
      }
  
      string key;
      if (!ReadUTF8String(entries[index].key, &key)) {
        BPLOG(ERROR) << "Minidump could not read simple_string_dictionary key";
        return false;
      }
  
      string value;
      if (!ReadUTF8String(entries[index].value, &value)) {
        BPLOG(ERROR) << "Minidump could not read simple_string_dictionary value";
        return false;
      }
  
      if (simple_string_dictionary->find(key) !=
          simple_string_dictionary->end()) {
        BPLOG(ERROR)
            << "Minidump: discarding duplicate simple_string_dictionary value "
            << value << " for key " << key;
      } else {
        simple_string_dictionary->insert(std::make_pair(key, value));
      }
    }
  
    return true;
  }
  
  
  bool Minidump::SeekToStreamType(uint32_t  stream_type,
                                  uint32_t* stream_length) {
    BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires "
                                       "|stream_length|";
    assert(stream_length);
    *stream_length = 0;
  
    if (!valid_) {
      BPLOG(ERROR) << "Invalid Mindump for SeekToStreamType";
      return false;
    }
  
    MinidumpStreamMap::const_iterator iterator = stream_map_->find(stream_type);
    if (iterator == stream_map_->end()) {
      // This stream type didn't exist in the directory.
      BPLOG(INFO) << "SeekToStreamType: type " << stream_type << " not present";
      return false;
    }
  
    const MinidumpStreamInfo& info = iterator->second;
    if (info.stream_index >= header_.stream_count) {
      BPLOG(ERROR) << "SeekToStreamType: type " << stream_type <<
                      " out of range: " <<
                      info.stream_index << "/" << header_.stream_count;
      return false;
    }
  
    MDRawDirectory* directory_entry = &(*directory_)[info.stream_index];
    if (!SeekSet(directory_entry->location.rva)) {
      BPLOG(ERROR) << "SeekToStreamType could not seek to stream type " <<
                      stream_type;
      return false;
    }
  
    *stream_length = directory_entry->location.data_size;
  
    return true;
  }
  
  
  template<typename T>
  T* Minidump::GetStream(T** stream) {
    // stream is a garbage parameter that's present only to account for C++'s
    // inability to overload a method based solely on its return type.
  
    const uint32_t stream_type = T::kStreamType;
  
    BPLOG_IF(ERROR, !stream) << "Minidump::GetStream type " << stream_type <<
                                " requires |stream|";
    assert(stream);
    *stream = NULL;
  
    if (!valid_) {
      BPLOG(ERROR) << "Invalid Minidump for GetStream type " << stream_type;
      return NULL;
    }
  
    MinidumpStreamMap::iterator iterator = stream_map_->find(stream_type);
    if (iterator == stream_map_->end()) {
      // This stream type didn't exist in the directory.
      BPLOG(INFO) << "GetStream: type " << stream_type << " not present";
      return NULL;
    }
  
    // Get a pointer so that the stored stream field can be altered.
    MinidumpStreamInfo* info = &iterator->second;
  
    if (info->stream) {
      // This cast is safe because info.stream is only populated by this
      // method, and there is a direct correlation between T and stream_type.
      *stream = static_cast<T*>(info->stream);
      return *stream;
    }
  
    uint32_t stream_length;
    if (!SeekToStreamType(stream_type, &stream_length)) {
      BPLOG(ERROR) << "GetStream could not seek to stream type " << stream_type;
      return NULL;
    }
  
    scoped_ptr<T> new_stream(new T(this));
  
    if (!new_stream->Read(stream_length)) {
      BPLOG(ERROR) << "GetStream could not read stream type " << stream_type;
      return NULL;
    }
  
    *stream = new_stream.release();
    info->stream = *stream;
    return *stream;
  }
  
  }  // namespace google_breakpad
  