michael@0: // Copyright (c) 2010 Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // minidump.cc: A minidump reader. michael@0: // michael@0: // See minidump.h for documentation. michael@0: // michael@0: // Author: Mark Mentovai michael@0: michael@0: #include "google_breakpad/processor/minidump.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifdef _WIN32 michael@0: #include michael@0: #define PRIx64 "llx" michael@0: #define PRIx32 "lx" michael@0: #define snprintf _snprintf michael@0: #else // _WIN32 michael@0: #include michael@0: #define O_BINARY 0 michael@0: #endif // _WIN32 michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "processor/range_map-inl.h" michael@0: michael@0: #include "common/scoped_ptr.h" michael@0: #include "processor/basic_code_module.h" michael@0: #include "processor/basic_code_modules.h" michael@0: #include "common/logging.h" michael@0: michael@0: michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: michael@0: using std::istream; michael@0: using std::ifstream; michael@0: using std::numeric_limits; michael@0: using std::vector; michael@0: michael@0: michael@0: // michael@0: // Swapping routines michael@0: // michael@0: // Inlining these doesn't increase code size significantly, and it saves michael@0: // a whole lot of unnecessary jumping back and forth. michael@0: // michael@0: michael@0: michael@0: // Swapping an 8-bit quantity is a no-op. This function is only provided michael@0: // to account for certain templatized operations that require swapping for michael@0: // wider types but handle uint8_t too michael@0: // (MinidumpMemoryRegion::GetMemoryAtAddressInternal). michael@0: static inline void Swap(uint8_t* value) { michael@0: } michael@0: michael@0: michael@0: // Optimization: don't need to AND the furthest right shift, because we're michael@0: // shifting an unsigned quantity. The standard requires zero-filling in this michael@0: // case. If the quantities were signed, a bitmask whould be needed for this michael@0: // right shift to avoid an arithmetic shift (which retains the sign bit). michael@0: // The furthest left shift never needs to be ANDed bitmask. michael@0: michael@0: michael@0: static inline void Swap(uint16_t* value) { michael@0: *value = (*value >> 8) | michael@0: (*value << 8); michael@0: } michael@0: michael@0: michael@0: static inline void Swap(uint32_t* value) { michael@0: *value = (*value >> 24) | michael@0: ((*value >> 8) & 0x0000ff00) | michael@0: ((*value << 8) & 0x00ff0000) | michael@0: (*value << 24); michael@0: } michael@0: michael@0: michael@0: static inline void Swap(uint64_t* value) { michael@0: uint32_t* value32 = reinterpret_cast(value); michael@0: Swap(&value32[0]); michael@0: Swap(&value32[1]); michael@0: uint32_t temp = value32[0]; michael@0: value32[0] = value32[1]; michael@0: value32[1] = temp; michael@0: } michael@0: michael@0: michael@0: // Given a pointer to a 128-bit int in the minidump data, set the "low" michael@0: // and "high" fields appropriately. michael@0: static void Normalize128(uint128_struct* value, bool is_big_endian) { michael@0: // The struct format is [high, low], so if the format is big-endian, michael@0: // the most significant bytes will already be in the high field. michael@0: if (!is_big_endian) { michael@0: uint64_t temp = value->low; michael@0: value->low = value->high; michael@0: value->high = temp; michael@0: } michael@0: } michael@0: michael@0: // This just swaps each int64 half of the 128-bit value. michael@0: // The value should also be normalized by calling Normalize128(). michael@0: static void Swap(uint128_struct* value) { michael@0: Swap(&value->low); michael@0: Swap(&value->high); michael@0: } michael@0: michael@0: michael@0: static inline void Swap(MDLocationDescriptor* location_descriptor) { michael@0: Swap(&location_descriptor->data_size); michael@0: Swap(&location_descriptor->rva); michael@0: } michael@0: michael@0: michael@0: static inline void Swap(MDMemoryDescriptor* memory_descriptor) { michael@0: Swap(&memory_descriptor->start_of_memory_range); michael@0: Swap(&memory_descriptor->memory); michael@0: } michael@0: michael@0: michael@0: static inline void Swap(MDGUID* guid) { michael@0: Swap(&guid->data1); michael@0: Swap(&guid->data2); michael@0: Swap(&guid->data3); michael@0: // Don't swap guid->data4[] because it contains 8-bit quantities. michael@0: } michael@0: michael@0: michael@0: // michael@0: // Character conversion routines michael@0: // michael@0: michael@0: michael@0: // Standard wide-character conversion routines depend on the system's own michael@0: // idea of what width a wide character should be: some use 16 bits, and michael@0: // some use 32 bits. For the purposes of a minidump, wide strings are michael@0: // always represented with 16-bit UTF-16 chracters. iconv isn't available michael@0: // everywhere, and its interface varies where it is available. iconv also michael@0: // deals purely with char* pointers, so in addition to considering the swap michael@0: // parameter, a converter that uses iconv would also need to take the host michael@0: // CPU's endianness into consideration. It doesn't seems worth the trouble michael@0: // of making it a dependency when we don't care about anything but UTF-16. michael@0: static string* UTF16ToUTF8(const vector& in, michael@0: bool swap) { michael@0: scoped_ptr out(new string()); michael@0: michael@0: // Set the string's initial capacity to the number of UTF-16 characters, michael@0: // because the UTF-8 representation will always be at least this long. michael@0: // If the UTF-8 representation is longer, the string will grow dynamically. michael@0: out->reserve(in.size()); michael@0: michael@0: for (vector::const_iterator iterator = in.begin(); michael@0: iterator != in.end(); michael@0: ++iterator) { michael@0: // Get a 16-bit value from the input michael@0: uint16_t in_word = *iterator; michael@0: if (swap) michael@0: Swap(&in_word); michael@0: michael@0: // Convert the input value (in_word) into a Unicode code point (unichar). michael@0: uint32_t unichar; michael@0: if (in_word >= 0xdc00 && in_word <= 0xdcff) { michael@0: BPLOG(ERROR) << "UTF16ToUTF8 found low surrogate " << michael@0: HexString(in_word) << " without high"; michael@0: return NULL; michael@0: } else if (in_word >= 0xd800 && in_word <= 0xdbff) { michael@0: // High surrogate. michael@0: unichar = (in_word - 0xd7c0) << 10; michael@0: if (++iterator == in.end()) { michael@0: BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " << michael@0: HexString(in_word) << " at end of string"; michael@0: return NULL; michael@0: } michael@0: uint32_t high_word = in_word; michael@0: in_word = *iterator; michael@0: if (in_word < 0xdc00 || in_word > 0xdcff) { michael@0: BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " << michael@0: HexString(high_word) << " without low " << michael@0: HexString(in_word); michael@0: return NULL; michael@0: } michael@0: unichar |= in_word & 0x03ff; michael@0: } else { michael@0: // The ordinary case, a single non-surrogate Unicode character encoded michael@0: // as a single 16-bit value. michael@0: unichar = in_word; michael@0: } michael@0: michael@0: // Convert the Unicode code point (unichar) into its UTF-8 representation, michael@0: // appending it to the out string. michael@0: if (unichar < 0x80) { michael@0: (*out) += unichar; michael@0: } else if (unichar < 0x800) { michael@0: (*out) += 0xc0 | (unichar >> 6); michael@0: (*out) += 0x80 | (unichar & 0x3f); michael@0: } else if (unichar < 0x10000) { michael@0: (*out) += 0xe0 | (unichar >> 12); michael@0: (*out) += 0x80 | ((unichar >> 6) & 0x3f); michael@0: (*out) += 0x80 | (unichar & 0x3f); michael@0: } else if (unichar < 0x200000) { michael@0: (*out) += 0xf0 | (unichar >> 18); michael@0: (*out) += 0x80 | ((unichar >> 12) & 0x3f); michael@0: (*out) += 0x80 | ((unichar >> 6) & 0x3f); michael@0: (*out) += 0x80 | (unichar & 0x3f); michael@0: } else { michael@0: BPLOG(ERROR) << "UTF16ToUTF8 cannot represent high value " << michael@0: HexString(unichar) << " in UTF-8"; michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: return out.release(); michael@0: } michael@0: michael@0: // Return the smaller of the number of code units in the UTF-16 string, michael@0: // not including the terminating null word, or maxlen. michael@0: static size_t UTF16codeunits(const uint16_t *string, size_t maxlen) { michael@0: size_t count = 0; michael@0: while (count < maxlen && string[count] != 0) michael@0: count++; michael@0: return count; michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpObject michael@0: // michael@0: michael@0: michael@0: MinidumpObject::MinidumpObject(Minidump* minidump) michael@0: : minidump_(minidump), michael@0: valid_(false) { michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpStream michael@0: // michael@0: michael@0: michael@0: MinidumpStream::MinidumpStream(Minidump* minidump) michael@0: : MinidumpObject(minidump) { michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpContext michael@0: // michael@0: michael@0: michael@0: MinidumpContext::MinidumpContext(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: context_(), michael@0: context_flags_(0) { michael@0: } michael@0: michael@0: michael@0: MinidumpContext::~MinidumpContext() { michael@0: FreeContext(); michael@0: } michael@0: michael@0: michael@0: bool MinidumpContext::Read(uint32_t expected_size) { michael@0: valid_ = false; michael@0: michael@0: FreeContext(); michael@0: michael@0: // First, figure out what type of CPU this context structure is for. michael@0: // For some reason, the AMD64 Context doesn't have context_flags michael@0: // at the beginning of the structure, so special case it here. michael@0: if (expected_size == sizeof(MDRawContextAMD64)) { michael@0: BPLOG(INFO) << "MinidumpContext: looks like AMD64 context"; michael@0: michael@0: scoped_ptr context_amd64(new MDRawContextAMD64()); michael@0: if (!minidump_->ReadBytes(context_amd64.get(), michael@0: sizeof(MDRawContextAMD64))) { michael@0: BPLOG(ERROR) << "MinidumpContext could not read amd64 context"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) michael@0: Swap(&context_amd64->context_flags); michael@0: michael@0: uint32_t cpu_type = context_amd64->context_flags & MD_CONTEXT_CPU_MASK; michael@0: if (cpu_type == 0) { michael@0: if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) { michael@0: context_amd64->context_flags |= cpu_type; michael@0: } else { michael@0: BPLOG(ERROR) << "Failed to preserve the current stream position"; michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (cpu_type != MD_CONTEXT_AMD64) { michael@0: //TODO: fall through to switch below? michael@0: // need a Tell method to be able to SeekSet back to beginning michael@0: // http://code.google.com/p/google-breakpad/issues/detail?id=224 michael@0: BPLOG(ERROR) << "MinidumpContext not actually amd64 context"; michael@0: return false; michael@0: } michael@0: michael@0: // Do this after reading the entire MDRawContext structure because michael@0: // GetSystemInfo may seek minidump to a new position. michael@0: if (!CheckAgainstSystemInfo(cpu_type)) { michael@0: BPLOG(ERROR) << "MinidumpContext amd64 does not match system info"; michael@0: return false; michael@0: } michael@0: michael@0: // Normalize the 128-bit types in the dump. michael@0: // Since this is AMD64, by definition, the values are little-endian. michael@0: for (unsigned int vr_index = 0; michael@0: vr_index < MD_CONTEXT_AMD64_VR_COUNT; michael@0: ++vr_index) michael@0: Normalize128(&context_amd64->vector_register[vr_index], false); michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&context_amd64->p1_home); michael@0: Swap(&context_amd64->p2_home); michael@0: Swap(&context_amd64->p3_home); michael@0: Swap(&context_amd64->p4_home); michael@0: Swap(&context_amd64->p5_home); michael@0: Swap(&context_amd64->p6_home); michael@0: // context_flags is already swapped michael@0: Swap(&context_amd64->mx_csr); michael@0: Swap(&context_amd64->cs); michael@0: Swap(&context_amd64->ds); michael@0: Swap(&context_amd64->es); michael@0: Swap(&context_amd64->fs); michael@0: Swap(&context_amd64->ss); michael@0: Swap(&context_amd64->eflags); michael@0: Swap(&context_amd64->dr0); michael@0: Swap(&context_amd64->dr1); michael@0: Swap(&context_amd64->dr2); michael@0: Swap(&context_amd64->dr3); michael@0: Swap(&context_amd64->dr6); michael@0: Swap(&context_amd64->dr7); michael@0: Swap(&context_amd64->rax); michael@0: Swap(&context_amd64->rcx); michael@0: Swap(&context_amd64->rdx); michael@0: Swap(&context_amd64->rbx); michael@0: Swap(&context_amd64->rsp); michael@0: Swap(&context_amd64->rbp); michael@0: Swap(&context_amd64->rsi); michael@0: Swap(&context_amd64->rdi); michael@0: Swap(&context_amd64->r8); michael@0: Swap(&context_amd64->r9); michael@0: Swap(&context_amd64->r10); michael@0: Swap(&context_amd64->r11); michael@0: Swap(&context_amd64->r12); michael@0: Swap(&context_amd64->r13); michael@0: Swap(&context_amd64->r14); michael@0: Swap(&context_amd64->r15); michael@0: Swap(&context_amd64->rip); michael@0: //FIXME: I'm not sure what actually determines michael@0: // which member of the union {flt_save, sse_registers} michael@0: // is valid. We're not currently using either, michael@0: // but it would be good to have them swapped properly. michael@0: michael@0: for (unsigned int vr_index = 0; michael@0: vr_index < MD_CONTEXT_AMD64_VR_COUNT; michael@0: ++vr_index) michael@0: Swap(&context_amd64->vector_register[vr_index]); michael@0: Swap(&context_amd64->vector_control); michael@0: Swap(&context_amd64->debug_control); michael@0: Swap(&context_amd64->last_branch_to_rip); michael@0: Swap(&context_amd64->last_branch_from_rip); michael@0: Swap(&context_amd64->last_exception_to_rip); michael@0: Swap(&context_amd64->last_exception_from_rip); michael@0: } michael@0: michael@0: context_flags_ = context_amd64->context_flags; michael@0: michael@0: context_.amd64 = context_amd64.release(); michael@0: } michael@0: else { michael@0: uint32_t context_flags; michael@0: if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) { michael@0: BPLOG(ERROR) << "MinidumpContext could not read context flags"; michael@0: return false; michael@0: } michael@0: if (minidump_->swap()) michael@0: Swap(&context_flags); michael@0: michael@0: uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK; michael@0: if (cpu_type == 0) { michael@0: // Unfortunately the flag for MD_CONTEXT_ARM that was taken michael@0: // from a Windows CE SDK header conflicts in practice with michael@0: // the CONTEXT_XSTATE flag. MD_CONTEXT_ARM has been renumbered, michael@0: // but handle dumps with the legacy value gracefully here. michael@0: if (context_flags & MD_CONTEXT_ARM_OLD) { michael@0: context_flags |= MD_CONTEXT_ARM; michael@0: context_flags &= ~MD_CONTEXT_ARM_OLD; michael@0: cpu_type = MD_CONTEXT_ARM; michael@0: } michael@0: } michael@0: michael@0: if (cpu_type == 0) { michael@0: if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) { michael@0: context_flags |= cpu_type; michael@0: } else { michael@0: BPLOG(ERROR) << "Failed to preserve the current stream position"; michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Allocate the context structure for the correct CPU and fill it. The michael@0: // casts are slightly unorthodox, but it seems better to do that than to michael@0: // maintain a separate pointer for each type of CPU context structure michael@0: // when only one of them will be used. michael@0: switch (cpu_type) { michael@0: case MD_CONTEXT_X86: { michael@0: if (expected_size != sizeof(MDRawContextX86)) { michael@0: BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " << michael@0: expected_size << " != " << sizeof(MDRawContextX86); michael@0: return false; michael@0: } michael@0: michael@0: scoped_ptr context_x86(new MDRawContextX86()); michael@0: michael@0: // Set the context_flags member, which has already been read, and michael@0: // read the rest of the structure beginning with the first member michael@0: // after context_flags. michael@0: context_x86->context_flags = context_flags; michael@0: michael@0: size_t flags_size = sizeof(context_x86->context_flags); michael@0: uint8_t* context_after_flags = michael@0: reinterpret_cast(context_x86.get()) + flags_size; michael@0: if (!minidump_->ReadBytes(context_after_flags, michael@0: sizeof(MDRawContextX86) - flags_size)) { michael@0: BPLOG(ERROR) << "MinidumpContext could not read x86 context"; michael@0: return false; michael@0: } michael@0: michael@0: // Do this after reading the entire MDRawContext structure because michael@0: // GetSystemInfo may seek minidump to a new position. michael@0: if (!CheckAgainstSystemInfo(cpu_type)) { michael@0: BPLOG(ERROR) << "MinidumpContext x86 does not match system info"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: // context_x86->context_flags was already swapped. michael@0: Swap(&context_x86->dr0); michael@0: Swap(&context_x86->dr1); michael@0: Swap(&context_x86->dr2); michael@0: Swap(&context_x86->dr3); michael@0: Swap(&context_x86->dr6); michael@0: Swap(&context_x86->dr7); michael@0: Swap(&context_x86->float_save.control_word); michael@0: Swap(&context_x86->float_save.status_word); michael@0: Swap(&context_x86->float_save.tag_word); michael@0: Swap(&context_x86->float_save.error_offset); michael@0: Swap(&context_x86->float_save.error_selector); michael@0: Swap(&context_x86->float_save.data_offset); michael@0: Swap(&context_x86->float_save.data_selector); michael@0: // context_x86->float_save.register_area[] contains 8-bit quantities michael@0: // and does not need to be swapped. michael@0: Swap(&context_x86->float_save.cr0_npx_state); michael@0: Swap(&context_x86->gs); michael@0: Swap(&context_x86->fs); michael@0: Swap(&context_x86->es); michael@0: Swap(&context_x86->ds); michael@0: Swap(&context_x86->edi); michael@0: Swap(&context_x86->esi); michael@0: Swap(&context_x86->ebx); michael@0: Swap(&context_x86->edx); michael@0: Swap(&context_x86->ecx); michael@0: Swap(&context_x86->eax); michael@0: Swap(&context_x86->ebp); michael@0: Swap(&context_x86->eip); michael@0: Swap(&context_x86->cs); michael@0: Swap(&context_x86->eflags); michael@0: Swap(&context_x86->esp); michael@0: Swap(&context_x86->ss); michael@0: // context_x86->extended_registers[] contains 8-bit quantities and michael@0: // does not need to be swapped. michael@0: } michael@0: michael@0: context_.x86 = context_x86.release(); michael@0: michael@0: break; michael@0: } michael@0: michael@0: case MD_CONTEXT_PPC: { michael@0: if (expected_size != sizeof(MDRawContextPPC)) { michael@0: BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " << michael@0: expected_size << " != " << sizeof(MDRawContextPPC); michael@0: return false; michael@0: } michael@0: michael@0: scoped_ptr context_ppc(new MDRawContextPPC()); michael@0: michael@0: // Set the context_flags member, which has already been read, and michael@0: // read the rest of the structure beginning with the first member michael@0: // after context_flags. michael@0: context_ppc->context_flags = context_flags; michael@0: michael@0: size_t flags_size = sizeof(context_ppc->context_flags); michael@0: uint8_t* context_after_flags = michael@0: reinterpret_cast(context_ppc.get()) + flags_size; michael@0: if (!minidump_->ReadBytes(context_after_flags, michael@0: sizeof(MDRawContextPPC) - flags_size)) { michael@0: BPLOG(ERROR) << "MinidumpContext could not read ppc context"; michael@0: return false; michael@0: } michael@0: michael@0: // Do this after reading the entire MDRawContext structure because michael@0: // GetSystemInfo may seek minidump to a new position. michael@0: if (!CheckAgainstSystemInfo(cpu_type)) { michael@0: BPLOG(ERROR) << "MinidumpContext ppc does not match system info"; michael@0: return false; michael@0: } michael@0: michael@0: // Normalize the 128-bit types in the dump. michael@0: // Since this is PowerPC, by definition, the values are big-endian. michael@0: for (unsigned int vr_index = 0; michael@0: vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; michael@0: ++vr_index) { michael@0: Normalize128(&context_ppc->vector_save.save_vr[vr_index], true); michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: // context_ppc->context_flags was already swapped. michael@0: Swap(&context_ppc->srr0); michael@0: Swap(&context_ppc->srr1); michael@0: for (unsigned int gpr_index = 0; michael@0: gpr_index < MD_CONTEXT_PPC_GPR_COUNT; michael@0: ++gpr_index) { michael@0: Swap(&context_ppc->gpr[gpr_index]); michael@0: } michael@0: Swap(&context_ppc->cr); michael@0: Swap(&context_ppc->xer); michael@0: Swap(&context_ppc->lr); michael@0: Swap(&context_ppc->ctr); michael@0: Swap(&context_ppc->mq); michael@0: Swap(&context_ppc->vrsave); michael@0: for (unsigned int fpr_index = 0; michael@0: fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; michael@0: ++fpr_index) { michael@0: Swap(&context_ppc->float_save.fpregs[fpr_index]); michael@0: } michael@0: // Don't swap context_ppc->float_save.fpscr_pad because it is only michael@0: // used for padding. michael@0: Swap(&context_ppc->float_save.fpscr); michael@0: for (unsigned int vr_index = 0; michael@0: vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; michael@0: ++vr_index) { michael@0: Swap(&context_ppc->vector_save.save_vr[vr_index]); michael@0: } michael@0: Swap(&context_ppc->vector_save.save_vscr); michael@0: // Don't swap the padding fields in vector_save. michael@0: Swap(&context_ppc->vector_save.save_vrvalid); michael@0: } michael@0: michael@0: context_.ppc = context_ppc.release(); michael@0: michael@0: break; michael@0: } michael@0: michael@0: case MD_CONTEXT_SPARC: { michael@0: if (expected_size != sizeof(MDRawContextSPARC)) { michael@0: BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " << michael@0: expected_size << " != " << sizeof(MDRawContextSPARC); michael@0: return false; michael@0: } michael@0: michael@0: scoped_ptr context_sparc(new MDRawContextSPARC()); michael@0: michael@0: // Set the context_flags member, which has already been read, and michael@0: // read the rest of the structure beginning with the first member michael@0: // after context_flags. michael@0: context_sparc->context_flags = context_flags; michael@0: michael@0: size_t flags_size = sizeof(context_sparc->context_flags); michael@0: uint8_t* context_after_flags = michael@0: reinterpret_cast(context_sparc.get()) + flags_size; michael@0: if (!minidump_->ReadBytes(context_after_flags, michael@0: sizeof(MDRawContextSPARC) - flags_size)) { michael@0: BPLOG(ERROR) << "MinidumpContext could not read sparc context"; michael@0: return false; michael@0: } michael@0: michael@0: // Do this after reading the entire MDRawContext structure because michael@0: // GetSystemInfo may seek minidump to a new position. michael@0: if (!CheckAgainstSystemInfo(cpu_type)) { michael@0: BPLOG(ERROR) << "MinidumpContext sparc does not match system info"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: // context_sparc->context_flags was already swapped. michael@0: for (unsigned int gpr_index = 0; michael@0: gpr_index < MD_CONTEXT_SPARC_GPR_COUNT; michael@0: ++gpr_index) { michael@0: Swap(&context_sparc->g_r[gpr_index]); michael@0: } michael@0: Swap(&context_sparc->ccr); michael@0: Swap(&context_sparc->pc); michael@0: Swap(&context_sparc->npc); michael@0: Swap(&context_sparc->y); michael@0: Swap(&context_sparc->asi); michael@0: Swap(&context_sparc->fprs); michael@0: for (unsigned int fpr_index = 0; michael@0: fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; michael@0: ++fpr_index) { michael@0: Swap(&context_sparc->float_save.regs[fpr_index]); michael@0: } michael@0: Swap(&context_sparc->float_save.filler); michael@0: Swap(&context_sparc->float_save.fsr); michael@0: } michael@0: context_.ctx_sparc = context_sparc.release(); michael@0: michael@0: break; michael@0: } michael@0: michael@0: case MD_CONTEXT_ARM: { michael@0: if (expected_size != sizeof(MDRawContextARM)) { michael@0: BPLOG(ERROR) << "MinidumpContext arm size mismatch, " << michael@0: expected_size << " != " << sizeof(MDRawContextARM); michael@0: return false; michael@0: } michael@0: michael@0: scoped_ptr context_arm(new MDRawContextARM()); michael@0: michael@0: // Set the context_flags member, which has already been read, and michael@0: // read the rest of the structure beginning with the first member michael@0: // after context_flags. michael@0: context_arm->context_flags = context_flags; michael@0: michael@0: size_t flags_size = sizeof(context_arm->context_flags); michael@0: uint8_t* context_after_flags = michael@0: reinterpret_cast(context_arm.get()) + flags_size; michael@0: if (!minidump_->ReadBytes(context_after_flags, michael@0: sizeof(MDRawContextARM) - flags_size)) { michael@0: BPLOG(ERROR) << "MinidumpContext could not read arm context"; michael@0: return false; michael@0: } michael@0: michael@0: // Do this after reading the entire MDRawContext structure because michael@0: // GetSystemInfo may seek minidump to a new position. michael@0: if (!CheckAgainstSystemInfo(cpu_type)) { michael@0: BPLOG(ERROR) << "MinidumpContext arm does not match system info"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: // context_arm->context_flags was already swapped. michael@0: for (unsigned int ireg_index = 0; michael@0: ireg_index < MD_CONTEXT_ARM_GPR_COUNT; michael@0: ++ireg_index) { michael@0: Swap(&context_arm->iregs[ireg_index]); michael@0: } michael@0: Swap(&context_arm->cpsr); michael@0: Swap(&context_arm->float_save.fpscr); michael@0: for (unsigned int fpr_index = 0; michael@0: fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; michael@0: ++fpr_index) { michael@0: Swap(&context_arm->float_save.regs[fpr_index]); michael@0: } michael@0: for (unsigned int fpe_index = 0; michael@0: fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; michael@0: ++fpe_index) { michael@0: Swap(&context_arm->float_save.extra[fpe_index]); michael@0: } michael@0: } michael@0: context_.arm = context_arm.release(); michael@0: michael@0: break; michael@0: } michael@0: michael@0: default: { michael@0: // Unknown context type - Don't log as an error yet. Let the michael@0: // caller work that out. michael@0: BPLOG(INFO) << "MinidumpContext unknown context type " << michael@0: HexString(cpu_type); michael@0: return false; michael@0: break; michael@0: } michael@0: } michael@0: context_flags_ = context_flags; michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: uint32_t MinidumpContext::GetContextCPU() const { michael@0: if (!valid_) { michael@0: // Don't log a message, GetContextCPU can be legitimately called with michael@0: // valid_ false by FreeContext, which is called by Read. michael@0: return 0; michael@0: } michael@0: michael@0: return context_flags_ & MD_CONTEXT_CPU_MASK; michael@0: } michael@0: michael@0: bool MinidumpContext::GetInstructionPointer(uint64_t* ip) const { michael@0: BPLOG_IF(ERROR, !ip) << "MinidumpContext::GetInstructionPointer " michael@0: "requires |ip|"; michael@0: assert(ip); michael@0: *ip = 0; michael@0: michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpContext for GetInstructionPointer"; michael@0: return false; michael@0: } michael@0: michael@0: switch (context_flags_ & MD_CONTEXT_CPU_MASK) { michael@0: case MD_CONTEXT_AMD64: michael@0: *ip = context_.amd64->rip; michael@0: break; michael@0: case MD_CONTEXT_ARM: michael@0: *ip = context_.arm->iregs[MD_CONTEXT_ARM_REG_PC]; michael@0: break; michael@0: case MD_CONTEXT_PPC: michael@0: *ip = context_.ppc->srr0; michael@0: break; michael@0: case MD_CONTEXT_SPARC: michael@0: *ip = context_.ctx_sparc->pc; michael@0: break; michael@0: case MD_CONTEXT_X86: michael@0: *ip = context_.x86->eip; michael@0: break; michael@0: default: michael@0: // This should never happen. michael@0: BPLOG(ERROR) << "Unknown CPU architecture in GetInstructionPointer"; michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: const MDRawContextX86* MinidumpContext::GetContextX86() const { michael@0: if (GetContextCPU() != MD_CONTEXT_X86) { michael@0: BPLOG(ERROR) << "MinidumpContext cannot get x86 context"; michael@0: return NULL; michael@0: } michael@0: michael@0: return context_.x86; michael@0: } michael@0: michael@0: michael@0: const MDRawContextPPC* MinidumpContext::GetContextPPC() const { michael@0: if (GetContextCPU() != MD_CONTEXT_PPC) { michael@0: BPLOG(ERROR) << "MinidumpContext cannot get ppc context"; michael@0: return NULL; michael@0: } michael@0: michael@0: return context_.ppc; michael@0: } michael@0: michael@0: const MDRawContextAMD64* MinidumpContext::GetContextAMD64() const { michael@0: if (GetContextCPU() != MD_CONTEXT_AMD64) { michael@0: BPLOG(ERROR) << "MinidumpContext cannot get amd64 context"; michael@0: return NULL; michael@0: } michael@0: michael@0: return context_.amd64; michael@0: } michael@0: michael@0: const MDRawContextSPARC* MinidumpContext::GetContextSPARC() const { michael@0: if (GetContextCPU() != MD_CONTEXT_SPARC) { michael@0: BPLOG(ERROR) << "MinidumpContext cannot get sparc context"; michael@0: return NULL; michael@0: } michael@0: michael@0: return context_.ctx_sparc; michael@0: } michael@0: michael@0: const MDRawContextARM* MinidumpContext::GetContextARM() const { michael@0: if (GetContextCPU() != MD_CONTEXT_ARM) { michael@0: BPLOG(ERROR) << "MinidumpContext cannot get arm context"; michael@0: return NULL; michael@0: } michael@0: michael@0: return context_.arm; michael@0: } michael@0: michael@0: void MinidumpContext::FreeContext() { michael@0: switch (GetContextCPU()) { michael@0: case MD_CONTEXT_X86: michael@0: delete context_.x86; michael@0: break; michael@0: michael@0: case MD_CONTEXT_PPC: michael@0: delete context_.ppc; michael@0: break; michael@0: michael@0: case MD_CONTEXT_AMD64: michael@0: delete context_.amd64; michael@0: break; michael@0: michael@0: case MD_CONTEXT_SPARC: michael@0: delete context_.ctx_sparc; michael@0: break; michael@0: michael@0: case MD_CONTEXT_ARM: michael@0: delete context_.arm; michael@0: break; michael@0: michael@0: default: michael@0: // There is no context record (valid_ is false) or there's a michael@0: // context record for an unknown CPU (shouldn't happen, only known michael@0: // records are stored by Read). michael@0: break; michael@0: } michael@0: michael@0: context_flags_ = 0; michael@0: context_.base = NULL; michael@0: } michael@0: michael@0: michael@0: bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) { michael@0: // It's OK if the minidump doesn't contain an MD_SYSTEM_INFO_STREAM, michael@0: // as this function just implements a sanity check. michael@0: MinidumpSystemInfo* system_info = minidump_->GetSystemInfo(); michael@0: if (!system_info) { michael@0: BPLOG(INFO) << "MinidumpContext could not be compared against " michael@0: "MinidumpSystemInfo"; michael@0: return true; michael@0: } michael@0: michael@0: // If there is an MD_SYSTEM_INFO_STREAM, it should contain valid system info. michael@0: const MDRawSystemInfo* raw_system_info = system_info->system_info(); michael@0: if (!raw_system_info) { michael@0: BPLOG(INFO) << "MinidumpContext could not be compared against " michael@0: "MDRawSystemInfo"; michael@0: return false; michael@0: } michael@0: michael@0: MDCPUArchitecture system_info_cpu_type = static_cast( michael@0: raw_system_info->processor_architecture); michael@0: michael@0: // Compare the CPU type of the context record to the CPU type in the michael@0: // minidump's system info stream. michael@0: bool return_value = false; michael@0: switch (context_cpu_type) { michael@0: case MD_CONTEXT_X86: michael@0: if (system_info_cpu_type == MD_CPU_ARCHITECTURE_X86 || michael@0: system_info_cpu_type == MD_CPU_ARCHITECTURE_X86_WIN64 || michael@0: system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) { michael@0: return_value = true; michael@0: } michael@0: break; michael@0: michael@0: case MD_CONTEXT_PPC: michael@0: if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC) michael@0: return_value = true; michael@0: break; michael@0: michael@0: case MD_CONTEXT_AMD64: michael@0: if (system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) michael@0: return_value = true; michael@0: break; michael@0: michael@0: case MD_CONTEXT_SPARC: michael@0: if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC) michael@0: return_value = true; michael@0: break; michael@0: michael@0: case MD_CONTEXT_ARM: michael@0: if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM) michael@0: return_value = true; michael@0: break; michael@0: } michael@0: michael@0: BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " << michael@0: HexString(context_cpu_type) << michael@0: " wrong for MinidumpSystemInfo CPU " << michael@0: HexString(system_info_cpu_type); michael@0: michael@0: return return_value; michael@0: } michael@0: michael@0: michael@0: void MinidumpContext::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpContext cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: switch (GetContextCPU()) { michael@0: case MD_CONTEXT_X86: { michael@0: const MDRawContextX86* context_x86 = GetContextX86(); michael@0: printf("MDRawContextX86\n"); michael@0: printf(" context_flags = 0x%x\n", michael@0: context_x86->context_flags); michael@0: printf(" dr0 = 0x%x\n", context_x86->dr0); michael@0: printf(" dr1 = 0x%x\n", context_x86->dr1); michael@0: printf(" dr2 = 0x%x\n", context_x86->dr2); michael@0: printf(" dr3 = 0x%x\n", context_x86->dr3); michael@0: printf(" dr6 = 0x%x\n", context_x86->dr6); michael@0: printf(" dr7 = 0x%x\n", context_x86->dr7); michael@0: printf(" float_save.control_word = 0x%x\n", michael@0: context_x86->float_save.control_word); michael@0: printf(" float_save.status_word = 0x%x\n", michael@0: context_x86->float_save.status_word); michael@0: printf(" float_save.tag_word = 0x%x\n", michael@0: context_x86->float_save.tag_word); michael@0: printf(" float_save.error_offset = 0x%x\n", michael@0: context_x86->float_save.error_offset); michael@0: printf(" float_save.error_selector = 0x%x\n", michael@0: context_x86->float_save.error_selector); michael@0: printf(" float_save.data_offset = 0x%x\n", michael@0: context_x86->float_save.data_offset); michael@0: printf(" float_save.data_selector = 0x%x\n", michael@0: context_x86->float_save.data_selector); michael@0: printf(" float_save.register_area[%2d] = 0x", michael@0: MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE); michael@0: for (unsigned int register_index = 0; michael@0: register_index < MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE; michael@0: ++register_index) { michael@0: printf("%02x", context_x86->float_save.register_area[register_index]); michael@0: } michael@0: printf("\n"); michael@0: printf(" float_save.cr0_npx_state = 0x%x\n", michael@0: context_x86->float_save.cr0_npx_state); michael@0: printf(" gs = 0x%x\n", context_x86->gs); michael@0: printf(" fs = 0x%x\n", context_x86->fs); michael@0: printf(" es = 0x%x\n", context_x86->es); michael@0: printf(" ds = 0x%x\n", context_x86->ds); michael@0: printf(" edi = 0x%x\n", context_x86->edi); michael@0: printf(" esi = 0x%x\n", context_x86->esi); michael@0: printf(" ebx = 0x%x\n", context_x86->ebx); michael@0: printf(" edx = 0x%x\n", context_x86->edx); michael@0: printf(" ecx = 0x%x\n", context_x86->ecx); michael@0: printf(" eax = 0x%x\n", context_x86->eax); michael@0: printf(" ebp = 0x%x\n", context_x86->ebp); michael@0: printf(" eip = 0x%x\n", context_x86->eip); michael@0: printf(" cs = 0x%x\n", context_x86->cs); michael@0: printf(" eflags = 0x%x\n", context_x86->eflags); michael@0: printf(" esp = 0x%x\n", context_x86->esp); michael@0: printf(" ss = 0x%x\n", context_x86->ss); michael@0: printf(" extended_registers[%3d] = 0x", michael@0: MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE); michael@0: for (unsigned int register_index = 0; michael@0: register_index < MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE; michael@0: ++register_index) { michael@0: printf("%02x", context_x86->extended_registers[register_index]); michael@0: } michael@0: printf("\n\n"); michael@0: michael@0: break; michael@0: } michael@0: michael@0: case MD_CONTEXT_PPC: { michael@0: const MDRawContextPPC* context_ppc = GetContextPPC(); michael@0: printf("MDRawContextPPC\n"); michael@0: printf(" context_flags = 0x%x\n", michael@0: context_ppc->context_flags); michael@0: printf(" srr0 = 0x%x\n", context_ppc->srr0); michael@0: printf(" srr1 = 0x%x\n", context_ppc->srr1); michael@0: for (unsigned int gpr_index = 0; michael@0: gpr_index < MD_CONTEXT_PPC_GPR_COUNT; michael@0: ++gpr_index) { michael@0: printf(" gpr[%2d] = 0x%x\n", michael@0: gpr_index, context_ppc->gpr[gpr_index]); michael@0: } michael@0: printf(" cr = 0x%x\n", context_ppc->cr); michael@0: printf(" xer = 0x%x\n", context_ppc->xer); michael@0: printf(" lr = 0x%x\n", context_ppc->lr); michael@0: printf(" ctr = 0x%x\n", context_ppc->ctr); michael@0: printf(" mq = 0x%x\n", context_ppc->mq); michael@0: printf(" vrsave = 0x%x\n", context_ppc->vrsave); michael@0: for (unsigned int fpr_index = 0; michael@0: fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; michael@0: ++fpr_index) { michael@0: printf(" float_save.fpregs[%2d] = 0x%" PRIx64 "\n", michael@0: fpr_index, context_ppc->float_save.fpregs[fpr_index]); michael@0: } michael@0: printf(" float_save.fpscr = 0x%x\n", michael@0: context_ppc->float_save.fpscr); michael@0: // TODO(mmentovai): print the 128-bit quantities in michael@0: // context_ppc->vector_save. This isn't done yet because printf michael@0: // doesn't support 128-bit quantities, and printing them using michael@0: // PRIx64 as two 64-bit quantities requires knowledge of the CPU's michael@0: // byte ordering. michael@0: printf(" vector_save.save_vrvalid = 0x%x\n", michael@0: context_ppc->vector_save.save_vrvalid); michael@0: printf("\n"); michael@0: michael@0: break; michael@0: } michael@0: michael@0: case MD_CONTEXT_AMD64: { michael@0: const MDRawContextAMD64* context_amd64 = GetContextAMD64(); michael@0: printf("MDRawContextAMD64\n"); michael@0: printf(" p1_home = 0x%" PRIx64 "\n", michael@0: context_amd64->p1_home); michael@0: printf(" p2_home = 0x%" PRIx64 "\n", michael@0: context_amd64->p2_home); michael@0: printf(" p3_home = 0x%" PRIx64 "\n", michael@0: context_amd64->p3_home); michael@0: printf(" p4_home = 0x%" PRIx64 "\n", michael@0: context_amd64->p4_home); michael@0: printf(" p5_home = 0x%" PRIx64 "\n", michael@0: context_amd64->p5_home); michael@0: printf(" p6_home = 0x%" PRIx64 "\n", michael@0: context_amd64->p6_home); michael@0: printf(" context_flags = 0x%x\n", michael@0: context_amd64->context_flags); michael@0: printf(" mx_csr = 0x%x\n", michael@0: context_amd64->mx_csr); michael@0: printf(" cs = 0x%x\n", context_amd64->cs); michael@0: printf(" ds = 0x%x\n", context_amd64->ds); michael@0: printf(" es = 0x%x\n", context_amd64->es); michael@0: printf(" fs = 0x%x\n", context_amd64->fs); michael@0: printf(" gs = 0x%x\n", context_amd64->gs); michael@0: printf(" ss = 0x%x\n", context_amd64->ss); michael@0: printf(" eflags = 0x%x\n", context_amd64->eflags); michael@0: printf(" dr0 = 0x%" PRIx64 "\n", context_amd64->dr0); michael@0: printf(" dr1 = 0x%" PRIx64 "\n", context_amd64->dr1); michael@0: printf(" dr2 = 0x%" PRIx64 "\n", context_amd64->dr2); michael@0: printf(" dr3 = 0x%" PRIx64 "\n", context_amd64->dr3); michael@0: printf(" dr6 = 0x%" PRIx64 "\n", context_amd64->dr6); michael@0: printf(" dr7 = 0x%" PRIx64 "\n", context_amd64->dr7); michael@0: printf(" rax = 0x%" PRIx64 "\n", context_amd64->rax); michael@0: printf(" rcx = 0x%" PRIx64 "\n", context_amd64->rcx); michael@0: printf(" rdx = 0x%" PRIx64 "\n", context_amd64->rdx); michael@0: printf(" rbx = 0x%" PRIx64 "\n", context_amd64->rbx); michael@0: printf(" rsp = 0x%" PRIx64 "\n", context_amd64->rsp); michael@0: printf(" rbp = 0x%" PRIx64 "\n", context_amd64->rbp); michael@0: printf(" rsi = 0x%" PRIx64 "\n", context_amd64->rsi); michael@0: printf(" rdi = 0x%" PRIx64 "\n", context_amd64->rdi); michael@0: printf(" r8 = 0x%" PRIx64 "\n", context_amd64->r8); michael@0: printf(" r9 = 0x%" PRIx64 "\n", context_amd64->r9); michael@0: printf(" r10 = 0x%" PRIx64 "\n", context_amd64->r10); michael@0: printf(" r11 = 0x%" PRIx64 "\n", context_amd64->r11); michael@0: printf(" r12 = 0x%" PRIx64 "\n", context_amd64->r12); michael@0: printf(" r13 = 0x%" PRIx64 "\n", context_amd64->r13); michael@0: printf(" r14 = 0x%" PRIx64 "\n", context_amd64->r14); michael@0: printf(" r15 = 0x%" PRIx64 "\n", context_amd64->r15); michael@0: printf(" rip = 0x%" PRIx64 "\n", context_amd64->rip); michael@0: //TODO: print xmm, vector, debug registers michael@0: printf("\n"); michael@0: break; michael@0: } michael@0: michael@0: case MD_CONTEXT_SPARC: { michael@0: const MDRawContextSPARC* context_sparc = GetContextSPARC(); michael@0: printf("MDRawContextSPARC\n"); michael@0: printf(" context_flags = 0x%x\n", michael@0: context_sparc->context_flags); michael@0: for (unsigned int g_r_index = 0; michael@0: g_r_index < MD_CONTEXT_SPARC_GPR_COUNT; michael@0: ++g_r_index) { michael@0: printf(" g_r[%2d] = 0x%" PRIx64 "\n", michael@0: g_r_index, context_sparc->g_r[g_r_index]); michael@0: } michael@0: printf(" ccr = 0x%" PRIx64 "\n", context_sparc->ccr); michael@0: printf(" pc = 0x%" PRIx64 "\n", context_sparc->pc); michael@0: printf(" npc = 0x%" PRIx64 "\n", context_sparc->npc); michael@0: printf(" y = 0x%" PRIx64 "\n", context_sparc->y); michael@0: printf(" asi = 0x%" PRIx64 "\n", context_sparc->asi); michael@0: printf(" fprs = 0x%" PRIx64 "\n", context_sparc->fprs); michael@0: michael@0: for (unsigned int fpr_index = 0; michael@0: fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; michael@0: ++fpr_index) { michael@0: printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", michael@0: fpr_index, context_sparc->float_save.regs[fpr_index]); michael@0: } michael@0: printf(" float_save.filler = 0x%" PRIx64 "\n", michael@0: context_sparc->float_save.filler); michael@0: printf(" float_save.fsr = 0x%" PRIx64 "\n", michael@0: context_sparc->float_save.fsr); michael@0: break; michael@0: } michael@0: michael@0: case MD_CONTEXT_ARM: { michael@0: const MDRawContextARM* context_arm = GetContextARM(); michael@0: printf("MDRawContextARM\n"); michael@0: printf(" context_flags = 0x%x\n", michael@0: context_arm->context_flags); michael@0: for (unsigned int ireg_index = 0; michael@0: ireg_index < MD_CONTEXT_ARM_GPR_COUNT; michael@0: ++ireg_index) { michael@0: printf(" iregs[%2d] = 0x%x\n", michael@0: ireg_index, context_arm->iregs[ireg_index]); michael@0: } michael@0: printf(" cpsr = 0x%x\n", context_arm->cpsr); michael@0: printf(" float_save.fpscr = 0x%" PRIx64 "\n", michael@0: context_arm->float_save.fpscr); michael@0: for (unsigned int fpr_index = 0; michael@0: fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; michael@0: ++fpr_index) { michael@0: printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", michael@0: fpr_index, context_arm->float_save.regs[fpr_index]); michael@0: } michael@0: for (unsigned int fpe_index = 0; michael@0: fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; michael@0: ++fpe_index) { michael@0: printf(" float_save.extra[%2d] = 0x%" PRIx32 "\n", michael@0: fpe_index, context_arm->float_save.extra[fpe_index]); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: michael@0: default: { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpMemoryRegion michael@0: // michael@0: michael@0: michael@0: uint32_t MinidumpMemoryRegion::max_bytes_ = 1024 * 1024; // 1MB michael@0: michael@0: michael@0: MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump) michael@0: : MinidumpObject(minidump), michael@0: descriptor_(NULL), michael@0: memory_(NULL) { michael@0: } michael@0: michael@0: michael@0: MinidumpMemoryRegion::~MinidumpMemoryRegion() { michael@0: delete memory_; michael@0: } michael@0: michael@0: michael@0: void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) { michael@0: descriptor_ = descriptor; michael@0: valid_ = descriptor && michael@0: descriptor_->memory.data_size <= michael@0: numeric_limits::max() - michael@0: descriptor_->start_of_memory_range; michael@0: } michael@0: michael@0: michael@0: const uint8_t* MinidumpMemoryRegion::GetMemory() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (!memory_) { michael@0: if (descriptor_->memory.data_size == 0) { michael@0: BPLOG(ERROR) << "MinidumpMemoryRegion is empty"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (!minidump_->SeekSet(descriptor_->memory.rva)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryRegion could not seek to memory region"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (descriptor_->memory.data_size > max_bytes_) { michael@0: BPLOG(ERROR) << "MinidumpMemoryRegion size " << michael@0: descriptor_->memory.data_size << " exceeds maximum " << michael@0: max_bytes_; michael@0: return NULL; michael@0: } michael@0: michael@0: scoped_ptr< vector > memory( michael@0: new vector(descriptor_->memory.data_size)); michael@0: michael@0: if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryRegion could not read memory region"; michael@0: return NULL; michael@0: } michael@0: michael@0: memory_ = memory.release(); michael@0: } michael@0: michael@0: return &(*memory_)[0]; michael@0: } michael@0: michael@0: michael@0: uint64_t MinidumpMemoryRegion::GetBase() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase"; michael@0: return static_cast(-1); michael@0: } michael@0: michael@0: return descriptor_->start_of_memory_range; michael@0: } michael@0: michael@0: michael@0: uint32_t MinidumpMemoryRegion::GetSize() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize"; michael@0: return 0; michael@0: } michael@0: michael@0: return descriptor_->memory.data_size; michael@0: } michael@0: michael@0: michael@0: void MinidumpMemoryRegion::FreeMemory() { michael@0: delete memory_; michael@0: memory_ = NULL; michael@0: } michael@0: michael@0: michael@0: template michael@0: bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(uint64_t address, michael@0: T* value) const { michael@0: BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal " michael@0: "requires |value|"; michael@0: assert(value); michael@0: *value = 0; michael@0: michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for " michael@0: "GetMemoryAtAddressInternal"; michael@0: return false; michael@0: } michael@0: michael@0: // Common failure case michael@0: if (address < descriptor_->start_of_memory_range || michael@0: sizeof(T) > numeric_limits::max() - address || michael@0: address + sizeof(T) > descriptor_->start_of_memory_range + michael@0: descriptor_->memory.data_size) { michael@0: BPLOG(INFO) << "MinidumpMemoryRegion request out of range: " << michael@0: HexString(address) << "+" << sizeof(T) << "/" << michael@0: HexString(descriptor_->start_of_memory_range) << "+" << michael@0: HexString(descriptor_->memory.data_size); michael@0: return false; michael@0: } michael@0: michael@0: const uint8_t* memory = GetMemory(); michael@0: if (!memory) { michael@0: // GetMemory already logged a perfectly good message. michael@0: return false; michael@0: } michael@0: michael@0: // If the CPU requires memory accesses to be aligned, this can crash. michael@0: // x86 and ppc are able to cope, though. michael@0: *value = *reinterpret_cast( michael@0: &memory[address - descriptor_->start_of_memory_range]); michael@0: michael@0: if (minidump_->swap()) michael@0: Swap(value); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, michael@0: uint8_t* value) const { michael@0: return GetMemoryAtAddressInternal(address, value); michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, michael@0: uint16_t* value) const { michael@0: return GetMemoryAtAddressInternal(address, value); michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, michael@0: uint32_t* value) const { michael@0: return GetMemoryAtAddressInternal(address, value); michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, michael@0: uint64_t* value) const { michael@0: return GetMemoryAtAddressInternal(address, value); michael@0: } michael@0: michael@0: michael@0: void MinidumpMemoryRegion::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpMemoryRegion cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: const uint8_t* memory = GetMemory(); michael@0: if (memory) { michael@0: printf("0x"); michael@0: for (unsigned int byte_index = 0; michael@0: byte_index < descriptor_->memory.data_size; michael@0: byte_index++) { michael@0: printf("%02x", memory[byte_index]); michael@0: } michael@0: printf("\n"); michael@0: } else { michael@0: printf("No memory\n"); michael@0: } michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpThread michael@0: // michael@0: michael@0: michael@0: MinidumpThread::MinidumpThread(Minidump* minidump) michael@0: : MinidumpObject(minidump), michael@0: thread_(), michael@0: memory_(NULL), michael@0: context_(NULL) { michael@0: } michael@0: michael@0: michael@0: MinidumpThread::~MinidumpThread() { michael@0: delete memory_; michael@0: delete context_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpThread::Read() { michael@0: // Invalidate cached data. michael@0: delete memory_; michael@0: memory_ = NULL; michael@0: delete context_; michael@0: context_ = NULL; michael@0: michael@0: valid_ = false; michael@0: michael@0: if (!minidump_->ReadBytes(&thread_, sizeof(thread_))) { michael@0: BPLOG(ERROR) << "MinidumpThread cannot read thread"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&thread_.thread_id); michael@0: Swap(&thread_.suspend_count); michael@0: Swap(&thread_.priority_class); michael@0: Swap(&thread_.priority); michael@0: Swap(&thread_.teb); michael@0: Swap(&thread_.stack); michael@0: Swap(&thread_.thread_context); michael@0: } michael@0: michael@0: // Check for base + size overflow or undersize. michael@0: if (thread_.stack.memory.data_size == 0 || michael@0: thread_.stack.memory.data_size > numeric_limits::max() - michael@0: thread_.stack.start_of_memory_range) { michael@0: // This is ok, but log an error anyway. michael@0: BPLOG(ERROR) << "MinidumpThread has a memory region problem, " << michael@0: HexString(thread_.stack.start_of_memory_range) << "+" << michael@0: HexString(thread_.stack.memory.data_size); michael@0: } else { michael@0: memory_ = new MinidumpMemoryRegion(minidump_); michael@0: memory_->SetDescriptor(&thread_.stack); michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: MinidumpMemoryRegion* MinidumpThread::GetMemory() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpThread for GetMemory"; michael@0: return NULL; michael@0: } michael@0: michael@0: return memory_; michael@0: } michael@0: michael@0: michael@0: MinidumpContext* MinidumpThread::GetContext() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpThread for GetContext"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (!context_) { michael@0: if (!minidump_->SeekSet(thread_.thread_context.rva)) { michael@0: BPLOG(ERROR) << "MinidumpThread cannot seek to context"; michael@0: return NULL; michael@0: } michael@0: michael@0: scoped_ptr context(new MinidumpContext(minidump_)); michael@0: michael@0: if (!context->Read(thread_.thread_context.data_size)) { michael@0: BPLOG(ERROR) << "MinidumpThread cannot read context"; michael@0: return NULL; michael@0: } michael@0: michael@0: context_ = context.release(); michael@0: } michael@0: michael@0: return context_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpThread::GetThreadID(uint32_t *thread_id) const { michael@0: BPLOG_IF(ERROR, !thread_id) << "MinidumpThread::GetThreadID requires " michael@0: "|thread_id|"; michael@0: assert(thread_id); michael@0: *thread_id = 0; michael@0: michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpThread for GetThreadID"; michael@0: return false; michael@0: } michael@0: michael@0: *thread_id = thread_.thread_id; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: void MinidumpThread::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpThread cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDRawThread\n"); michael@0: printf(" thread_id = 0x%x\n", thread_.thread_id); michael@0: printf(" suspend_count = %d\n", thread_.suspend_count); michael@0: printf(" priority_class = 0x%x\n", thread_.priority_class); michael@0: printf(" priority = 0x%x\n", thread_.priority); michael@0: printf(" teb = 0x%" PRIx64 "\n", thread_.teb); michael@0: printf(" stack.start_of_memory_range = 0x%" PRIx64 "\n", michael@0: thread_.stack.start_of_memory_range); michael@0: printf(" stack.memory.data_size = 0x%x\n", michael@0: thread_.stack.memory.data_size); michael@0: printf(" stack.memory.rva = 0x%x\n", thread_.stack.memory.rva); michael@0: printf(" thread_context.data_size = 0x%x\n", michael@0: thread_.thread_context.data_size); michael@0: printf(" thread_context.rva = 0x%x\n", michael@0: thread_.thread_context.rva); michael@0: michael@0: MinidumpContext* context = GetContext(); michael@0: if (context) { michael@0: printf("\n"); michael@0: context->Print(); michael@0: } else { michael@0: printf(" (no context)\n"); michael@0: printf("\n"); michael@0: } michael@0: michael@0: MinidumpMemoryRegion* memory = GetMemory(); michael@0: if (memory) { michael@0: printf("Stack\n"); michael@0: memory->Print(); michael@0: } else { michael@0: printf("No stack\n"); michael@0: } michael@0: printf("\n"); michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpThreadList michael@0: // michael@0: michael@0: michael@0: uint32_t MinidumpThreadList::max_threads_ = 4096; michael@0: michael@0: michael@0: MinidumpThreadList::MinidumpThreadList(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: id_to_thread_map_(), michael@0: threads_(NULL), michael@0: thread_count_(0) { michael@0: } michael@0: michael@0: michael@0: MinidumpThreadList::~MinidumpThreadList() { michael@0: delete threads_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpThreadList::Read(uint32_t expected_size) { michael@0: // Invalidate cached data. michael@0: id_to_thread_map_.clear(); michael@0: delete threads_; michael@0: threads_ = NULL; michael@0: thread_count_ = 0; michael@0: michael@0: valid_ = false; michael@0: michael@0: uint32_t thread_count; michael@0: if (expected_size < sizeof(thread_count)) { michael@0: BPLOG(ERROR) << "MinidumpThreadList count size mismatch, " << michael@0: expected_size << " < " << sizeof(thread_count); michael@0: return false; michael@0: } michael@0: if (!minidump_->ReadBytes(&thread_count, sizeof(thread_count))) { michael@0: BPLOG(ERROR) << "MinidumpThreadList cannot read thread count"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) michael@0: Swap(&thread_count); michael@0: michael@0: if (thread_count > numeric_limits::max() / sizeof(MDRawThread)) { michael@0: BPLOG(ERROR) << "MinidumpThreadList thread count " << thread_count << michael@0: " would cause multiplication overflow"; michael@0: return false; michael@0: } michael@0: michael@0: if (expected_size != sizeof(thread_count) + michael@0: thread_count * sizeof(MDRawThread)) { michael@0: // may be padded with 4 bytes on 64bit ABIs for alignment michael@0: if (expected_size == sizeof(thread_count) + 4 + michael@0: thread_count * sizeof(MDRawThread)) { michael@0: uint32_t useless; michael@0: if (!minidump_->ReadBytes(&useless, 4)) { michael@0: BPLOG(ERROR) << "MinidumpThreadList cannot read threadlist padded bytes"; michael@0: return false; michael@0: } michael@0: } else { michael@0: BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size << michael@0: " != " << sizeof(thread_count) + michael@0: thread_count * sizeof(MDRawThread); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: michael@0: if (thread_count > max_threads_) { michael@0: BPLOG(ERROR) << "MinidumpThreadList count " << thread_count << michael@0: " exceeds maximum " << max_threads_; michael@0: return false; michael@0: } michael@0: michael@0: if (thread_count != 0) { michael@0: scoped_ptr threads( michael@0: new MinidumpThreads(thread_count, MinidumpThread(minidump_))); michael@0: michael@0: for (unsigned int thread_index = 0; michael@0: thread_index < thread_count; michael@0: ++thread_index) { michael@0: MinidumpThread* thread = &(*threads)[thread_index]; michael@0: michael@0: // Assume that the file offset is correct after the last read. michael@0: if (!thread->Read()) { michael@0: BPLOG(ERROR) << "MinidumpThreadList cannot read thread " << michael@0: thread_index << "/" << thread_count; michael@0: return false; michael@0: } michael@0: michael@0: uint32_t thread_id; michael@0: if (!thread->GetThreadID(&thread_id)) { michael@0: BPLOG(ERROR) << "MinidumpThreadList cannot get thread ID for thread " << michael@0: thread_index << "/" << thread_count; michael@0: return false; michael@0: } michael@0: michael@0: if (GetThreadByID(thread_id)) { michael@0: // Another thread with this ID is already in the list. Data error. michael@0: BPLOG(ERROR) << "MinidumpThreadList found multiple threads with ID " << michael@0: HexString(thread_id) << " at thread " << michael@0: thread_index << "/" << thread_count; michael@0: return false; michael@0: } michael@0: id_to_thread_map_[thread_id] = thread; michael@0: } michael@0: michael@0: threads_ = threads.release(); michael@0: } michael@0: michael@0: thread_count_ = thread_count; michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: MinidumpThread* MinidumpThreadList::GetThreadAtIndex(unsigned int index) michael@0: const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpThreadList for GetThreadAtIndex"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (index >= thread_count_) { michael@0: BPLOG(ERROR) << "MinidumpThreadList index out of range: " << michael@0: index << "/" << thread_count_; michael@0: return NULL; michael@0: } michael@0: michael@0: return &(*threads_)[index]; michael@0: } michael@0: michael@0: michael@0: MinidumpThread* MinidumpThreadList::GetThreadByID(uint32_t thread_id) { michael@0: // Don't check valid_. Read calls this method before everything is michael@0: // validated. It is safe to not check valid_ here. michael@0: return id_to_thread_map_[thread_id]; michael@0: } michael@0: michael@0: michael@0: void MinidumpThreadList::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpThreadList cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MinidumpThreadList\n"); michael@0: printf(" thread_count = %d\n", thread_count_); michael@0: printf("\n"); michael@0: michael@0: for (unsigned int thread_index = 0; michael@0: thread_index < thread_count_; michael@0: ++thread_index) { michael@0: printf("thread[%d]\n", thread_index); michael@0: michael@0: (*threads_)[thread_index].Print(); michael@0: } michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpModule michael@0: // michael@0: michael@0: michael@0: uint32_t MinidumpModule::max_cv_bytes_ = 32768; michael@0: uint32_t MinidumpModule::max_misc_bytes_ = 32768; michael@0: michael@0: michael@0: MinidumpModule::MinidumpModule(Minidump* minidump) michael@0: : MinidumpObject(minidump), michael@0: module_valid_(false), michael@0: has_debug_info_(false), michael@0: module_(), michael@0: name_(NULL), michael@0: cv_record_(NULL), michael@0: cv_record_signature_(MD_CVINFOUNKNOWN_SIGNATURE), michael@0: misc_record_(NULL) { michael@0: } michael@0: michael@0: michael@0: MinidumpModule::~MinidumpModule() { michael@0: delete name_; michael@0: delete cv_record_; michael@0: delete misc_record_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpModule::Read() { michael@0: // Invalidate cached data. michael@0: delete name_; michael@0: name_ = NULL; michael@0: delete cv_record_; michael@0: cv_record_ = NULL; michael@0: cv_record_signature_ = MD_CVINFOUNKNOWN_SIGNATURE; michael@0: delete misc_record_; michael@0: misc_record_ = NULL; michael@0: michael@0: module_valid_ = false; michael@0: has_debug_info_ = false; michael@0: valid_ = false; michael@0: michael@0: if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) { michael@0: BPLOG(ERROR) << "MinidumpModule cannot read module"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&module_.base_of_image); michael@0: Swap(&module_.size_of_image); michael@0: Swap(&module_.checksum); michael@0: Swap(&module_.time_date_stamp); michael@0: Swap(&module_.module_name_rva); michael@0: Swap(&module_.version_info.signature); michael@0: Swap(&module_.version_info.struct_version); michael@0: Swap(&module_.version_info.file_version_hi); michael@0: Swap(&module_.version_info.file_version_lo); michael@0: Swap(&module_.version_info.product_version_hi); michael@0: Swap(&module_.version_info.product_version_lo); michael@0: Swap(&module_.version_info.file_flags_mask); michael@0: Swap(&module_.version_info.file_flags); michael@0: Swap(&module_.version_info.file_os); michael@0: Swap(&module_.version_info.file_type); michael@0: Swap(&module_.version_info.file_subtype); michael@0: Swap(&module_.version_info.file_date_hi); michael@0: Swap(&module_.version_info.file_date_lo); michael@0: Swap(&module_.cv_record); michael@0: Swap(&module_.misc_record); michael@0: // Don't swap reserved fields because their contents are unknown (as michael@0: // are their proper widths). michael@0: } michael@0: michael@0: // Check for base + size overflow or undersize. michael@0: if (module_.size_of_image == 0 || michael@0: module_.size_of_image > michael@0: numeric_limits::max() - module_.base_of_image) { michael@0: BPLOG(ERROR) << "MinidumpModule has a module problem, " << michael@0: HexString(module_.base_of_image) << "+" << michael@0: HexString(module_.size_of_image); michael@0: return false; michael@0: } michael@0: michael@0: module_valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool MinidumpModule::ReadAuxiliaryData() { michael@0: if (!module_valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModule for ReadAuxiliaryData"; michael@0: return false; michael@0: } michael@0: michael@0: // Each module must have a name. michael@0: name_ = minidump_->ReadString(module_.module_name_rva); michael@0: if (!name_) { michael@0: BPLOG(ERROR) << "MinidumpModule could not read name"; michael@0: return false; michael@0: } michael@0: michael@0: // At this point, we have enough info for the module to be valid. michael@0: valid_ = true; michael@0: michael@0: // CodeView and miscellaneous debug records are only required if the michael@0: // module indicates that they exist. michael@0: if (module_.cv_record.data_size && !GetCVRecord(NULL)) { michael@0: BPLOG(ERROR) << "MinidumpModule has no CodeView record, " michael@0: "but one was expected"; michael@0: return false; michael@0: } michael@0: michael@0: if (module_.misc_record.data_size && !GetMiscRecord(NULL)) { michael@0: BPLOG(ERROR) << "MinidumpModule has no miscellaneous debug record, " michael@0: "but one was expected"; michael@0: return false; michael@0: } michael@0: michael@0: has_debug_info_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: string MinidumpModule::code_file() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModule for code_file"; michael@0: return ""; michael@0: } michael@0: michael@0: return *name_; michael@0: } michael@0: michael@0: michael@0: string MinidumpModule::code_identifier() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModule for code_identifier"; michael@0: return ""; michael@0: } michael@0: michael@0: if (!has_debug_info_) michael@0: return ""; michael@0: michael@0: MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo(); michael@0: if (!minidump_system_info) { michael@0: BPLOG(ERROR) << "MinidumpModule code_identifier requires " michael@0: "MinidumpSystemInfo"; michael@0: return ""; michael@0: } michael@0: michael@0: const MDRawSystemInfo *raw_system_info = minidump_system_info->system_info(); michael@0: if (!raw_system_info) { michael@0: BPLOG(ERROR) << "MinidumpModule code_identifier requires MDRawSystemInfo"; michael@0: return ""; michael@0: } michael@0: michael@0: string identifier; michael@0: michael@0: switch (raw_system_info->platform_id) { michael@0: case MD_OS_WIN32_NT: michael@0: case MD_OS_WIN32_WINDOWS: { michael@0: // Use the same format that the MS symbol server uses in filesystem michael@0: // hierarchies. michael@0: char identifier_string[17]; michael@0: snprintf(identifier_string, sizeof(identifier_string), "%08X%x", michael@0: module_.time_date_stamp, module_.size_of_image); michael@0: identifier = identifier_string; michael@0: break; michael@0: } michael@0: michael@0: case MD_OS_MAC_OS_X: michael@0: case MD_OS_IOS: michael@0: case MD_OS_SOLARIS: michael@0: case MD_OS_ANDROID: michael@0: case MD_OS_LINUX: { michael@0: // TODO(mmentovai): support uuid extension if present, otherwise fall michael@0: // back to version (from LC_ID_DYLIB?), otherwise fall back to something michael@0: // else. michael@0: identifier = "id"; michael@0: break; michael@0: } michael@0: michael@0: default: { michael@0: // Without knowing what OS generated the dump, we can't generate a good michael@0: // identifier. Return an empty string, signalling failure. michael@0: BPLOG(ERROR) << "MinidumpModule code_identifier requires known platform, " michael@0: "found " << HexString(raw_system_info->platform_id); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return identifier; michael@0: } michael@0: michael@0: michael@0: string MinidumpModule::debug_file() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModule for debug_file"; michael@0: return ""; michael@0: } michael@0: michael@0: if (!has_debug_info_) michael@0: return ""; michael@0: michael@0: string file; michael@0: // Prefer the CodeView record if present. michael@0: if (cv_record_) { michael@0: if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { michael@0: // It's actually an MDCVInfoPDB70 structure. michael@0: const MDCVInfoPDB70* cv_record_70 = michael@0: reinterpret_cast(&(*cv_record_)[0]); michael@0: assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); michael@0: michael@0: // GetCVRecord guarantees pdb_file_name is null-terminated. michael@0: file = reinterpret_cast(cv_record_70->pdb_file_name); michael@0: } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { michael@0: // It's actually an MDCVInfoPDB20 structure. michael@0: const MDCVInfoPDB20* cv_record_20 = michael@0: reinterpret_cast(&(*cv_record_)[0]); michael@0: assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); michael@0: michael@0: // GetCVRecord guarantees pdb_file_name is null-terminated. michael@0: file = reinterpret_cast(cv_record_20->pdb_file_name); michael@0: } michael@0: michael@0: // If there's a CodeView record but it doesn't match a known signature, michael@0: // try the miscellaneous record. michael@0: } michael@0: michael@0: if (file.empty()) { michael@0: // No usable CodeView record. Try the miscellaneous debug record. michael@0: if (misc_record_) { michael@0: const MDImageDebugMisc* misc_record = michael@0: reinterpret_cast(&(*misc_record_)[0]); michael@0: if (!misc_record->unicode) { michael@0: // If it's not Unicode, just stuff it into the string. It's unclear michael@0: // if misc_record->data is 0-terminated, so use an explicit size. michael@0: file = string( michael@0: reinterpret_cast(misc_record->data), michael@0: module_.misc_record.data_size - MDImageDebugMisc_minsize); michael@0: } else { michael@0: // There's a misc_record but it encodes the debug filename in UTF-16. michael@0: // (Actually, because miscellaneous records are so old, it's probably michael@0: // UCS-2.) Convert it to UTF-8 for congruity with the other strings michael@0: // that this method (and all other methods in the Minidump family) michael@0: // return. michael@0: michael@0: unsigned int bytes = michael@0: module_.misc_record.data_size - MDImageDebugMisc_minsize; michael@0: if (bytes % 2 == 0) { michael@0: unsigned int utf16_words = bytes / 2; michael@0: michael@0: // UTF16ToUTF8 expects a vector, so create a temporary one michael@0: // and copy the UTF-16 data into it. michael@0: vector string_utf16(utf16_words); michael@0: if (utf16_words) michael@0: memcpy(&string_utf16[0], &misc_record->data, bytes); michael@0: michael@0: // GetMiscRecord already byte-swapped the data[] field if it contains michael@0: // UTF-16, so pass false as the swap argument. michael@0: scoped_ptr new_file(UTF16ToUTF8(string_utf16, false)); michael@0: file = *new_file; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Relatively common case michael@0: BPLOG_IF(INFO, file.empty()) << "MinidumpModule could not determine " michael@0: "debug_file for " << *name_; michael@0: michael@0: return file; michael@0: } michael@0: michael@0: michael@0: string MinidumpModule::debug_identifier() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModule for debug_identifier"; michael@0: return ""; michael@0: } michael@0: michael@0: if (!has_debug_info_) michael@0: return ""; michael@0: michael@0: string identifier; michael@0: michael@0: // Use the CodeView record if present. michael@0: if (cv_record_) { michael@0: if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { michael@0: // It's actually an MDCVInfoPDB70 structure. michael@0: const MDCVInfoPDB70* cv_record_70 = michael@0: reinterpret_cast(&(*cv_record_)[0]); michael@0: assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); michael@0: michael@0: // Use the same format that the MS symbol server uses in filesystem michael@0: // hierarchies. michael@0: char identifier_string[41]; michael@0: snprintf(identifier_string, sizeof(identifier_string), michael@0: "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", michael@0: cv_record_70->signature.data1, michael@0: cv_record_70->signature.data2, michael@0: cv_record_70->signature.data3, michael@0: cv_record_70->signature.data4[0], michael@0: cv_record_70->signature.data4[1], michael@0: cv_record_70->signature.data4[2], michael@0: cv_record_70->signature.data4[3], michael@0: cv_record_70->signature.data4[4], michael@0: cv_record_70->signature.data4[5], michael@0: cv_record_70->signature.data4[6], michael@0: cv_record_70->signature.data4[7], michael@0: cv_record_70->age); michael@0: identifier = identifier_string; michael@0: } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { michael@0: // It's actually an MDCVInfoPDB20 structure. michael@0: const MDCVInfoPDB20* cv_record_20 = michael@0: reinterpret_cast(&(*cv_record_)[0]); michael@0: assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); michael@0: michael@0: // Use the same format that the MS symbol server uses in filesystem michael@0: // hierarchies. michael@0: char identifier_string[17]; michael@0: snprintf(identifier_string, sizeof(identifier_string), michael@0: "%08X%x", cv_record_20->signature, cv_record_20->age); michael@0: identifier = identifier_string; michael@0: } michael@0: } michael@0: michael@0: // TODO(mmentovai): if there's no usable CodeView record, there might be a michael@0: // miscellaneous debug record. It only carries a filename, though, and no michael@0: // identifier. I'm not sure what the right thing to do for the identifier michael@0: // is in that case, but I don't expect to find many modules without a michael@0: // CodeView record (or some other Breakpad extension structure in place of michael@0: // a CodeView record). Treat it as an error (empty identifier) for now. michael@0: michael@0: // TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier(). michael@0: michael@0: // Relatively common case michael@0: BPLOG_IF(INFO, identifier.empty()) << "MinidumpModule could not determine " michael@0: "debug_identifier for " << *name_; michael@0: michael@0: return identifier; michael@0: } michael@0: michael@0: michael@0: string MinidumpModule::version() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModule for version"; michael@0: return ""; michael@0: } michael@0: michael@0: string version; michael@0: michael@0: if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE && michael@0: module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) { michael@0: char version_string[24]; michael@0: snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u", michael@0: module_.version_info.file_version_hi >> 16, michael@0: module_.version_info.file_version_hi & 0xffff, michael@0: module_.version_info.file_version_lo >> 16, michael@0: module_.version_info.file_version_lo & 0xffff); michael@0: version = version_string; michael@0: } michael@0: michael@0: // TODO(mmentovai): possibly support other struct types in place of michael@0: // the one used with MD_VSFIXEDFILEINFO_SIGNATURE. We can possibly use michael@0: // a different structure that better represents versioning facilities on michael@0: // Mac OS X and Linux, instead of forcing them to adhere to the dotted michael@0: // quad of 16-bit ints that Windows uses. michael@0: michael@0: BPLOG_IF(INFO, version.empty()) << "MinidumpModule could not determine " michael@0: "version for " << *name_; michael@0: michael@0: return version; michael@0: } michael@0: michael@0: michael@0: const CodeModule* MinidumpModule::Copy() const { michael@0: return new BasicCodeModule(this); michael@0: } michael@0: michael@0: michael@0: const uint8_t* MinidumpModule::GetCVRecord(uint32_t* size) { michael@0: if (!module_valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModule for GetCVRecord"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (!cv_record_) { michael@0: // This just guards against 0-sized CodeView records; more specific checks michael@0: // are used when the signature is checked against various structure types. michael@0: if (module_.cv_record.data_size == 0) { michael@0: return NULL; michael@0: } michael@0: michael@0: if (!minidump_->SeekSet(module_.cv_record.rva)) { michael@0: BPLOG(ERROR) << "MinidumpModule could not seek to CodeView record"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (module_.cv_record.data_size > max_cv_bytes_) { michael@0: BPLOG(ERROR) << "MinidumpModule CodeView record size " << michael@0: module_.cv_record.data_size << " exceeds maximum " << michael@0: max_cv_bytes_; michael@0: return NULL; michael@0: } michael@0: michael@0: // Allocating something that will be accessed as MDCVInfoPDB70 or michael@0: // MDCVInfoPDB20 but is allocated as uint8_t[] can cause alignment michael@0: // problems. x86 and ppc are able to cope, though. This allocation michael@0: // style is needed because the MDCVInfoPDB70 or MDCVInfoPDB20 are michael@0: // variable-sized due to their pdb_file_name fields; these structures michael@0: // are not MDCVInfoPDB70_minsize or MDCVInfoPDB20_minsize and treating michael@0: // them as such would result in incomplete structures or overruns. michael@0: scoped_ptr< vector > cv_record( michael@0: new vector(module_.cv_record.data_size)); michael@0: michael@0: if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size)) { michael@0: BPLOG(ERROR) << "MinidumpModule could not read CodeView record"; michael@0: return NULL; michael@0: } michael@0: michael@0: uint32_t signature = MD_CVINFOUNKNOWN_SIGNATURE; michael@0: if (module_.cv_record.data_size > sizeof(signature)) { michael@0: MDCVInfoPDB70* cv_record_signature = michael@0: reinterpret_cast(&(*cv_record)[0]); michael@0: signature = cv_record_signature->cv_signature; michael@0: if (minidump_->swap()) michael@0: Swap(&signature); michael@0: } michael@0: michael@0: if (signature == MD_CVINFOPDB70_SIGNATURE) { michael@0: // Now that the structure type is known, recheck the size. michael@0: if (MDCVInfoPDB70_minsize > module_.cv_record.data_size) { michael@0: BPLOG(ERROR) << "MinidumpModule CodeView7 record size mismatch, " << michael@0: MDCVInfoPDB70_minsize << " > " << michael@0: module_.cv_record.data_size; michael@0: return NULL; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: MDCVInfoPDB70* cv_record_70 = michael@0: reinterpret_cast(&(*cv_record)[0]); michael@0: Swap(&cv_record_70->cv_signature); michael@0: Swap(&cv_record_70->signature); michael@0: Swap(&cv_record_70->age); michael@0: // Don't swap cv_record_70.pdb_file_name because it's an array of 8-bit michael@0: // quantities. (It's a path, is it UTF-8?) michael@0: } michael@0: michael@0: // The last field of either structure is null-terminated 8-bit character michael@0: // data. Ensure that it's null-terminated. michael@0: if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') { michael@0: BPLOG(ERROR) << "MinidumpModule CodeView7 record string is not " michael@0: "0-terminated"; michael@0: return NULL; michael@0: } michael@0: } else if (signature == MD_CVINFOPDB20_SIGNATURE) { michael@0: // Now that the structure type is known, recheck the size. michael@0: if (MDCVInfoPDB20_minsize > module_.cv_record.data_size) { michael@0: BPLOG(ERROR) << "MinidumpModule CodeView2 record size mismatch, " << michael@0: MDCVInfoPDB20_minsize << " > " << michael@0: module_.cv_record.data_size; michael@0: return NULL; michael@0: } michael@0: if (minidump_->swap()) { michael@0: MDCVInfoPDB20* cv_record_20 = michael@0: reinterpret_cast(&(*cv_record)[0]); michael@0: Swap(&cv_record_20->cv_header.signature); michael@0: Swap(&cv_record_20->cv_header.offset); michael@0: Swap(&cv_record_20->signature); michael@0: Swap(&cv_record_20->age); michael@0: // Don't swap cv_record_20.pdb_file_name because it's an array of 8-bit michael@0: // quantities. (It's a path, is it UTF-8?) michael@0: } michael@0: michael@0: // The last field of either structure is null-terminated 8-bit character michael@0: // data. Ensure that it's null-terminated. michael@0: if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') { michael@0: BPLOG(ERROR) << "MindumpModule CodeView2 record string is not " michael@0: "0-terminated"; michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: // If the signature doesn't match something above, it's not something michael@0: // that Breakpad can presently handle directly. Because some modules in michael@0: // the wild contain such CodeView records as MD_CVINFOCV50_SIGNATURE, michael@0: // don't bail out here - allow the data to be returned to the user, michael@0: // although byte-swapping can't be done. michael@0: michael@0: // Store the vector type because that's how storage was allocated, but michael@0: // return it casted to uint8_t*. michael@0: cv_record_ = cv_record.release(); michael@0: cv_record_signature_ = signature; michael@0: } michael@0: michael@0: if (size) michael@0: *size = module_.cv_record.data_size; michael@0: michael@0: return &(*cv_record_)[0]; michael@0: } michael@0: michael@0: michael@0: const MDImageDebugMisc* MinidumpModule::GetMiscRecord(uint32_t* size) { michael@0: if (!module_valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModule for GetMiscRecord"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (!misc_record_) { michael@0: if (module_.misc_record.data_size == 0) { michael@0: return NULL; michael@0: } michael@0: michael@0: if (MDImageDebugMisc_minsize > module_.misc_record.data_size) { michael@0: BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record " michael@0: "size mismatch, " << MDImageDebugMisc_minsize << " > " << michael@0: module_.misc_record.data_size; michael@0: return NULL; michael@0: } michael@0: michael@0: if (!minidump_->SeekSet(module_.misc_record.rva)) { michael@0: BPLOG(ERROR) << "MinidumpModule could not seek to miscellaneous " michael@0: "debugging record"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (module_.misc_record.data_size > max_misc_bytes_) { michael@0: BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record size " << michael@0: module_.misc_record.data_size << " exceeds maximum " << michael@0: max_misc_bytes_; michael@0: return NULL; michael@0: } michael@0: michael@0: // Allocating something that will be accessed as MDImageDebugMisc but michael@0: // is allocated as uint8_t[] can cause alignment problems. x86 and michael@0: // ppc are able to cope, though. This allocation style is needed michael@0: // because the MDImageDebugMisc is variable-sized due to its data field; michael@0: // this structure is not MDImageDebugMisc_minsize and treating it as such michael@0: // would result in an incomplete structure or an overrun. michael@0: scoped_ptr< vector > misc_record_mem( michael@0: new vector(module_.misc_record.data_size)); michael@0: MDImageDebugMisc* misc_record = michael@0: reinterpret_cast(&(*misc_record_mem)[0]); michael@0: michael@0: if (!minidump_->ReadBytes(misc_record, module_.misc_record.data_size)) { michael@0: BPLOG(ERROR) << "MinidumpModule could not read miscellaneous debugging " michael@0: "record"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&misc_record->data_type); michael@0: Swap(&misc_record->length); michael@0: // Don't swap misc_record.unicode because it's an 8-bit quantity. michael@0: // Don't swap the reserved fields for the same reason, and because michael@0: // they don't contain any valid data. michael@0: if (misc_record->unicode) { michael@0: // There is a potential alignment problem, but shouldn't be a problem michael@0: // in practice due to the layout of MDImageDebugMisc. michael@0: uint16_t* data16 = reinterpret_cast(&(misc_record->data)); michael@0: unsigned int dataBytes = module_.misc_record.data_size - michael@0: MDImageDebugMisc_minsize; michael@0: unsigned int dataLength = dataBytes / 2; michael@0: for (unsigned int characterIndex = 0; michael@0: characterIndex < dataLength; michael@0: ++characterIndex) { michael@0: Swap(&data16[characterIndex]); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (module_.misc_record.data_size != misc_record->length) { michael@0: BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record data " michael@0: "size mismatch, " << module_.misc_record.data_size << michael@0: " != " << misc_record->length; michael@0: return NULL; michael@0: } michael@0: michael@0: // Store the vector type because that's how storage was allocated, but michael@0: // return it casted to MDImageDebugMisc*. michael@0: misc_record_ = misc_record_mem.release(); michael@0: } michael@0: michael@0: if (size) michael@0: *size = module_.misc_record.data_size; michael@0: michael@0: return reinterpret_cast(&(*misc_record_)[0]); michael@0: } michael@0: michael@0: michael@0: void MinidumpModule::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpModule cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDRawModule\n"); michael@0: printf(" base_of_image = 0x%" PRIx64 "\n", michael@0: module_.base_of_image); michael@0: printf(" size_of_image = 0x%x\n", michael@0: module_.size_of_image); michael@0: printf(" checksum = 0x%x\n", michael@0: module_.checksum); michael@0: printf(" time_date_stamp = 0x%x\n", michael@0: module_.time_date_stamp); michael@0: printf(" module_name_rva = 0x%x\n", michael@0: module_.module_name_rva); michael@0: printf(" version_info.signature = 0x%x\n", michael@0: module_.version_info.signature); michael@0: printf(" version_info.struct_version = 0x%x\n", michael@0: module_.version_info.struct_version); michael@0: printf(" version_info.file_version = 0x%x:0x%x\n", michael@0: module_.version_info.file_version_hi, michael@0: module_.version_info.file_version_lo); michael@0: printf(" version_info.product_version = 0x%x:0x%x\n", michael@0: module_.version_info.product_version_hi, michael@0: module_.version_info.product_version_lo); michael@0: printf(" version_info.file_flags_mask = 0x%x\n", michael@0: module_.version_info.file_flags_mask); michael@0: printf(" version_info.file_flags = 0x%x\n", michael@0: module_.version_info.file_flags); michael@0: printf(" version_info.file_os = 0x%x\n", michael@0: module_.version_info.file_os); michael@0: printf(" version_info.file_type = 0x%x\n", michael@0: module_.version_info.file_type); michael@0: printf(" version_info.file_subtype = 0x%x\n", michael@0: module_.version_info.file_subtype); michael@0: printf(" version_info.file_date = 0x%x:0x%x\n", michael@0: module_.version_info.file_date_hi, michael@0: module_.version_info.file_date_lo); michael@0: printf(" cv_record.data_size = %d\n", michael@0: module_.cv_record.data_size); michael@0: printf(" cv_record.rva = 0x%x\n", michael@0: module_.cv_record.rva); michael@0: printf(" misc_record.data_size = %d\n", michael@0: module_.misc_record.data_size); michael@0: printf(" misc_record.rva = 0x%x\n", michael@0: module_.misc_record.rva); michael@0: michael@0: printf(" (code_file) = \"%s\"\n", code_file().c_str()); michael@0: printf(" (code_identifier) = \"%s\"\n", michael@0: code_identifier().c_str()); michael@0: michael@0: uint32_t cv_record_size; michael@0: const uint8_t *cv_record = GetCVRecord(&cv_record_size); michael@0: if (cv_record) { michael@0: if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { michael@0: const MDCVInfoPDB70* cv_record_70 = michael@0: reinterpret_cast(cv_record); michael@0: assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); michael@0: michael@0: printf(" (cv_record).cv_signature = 0x%x\n", michael@0: cv_record_70->cv_signature); michael@0: printf(" (cv_record).signature = %08x-%04x-%04x-%02x%02x-", michael@0: cv_record_70->signature.data1, michael@0: cv_record_70->signature.data2, michael@0: cv_record_70->signature.data3, michael@0: cv_record_70->signature.data4[0], michael@0: cv_record_70->signature.data4[1]); michael@0: for (unsigned int guidIndex = 2; michael@0: guidIndex < 8; michael@0: ++guidIndex) { michael@0: printf("%02x", cv_record_70->signature.data4[guidIndex]); michael@0: } michael@0: printf("\n"); michael@0: printf(" (cv_record).age = %d\n", michael@0: cv_record_70->age); michael@0: printf(" (cv_record).pdb_file_name = \"%s\"\n", michael@0: cv_record_70->pdb_file_name); michael@0: } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { michael@0: const MDCVInfoPDB20* cv_record_20 = michael@0: reinterpret_cast(cv_record); michael@0: assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); michael@0: michael@0: printf(" (cv_record).cv_header.signature = 0x%x\n", michael@0: cv_record_20->cv_header.signature); michael@0: printf(" (cv_record).cv_header.offset = 0x%x\n", michael@0: cv_record_20->cv_header.offset); michael@0: printf(" (cv_record).signature = 0x%x\n", michael@0: cv_record_20->signature); michael@0: printf(" (cv_record).age = %d\n", michael@0: cv_record_20->age); michael@0: printf(" (cv_record).pdb_file_name = \"%s\"\n", michael@0: cv_record_20->pdb_file_name); michael@0: } else { michael@0: printf(" (cv_record) = "); michael@0: for (unsigned int cv_byte_index = 0; michael@0: cv_byte_index < cv_record_size; michael@0: ++cv_byte_index) { michael@0: printf("%02x", cv_record[cv_byte_index]); michael@0: } michael@0: printf("\n"); michael@0: } michael@0: } else { michael@0: printf(" (cv_record) = (null)\n"); michael@0: } michael@0: michael@0: const MDImageDebugMisc* misc_record = GetMiscRecord(NULL); michael@0: if (misc_record) { michael@0: printf(" (misc_record).data_type = 0x%x\n", michael@0: misc_record->data_type); michael@0: printf(" (misc_record).length = 0x%x\n", michael@0: misc_record->length); michael@0: printf(" (misc_record).unicode = %d\n", michael@0: misc_record->unicode); michael@0: // Don't bother printing the UTF-16, we don't really even expect to ever michael@0: // see this misc_record anyway. michael@0: if (misc_record->unicode) michael@0: printf(" (misc_record).data = \"%s\"\n", michael@0: misc_record->data); michael@0: else michael@0: printf(" (misc_record).data = (UTF-16)\n"); michael@0: } else { michael@0: printf(" (misc_record) = (null)\n"); michael@0: } michael@0: michael@0: printf(" (debug_file) = \"%s\"\n", debug_file().c_str()); michael@0: printf(" (debug_identifier) = \"%s\"\n", michael@0: debug_identifier().c_str()); michael@0: printf(" (version) = \"%s\"\n", version().c_str()); michael@0: printf("\n"); michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpModuleList michael@0: // michael@0: michael@0: michael@0: uint32_t MinidumpModuleList::max_modules_ = 1024; michael@0: michael@0: michael@0: MinidumpModuleList::MinidumpModuleList(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: range_map_(new RangeMap()), michael@0: modules_(NULL), michael@0: module_count_(0) { michael@0: } michael@0: michael@0: michael@0: MinidumpModuleList::~MinidumpModuleList() { michael@0: delete range_map_; michael@0: delete modules_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpModuleList::Read(uint32_t expected_size) { michael@0: // Invalidate cached data. michael@0: range_map_->Clear(); michael@0: delete modules_; michael@0: modules_ = NULL; michael@0: module_count_ = 0; michael@0: michael@0: valid_ = false; michael@0: michael@0: uint32_t module_count; michael@0: if (expected_size < sizeof(module_count)) { michael@0: BPLOG(ERROR) << "MinidumpModuleList count size mismatch, " << michael@0: expected_size << " < " << sizeof(module_count); michael@0: return false; michael@0: } michael@0: if (!minidump_->ReadBytes(&module_count, sizeof(module_count))) { michael@0: BPLOG(ERROR) << "MinidumpModuleList could not read module count"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) michael@0: Swap(&module_count); michael@0: michael@0: if (module_count > numeric_limits::max() / MD_MODULE_SIZE) { michael@0: BPLOG(ERROR) << "MinidumpModuleList module count " << module_count << michael@0: " would cause multiplication overflow"; michael@0: return false; michael@0: } michael@0: michael@0: if (expected_size != sizeof(module_count) + michael@0: module_count * MD_MODULE_SIZE) { michael@0: // may be padded with 4 bytes on 64bit ABIs for alignment michael@0: if (expected_size == sizeof(module_count) + 4 + michael@0: module_count * MD_MODULE_SIZE) { michael@0: uint32_t useless; michael@0: if (!minidump_->ReadBytes(&useless, 4)) { michael@0: BPLOG(ERROR) << "MinidumpModuleList cannot read modulelist padded bytes"; michael@0: return false; michael@0: } michael@0: } else { michael@0: BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size << michael@0: " != " << sizeof(module_count) + michael@0: module_count * MD_MODULE_SIZE; michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (module_count > max_modules_) { michael@0: BPLOG(ERROR) << "MinidumpModuleList count " << module_count_ << michael@0: " exceeds maximum " << max_modules_; michael@0: return false; michael@0: } michael@0: michael@0: if (module_count != 0) { michael@0: scoped_ptr modules( michael@0: new MinidumpModules(module_count, MinidumpModule(minidump_))); michael@0: michael@0: for (unsigned int module_index = 0; michael@0: module_index < module_count; michael@0: ++module_index) { michael@0: MinidumpModule* module = &(*modules)[module_index]; michael@0: michael@0: // Assume that the file offset is correct after the last read. michael@0: if (!module->Read()) { michael@0: BPLOG(ERROR) << "MinidumpModuleList could not read module " << michael@0: module_index << "/" << module_count; michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Loop through the module list once more to read additional data and michael@0: // build the range map. This is done in a second pass because michael@0: // MinidumpModule::ReadAuxiliaryData seeks around, and if it were michael@0: // included in the loop above, additional seeks would be needed where michael@0: // none are now to read contiguous data. michael@0: for (unsigned int module_index = 0; michael@0: module_index < module_count; michael@0: ++module_index) { michael@0: MinidumpModule* module = &(*modules)[module_index]; michael@0: michael@0: // ReadAuxiliaryData fails if any data that the module indicates should michael@0: // exist is missing, but we treat some such cases as valid anyway. See michael@0: // issue #222: if a debugging record is of a format that's too large to michael@0: // handle, it shouldn't render the entire dump invalid. Check module michael@0: // validity before giving up. michael@0: if (!module->ReadAuxiliaryData() && !module->valid()) { michael@0: BPLOG(ERROR) << "MinidumpModuleList could not read required module " michael@0: "auxiliary data for module " << michael@0: module_index << "/" << module_count; michael@0: return false; michael@0: } michael@0: michael@0: // It is safe to use module->code_file() after successfully calling michael@0: // module->ReadAuxiliaryData or noting that the module is valid. michael@0: michael@0: uint64_t base_address = module->base_address(); michael@0: uint64_t module_size = module->size(); michael@0: if (base_address == static_cast(-1)) { michael@0: BPLOG(ERROR) << "MinidumpModuleList found bad base address " michael@0: "for module " << module_index << "/" << module_count << michael@0: ", " << module->code_file(); michael@0: return false; michael@0: } michael@0: michael@0: if (!range_map_->StoreRange(base_address, module_size, module_index)) { michael@0: BPLOG(ERROR) << "MinidumpModuleList could not store module " << michael@0: module_index << "/" << module_count << ", " << michael@0: module->code_file() << ", " << michael@0: HexString(base_address) << "+" << michael@0: HexString(module_size); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: modules_ = modules.release(); michael@0: } michael@0: michael@0: module_count_ = module_count; michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: const MinidumpModule* MinidumpModuleList::GetModuleForAddress( michael@0: uint64_t address) const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleForAddress"; michael@0: return NULL; michael@0: } michael@0: michael@0: unsigned int module_index; michael@0: if (!range_map_->RetrieveRange(address, &module_index, NULL, NULL)) { michael@0: BPLOG(INFO) << "MinidumpModuleList has no module at " << michael@0: HexString(address); michael@0: return NULL; michael@0: } michael@0: michael@0: return GetModuleAtIndex(module_index); michael@0: } michael@0: michael@0: michael@0: const MinidumpModule* MinidumpModuleList::GetMainModule() const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModuleList for GetMainModule"; michael@0: return NULL; michael@0: } michael@0: michael@0: // The main code module is the first one present in a minidump file's michael@0: // MDRawModuleList. michael@0: return GetModuleAtIndex(0); michael@0: } michael@0: michael@0: michael@0: const MinidumpModule* MinidumpModuleList::GetModuleAtSequence( michael@0: unsigned int sequence) const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtSequence"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (sequence >= module_count_) { michael@0: BPLOG(ERROR) << "MinidumpModuleList sequence out of range: " << michael@0: sequence << "/" << module_count_; michael@0: return NULL; michael@0: } michael@0: michael@0: unsigned int module_index; michael@0: if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index, NULL, NULL)) { michael@0: BPLOG(ERROR) << "MinidumpModuleList has no module at sequence " << sequence; michael@0: return NULL; michael@0: } michael@0: michael@0: return GetModuleAtIndex(module_index); michael@0: } michael@0: michael@0: michael@0: const MinidumpModule* MinidumpModuleList::GetModuleAtIndex( michael@0: unsigned int index) const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtIndex"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (index >= module_count_) { michael@0: BPLOG(ERROR) << "MinidumpModuleList index out of range: " << michael@0: index << "/" << module_count_; michael@0: return NULL; michael@0: } michael@0: michael@0: return &(*modules_)[index]; michael@0: } michael@0: michael@0: michael@0: const CodeModules* MinidumpModuleList::Copy() const { michael@0: return new BasicCodeModules(this); michael@0: } michael@0: michael@0: michael@0: void MinidumpModuleList::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpModuleList cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MinidumpModuleList\n"); michael@0: printf(" module_count = %d\n", module_count_); michael@0: printf("\n"); michael@0: michael@0: for (unsigned int module_index = 0; michael@0: module_index < module_count_; michael@0: ++module_index) { michael@0: printf("module[%d]\n", module_index); michael@0: michael@0: (*modules_)[module_index].Print(); michael@0: } michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpMemoryList michael@0: // michael@0: michael@0: michael@0: uint32_t MinidumpMemoryList::max_regions_ = 4096; michael@0: michael@0: michael@0: MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: range_map_(new RangeMap()), michael@0: descriptors_(NULL), michael@0: regions_(NULL), michael@0: region_count_(0) { michael@0: } michael@0: michael@0: michael@0: MinidumpMemoryList::~MinidumpMemoryList() { michael@0: delete range_map_; michael@0: delete descriptors_; michael@0: delete regions_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryList::Read(uint32_t expected_size) { michael@0: // Invalidate cached data. michael@0: delete descriptors_; michael@0: descriptors_ = NULL; michael@0: delete regions_; michael@0: regions_ = NULL; michael@0: range_map_->Clear(); michael@0: region_count_ = 0; michael@0: michael@0: valid_ = false; michael@0: michael@0: uint32_t region_count; michael@0: if (expected_size < sizeof(region_count)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList count size mismatch, " << michael@0: expected_size << " < " << sizeof(region_count); michael@0: return false; michael@0: } michael@0: if (!minidump_->ReadBytes(®ion_count, sizeof(region_count))) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList could not read memory region count"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) michael@0: Swap(®ion_count); michael@0: michael@0: if (region_count > michael@0: numeric_limits::max() / sizeof(MDMemoryDescriptor)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList region count " << region_count << michael@0: " would cause multiplication overflow"; michael@0: return false; michael@0: } michael@0: michael@0: if (expected_size != sizeof(region_count) + michael@0: region_count * sizeof(MDMemoryDescriptor)) { michael@0: // may be padded with 4 bytes on 64bit ABIs for alignment michael@0: if (expected_size == sizeof(region_count) + 4 + michael@0: region_count * sizeof(MDMemoryDescriptor)) { michael@0: uint32_t useless; michael@0: if (!minidump_->ReadBytes(&useless, 4)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded bytes"; michael@0: return false; michael@0: } michael@0: } else { michael@0: BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size << michael@0: " != " << sizeof(region_count) + michael@0: region_count * sizeof(MDMemoryDescriptor); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (region_count > max_regions_) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList count " << region_count << michael@0: " exceeds maximum " << max_regions_; michael@0: return false; michael@0: } michael@0: michael@0: if (region_count != 0) { michael@0: scoped_ptr descriptors( michael@0: new MemoryDescriptors(region_count)); michael@0: michael@0: // Read the entire array in one fell swoop, instead of reading one entry michael@0: // at a time in the loop. michael@0: if (!minidump_->ReadBytes(&(*descriptors)[0], michael@0: sizeof(MDMemoryDescriptor) * region_count)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList could not read memory region list"; michael@0: return false; michael@0: } michael@0: michael@0: scoped_ptr regions( michael@0: new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_))); michael@0: michael@0: for (unsigned int region_index = 0; michael@0: region_index < region_count; michael@0: ++region_index) { michael@0: MDMemoryDescriptor* descriptor = &(*descriptors)[region_index]; michael@0: michael@0: if (minidump_->swap()) michael@0: Swap(descriptor); michael@0: michael@0: uint64_t base_address = descriptor->start_of_memory_range; michael@0: uint32_t region_size = descriptor->memory.data_size; michael@0: michael@0: // Check for base + size overflow or undersize. michael@0: if (region_size == 0 || michael@0: region_size > numeric_limits::max() - base_address) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList has a memory region problem, " << michael@0: " region " << region_index << "/" << region_count << michael@0: ", " << HexString(base_address) << "+" << michael@0: HexString(region_size); michael@0: return false; michael@0: } michael@0: michael@0: if (!range_map_->StoreRange(base_address, region_size, region_index)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList could not store memory region " << michael@0: region_index << "/" << region_count << ", " << michael@0: HexString(base_address) << "+" << michael@0: HexString(region_size); michael@0: return false; michael@0: } michael@0: michael@0: (*regions)[region_index].SetDescriptor(descriptor); michael@0: } michael@0: michael@0: descriptors_ = descriptors.release(); michael@0: regions_ = regions.release(); michael@0: } michael@0: michael@0: region_count_ = region_count; michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionAtIndex( michael@0: unsigned int index) { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionAtIndex"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (index >= region_count_) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList index out of range: " << michael@0: index << "/" << region_count_; michael@0: return NULL; michael@0: } michael@0: michael@0: return &(*regions_)[index]; michael@0: } michael@0: michael@0: michael@0: MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress( michael@0: uint64_t address) { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionForAddress"; michael@0: return NULL; michael@0: } michael@0: michael@0: unsigned int region_index; michael@0: if (!range_map_->RetrieveRange(address, ®ion_index, NULL, NULL)) { michael@0: BPLOG(INFO) << "MinidumpMemoryList has no memory region at " << michael@0: HexString(address); michael@0: return NULL; michael@0: } michael@0: michael@0: return GetMemoryRegionAtIndex(region_index); michael@0: } michael@0: michael@0: michael@0: void MinidumpMemoryList::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpMemoryList cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MinidumpMemoryList\n"); michael@0: printf(" region_count = %d\n", region_count_); michael@0: printf("\n"); michael@0: michael@0: for (unsigned int region_index = 0; michael@0: region_index < region_count_; michael@0: ++region_index) { michael@0: MDMemoryDescriptor* descriptor = &(*descriptors_)[region_index]; michael@0: printf("region[%d]\n", region_index); michael@0: printf("MDMemoryDescriptor\n"); michael@0: printf(" start_of_memory_range = 0x%" PRIx64 "\n", michael@0: descriptor->start_of_memory_range); michael@0: printf(" memory.data_size = 0x%x\n", descriptor->memory.data_size); michael@0: printf(" memory.rva = 0x%x\n", descriptor->memory.rva); michael@0: MinidumpMemoryRegion* region = GetMemoryRegionAtIndex(region_index); michael@0: if (region) { michael@0: printf("Memory\n"); michael@0: region->Print(); michael@0: } else { michael@0: printf("No memory\n"); michael@0: } michael@0: printf("\n"); michael@0: } michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpException michael@0: // michael@0: michael@0: michael@0: MinidumpException::MinidumpException(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: exception_(), michael@0: context_(NULL) { michael@0: } michael@0: michael@0: michael@0: MinidumpException::~MinidumpException() { michael@0: delete context_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpException::Read(uint32_t expected_size) { michael@0: // Invalidate cached data. michael@0: delete context_; michael@0: context_ = NULL; michael@0: michael@0: valid_ = false; michael@0: michael@0: if (expected_size != sizeof(exception_)) { michael@0: BPLOG(ERROR) << "MinidumpException size mismatch, " << expected_size << michael@0: " != " << sizeof(exception_); michael@0: return false; michael@0: } michael@0: michael@0: if (!minidump_->ReadBytes(&exception_, sizeof(exception_))) { michael@0: BPLOG(ERROR) << "MinidumpException cannot read exception"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&exception_.thread_id); michael@0: // exception_.__align is for alignment only and does not need to be michael@0: // swapped. michael@0: Swap(&exception_.exception_record.exception_code); michael@0: Swap(&exception_.exception_record.exception_flags); michael@0: Swap(&exception_.exception_record.exception_record); michael@0: Swap(&exception_.exception_record.exception_address); michael@0: Swap(&exception_.exception_record.number_parameters); michael@0: // exception_.exception_record.__align is for alignment only and does not michael@0: // need to be swapped. michael@0: for (unsigned int parameter_index = 0; michael@0: parameter_index < MD_EXCEPTION_MAXIMUM_PARAMETERS; michael@0: ++parameter_index) { michael@0: Swap(&exception_.exception_record.exception_information[parameter_index]); michael@0: } michael@0: Swap(&exception_.thread_context); michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool MinidumpException::GetThreadID(uint32_t *thread_id) const { michael@0: BPLOG_IF(ERROR, !thread_id) << "MinidumpException::GetThreadID requires " michael@0: "|thread_id|"; michael@0: assert(thread_id); michael@0: *thread_id = 0; michael@0: michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpException for GetThreadID"; michael@0: return false; michael@0: } michael@0: michael@0: *thread_id = exception_.thread_id; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: MinidumpContext* MinidumpException::GetContext() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpException for GetContext"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (!context_) { michael@0: if (!minidump_->SeekSet(exception_.thread_context.rva)) { michael@0: BPLOG(ERROR) << "MinidumpException cannot seek to context"; michael@0: return NULL; michael@0: } michael@0: michael@0: scoped_ptr context(new MinidumpContext(minidump_)); michael@0: michael@0: // Don't log as an error if we can still fall back on the thread's context michael@0: // (which must be possible if we got this far.) michael@0: if (!context->Read(exception_.thread_context.data_size)) { michael@0: BPLOG(INFO) << "MinidumpException cannot read context"; michael@0: return NULL; michael@0: } michael@0: michael@0: context_ = context.release(); michael@0: } michael@0: michael@0: return context_; michael@0: } michael@0: michael@0: michael@0: void MinidumpException::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpException cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDException\n"); michael@0: printf(" thread_id = 0x%x\n", michael@0: exception_.thread_id); michael@0: printf(" exception_record.exception_code = 0x%x\n", michael@0: exception_.exception_record.exception_code); michael@0: printf(" exception_record.exception_flags = 0x%x\n", michael@0: exception_.exception_record.exception_flags); michael@0: printf(" exception_record.exception_record = 0x%" PRIx64 "\n", michael@0: exception_.exception_record.exception_record); michael@0: printf(" exception_record.exception_address = 0x%" PRIx64 "\n", michael@0: exception_.exception_record.exception_address); michael@0: printf(" exception_record.number_parameters = %d\n", michael@0: exception_.exception_record.number_parameters); michael@0: for (unsigned int parameterIndex = 0; michael@0: parameterIndex < exception_.exception_record.number_parameters; michael@0: ++parameterIndex) { michael@0: printf(" exception_record.exception_information[%2d] = 0x%" PRIx64 "\n", michael@0: parameterIndex, michael@0: exception_.exception_record.exception_information[parameterIndex]); michael@0: } michael@0: printf(" thread_context.data_size = %d\n", michael@0: exception_.thread_context.data_size); michael@0: printf(" thread_context.rva = 0x%x\n", michael@0: exception_.thread_context.rva); michael@0: MinidumpContext* context = GetContext(); michael@0: if (context) { michael@0: printf("\n"); michael@0: context->Print(); michael@0: } else { michael@0: printf(" (no context)\n"); michael@0: printf("\n"); michael@0: } michael@0: } michael@0: michael@0: // michael@0: // MinidumpAssertion michael@0: // michael@0: michael@0: michael@0: MinidumpAssertion::MinidumpAssertion(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: assertion_(), michael@0: expression_(), michael@0: function_(), michael@0: file_() { michael@0: } michael@0: michael@0: michael@0: MinidumpAssertion::~MinidumpAssertion() { michael@0: } michael@0: michael@0: michael@0: bool MinidumpAssertion::Read(uint32_t expected_size) { michael@0: // Invalidate cached data. michael@0: valid_ = false; michael@0: michael@0: if (expected_size != sizeof(assertion_)) { michael@0: BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size << michael@0: " != " << sizeof(assertion_); michael@0: return false; michael@0: } michael@0: michael@0: if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) { michael@0: BPLOG(ERROR) << "MinidumpAssertion cannot read assertion"; michael@0: return false; michael@0: } michael@0: michael@0: // Each of {expression, function, file} is a UTF-16 string, michael@0: // we'll convert them to UTF-8 for ease of use. michael@0: // expression michael@0: // Since we don't have an explicit byte length for each string, michael@0: // we use UTF16codeunits to calculate word length, then derive byte michael@0: // length from that. michael@0: uint32_t word_length = UTF16codeunits(assertion_.expression, michael@0: sizeof(assertion_.expression)); michael@0: if (word_length > 0) { michael@0: uint32_t byte_length = word_length * 2; michael@0: vector expression_utf16(word_length); michael@0: memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length); michael@0: michael@0: scoped_ptr new_expression(UTF16ToUTF8(expression_utf16, michael@0: minidump_->swap())); michael@0: if (new_expression.get()) michael@0: expression_ = *new_expression; michael@0: } michael@0: michael@0: // assertion michael@0: word_length = UTF16codeunits(assertion_.function, michael@0: sizeof(assertion_.function)); michael@0: if (word_length) { michael@0: uint32_t byte_length = word_length * 2; michael@0: vector function_utf16(word_length); michael@0: memcpy(&function_utf16[0], &assertion_.function[0], byte_length); michael@0: scoped_ptr new_function(UTF16ToUTF8(function_utf16, michael@0: minidump_->swap())); michael@0: if (new_function.get()) michael@0: function_ = *new_function; michael@0: } michael@0: michael@0: // file michael@0: word_length = UTF16codeunits(assertion_.file, michael@0: sizeof(assertion_.file)); michael@0: if (word_length > 0) { michael@0: uint32_t byte_length = word_length * 2; michael@0: vector file_utf16(word_length); michael@0: memcpy(&file_utf16[0], &assertion_.file[0], byte_length); michael@0: scoped_ptr new_file(UTF16ToUTF8(file_utf16, michael@0: minidump_->swap())); michael@0: if (new_file.get()) michael@0: file_ = *new_file; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&assertion_.line); michael@0: Swap(&assertion_.type); michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: void MinidumpAssertion::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDAssertion\n"); michael@0: printf(" expression = %s\n", michael@0: expression_.c_str()); michael@0: printf(" function = %s\n", michael@0: function_.c_str()); michael@0: printf(" file = %s\n", michael@0: file_.c_str()); michael@0: printf(" line = %u\n", michael@0: assertion_.line); michael@0: printf(" type = %u\n", michael@0: assertion_.type); michael@0: printf("\n"); michael@0: } michael@0: michael@0: // michael@0: // MinidumpSystemInfo michael@0: // michael@0: michael@0: michael@0: MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: system_info_(), michael@0: csd_version_(NULL), michael@0: cpu_vendor_(NULL) { michael@0: } michael@0: michael@0: michael@0: MinidumpSystemInfo::~MinidumpSystemInfo() { michael@0: delete csd_version_; michael@0: delete cpu_vendor_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpSystemInfo::Read(uint32_t expected_size) { michael@0: // Invalidate cached data. michael@0: delete csd_version_; michael@0: csd_version_ = NULL; michael@0: delete cpu_vendor_; michael@0: cpu_vendor_ = NULL; michael@0: michael@0: valid_ = false; michael@0: michael@0: if (expected_size != sizeof(system_info_)) { michael@0: BPLOG(ERROR) << "MinidumpSystemInfo size mismatch, " << expected_size << michael@0: " != " << sizeof(system_info_); michael@0: return false; michael@0: } michael@0: michael@0: if (!minidump_->ReadBytes(&system_info_, sizeof(system_info_))) { michael@0: BPLOG(ERROR) << "MinidumpSystemInfo cannot read system info"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&system_info_.processor_architecture); michael@0: Swap(&system_info_.processor_level); michael@0: Swap(&system_info_.processor_revision); michael@0: // number_of_processors and product_type are 8-bit quantities and need no michael@0: // swapping. michael@0: Swap(&system_info_.major_version); michael@0: Swap(&system_info_.minor_version); michael@0: Swap(&system_info_.build_number); michael@0: Swap(&system_info_.platform_id); michael@0: Swap(&system_info_.csd_version_rva); michael@0: Swap(&system_info_.suite_mask); michael@0: // Don't swap the reserved2 field because its contents are unknown. michael@0: michael@0: if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || michael@0: system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) { michael@0: for (unsigned int i = 0; i < 3; ++i) michael@0: Swap(&system_info_.cpu.x86_cpu_info.vendor_id[i]); michael@0: Swap(&system_info_.cpu.x86_cpu_info.version_information); michael@0: Swap(&system_info_.cpu.x86_cpu_info.feature_information); michael@0: Swap(&system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); michael@0: } else { michael@0: for (unsigned int i = 0; i < 2; ++i) michael@0: Swap(&system_info_.cpu.other_cpu_info.processor_features[i]); michael@0: } michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: string MinidumpSystemInfo::GetOS() { michael@0: string os; michael@0: michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetOS"; michael@0: return os; michael@0: } michael@0: michael@0: switch (system_info_.platform_id) { michael@0: case MD_OS_WIN32_NT: michael@0: case MD_OS_WIN32_WINDOWS: michael@0: os = "windows"; michael@0: break; michael@0: michael@0: case MD_OS_MAC_OS_X: michael@0: os = "mac"; michael@0: break; michael@0: michael@0: case MD_OS_IOS: michael@0: os = "ios"; michael@0: break; michael@0: michael@0: case MD_OS_LINUX: michael@0: os = "linux"; michael@0: break; michael@0: michael@0: case MD_OS_SOLARIS: michael@0: os = "solaris"; michael@0: break; michael@0: michael@0: case MD_OS_ANDROID: michael@0: os = "android"; michael@0: break; michael@0: michael@0: default: michael@0: BPLOG(ERROR) << "MinidumpSystemInfo unknown OS for platform " << michael@0: HexString(system_info_.platform_id); michael@0: break; michael@0: } michael@0: michael@0: return os; michael@0: } michael@0: michael@0: michael@0: string MinidumpSystemInfo::GetCPU() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPU"; michael@0: return ""; michael@0: } michael@0: michael@0: string cpu; michael@0: michael@0: switch (system_info_.processor_architecture) { michael@0: case MD_CPU_ARCHITECTURE_X86: michael@0: case MD_CPU_ARCHITECTURE_X86_WIN64: michael@0: cpu = "x86"; michael@0: break; michael@0: michael@0: case MD_CPU_ARCHITECTURE_AMD64: michael@0: cpu = "x86-64"; michael@0: break; michael@0: michael@0: case MD_CPU_ARCHITECTURE_PPC: michael@0: cpu = "ppc"; michael@0: break; michael@0: michael@0: case MD_CPU_ARCHITECTURE_SPARC: michael@0: cpu = "sparc"; michael@0: break; michael@0: michael@0: case MD_CPU_ARCHITECTURE_ARM: michael@0: cpu = "arm"; michael@0: break; michael@0: michael@0: default: michael@0: BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " << michael@0: HexString(system_info_.processor_architecture); michael@0: break; michael@0: } michael@0: michael@0: return cpu; michael@0: } michael@0: michael@0: michael@0: const string* MinidumpSystemInfo::GetCSDVersion() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCSDVersion"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (!csd_version_) michael@0: csd_version_ = minidump_->ReadString(system_info_.csd_version_rva); michael@0: michael@0: BPLOG_IF(ERROR, !csd_version_) << "MinidumpSystemInfo could not read " michael@0: "CSD version"; michael@0: michael@0: return csd_version_; michael@0: } michael@0: michael@0: michael@0: const string* MinidumpSystemInfo::GetCPUVendor() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPUVendor"; michael@0: return NULL; michael@0: } michael@0: michael@0: // CPU vendor information can only be determined from x86 minidumps. michael@0: if (!cpu_vendor_ && michael@0: (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || michael@0: system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64)) { michael@0: char cpu_vendor_string[13]; michael@0: snprintf(cpu_vendor_string, sizeof(cpu_vendor_string), michael@0: "%c%c%c%c%c%c%c%c%c%c%c%c", michael@0: system_info_.cpu.x86_cpu_info.vendor_id[0] & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 8) & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 16) & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 24) & 0xff, michael@0: system_info_.cpu.x86_cpu_info.vendor_id[1] & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 8) & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 16) & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 24) & 0xff, michael@0: system_info_.cpu.x86_cpu_info.vendor_id[2] & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 8) & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 16) & 0xff, michael@0: (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 24) & 0xff); michael@0: cpu_vendor_ = new string(cpu_vendor_string); michael@0: } michael@0: michael@0: return cpu_vendor_; michael@0: } michael@0: michael@0: michael@0: void MinidumpSystemInfo::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpSystemInfo cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDRawSystemInfo\n"); michael@0: printf(" processor_architecture = %d\n", michael@0: system_info_.processor_architecture); michael@0: printf(" processor_level = %d\n", michael@0: system_info_.processor_level); michael@0: printf(" processor_revision = 0x%x\n", michael@0: system_info_.processor_revision); michael@0: printf(" number_of_processors = %d\n", michael@0: system_info_.number_of_processors); michael@0: printf(" product_type = %d\n", michael@0: system_info_.product_type); michael@0: printf(" major_version = %d\n", michael@0: system_info_.major_version); michael@0: printf(" minor_version = %d\n", michael@0: system_info_.minor_version); michael@0: printf(" build_number = %d\n", michael@0: system_info_.build_number); michael@0: printf(" platform_id = %d\n", michael@0: system_info_.platform_id); michael@0: printf(" csd_version_rva = 0x%x\n", michael@0: system_info_.csd_version_rva); michael@0: printf(" suite_mask = 0x%x\n", michael@0: system_info_.suite_mask); michael@0: for (unsigned int i = 0; i < 3; ++i) { michael@0: printf(" cpu.x86_cpu_info.vendor_id[%d] = 0x%x\n", michael@0: i, system_info_.cpu.x86_cpu_info.vendor_id[i]); michael@0: } michael@0: printf(" cpu.x86_cpu_info.version_information = 0x%x\n", michael@0: system_info_.cpu.x86_cpu_info.version_information); michael@0: printf(" cpu.x86_cpu_info.feature_information = 0x%x\n", michael@0: system_info_.cpu.x86_cpu_info.feature_information); michael@0: printf(" cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n", michael@0: system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); michael@0: const string* csd_version = GetCSDVersion(); michael@0: if (csd_version) { michael@0: printf(" (csd_version) = \"%s\"\n", michael@0: csd_version->c_str()); michael@0: } else { michael@0: printf(" (csd_version) = (null)\n"); michael@0: } michael@0: const string* cpu_vendor = GetCPUVendor(); michael@0: if (cpu_vendor) { michael@0: printf(" (cpu_vendor) = \"%s\"\n", michael@0: cpu_vendor->c_str()); michael@0: } else { michael@0: printf(" (cpu_vendor) = (null)\n"); michael@0: } michael@0: printf("\n"); michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpMiscInfo michael@0: // michael@0: michael@0: michael@0: MinidumpMiscInfo::MinidumpMiscInfo(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: misc_info_() { michael@0: } michael@0: michael@0: michael@0: bool MinidumpMiscInfo::Read(uint32_t expected_size) { michael@0: valid_ = false; michael@0: michael@0: if (expected_size != MD_MISCINFO_SIZE && michael@0: expected_size != MD_MISCINFO2_SIZE) { michael@0: BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size << michael@0: " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE << michael@0: ")"; michael@0: return false; michael@0: } michael@0: michael@0: if (!minidump_->ReadBytes(&misc_info_, expected_size)) { michael@0: BPLOG(ERROR) << "MinidumpMiscInfo cannot read miscellaneous info"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&misc_info_.size_of_info); michael@0: Swap(&misc_info_.flags1); michael@0: Swap(&misc_info_.process_id); michael@0: Swap(&misc_info_.process_create_time); michael@0: Swap(&misc_info_.process_user_time); michael@0: Swap(&misc_info_.process_kernel_time); michael@0: if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { michael@0: Swap(&misc_info_.processor_max_mhz); michael@0: Swap(&misc_info_.processor_current_mhz); michael@0: Swap(&misc_info_.processor_mhz_limit); michael@0: Swap(&misc_info_.processor_max_idle_state); michael@0: Swap(&misc_info_.processor_current_idle_state); michael@0: } michael@0: } michael@0: michael@0: if (expected_size != misc_info_.size_of_info) { michael@0: BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << michael@0: expected_size << " != " << misc_info_.size_of_info; michael@0: return false; michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: void MinidumpMiscInfo::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpMiscInfo cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDRawMiscInfo\n"); michael@0: printf(" size_of_info = %d\n", misc_info_.size_of_info); michael@0: printf(" flags1 = 0x%x\n", misc_info_.flags1); michael@0: printf(" process_id = 0x%x\n", misc_info_.process_id); michael@0: printf(" process_create_time = 0x%x\n", michael@0: misc_info_.process_create_time); michael@0: printf(" process_user_time = 0x%x\n", michael@0: misc_info_.process_user_time); michael@0: printf(" process_kernel_time = 0x%x\n", michael@0: misc_info_.process_kernel_time); michael@0: if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { michael@0: printf(" processor_max_mhz = %d\n", michael@0: misc_info_.processor_max_mhz); michael@0: printf(" processor_current_mhz = %d\n", michael@0: misc_info_.processor_current_mhz); michael@0: printf(" processor_mhz_limit = %d\n", michael@0: misc_info_.processor_mhz_limit); michael@0: printf(" processor_max_idle_state = 0x%x\n", michael@0: misc_info_.processor_max_idle_state); michael@0: printf(" processor_current_idle_state = 0x%x\n", michael@0: misc_info_.processor_current_idle_state); michael@0: } michael@0: printf("\n"); michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpBreakpadInfo michael@0: // michael@0: michael@0: michael@0: MinidumpBreakpadInfo::MinidumpBreakpadInfo(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: breakpad_info_() { michael@0: } michael@0: michael@0: michael@0: bool MinidumpBreakpadInfo::Read(uint32_t expected_size) { michael@0: valid_ = false; michael@0: michael@0: if (expected_size != sizeof(breakpad_info_)) { michael@0: BPLOG(ERROR) << "MinidumpBreakpadInfo size mismatch, " << expected_size << michael@0: " != " << sizeof(breakpad_info_); michael@0: return false; michael@0: } michael@0: michael@0: if (!minidump_->ReadBytes(&breakpad_info_, sizeof(breakpad_info_))) { michael@0: BPLOG(ERROR) << "MinidumpBreakpadInfo cannot read Breakpad info"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&breakpad_info_.validity); michael@0: Swap(&breakpad_info_.dump_thread_id); michael@0: Swap(&breakpad_info_.requesting_thread_id); michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool MinidumpBreakpadInfo::GetDumpThreadID(uint32_t *thread_id) const { michael@0: BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetDumpThreadID " michael@0: "requires |thread_id|"; michael@0: assert(thread_id); michael@0: *thread_id = 0; michael@0: michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetDumpThreadID"; michael@0: return false; michael@0: } michael@0: michael@0: if (!(breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID)) { michael@0: BPLOG(INFO) << "MinidumpBreakpadInfo has no dump thread"; michael@0: return false; michael@0: } michael@0: michael@0: *thread_id = breakpad_info_.dump_thread_id; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool MinidumpBreakpadInfo::GetRequestingThreadID(uint32_t *thread_id) michael@0: const { michael@0: BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetRequestingThreadID " michael@0: "requires |thread_id|"; michael@0: assert(thread_id); michael@0: *thread_id = 0; michael@0: michael@0: if (!thread_id || !valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetRequestingThreadID"; michael@0: return false; michael@0: } michael@0: michael@0: if (!(breakpad_info_.validity & michael@0: MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID)) { michael@0: BPLOG(INFO) << "MinidumpBreakpadInfo has no requesting thread"; michael@0: return false; michael@0: } michael@0: michael@0: *thread_id = breakpad_info_.requesting_thread_id; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: void MinidumpBreakpadInfo::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpBreakpadInfo cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDRawBreakpadInfo\n"); michael@0: printf(" validity = 0x%x\n", breakpad_info_.validity); michael@0: michael@0: if (breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID) { michael@0: printf(" dump_thread_id = 0x%x\n", breakpad_info_.dump_thread_id); michael@0: } else { michael@0: printf(" dump_thread_id = (invalid)\n"); michael@0: } michael@0: michael@0: if (breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID) { michael@0: printf(" requesting_thread_id = 0x%x\n", michael@0: breakpad_info_.requesting_thread_id); michael@0: } else { michael@0: printf(" requesting_thread_id = (invalid)\n"); michael@0: } michael@0: michael@0: printf("\n"); michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpMemoryInfo michael@0: // michael@0: michael@0: michael@0: MinidumpMemoryInfo::MinidumpMemoryInfo(Minidump* minidump) michael@0: : MinidumpObject(minidump), michael@0: memory_info_() { michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryInfo::IsExecutable() const { michael@0: uint32_t protection = michael@0: memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK; michael@0: return protection == MD_MEMORY_PROTECT_EXECUTE || michael@0: protection == MD_MEMORY_PROTECT_EXECUTE_READ || michael@0: protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE; michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryInfo::IsWritable() const { michael@0: uint32_t protection = michael@0: memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK; michael@0: return protection == MD_MEMORY_PROTECT_READWRITE || michael@0: protection == MD_MEMORY_PROTECT_WRITECOPY || michael@0: protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE || michael@0: protection == MD_MEMORY_PROTECT_EXECUTE_WRITECOPY; michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryInfo::Read() { michael@0: valid_ = false; michael@0: michael@0: if (!minidump_->ReadBytes(&memory_info_, sizeof(memory_info_))) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfo cannot read memory info"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&memory_info_.base_address); michael@0: Swap(&memory_info_.allocation_base); michael@0: Swap(&memory_info_.allocation_protection); michael@0: Swap(&memory_info_.region_size); michael@0: Swap(&memory_info_.state); michael@0: Swap(&memory_info_.protection); michael@0: Swap(&memory_info_.type); michael@0: } michael@0: michael@0: // Check for base + size overflow or undersize. michael@0: if (memory_info_.region_size == 0 || michael@0: memory_info_.region_size > numeric_limits::max() - michael@0: memory_info_.base_address) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfo has a memory region problem, " << michael@0: HexString(memory_info_.base_address) << "+" << michael@0: HexString(memory_info_.region_size); michael@0: return false; michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: void MinidumpMemoryInfo::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfo cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDRawMemoryInfo\n"); michael@0: printf(" base_address = 0x%" PRIx64 "\n", michael@0: memory_info_.base_address); michael@0: printf(" allocation_base = 0x%" PRIx64 "\n", michael@0: memory_info_.allocation_base); michael@0: printf(" allocation_protection = 0x%x\n", michael@0: memory_info_.allocation_protection); michael@0: printf(" region_size = 0x%" PRIx64 "\n", memory_info_.region_size); michael@0: printf(" state = 0x%x\n", memory_info_.state); michael@0: printf(" protection = 0x%x\n", memory_info_.protection); michael@0: printf(" type = 0x%x\n", memory_info_.type); michael@0: } michael@0: michael@0: michael@0: // michael@0: // MinidumpMemoryInfoList michael@0: // michael@0: michael@0: michael@0: MinidumpMemoryInfoList::MinidumpMemoryInfoList(Minidump* minidump) michael@0: : MinidumpStream(minidump), michael@0: range_map_(new RangeMap()), michael@0: infos_(NULL), michael@0: info_count_(0) { michael@0: } michael@0: michael@0: michael@0: MinidumpMemoryInfoList::~MinidumpMemoryInfoList() { michael@0: delete range_map_; michael@0: delete infos_; michael@0: } michael@0: michael@0: michael@0: bool MinidumpMemoryInfoList::Read(uint32_t expected_size) { michael@0: // Invalidate cached data. michael@0: delete infos_; michael@0: infos_ = NULL; michael@0: range_map_->Clear(); michael@0: info_count_ = 0; michael@0: michael@0: valid_ = false; michael@0: michael@0: MDRawMemoryInfoList header; michael@0: if (expected_size < sizeof(MDRawMemoryInfoList)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " << michael@0: expected_size << " < " << sizeof(MDRawMemoryInfoList); michael@0: return false; michael@0: } michael@0: if (!minidump_->ReadBytes(&header, sizeof(header))) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList could not read header"; michael@0: return false; michael@0: } michael@0: michael@0: if (minidump_->swap()) { michael@0: Swap(&header.size_of_header); michael@0: Swap(&header.size_of_entry); michael@0: Swap(&header.number_of_entries); michael@0: } michael@0: michael@0: // Sanity check that the header is the expected size. michael@0: //TODO(ted): could possibly handle this more gracefully, assuming michael@0: // that future versions of the structs would be backwards-compatible. michael@0: if (header.size_of_header != sizeof(MDRawMemoryInfoList)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " << michael@0: header.size_of_header << " != " << michael@0: sizeof(MDRawMemoryInfoList); michael@0: return false; michael@0: } michael@0: michael@0: // Sanity check that the entries are the expected size. michael@0: if (header.size_of_entry != sizeof(MDRawMemoryInfo)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList entry size mismatch, " << michael@0: header.size_of_entry << " != " << michael@0: sizeof(MDRawMemoryInfo); michael@0: return false; michael@0: } michael@0: michael@0: if (header.number_of_entries > michael@0: numeric_limits::max() / sizeof(MDRawMemoryInfo)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList info count " << michael@0: header.number_of_entries << michael@0: " would cause multiplication overflow"; michael@0: return false; michael@0: } michael@0: michael@0: if (expected_size != sizeof(MDRawMemoryInfoList) + michael@0: header.number_of_entries * sizeof(MDRawMemoryInfo)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList size mismatch, " << expected_size << michael@0: " != " << sizeof(MDRawMemoryInfoList) + michael@0: header.number_of_entries * sizeof(MDRawMemoryInfo); michael@0: return false; michael@0: } michael@0: michael@0: if (header.number_of_entries != 0) { michael@0: scoped_ptr infos( michael@0: new MinidumpMemoryInfos(header.number_of_entries, michael@0: MinidumpMemoryInfo(minidump_))); michael@0: michael@0: for (unsigned int index = 0; michael@0: index < header.number_of_entries; michael@0: ++index) { michael@0: MinidumpMemoryInfo* info = &(*infos)[index]; michael@0: michael@0: // Assume that the file offset is correct after the last read. michael@0: if (!info->Read()) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList cannot read info " << michael@0: index << "/" << header.number_of_entries; michael@0: return false; michael@0: } michael@0: michael@0: uint64_t base_address = info->GetBase(); michael@0: uint32_t region_size = info->GetSize(); michael@0: michael@0: if (!range_map_->StoreRange(base_address, region_size, index)) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList could not store" michael@0: " memory region " << michael@0: index << "/" << header.number_of_entries << ", " << michael@0: HexString(base_address) << "+" << michael@0: HexString(region_size); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: infos_ = infos.release(); michael@0: } michael@0: michael@0: info_count_ = header.number_of_entries; michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoAtIndex( michael@0: unsigned int index) const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for GetMemoryInfoAtIndex"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (index >= info_count_) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList index out of range: " << michael@0: index << "/" << info_count_; michael@0: return NULL; michael@0: } michael@0: michael@0: return &(*infos_)[index]; michael@0: } michael@0: michael@0: michael@0: const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoForAddress( michael@0: uint64_t address) const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for" michael@0: " GetMemoryInfoForAddress"; michael@0: return NULL; michael@0: } michael@0: michael@0: unsigned int info_index; michael@0: if (!range_map_->RetrieveRange(address, &info_index, NULL, NULL)) { michael@0: BPLOG(INFO) << "MinidumpMemoryInfoList has no memory info at " << michael@0: HexString(address); michael@0: return NULL; michael@0: } michael@0: michael@0: return GetMemoryInfoAtIndex(info_index); michael@0: } michael@0: michael@0: michael@0: void MinidumpMemoryInfoList::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "MinidumpMemoryInfoList cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MinidumpMemoryInfoList\n"); michael@0: printf(" info_count = %d\n", info_count_); michael@0: printf("\n"); michael@0: michael@0: for (unsigned int info_index = 0; michael@0: info_index < info_count_; michael@0: ++info_index) { michael@0: printf("info[%d]\n", info_index); michael@0: (*infos_)[info_index].Print(); michael@0: printf("\n"); michael@0: } michael@0: } michael@0: michael@0: michael@0: // michael@0: // Minidump michael@0: // michael@0: michael@0: michael@0: uint32_t Minidump::max_streams_ = 128; michael@0: unsigned int Minidump::max_string_length_ = 1024; michael@0: michael@0: michael@0: Minidump::Minidump(const string& path) michael@0: : header_(), michael@0: directory_(NULL), michael@0: stream_map_(new MinidumpStreamMap()), michael@0: path_(path), michael@0: stream_(NULL), michael@0: swap_(false), michael@0: valid_(false) { michael@0: } michael@0: michael@0: Minidump::Minidump(istream& stream) michael@0: : header_(), michael@0: directory_(NULL), michael@0: stream_map_(new MinidumpStreamMap()), michael@0: path_(), michael@0: stream_(&stream), michael@0: swap_(false), michael@0: valid_(false) { michael@0: } michael@0: michael@0: Minidump::~Minidump() { michael@0: if (stream_) { michael@0: BPLOG(INFO) << "Minidump closing minidump"; michael@0: } michael@0: if (!path_.empty()) { michael@0: delete stream_; michael@0: } michael@0: delete directory_; michael@0: delete stream_map_; michael@0: } michael@0: michael@0: michael@0: bool Minidump::Open() { michael@0: if (stream_ != NULL) { michael@0: BPLOG(INFO) << "Minidump reopening minidump " << path_; michael@0: michael@0: // The file is already open. Seek to the beginning, which is the position michael@0: // the file would be at if it were opened anew. michael@0: return SeekSet(0); michael@0: } michael@0: michael@0: stream_ = new ifstream(path_.c_str(), std::ios::in | std::ios::binary); michael@0: if (!stream_ || !stream_->good()) { michael@0: string error_string; michael@0: int error_code = ErrnoString(&error_string); michael@0: BPLOG(ERROR) << "Minidump could not open minidump " << path_ << michael@0: ", error " << error_code << ": " << error_string; michael@0: return false; michael@0: } michael@0: michael@0: BPLOG(INFO) << "Minidump opened minidump " << path_; michael@0: return true; michael@0: } michael@0: michael@0: bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t *context_cpu_flags) { michael@0: // Initialize output parameters michael@0: *context_cpu_flags = 0; michael@0: michael@0: // Save the current stream position michael@0: off_t saved_position = Tell(); michael@0: if (saved_position == -1) { michael@0: // Failed to save the current stream position. michael@0: // Returns true because the current position of the stream is preserved. michael@0: return true; michael@0: } michael@0: michael@0: const MDRawSystemInfo* system_info = michael@0: GetSystemInfo() ? GetSystemInfo()->system_info() : NULL; michael@0: michael@0: if (system_info != NULL) { michael@0: switch (system_info->processor_architecture) { michael@0: case MD_CPU_ARCHITECTURE_X86: michael@0: *context_cpu_flags = MD_CONTEXT_X86; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_MIPS: michael@0: *context_cpu_flags = MD_CONTEXT_MIPS; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_ALPHA: michael@0: *context_cpu_flags = MD_CONTEXT_ALPHA; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_PPC: michael@0: *context_cpu_flags = MD_CONTEXT_PPC; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_SHX: michael@0: *context_cpu_flags = MD_CONTEXT_SHX; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_ARM: michael@0: *context_cpu_flags = MD_CONTEXT_ARM; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_IA64: michael@0: *context_cpu_flags = MD_CONTEXT_IA64; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_ALPHA64: michael@0: *context_cpu_flags = 0; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_MSIL: michael@0: *context_cpu_flags = 0; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_AMD64: michael@0: *context_cpu_flags = MD_CONTEXT_AMD64; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_X86_WIN64: michael@0: *context_cpu_flags = 0; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_SPARC: michael@0: *context_cpu_flags = MD_CONTEXT_SPARC; michael@0: break; michael@0: case MD_CPU_ARCHITECTURE_UNKNOWN: michael@0: *context_cpu_flags = 0; michael@0: break; michael@0: default: michael@0: *context_cpu_flags = 0; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Restore position and return michael@0: return SeekSet(saved_position); michael@0: } michael@0: michael@0: michael@0: bool Minidump::Read() { michael@0: // Invalidate cached data. michael@0: delete directory_; michael@0: directory_ = NULL; michael@0: stream_map_->clear(); michael@0: michael@0: valid_ = false; michael@0: michael@0: if (!Open()) { michael@0: BPLOG(ERROR) << "Minidump cannot open minidump"; michael@0: return false; michael@0: } michael@0: michael@0: if (!ReadBytes(&header_, sizeof(MDRawHeader))) { michael@0: BPLOG(ERROR) << "Minidump cannot read header"; michael@0: return false; michael@0: } michael@0: michael@0: if (header_.signature != MD_HEADER_SIGNATURE) { michael@0: // The file may be byte-swapped. Under the present architecture, these michael@0: // classes don't know or need to know what CPU (or endianness) the michael@0: // minidump was produced on in order to parse it. Use the signature as michael@0: // a byte order marker. michael@0: uint32_t signature_swapped = header_.signature; michael@0: Swap(&signature_swapped); michael@0: if (signature_swapped != MD_HEADER_SIGNATURE) { michael@0: // This isn't a minidump or a byte-swapped minidump. michael@0: BPLOG(ERROR) << "Minidump header signature mismatch: (" << michael@0: HexString(header_.signature) << ", " << michael@0: HexString(signature_swapped) << ") != " << michael@0: HexString(MD_HEADER_SIGNATURE); michael@0: return false; michael@0: } michael@0: swap_ = true; michael@0: } else { michael@0: // The file is not byte-swapped. Set swap_ false (it may have been true michael@0: // if the object is being reused?) michael@0: swap_ = false; michael@0: } michael@0: michael@0: BPLOG(INFO) << "Minidump " << (swap_ ? "" : "not ") << michael@0: "byte-swapping minidump"; michael@0: michael@0: if (swap_) { michael@0: Swap(&header_.signature); michael@0: Swap(&header_.version); michael@0: Swap(&header_.stream_count); michael@0: Swap(&header_.stream_directory_rva); michael@0: Swap(&header_.checksum); michael@0: Swap(&header_.time_date_stamp); michael@0: Swap(&header_.flags); michael@0: } michael@0: michael@0: // Version check. The high 16 bits of header_.version contain something michael@0: // else "implementation specific." michael@0: if ((header_.version & 0x0000ffff) != MD_HEADER_VERSION) { michael@0: BPLOG(ERROR) << "Minidump version mismatch: " << michael@0: HexString(header_.version & 0x0000ffff) << " != " << michael@0: HexString(MD_HEADER_VERSION); michael@0: return false; michael@0: } michael@0: michael@0: if (!SeekSet(header_.stream_directory_rva)) { michael@0: BPLOG(ERROR) << "Minidump cannot seek to stream directory"; michael@0: return false; michael@0: } michael@0: michael@0: if (header_.stream_count > max_streams_) { michael@0: BPLOG(ERROR) << "Minidump stream count " << header_.stream_count << michael@0: " exceeds maximum " << max_streams_; michael@0: return false; michael@0: } michael@0: michael@0: if (header_.stream_count != 0) { michael@0: scoped_ptr directory( michael@0: new MinidumpDirectoryEntries(header_.stream_count)); michael@0: michael@0: // Read the entire array in one fell swoop, instead of reading one entry michael@0: // at a time in the loop. michael@0: if (!ReadBytes(&(*directory)[0], michael@0: sizeof(MDRawDirectory) * header_.stream_count)) { michael@0: BPLOG(ERROR) << "Minidump cannot read stream directory"; michael@0: return false; michael@0: } michael@0: michael@0: for (unsigned int stream_index = 0; michael@0: stream_index < header_.stream_count; michael@0: ++stream_index) { michael@0: MDRawDirectory* directory_entry = &(*directory)[stream_index]; michael@0: michael@0: if (swap_) { michael@0: Swap(&directory_entry->stream_type); michael@0: Swap(&directory_entry->location); michael@0: } michael@0: michael@0: // Initialize the stream_map_ map, which speeds locating a stream by michael@0: // type. michael@0: unsigned int stream_type = directory_entry->stream_type; michael@0: switch (stream_type) { michael@0: case MD_THREAD_LIST_STREAM: michael@0: case MD_MODULE_LIST_STREAM: michael@0: case MD_MEMORY_LIST_STREAM: michael@0: case MD_EXCEPTION_STREAM: michael@0: case MD_SYSTEM_INFO_STREAM: michael@0: case MD_MISC_INFO_STREAM: michael@0: case MD_BREAKPAD_INFO_STREAM: { michael@0: if (stream_map_->find(stream_type) != stream_map_->end()) { michael@0: // Another stream with this type was already found. A minidump michael@0: // file should contain at most one of each of these stream types. michael@0: BPLOG(ERROR) << "Minidump found multiple streams of type " << michael@0: stream_type << ", but can only deal with one"; michael@0: return false; michael@0: } michael@0: // Fall through to default michael@0: } michael@0: michael@0: default: { michael@0: // Overwrites for stream types other than those above, but it's michael@0: // expected to be the user's burden in that case. michael@0: (*stream_map_)[stream_type].stream_index = stream_index; michael@0: } michael@0: } michael@0: } michael@0: michael@0: directory_ = directory.release(); michael@0: } michael@0: michael@0: valid_ = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: MinidumpThreadList* Minidump::GetThreadList() { michael@0: MinidumpThreadList* thread_list; michael@0: return GetStream(&thread_list); michael@0: } michael@0: michael@0: michael@0: MinidumpModuleList* Minidump::GetModuleList() { michael@0: MinidumpModuleList* module_list; michael@0: return GetStream(&module_list); michael@0: } michael@0: michael@0: michael@0: MinidumpMemoryList* Minidump::GetMemoryList() { michael@0: MinidumpMemoryList* memory_list; michael@0: return GetStream(&memory_list); michael@0: } michael@0: michael@0: michael@0: MinidumpException* Minidump::GetException() { michael@0: MinidumpException* exception; michael@0: return GetStream(&exception); michael@0: } michael@0: michael@0: MinidumpAssertion* Minidump::GetAssertion() { michael@0: MinidumpAssertion* assertion; michael@0: return GetStream(&assertion); michael@0: } michael@0: michael@0: michael@0: MinidumpSystemInfo* Minidump::GetSystemInfo() { michael@0: MinidumpSystemInfo* system_info; michael@0: return GetStream(&system_info); michael@0: } michael@0: michael@0: michael@0: MinidumpMiscInfo* Minidump::GetMiscInfo() { michael@0: MinidumpMiscInfo* misc_info; michael@0: return GetStream(&misc_info); michael@0: } michael@0: michael@0: michael@0: MinidumpBreakpadInfo* Minidump::GetBreakpadInfo() { michael@0: MinidumpBreakpadInfo* breakpad_info; michael@0: return GetStream(&breakpad_info); michael@0: } michael@0: michael@0: MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() { michael@0: MinidumpMemoryInfoList* memory_info_list; michael@0: return GetStream(&memory_info_list); michael@0: } michael@0: michael@0: michael@0: void Minidump::Print() { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Minidump cannot print invalid data"; michael@0: return; michael@0: } michael@0: michael@0: printf("MDRawHeader\n"); michael@0: printf(" signature = 0x%x\n", header_.signature); michael@0: printf(" version = 0x%x\n", header_.version); michael@0: printf(" stream_count = %d\n", header_.stream_count); michael@0: printf(" stream_directory_rva = 0x%x\n", header_.stream_directory_rva); michael@0: printf(" checksum = 0x%x\n", header_.checksum); michael@0: struct tm timestruct; michael@0: #ifdef _WIN32 michael@0: gmtime_s(×truct, reinterpret_cast(&header_.time_date_stamp)); michael@0: #else michael@0: gmtime_r(reinterpret_cast(&header_.time_date_stamp), ×truct); michael@0: #endif michael@0: char timestr[20]; michael@0: strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", ×truct); michael@0: printf(" time_date_stamp = 0x%x %s\n", header_.time_date_stamp, michael@0: timestr); michael@0: printf(" flags = 0x%" PRIx64 "\n", header_.flags); michael@0: printf("\n"); michael@0: michael@0: for (unsigned int stream_index = 0; michael@0: stream_index < header_.stream_count; michael@0: ++stream_index) { michael@0: MDRawDirectory* directory_entry = &(*directory_)[stream_index]; michael@0: michael@0: printf("mDirectory[%d]\n", stream_index); michael@0: printf("MDRawDirectory\n"); michael@0: printf(" stream_type = %d\n", directory_entry->stream_type); michael@0: printf(" location.data_size = %d\n", michael@0: directory_entry->location.data_size); michael@0: printf(" location.rva = 0x%x\n", directory_entry->location.rva); michael@0: printf("\n"); michael@0: } michael@0: michael@0: printf("Streams:\n"); michael@0: for (MinidumpStreamMap::const_iterator iterator = stream_map_->begin(); michael@0: iterator != stream_map_->end(); michael@0: ++iterator) { michael@0: uint32_t stream_type = iterator->first; michael@0: MinidumpStreamInfo info = iterator->second; michael@0: printf(" stream type 0x%x at index %d\n", stream_type, info.stream_index); michael@0: } michael@0: printf("\n"); michael@0: } michael@0: michael@0: michael@0: const MDRawDirectory* Minidump::GetDirectoryEntryAtIndex(unsigned int index) michael@0: const { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid Minidump for GetDirectoryEntryAtIndex"; michael@0: return NULL; michael@0: } michael@0: michael@0: if (index >= header_.stream_count) { michael@0: BPLOG(ERROR) << "Minidump stream directory index out of range: " << michael@0: index << "/" << header_.stream_count; michael@0: return NULL; michael@0: } michael@0: michael@0: return &(*directory_)[index]; michael@0: } michael@0: michael@0: michael@0: bool Minidump::ReadBytes(void* bytes, size_t count) { michael@0: // Can't check valid_ because Read needs to call this method before michael@0: // validity can be determined. michael@0: if (!stream_) { michael@0: return false; michael@0: } michael@0: stream_->read(static_cast(bytes), count); michael@0: size_t bytes_read = stream_->gcount(); michael@0: if (bytes_read != count) { michael@0: if (bytes_read == size_t(-1)) { michael@0: string error_string; michael@0: int error_code = ErrnoString(&error_string); michael@0: BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string; michael@0: } else { michael@0: BPLOG(ERROR) << "ReadBytes: read " << bytes_read << "/" << count; michael@0: } michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool Minidump::SeekSet(off_t offset) { michael@0: // Can't check valid_ because Read needs to call this method before michael@0: // validity can be determined. michael@0: if (!stream_) { michael@0: return false; michael@0: } michael@0: stream_->seekg(offset, std::ios_base::beg); michael@0: if (!stream_->good()) { michael@0: string error_string; michael@0: int error_code = ErrnoString(&error_string); michael@0: BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string; michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: off_t Minidump::Tell() { michael@0: if (!valid_ || !stream_) { michael@0: return (off_t)-1; michael@0: } michael@0: michael@0: return stream_->tellg(); michael@0: } michael@0: michael@0: michael@0: string* Minidump::ReadString(off_t offset) { michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid Minidump for ReadString"; michael@0: return NULL; michael@0: } michael@0: if (!SeekSet(offset)) { michael@0: BPLOG(ERROR) << "ReadString could not seek to string at offset " << offset; michael@0: return NULL; michael@0: } michael@0: michael@0: uint32_t bytes; michael@0: if (!ReadBytes(&bytes, sizeof(bytes))) { michael@0: BPLOG(ERROR) << "ReadString could not read string size at offset " << michael@0: offset; michael@0: return NULL; michael@0: } michael@0: if (swap_) michael@0: Swap(&bytes); michael@0: michael@0: if (bytes % 2 != 0) { michael@0: BPLOG(ERROR) << "ReadString found odd-sized " << bytes << michael@0: "-byte string at offset " << offset; michael@0: return NULL; michael@0: } michael@0: unsigned int utf16_words = bytes / 2; michael@0: michael@0: if (utf16_words > max_string_length_) { michael@0: BPLOG(ERROR) << "ReadString string length " << utf16_words << michael@0: " exceeds maximum " << max_string_length_ << michael@0: " at offset " << offset; michael@0: return NULL; michael@0: } michael@0: michael@0: vector string_utf16(utf16_words); michael@0: michael@0: if (utf16_words) { michael@0: if (!ReadBytes(&string_utf16[0], bytes)) { michael@0: BPLOG(ERROR) << "ReadString could not read " << bytes << michael@0: "-byte string at offset " << offset; michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: return UTF16ToUTF8(string_utf16, swap_); michael@0: } michael@0: michael@0: michael@0: bool Minidump::SeekToStreamType(uint32_t stream_type, michael@0: uint32_t* stream_length) { michael@0: BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires " michael@0: "|stream_length|"; michael@0: assert(stream_length); michael@0: *stream_length = 0; michael@0: michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid Mindump for SeekToStreamType"; michael@0: return false; michael@0: } michael@0: michael@0: MinidumpStreamMap::const_iterator iterator = stream_map_->find(stream_type); michael@0: if (iterator == stream_map_->end()) { michael@0: // This stream type didn't exist in the directory. michael@0: BPLOG(INFO) << "SeekToStreamType: type " << stream_type << " not present"; michael@0: return false; michael@0: } michael@0: michael@0: MinidumpStreamInfo info = iterator->second; michael@0: if (info.stream_index >= header_.stream_count) { michael@0: BPLOG(ERROR) << "SeekToStreamType: type " << stream_type << michael@0: " out of range: " << michael@0: info.stream_index << "/" << header_.stream_count; michael@0: return false; michael@0: } michael@0: michael@0: MDRawDirectory* directory_entry = &(*directory_)[info.stream_index]; michael@0: if (!SeekSet(directory_entry->location.rva)) { michael@0: BPLOG(ERROR) << "SeekToStreamType could not seek to stream type " << michael@0: stream_type; michael@0: return false; michael@0: } michael@0: michael@0: *stream_length = directory_entry->location.data_size; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: michael@0: template michael@0: T* Minidump::GetStream(T** stream) { michael@0: // stream is a garbage parameter that's present only to account for C++'s michael@0: // inability to overload a method based solely on its return type. michael@0: michael@0: const uint32_t stream_type = T::kStreamType; michael@0: michael@0: BPLOG_IF(ERROR, !stream) << "Minidump::GetStream type " << stream_type << michael@0: " requires |stream|"; michael@0: assert(stream); michael@0: *stream = NULL; michael@0: michael@0: if (!valid_) { michael@0: BPLOG(ERROR) << "Invalid Minidump for GetStream type " << stream_type; michael@0: return NULL; michael@0: } michael@0: michael@0: MinidumpStreamMap::iterator iterator = stream_map_->find(stream_type); michael@0: if (iterator == stream_map_->end()) { michael@0: // This stream type didn't exist in the directory. michael@0: BPLOG(INFO) << "GetStream: type " << stream_type << " not present"; michael@0: return NULL; michael@0: } michael@0: michael@0: // Get a pointer so that the stored stream field can be altered. michael@0: MinidumpStreamInfo* info = &iterator->second; michael@0: michael@0: if (info->stream) { michael@0: // This cast is safe because info.stream is only populated by this michael@0: // method, and there is a direct correlation between T and stream_type. michael@0: *stream = static_cast(info->stream); michael@0: return *stream; michael@0: } michael@0: michael@0: uint32_t stream_length; michael@0: if (!SeekToStreamType(stream_type, &stream_length)) { michael@0: BPLOG(ERROR) << "GetStream could not seek to stream type " << stream_type; michael@0: return NULL; michael@0: } michael@0: michael@0: scoped_ptr new_stream(new T(this)); michael@0: michael@0: if (!new_stream->Read(stream_length)) { michael@0: BPLOG(ERROR) << "GetStream could not read stream type " << stream_type; michael@0: return NULL; michael@0: } michael@0: michael@0: *stream = new_stream.release(); michael@0: info->stream = *stream; michael@0: return *stream; michael@0: } michael@0: michael@0: michael@0: } // namespace google_breakpad