// Copyright (C) 2006 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // minidump.cc: A minidump reader. // // See minidump.h for documentation. // // Author: Mark Mentovai #include #include #include #include #ifdef _WIN32 #include typedef SSIZE_T ssize_t; #define open _open #define read _read #define lseek _lseek #else // _WIN32 #define O_BINARY 0 #endif // _WIN32 #include #include #include #include "processor/minidump.h" namespace google_airbag { using std::auto_ptr; using std::vector; // // Swapping routines // // Inlining these doesn't increase code size significantly, and it saves // a whole lot of unnecessary jumping back and forth. // // Swapping an 8-bit quantity is a no-op. This function is only provided // to account for certain templatized operations that require swapping for // wider types but handle u_int8_t too // (MinidumpMemoryRegion::GetMemoryAtAddressInternal). static inline void Swap(u_int8_t* value) { } // Optimization: don't need to AND the furthest right shift, because we're // shifting an unsigned quantity. The standard requires zero-filling in this // case. If the quantities were signed, a bitmask whould be needed for this // right shift to avoid an arithmetic shift (which retains the sign bit). // The furthest left shift never needs to be ANDed bitmask. static inline void Swap(u_int16_t* value) { *value = (*value >> 8) | (*value << 8); } static inline void Swap(u_int32_t* value) { *value = (*value >> 24) | ((*value >> 8) & 0x0000ff00) | ((*value << 8) & 0x00ff0000) | (*value << 24); } static inline void Swap(u_int64_t* value) { *value = (*value >> 56) | ((*value >> 40) & 0x000000000000ff00LL) | ((*value >> 24) & 0x0000000000ff0000LL) | ((*value >> 8) & 0x00000000ff000000LL) | ((*value << 8) & 0x000000ff00000000LL) | ((*value << 24) & 0x0000ff0000000000LL) | ((*value << 40) & 0x00ff000000000000LL) | (*value << 56); } static inline void Swap(MDLocationDescriptor* location_descriptor) { Swap(&location_descriptor->data_size); Swap(&location_descriptor->rva); } static inline void Swap(MDMemoryDescriptor* memory_descriptor) { Swap(&memory_descriptor->start_of_memory_range); Swap(&memory_descriptor->memory); } static inline void Swap(MDGUID* guid) { Swap(&guid->data1); Swap(&guid->data2); Swap(&guid->data3); // Don't swap guid->data4[] because it contains 8-bit quantities. } // // Character conversion routines // // Standard wide-character conversion routines depend on the system's own // idea of what width a wide character should be: some use 16 bits, and // some use 32 bits. For the purposes of a minidump, wide strings are // always represented with 16-bit UTF-16 chracters. iconv isn't available // everywhere, and its interface varies where it is available. iconv also // deals purely with char* pointers, so in addition to considering the swap // parameter, a converter that uses iconv would also need to take the host // CPU's endianness into consideration. It doesn't seems worth the trouble // of making it a dependency when we don't care about anything but UTF-16. static string* UTF16ToUTF8(const vector& in, bool swap) { auto_ptr out(new string()); // Set the string's initial capacity to the number of UTF-16 characters, // because the UTF-8 representation will always be at least this long. // If the UTF-8 representation is longer, the string will grow dynamically. out->reserve(in.size()); for (vector::const_iterator iterator = in.begin(); iterator != in.end(); ++iterator) { // Get a 16-bit value from the input u_int16_t in_word = *iterator; if (swap) Swap(&in_word); // Convert the input value (in_word) into a Unicode code point (unichar). u_int32_t unichar; if (in_word >= 0xdc00 && in_word <= 0xdcff) { // Low surrogate not following high surrogate, fail. return NULL; } else if (in_word >= 0xd800 && in_word <= 0xdbff) { // High surrogate. unichar = (in_word - 0xd7c0) << 10; if (++iterator == in.end()) { // End of input return NULL; } in_word = *iterator; if (in_word < 0xdc00 || in_word > 0xdcff) { // Expected low surrogate, found something else return NULL; } unichar |= in_word & 0x03ff; } else { // The ordinary case, a single non-surrogate Unicode character encoded // as a single 16-bit value. unichar = in_word; } // Convert the Unicode code point (unichar) into its UTF-8 representation, // appending it to the out string. if (unichar < 0x80) { (*out) += unichar; } else if (unichar < 0x800) { (*out) += 0xc0 | (unichar >> 6); (*out) += 0x80 | (unichar & 0x3f); } else if (unichar < 0x10000) { (*out) += 0xe0 | (unichar >> 12); (*out) += 0x80 | ((unichar >> 6) & 0x3f); (*out) += 0x80 | (unichar & 0x3f); } else if (unichar < 0x200000) { (*out) += 0xf0 | (unichar >> 18); (*out) += 0x80 | ((unichar >> 12) & 0x3f); (*out) += 0x80 | ((unichar >> 6) & 0x3f); (*out) += 0x80 | (unichar & 0x3f); } else { // Some (high) value that's not (presently) defined in UTF-8 return NULL; } } return out.release(); } // // MinidumpObject // MinidumpObject::MinidumpObject(Minidump* minidump) : minidump_(minidump), valid_(false) { } // // MinidumpStream // MinidumpStream::MinidumpStream(Minidump* minidump) : MinidumpObject(minidump) { } // // MinidumpContext // MinidumpContext::MinidumpContext(Minidump* minidump) : MinidumpStream(minidump), context_() { } bool MinidumpContext::Read(u_int32_t expected_size) { valid_ = false; if (expected_size != sizeof(context_)) return false; if (!minidump_->ReadBytes(&context_, sizeof(context_))) return false; if (minidump_->swap()) { Swap(&context_.context_flags); Swap(&context_.dr0); Swap(&context_.dr1); Swap(&context_.dr2); Swap(&context_.dr3); Swap(&context_.dr6); Swap(&context_.dr7); Swap(&context_.float_save.control_word); Swap(&context_.float_save.status_word); Swap(&context_.float_save.tag_word); Swap(&context_.float_save.error_offset); Swap(&context_.float_save.error_selector); Swap(&context_.float_save.data_offset); Swap(&context_.float_save.data_selector); // context_.float_save.register_area[] contains 8-bit quantities and does // not need to be swapped. Swap(&context_.float_save.cr0_npx_state); Swap(&context_.gs); Swap(&context_.fs); Swap(&context_.es); Swap(&context_.ds); Swap(&context_.edi); Swap(&context_.esi); Swap(&context_.ebx); Swap(&context_.edx); Swap(&context_.ecx); Swap(&context_.eax); Swap(&context_.ebp); Swap(&context_.eip); Swap(&context_.cs); Swap(&context_.eflags); Swap(&context_.esp); Swap(&context_.ss); // context_.extended_registers[] contains 8-bit quantities and does not // need to be swapped. } valid_ = true; return true; } void MinidumpContext::Print() { if (!valid_) return; printf("MDRawContextX86\n"); printf(" context_flags = 0x%x\n", context_.context_flags); printf(" dr0 = 0x%x\n", context_.dr0); printf(" dr1 = 0x%x\n", context_.dr1); printf(" dr2 = 0x%x\n", context_.dr2); printf(" dr3 = 0x%x\n", context_.dr3); printf(" dr6 = 0x%x\n", context_.dr6); printf(" dr7 = 0x%x\n", context_.dr7); printf(" float_save.control_word = 0x%x\n", context_.float_save.control_word); printf(" float_save.status_word = 0x%x\n", context_.float_save.status_word); printf(" float_save.tag_word = 0x%x\n", context_.float_save.tag_word); printf(" float_save.error_offset = 0x%x\n", context_.float_save.error_offset); printf(" float_save.error_selector = 0x%x\n", context_.float_save.error_selector); printf(" float_save.data_offset = 0x%x\n", context_.float_save.data_offset); printf(" float_save.data_selector = 0x%x\n", context_.float_save.data_selector); printf(" float_save.register_area[%2d] = 0x", MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE); for (unsigned int register_index = 0; register_index < MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE; ++register_index) { printf("%02x", context_.float_save.register_area[register_index]); } printf("\n"); printf(" float_save.cr0_npx_state = 0x%x\n", context_.float_save.cr0_npx_state); printf(" gs = 0x%x\n", context_.gs); printf(" fs = 0x%x\n", context_.fs); printf(" es = 0x%x\n", context_.es); printf(" ds = 0x%x\n", context_.ds); printf(" edi = 0x%x\n", context_.edi); printf(" esi = 0x%x\n", context_.esi); printf(" ebx = 0x%x\n", context_.ebx); printf(" edx = 0x%x\n", context_.edx); printf(" ecx = 0x%x\n", context_.ecx); printf(" eax = 0x%x\n", context_.eax); printf(" ebp = 0x%x\n", context_.ebp); printf(" eip = 0x%x\n", context_.eip); printf(" cs = 0x%x\n", context_.cs); printf(" eflags = 0x%x\n", context_.eflags); printf(" esp = 0x%x\n", context_.esp); printf(" ss = 0x%x\n", context_.ss); printf(" extended_registers[%3d] = 0x", MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE); for (unsigned int register_index = 0; register_index < MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE; ++register_index) { printf("%02x", context_.extended_registers[register_index]); } printf("\n\n"); } // // MinidumpMemoryRegion // MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump) : MinidumpObject(minidump), descriptor_(NULL), memory_(NULL) { } MinidumpMemoryRegion::~MinidumpMemoryRegion() { delete memory_; } void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) { descriptor_ = descriptor; valid_ = descriptor && (descriptor_->start_of_memory_range + descriptor_->memory.data_size) > descriptor_->start_of_memory_range; } const u_int8_t* MinidumpMemoryRegion::GetMemory() { if (!valid_) return NULL; if (!memory_) { if (!minidump_->SeekSet(descriptor_->memory.rva)) return NULL; // TODO(mmentovai): verify rational size! auto_ptr > memory( new vector(descriptor_->memory.data_size)); if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size)) return NULL; memory_ = memory.release(); } return &(*memory_)[0]; } u_int64_t MinidumpMemoryRegion::GetBase() { return valid_ ? descriptor_->start_of_memory_range : (u_int64_t)-1; } u_int32_t MinidumpMemoryRegion::GetSize() { return valid_ ? descriptor_->memory.data_size : 0; } void MinidumpMemoryRegion::FreeMemory() { delete memory_; memory_ = NULL; } template bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(u_int64_t address, T* value) { if (!valid_ || !value) return false; if (address < descriptor_->start_of_memory_range || address + sizeof(T) > descriptor_->start_of_memory_range + descriptor_->memory.data_size) { return false; } const u_int8_t* memory = GetMemory(); if (!memory) return false; // If the CPU requires memory accesses to be aligned, this can crash. // x86 and ppc are able to cope, though. *value = *reinterpret_cast( &memory[address - descriptor_->start_of_memory_range]); if (minidump_->swap()) Swap(value); return true; } bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, u_int8_t* value) { return GetMemoryAtAddressInternal(address, value); } bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, u_int16_t* value) { return GetMemoryAtAddressInternal(address, value); } bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, u_int32_t* value) { return GetMemoryAtAddressInternal(address, value); } bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, u_int64_t* value) { return GetMemoryAtAddressInternal(address, value); } void MinidumpMemoryRegion::Print() { if (!valid_) return; const u_int8_t* memory = GetMemory(); if (memory) { printf("0x"); for (unsigned int byte_index = 0; byte_index < descriptor_->memory.data_size; byte_index++) { printf("%02x", memory[byte_index]); } printf("\n"); } else { printf("No memory\n"); } } // // MinidumpThread // MinidumpThread::MinidumpThread(Minidump* minidump) : MinidumpObject(minidump), thread_(), memory_(NULL), context_(NULL) { } MinidumpThread::~MinidumpThread() { delete memory_; delete context_; } bool MinidumpThread::Read() { // Invalidate cached data. delete memory_; memory_ = NULL; delete context_; context_ = NULL; valid_ = false; if (!minidump_->ReadBytes(&thread_, sizeof(thread_))) return false; if (minidump_->swap()) { Swap(&thread_.thread_id); Swap(&thread_.suspend_count); Swap(&thread_.priority_class); Swap(&thread_.priority); Swap(&thread_.teb); Swap(&thread_.stack); Swap(&thread_.thread_context); } // Check for base + size overflow or undersize. A separate size==0 // check is needed in case base == 0. u_int64_t high_address = thread_.stack.start_of_memory_range + thread_.stack.memory.data_size - 1; if (thread_.stack.memory.data_size == 0 || high_address < thread_.stack.start_of_memory_range) return false; memory_ = new MinidumpMemoryRegion(minidump_); memory_->SetDescriptor(&thread_.stack); valid_ = true; return true; } MinidumpMemoryRegion* MinidumpThread::GetMemory() { return !valid_ ? NULL : memory_; } MinidumpContext* MinidumpThread::GetContext() { if (!valid_) return NULL; if (!context_) { if (!minidump_->SeekSet(thread_.thread_context.rva)) return NULL; auto_ptr context(new MinidumpContext(minidump_)); if (!context->Read(thread_.thread_context.data_size)) return NULL; context_ = context.release(); } return context_; } u_int32_t MinidumpThread::GetThreadID() { return valid_ ? thread_.thread_id : (u_int32_t)-1; } void MinidumpThread::Print() { if (!valid_) return; printf("MDRawThread\n"); printf(" thread_id = 0x%x\n", thread_.thread_id); printf(" suspend_count = %d\n", thread_.suspend_count); printf(" priority_class = 0x%x\n", thread_.priority_class); printf(" priority = 0x%x\n", thread_.priority); printf(" teb = 0x%llx\n", thread_.teb); printf(" stack.start_of_memory_range = 0x%llx\n", thread_.stack.start_of_memory_range); printf(" stack.memory.data_size = 0x%x\n", thread_.stack.memory.data_size); printf(" stack.memory.rva = 0x%x\n", thread_.stack.memory.rva); printf(" thread_context.data_size = 0x%x\n", thread_.thread_context.data_size); printf(" thread_context.rva = 0x%x\n", thread_.thread_context.rva); MinidumpContext* context = GetContext(); if (context) { printf("\n"); context->Print(); } else { printf(" (no context)\n"); printf("\n"); } MinidumpMemoryRegion* memory = GetMemory(); if (memory) { printf("Stack\n"); memory->Print(); } else { printf("No stack\n"); } printf("\n"); } // // MinidumpThreadList // MinidumpThreadList::MinidumpThreadList(Minidump* minidump) : MinidumpStream(minidump), id_to_thread_map_(), threads_(NULL), thread_count_(0) { } MinidumpThreadList::~MinidumpThreadList() { delete threads_; } bool MinidumpThreadList::Read(u_int32_t expected_size) { // Invalidate cached data. id_to_thread_map_.clear(); delete threads_; threads_ = NULL; thread_count_ = 0; valid_ = false; u_int32_t thread_count; if (expected_size < sizeof(thread_count)) return false; if (!minidump_->ReadBytes(&thread_count, sizeof(thread_count))) return false; if (minidump_->swap()) Swap(&thread_count); if (expected_size != sizeof(thread_count) + thread_count * sizeof(MDRawThread)) { return false; } // TODO(mmentovai): verify rational size! auto_ptr threads( new MinidumpThreads(thread_count, MinidumpThread(minidump_))); for (unsigned int thread_index = 0; thread_index < thread_count; ++thread_index) { MinidumpThread* thread = &(*threads)[thread_index]; // Assume that the file offset is correct after the last read. if (!thread->Read()) return false; u_int32_t thread_id = thread->GetThreadID(); if (GetThreadByID(thread_id)) { // Another thread with this ID is already in the list. Data error. return false; } id_to_thread_map_[thread_id] = thread; } threads_ = threads.release(); thread_count_ = thread_count; valid_ = true; return true; } MinidumpThread* MinidumpThreadList::GetThreadAtIndex(unsigned int index) const { if (!valid_ || index >= thread_count_) return NULL; return &(*threads_)[index]; } MinidumpThread* MinidumpThreadList::GetThreadByID(u_int32_t thread_id) { // Don't check valid_. Read calls this method before everything is // validated. It is safe to not check valid_ here. return id_to_thread_map_[thread_id]; } void MinidumpThreadList::Print() { if (!valid_) return; printf("MinidumpThreadList\n"); printf(" thread_count = %d\n", thread_count_); printf("\n"); for (unsigned int thread_index = 0; thread_index < thread_count_; ++thread_index) { printf("thread[%d]\n", thread_index); (*threads_)[thread_index].Print(); } } // // MinidumpModule // MinidumpModule::MinidumpModule(Minidump* minidump) : MinidumpObject(minidump), module_(), name_(NULL), cv_record_(NULL), misc_record_(NULL), debug_filename_(NULL) { } MinidumpModule::~MinidumpModule() { delete name_; delete cv_record_; delete misc_record_; delete debug_filename_; } bool MinidumpModule::Read() { // Invalidate cached data. delete name_; name_ = NULL; delete cv_record_; cv_record_ = NULL; delete misc_record_; misc_record_ = NULL; delete debug_filename_; debug_filename_ = NULL; valid_ = false; if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) return false; if (minidump_->swap()) { Swap(&module_.base_of_image); Swap(&module_.size_of_image); Swap(&module_.checksum); Swap(&module_.time_date_stamp); Swap(&module_.module_name_rva); Swap(&module_.version_info.signature); Swap(&module_.version_info.struct_version); Swap(&module_.version_info.file_version_hi); Swap(&module_.version_info.file_version_lo); Swap(&module_.version_info.product_version_hi); Swap(&module_.version_info.product_version_lo); Swap(&module_.version_info.file_flags_mask); Swap(&module_.version_info.file_flags); Swap(&module_.version_info.file_os); Swap(&module_.version_info.file_type); Swap(&module_.version_info.file_subtype); Swap(&module_.version_info.file_date_hi); Swap(&module_.version_info.file_date_lo); Swap(&module_.cv_record); Swap(&module_.misc_record); // Don't swap reserved fields because their contents are unknown (as // are their proper widths). } // Check for base + size overflow or undersize. A separate size==0 // check is needed in case base == 0. u_int64_t high_address = module_.base_of_image + module_.size_of_image - 1; if (module_.size_of_image == 0 || high_address < module_.base_of_image) return false; valid_ = true; return true; } const string* MinidumpModule::GetName() { if (!valid_) return NULL; if (!name_) name_ = minidump_->ReadString(module_.module_name_rva); return name_; } const u_int8_t* MinidumpModule::GetCVRecord() { if (!valid_) return NULL; if (!cv_record_) { // Only check against the smallest possible structure size now - recheck // if necessary later if the actual structure is larger. if (sizeof(MDCVInfoPDB20) > module_.cv_record.data_size) return NULL; if (!minidump_->SeekSet(module_.cv_record.rva)) return NULL; // TODO(mmentovai): verify rational size! // Allocating something that will be accessed as MDCVInfoPDB70 or // MDCVInfoPDB20 but is allocated as u_int8_t[] can cause alignment // problems. x86 and ppc are able to cope, though. This allocation // style is needed because the MDCVInfoPDB70 or MDCVInfoPDB20 are // variable-sized due to their pdb_file_name fields; these structures // are not sizeof(MDCVInfoPDB70) or sizeof(MDCVInfoPDB20) and treating // them as such would result in incomplete structures or overruns. auto_ptr > cv_record( new vector(module_.cv_record.data_size)); if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size)) return NULL; MDCVInfoPDB70* cv_record_70 = reinterpret_cast(&(*cv_record)[0]); u_int32_t signature = cv_record_70->cv_signature; if (minidump_->swap()) Swap(&signature); if (signature == MD_CVINFOPDB70_SIGNATURE) { // Now that the structure type is known, recheck the size. if (sizeof(MDCVInfoPDB70) > module_.cv_record.data_size) return NULL; if (minidump_->swap()) { Swap(&cv_record_70->cv_signature); Swap(&cv_record_70->signature); Swap(&cv_record_70->age); // Don't swap cv_record_70.pdb_file_name because it's an array of 8-bit // quanities. (It's a path, is it UTF-8?) } } else if (signature == MD_CVINFOPDB20_SIGNATURE) { if (minidump_->swap()) { MDCVInfoPDB20* cv_record_20 = reinterpret_cast(&(*cv_record)[0]); Swap(&cv_record_20->cv_header.signature); Swap(&cv_record_20->cv_header.offset); Swap(&cv_record_20->signature); Swap(&cv_record_20->age); // Don't swap cv_record_20.pdb_file_name because it's an array of 8-bit // quantities. (It's a path, is it UTF-8?) } } else { // Some unknown structure type. We don't need to bail out here, but we // do instead of returning it, because this method guarantees properly // swapped data, and data in an unknown format can't possibly be swapped. return NULL; } // The last field of either structure is null-terminated 8-bit character // data. Ensure that it's null-terminated. if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') return NULL; // Store the vector type because that's how storage was allocated, but // return it casted to u_int8_t*. cv_record_ = cv_record.release(); } return &(*cv_record_)[0]; } const MDImageDebugMisc* MinidumpModule::GetMiscRecord() { if (!valid_) return NULL; if (!misc_record_) { if (sizeof(MDImageDebugMisc) > module_.misc_record.data_size) return NULL; if (!minidump_->SeekSet(module_.misc_record.rva)) return NULL; // TODO(mmentovai): verify rational size! // Allocating something that will be accessed as MDImageDebugMisc but // is allocated as u_int8_t[] can cause alignment problems. x86 and // ppc are able to cope, though. This allocation style is needed // because the MDImageDebugMisc is variable-sized due to its data field; // this structure is not sizeof(MDImageDebugMisc) and treating it as such // would result in an incomplete structure or an overrun. auto_ptr > misc_record_mem( new vector(module_.misc_record.data_size)); MDImageDebugMisc* misc_record = reinterpret_cast(&(*misc_record_mem)[0]); if (!minidump_->ReadBytes(misc_record, module_.misc_record.data_size)) return NULL; if (minidump_->swap()) { Swap(&misc_record->data_type); Swap(&misc_record->length); // Don't swap misc_record.unicode because it's an 8-bit quantity. // Don't swap the reserved fields for the same reason, and because // they don't contain any valid data. if (misc_record->unicode) { // There is a potential alignment problem, but shouldn't be a problem // in practice due to the layout of MDImageDebugMisc. u_int16_t* data16 = reinterpret_cast(&(misc_record->data)); unsigned int dataBytes = module_.misc_record.data_size - sizeof(MDImageDebugMisc); unsigned int dataLength = dataBytes / 2; for (unsigned int characterIndex = 0; characterIndex < dataLength; ++characterIndex) { Swap(&data16[characterIndex]); } } } if (module_.misc_record.data_size != misc_record->length) return NULL; // Store the vector type because that's how storage was allocated, but // return it casted to MDImageDebugMisc*. misc_record_ = misc_record_mem.release(); } return reinterpret_cast(&(*misc_record_)[0]); } // This method will perform no allocation-size checking on its own; it relies // on GetCVRecord() and GetMiscRecord() to have made the determination that // the necessary structures aren't oversized. const string* MinidumpModule::GetDebugFilename() { if (!valid_) return NULL; if (!debug_filename_) { // Prefer the CodeView record if present. const MDCVInfoPDB70* cv_record_70 = reinterpret_cast(GetCVRecord()); if (cv_record_70) { if (cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE) { // GetCVRecord guarantees pdb_file_name is null-terminated. debug_filename_ = new string( reinterpret_cast(cv_record_70->pdb_file_name)); return debug_filename_; } else if (cv_record_70->cv_signature == MD_CVINFOPDB20_SIGNATURE) { // It's actually a MDCVInfoPDB20 structure. const MDCVInfoPDB20* cv_record_20 = reinterpret_cast(cv_record_70); // GetCVRecord guarantees pdb_file_name is null-terminated. debug_filename_ = new string( reinterpret_cast(cv_record_20->pdb_file_name)); return debug_filename_; } // If there's a CodeView record but it doesn't match either of those // signatures, try the miscellaneous record - but it's suspicious because // GetCVRecord shouldn't have returned a CodeView record that doesn't // match either signature. } // No usable CodeView record. Try the miscellaneous debug record. const MDImageDebugMisc* misc_record = GetMiscRecord(); if (!misc_record) return NULL; if (!misc_record->unicode) { // If it's not Unicode, just stuff it into the string. It's unclear // if misc_record->data is 0-terminated, so use an explicit size. debug_filename_ = new string( reinterpret_cast(misc_record->data), module_.misc_record.data_size - sizeof(MDImageDebugMisc)); return debug_filename_; } // There's a misc_record but it encodes the debug filename in UTF-16. // (Actually, because miscellaneous records are so old, it's probably // UCS-2.) Convert it to UTF-8 for congruity with the other strings that // this method (and all other methods in the Minidump family) return. unsigned int bytes = module_.misc_record.data_size - sizeof(MDImageDebugMisc); if (bytes % 2 != 0) return NULL; unsigned int utf16_words = bytes / 2; // UTF16ToUTF8 expects a vector, so create a temporary one and // copy the UTF-16 data into it. vector string_utf16(utf16_words); memcpy(&string_utf16[0], &misc_record->data, bytes); // GetMiscRecord already byte-swapped the data[] field if it contains // UTF-16, so pass false as the swap argument. debug_filename_ = UTF16ToUTF8(string_utf16, false); } return debug_filename_; } void MinidumpModule::Print() { if (!valid_) return; printf("MDRawModule\n"); printf(" base_of_image = 0x%llx\n", module_.base_of_image); printf(" size_of_image = 0x%x\n", module_.size_of_image); printf(" checksum = 0x%x\n", module_.checksum); printf(" time_date_stamp = 0x%x\n", module_.time_date_stamp); printf(" module_name_rva = 0x%x\n", module_.module_name_rva); printf(" version_info.signature = 0x%x\n", module_.version_info.signature); printf(" version_info.struct_version = 0x%x\n", module_.version_info.struct_version); printf(" version_info.file_version = 0x%x:0x%x\n", module_.version_info.file_version_hi, module_.version_info.file_version_lo); printf(" version_info.product_version = 0x%x:0x%x\n", module_.version_info.product_version_hi, module_.version_info.product_version_lo); printf(" version_info.file_flags_mask = 0x%x\n", module_.version_info.file_flags_mask); printf(" version_info.file_flags = 0x%x\n", module_.version_info.file_flags); printf(" version_info.file_os = 0x%x\n", module_.version_info.file_os); printf(" version_info.file_type = 0x%x\n", module_.version_info.file_type); printf(" version_info.file_subtype = 0x%x\n", module_.version_info.file_subtype); printf(" version_info.file_date = 0x%x:0x%x\n", module_.version_info.file_date_hi, module_.version_info.file_date_lo); printf(" cv_record.data_size = %d\n", module_.cv_record.data_size); printf(" cv_record.rva = 0x%x\n", module_.cv_record.rva); printf(" misc_record.data_size = %d\n", module_.misc_record.data_size); printf(" misc_record.rva = 0x%x\n", module_.misc_record.rva); const char* module_name = GetName()->c_str(); if (module_name) printf(" (module_name) = \"%s\"\n", module_name); else printf(" (module_name) = (null)\n"); const MDCVInfoPDB70* cv_record = reinterpret_cast(GetCVRecord()); if (cv_record) { if (cv_record->cv_signature == MD_CVINFOPDB70_SIGNATURE) { printf(" (cv_record).cv_signature = 0x%x\n", cv_record->cv_signature); printf(" (cv_record).signature = %08x-%04x-%04x-%02x%02x-", cv_record->signature.data1, cv_record->signature.data2, cv_record->signature.data3, cv_record->signature.data4[0], cv_record->signature.data4[1]); for (unsigned int guidIndex = 2; guidIndex < 8; ++guidIndex) { printf("%02x", cv_record->signature.data4[guidIndex]); } printf("\n"); printf(" (cv_record).age = %d\n", cv_record->age); printf(" (cv_record).pdb_file_name = \"%s\"\n", cv_record->pdb_file_name); } else { const MDCVInfoPDB20* cv_record_20 = reinterpret_cast(cv_record); printf(" (cv_record).cv_header.signature = 0x%x\n", cv_record_20->cv_header.signature); printf(" (cv_record).cv_header.offset = 0x%x\n", cv_record_20->cv_header.offset); printf(" (cv_record).signature = 0x%x\n", cv_record_20->signature); printf(" (cv_record).age = %d\n", cv_record_20->age); printf(" (cv_record).pdb_file_name = \"%s\"\n", cv_record_20->pdb_file_name); } } else { printf(" (cv_record) = (null)\n"); } const MDImageDebugMisc* misc_record = GetMiscRecord(); if (misc_record) { printf(" (misc_record).data_type = 0x%x\n", misc_record->data_type); printf(" (misc_record).length = 0x%x\n", misc_record->length); printf(" (misc_record).unicode = %d\n", misc_record->unicode); // Don't bother printing the UTF-16, we don't really even expect to ever // see this misc_record anyway. if (misc_record->unicode) printf(" (misc_record).data = \"%s\"\n", misc_record->data); else printf(" (misc_record).data = (UTF-16)\n"); } else { printf(" (misc_record) = (null)\n"); } const string* debug_filename = GetDebugFilename(); if (debug_filename) { printf(" (debug_filename) = \"%s\"\n", debug_filename->c_str()); } else { printf(" (debug_filename) = (null)\n"); } printf("\n"); } // // MinidumpModuleList // MinidumpModuleList::MinidumpModuleList(Minidump* minidump) : MinidumpStream(minidump), range_map_(), modules_(NULL), module_count_(0) { } MinidumpModuleList::~MinidumpModuleList() { delete modules_; } bool MinidumpModuleList::Read(u_int32_t expected_size) { // Invalidate cached data. range_map_.Clear(); delete modules_; modules_ = NULL; module_count_ = 0; valid_ = false; u_int32_t module_count; if (expected_size < sizeof(module_count)) return false; if (!minidump_->ReadBytes(&module_count, sizeof(module_count))) return false; if (minidump_->swap()) Swap(&module_count); if (expected_size != sizeof(module_count) + module_count * MD_MODULE_SIZE) { return false; } // TODO(mmentovai): verify rational size! auto_ptr modules( new MinidumpModules(module_count, MinidumpModule(minidump_))); for (unsigned int module_index = 0; module_index < module_count; ++module_index) { MinidumpModule* module = &(*modules)[module_index]; // Assume that the file offset is correct after the last read. if (!module->Read()) return false; u_int64_t base_address = module->base_address(); u_int64_t module_size = module->size(); if (base_address == (u_int64_t)-1) return false; if (!range_map_.StoreRange(base_address, module_size, module_index)) return false; } modules_ = modules.release(); module_count_ = module_count; valid_ = true; return true; } MinidumpModule* MinidumpModuleList::GetModuleAtIndex(unsigned int index) const { if (!valid_ || index >= module_count_) return NULL; return &(*modules_)[index]; } MinidumpModule* MinidumpModuleList::GetModuleForAddress(u_int64_t address) { if (!valid_) return NULL; unsigned int module_index; if (!range_map_.RetrieveRange(address, &module_index)) return NULL; return GetModuleAtIndex(module_index); } void MinidumpModuleList::Print() { if (!valid_) return; printf("MinidumpModuleList\n"); printf(" module_count = %d\n", module_count_); printf("\n"); for (unsigned int module_index = 0; module_index < module_count_; ++module_index) { printf("module[%d]\n", module_index); (*modules_)[module_index].Print(); } } // // MinidumpMemoryList // MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump) : MinidumpStream(minidump), range_map_(), descriptors_(NULL), regions_(NULL), region_count_(0) { } MinidumpMemoryList::~MinidumpMemoryList() { delete descriptors_; delete regions_; } bool MinidumpMemoryList::Read(u_int32_t expected_size) { // Invalidate cached data. delete descriptors_; descriptors_ = NULL; delete regions_; regions_ = NULL; range_map_.Clear(); region_count_ = 0; valid_ = false; u_int32_t region_count; if (expected_size < sizeof(region_count)) return false; if (!minidump_->ReadBytes(®ion_count, sizeof(region_count))) return false; if (minidump_->swap()) Swap(®ion_count); if (expected_size != sizeof(region_count) + region_count * sizeof(MDMemoryDescriptor)) { return false; } // TODO(mmentovai): verify rational size! auto_ptr descriptors(new MemoryDescriptors(region_count)); // Read the entire array in one fell swoop, instead of reading one entry // at a time in the loop. if (!minidump_->ReadBytes(&(*descriptors)[0], sizeof(MDMemoryDescriptor) * region_count)) { return false; } auto_ptr regions( new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_))); for (unsigned int region_index = 0; region_index < region_count; ++region_index) { MDMemoryDescriptor* descriptor = &(*descriptors)[region_index]; if (minidump_->swap()) Swap(&*descriptor); u_int64_t base_address = descriptor->start_of_memory_range; u_int32_t region_size = descriptor->memory.data_size; // Check for base + size overflow or undersize. A separate size==0 // check is needed in case base == 0. u_int64_t high_address = base_address + region_size - 1; if (region_size == 0 || high_address < base_address) return false; if (!range_map_.StoreRange(base_address, region_size, region_index)) return false; (*regions)[region_index].SetDescriptor(descriptor); } region_count_ = region_count; descriptors_ = descriptors.release(); regions_ = regions.release(); valid_ = true; return true; } MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionAtIndex( unsigned int index) { if (!valid_ || index >= region_count_) return NULL; return &(*regions_)[index]; } MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress( u_int64_t address) { if (!valid_) return NULL; unsigned int region_index; if (!range_map_.RetrieveRange(address, ®ion_index)) return NULL; return GetMemoryRegionAtIndex(region_index); } void MinidumpMemoryList::Print() { if (!valid_) return; printf("MinidumpMemoryList\n"); printf(" region_count = %d\n", region_count_); printf("\n"); for (unsigned int region_index = 0; region_index < region_count_; ++region_index) { MDMemoryDescriptor* descriptor = &(*descriptors_)[region_index]; printf("region[%d]\n", region_index); printf("MDMemoryDescriptor\n"); printf(" start_of_memory_range = 0x%llx\n", descriptor->start_of_memory_range); printf(" memory.data_size = 0x%x\n", descriptor->memory.data_size); printf(" memory.rva = 0x%x\n", descriptor->memory.rva); MinidumpMemoryRegion* region = GetMemoryRegionAtIndex(region_index); if (region) { printf("Memory\n"); region->Print(); } else { printf("No memory\n"); } printf("\n"); } } // // MinidumpException // MinidumpException::MinidumpException(Minidump* minidump) : MinidumpStream(minidump), exception_(), context_(NULL) { } MinidumpException::~MinidumpException() { delete context_; } bool MinidumpException::Read(u_int32_t expected_size) { // Invalidate cached data. delete context_; context_ = NULL; valid_ = false; if (expected_size != sizeof(exception_)) return false; if (!minidump_->ReadBytes(&exception_, sizeof(exception_))) return false; if (minidump_->swap()) { Swap(&exception_.thread_id); // exception_.__align is for alignment only and does not need to be // swapped. Swap(&exception_.exception_record.exception_code); Swap(&exception_.exception_record.exception_flags); Swap(&exception_.exception_record.exception_record); Swap(&exception_.exception_record.exception_address); Swap(&exception_.exception_record.number_parameters); // exception_.exception_record.__align is for alignment only and does not // need to be swapped. for (unsigned int parameter_index = 0; parameter_index < MD_EXCEPTION_MAXIMUM_PARAMETERS; ++parameter_index) { Swap(&exception_.exception_record.exception_information[parameter_index]); } Swap(&exception_.thread_context); } valid_ = true; return true; } u_int32_t MinidumpException::GetThreadID() { return valid_ ? exception_.thread_id : 0; } MinidumpContext* MinidumpException::GetContext() { if (!valid_) return NULL; if (!context_) { if (!minidump_->SeekSet(exception_.thread_context.rva)) return NULL; auto_ptr context(new MinidumpContext(minidump_)); if (!context->Read(exception_.thread_context.data_size)) return NULL; context_ = context.release(); } return context_; } void MinidumpException::Print() { if (!valid_) return; printf("MDException\n"); printf(" thread_id = 0x%x\n", exception_.thread_id); printf(" exception_record.exception_code = 0x%x\n", exception_.exception_record.exception_code); printf(" exception_record.exception_flags = 0x%x\n", exception_.exception_record.exception_flags); printf(" exception_record.exception_record = 0x%llx\n", exception_.exception_record.exception_record); printf(" exception_record.exception_address = 0x%llx\n", exception_.exception_record.exception_address); printf(" exception_record.number_parameters = %d\n", exception_.exception_record.number_parameters); for (unsigned int parameterIndex = 0; parameterIndex < exception_.exception_record.number_parameters; ++parameterIndex) { printf(" exception_record.exception_information[%2d] = 0x%llx\n", parameterIndex, exception_.exception_record.exception_information[parameterIndex]); } printf(" thread_context.data_size = %d\n", exception_.thread_context.data_size); printf(" thread_context.rva = 0x%x\n", exception_.thread_context.rva); MinidumpContext* context = GetContext(); if (context) { printf("\n"); context->Print(); } else { printf(" (no context)\n"); printf("\n"); } } // // MinidumpSystemInfo // MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump) : MinidumpStream(minidump), system_info_(), csd_version_(NULL) { } MinidumpSystemInfo::~MinidumpSystemInfo() { delete csd_version_; } bool MinidumpSystemInfo::Read(u_int32_t expected_size) { // Invalidate cached data. delete csd_version_; csd_version_ = NULL; valid_ = false; if (expected_size != sizeof(system_info_)) return false; if (!minidump_->ReadBytes(&system_info_, sizeof(system_info_))) return false; if (minidump_->swap()) { Swap(&system_info_.processor_architecture); Swap(&system_info_.processor_level); Swap(&system_info_.processor_revision); // number_of_processors and product_type are 8-bit quantities and need no // swapping. Swap(&system_info_.major_version); Swap(&system_info_.minor_version); Swap(&system_info_.build_number); Swap(&system_info_.platform_id); Swap(&system_info_.csd_version_rva); Swap(&system_info_.suite_mask); Swap(&system_info_.reserved2); // TODO(mmentovai): This obviously only supports x86 for the time being. for (unsigned int i = 0; i < 3; ++i) Swap(&system_info_.cpu.x86_cpu_info.vendor_id[i]); Swap(&system_info_.cpu.x86_cpu_info.version_information); Swap(&system_info_.cpu.x86_cpu_info.feature_information); Swap(&system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); } valid_ = true; return true; } const string* MinidumpSystemInfo::GetCSDVersion() { if (!valid_) return NULL; if (!csd_version_) csd_version_ = minidump_->ReadString(system_info_.csd_version_rva); return csd_version_; } void MinidumpSystemInfo::Print() { if (!valid_) return; printf("MDRawSystemInfo\n"); printf(" processor_architecture = %d\n", system_info_.processor_architecture); printf(" processor_level = %d\n", system_info_.processor_level); printf(" number_of_processors = %d\n", system_info_.number_of_processors); printf(" product_type = %d\n", system_info_.product_type); printf(" major_version = %d\n", system_info_.major_version); printf(" minor_version = %d\n", system_info_.minor_version); printf(" build_number = %d\n", system_info_.build_number); printf(" platform_id = %d\n", system_info_.platform_id); printf(" csd_version_rva = 0x%x\n", system_info_.csd_version_rva); printf(" suite_mask = 0x%x\n", system_info_.suite_mask); printf(" reserved2 = 0x%x\n", system_info_.reserved2); for (unsigned int i = 0; i < 3; ++i) { printf(" cpu.x86_cpu_info.vendor_id[%d] = 0x%x\n", i, system_info_.cpu.x86_cpu_info.vendor_id[i]); } printf(" cpu.x86_cpu_info.version_information = 0x%x\n", system_info_.cpu.x86_cpu_info.version_information); printf(" cpu.x86_cpu_info.feature_information = 0x%x\n", system_info_.cpu.x86_cpu_info.feature_information); printf(" cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n", system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); const char* csd_version = GetCSDVersion()->c_str(); if (csd_version) printf(" (csd_version) = \"%s\"\n", csd_version); else printf(" (csd_version) = (null)\n"); printf("\n"); } // // MinidumpMiscInfo // MinidumpMiscInfo::MinidumpMiscInfo(Minidump* minidump) : MinidumpStream(minidump), misc_info_() { } bool MinidumpMiscInfo::Read(u_int32_t expected_size) { valid_ = false; if (expected_size != MD_MISCINFO_SIZE && expected_size != MD_MISCINFO2_SIZE) { return false; } if (!minidump_->ReadBytes(&misc_info_, expected_size)) return false; if (minidump_->swap()) { Swap(&misc_info_.size_of_info); Swap(&misc_info_.flags1); Swap(&misc_info_.process_id); Swap(&misc_info_.process_create_time); Swap(&misc_info_.process_user_time); Swap(&misc_info_.process_kernel_time); if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { Swap(&misc_info_.processor_max_mhz); Swap(&misc_info_.processor_current_mhz); Swap(&misc_info_.processor_mhz_limit); Swap(&misc_info_.processor_max_idle_state); Swap(&misc_info_.processor_current_idle_state); } } if (misc_info_.size_of_info != expected_size) return false; valid_ = true; return true; } void MinidumpMiscInfo::Print() { if (!valid_) return; printf("MDRawMiscInfo\n"); printf(" size_of_info = %d\n", misc_info_.size_of_info); printf(" flags1 = 0x%x\n", misc_info_.flags1); printf(" process_id = 0x%x\n", misc_info_.process_id); printf(" process_create_time = 0x%x\n", misc_info_.process_create_time); printf(" process_user_time = 0x%x\n", misc_info_.process_user_time); printf(" process_kernel_time = 0x%x\n", misc_info_.process_kernel_time); if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { printf(" processor_max_mhz = %d\n", misc_info_.processor_max_mhz); printf(" processor_current_mhz = %d\n", misc_info_.processor_current_mhz); printf(" processor_mhz_limit = %d\n", misc_info_.processor_mhz_limit); printf(" processor_max_idle_state = 0x%x\n", misc_info_.processor_max_idle_state); printf(" processor_current_idle_state = 0x%x\n", misc_info_.processor_current_idle_state); } } // // Minidump // Minidump::Minidump(const string& path) : header_(), directory_(NULL), stream_map_(NULL), path_(path), fd_(-1), swap_(false), valid_(false) { } Minidump::~Minidump() { delete directory_; delete stream_map_; if (fd_ != -1) close(fd_); } bool Minidump::Open() { if (fd_ != -1) { // The file is already open. Seek to the beginning, which is the position // the file would be at if it were opened anew. return SeekSet(0); } // O_BINARY is useful (and defined) on Windows. On other platforms, it's // useless, and because it's defined as 0 above, harmless. fd_ = open(path_.c_str(), O_RDONLY | O_BINARY); if (fd_ == -1) return false; return true; } bool Minidump::Read() { // Invalidate cached data. delete directory_; directory_ = NULL; delete stream_map_; stream_map_ = NULL; valid_ = false; if (!Open()) return false; if (!ReadBytes(&header_, sizeof(MDRawHeader))) return false; if (header_.signature != MD_HEADER_SIGNATURE) { // The file may be byte-swapped. Under the present architecture, these // classes don't know or need to know what CPU (or endianness) the // minidump was produced on in order to parse it. Use the signature as // a byte order marker. u_int32_t signature_swapped = header_.signature; Swap(&signature_swapped); if (signature_swapped != MD_HEADER_SIGNATURE) { // This isn't a minidump or a byte-swapped minidump. return false; } swap_ = true; } else { // The file is not byte-swapped. Set swap_ false (it may have been true // if the object is being reused?) swap_ = false; } if (swap_) { Swap(&header_.signature); Swap(&header_.version); Swap(&header_.stream_count); Swap(&header_.stream_directory_rva); Swap(&header_.checksum); Swap(&header_.time_date_stamp); Swap(&header_.flags); } // Version check. The high 16 bits of header_.version contain something // else "implementation specific." if ((header_.version & 0x0000ffff) != MD_HEADER_VERSION) { return false; } if (!SeekSet(header_.stream_directory_rva)) return false; // TODO(mmentovai): verify rational size! auto_ptr directory( new MinidumpDirectoryEntries(header_.stream_count)); // Read the entire array in one fell swoop, instead of reading one entry // at a time in the loop. if (!ReadBytes(&(*directory)[0], sizeof(MDRawDirectory) * header_.stream_count)) return false; auto_ptr stream_map(new MinidumpStreamMap()); for (unsigned int stream_index = 0; stream_index < header_.stream_count; ++stream_index) { MDRawDirectory* directory_entry = &(*directory)[stream_index]; if (swap_) { Swap(&directory_entry->stream_type); Swap(&directory_entry->location); } // Initialize the stream_map map, which speeds locating a stream by // type. unsigned int stream_type = directory_entry->stream_type; switch(stream_type) { case THREAD_LIST_STREAM: case MODULE_LIST_STREAM: case MEMORY_LIST_STREAM: case EXCEPTION_STREAM: case SYSTEM_INFO_STREAM: case MISC_INFO_STREAM: { if (stream_map->find(stream_type) != stream_map->end()) { // Another stream with this type was already found. A minidump // file should contain at most one of each of these stream types. return false; } // Fall through to default } default: { // Overwrites for stream types other than those above, but it's // expected to be the user's burden in that case. (*stream_map)[stream_type].stream_index = stream_index; } } } directory_ = directory.release(); stream_map_ = stream_map.release(); valid_ = true; return true; } MinidumpThreadList* Minidump::GetThreadList() { MinidumpThreadList* thread_list; return GetStream(&thread_list); } MinidumpModuleList* Minidump::GetModuleList() { MinidumpModuleList* module_list; return GetStream(&module_list); } MinidumpMemoryList* Minidump::GetMemoryList() { MinidumpMemoryList* memory_list; return GetStream(&memory_list); } MinidumpException* Minidump::GetException() { MinidumpException* exception; return GetStream(&exception); } MinidumpSystemInfo* Minidump::GetSystemInfo() { MinidumpSystemInfo* system_info; return GetStream(&system_info); } MinidumpMiscInfo* Minidump::GetMiscInfo() { MinidumpMiscInfo* misc_info; return GetStream(&misc_info); } void Minidump::Print() { if (!valid_) return; printf("MDRawHeader\n"); printf(" signature = 0x%x\n", header_.signature); printf(" version = 0x%x\n", header_.version); printf(" stream_count = %d\n", header_.stream_count); printf(" stream_directory_rva = 0x%x\n", header_.stream_directory_rva); printf(" checksum = 0x%x\n", header_.checksum); struct tm* timestruct = gmtime((time_t*)&header_.time_date_stamp); char timestr[20]; strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", timestruct); printf(" time_date_stamp = 0x%x %s\n", header_.time_date_stamp, timestr); printf(" flags = 0x%llx\n", header_.flags); printf("\n"); for (unsigned int stream_index = 0; stream_index < header_.stream_count; ++stream_index) { MDRawDirectory* directory_entry = &(*directory_)[stream_index]; printf("mDirectory[%d]\n", stream_index); printf("MDRawDirectory\n"); printf(" stream_type = %d\n", directory_entry->stream_type); printf(" location.data_size = %d\n", directory_entry->location.data_size); printf(" location.rva = 0x%x\n", directory_entry->location.rva); printf("\n"); } printf("Streams:\n"); for (MinidumpStreamMap::const_iterator iterator = stream_map_->begin(); iterator != stream_map_->end(); ++iterator) { u_int32_t stream_type = iterator->first; MinidumpStreamInfo info = iterator->second; printf(" stream type %2d at index %d\n", stream_type, info.stream_index); } printf("\n"); } const MDRawDirectory* Minidump::GetDirectoryEntryAtIndex(unsigned int index) const { if (!valid_ || index >= header_.stream_count) return NULL; return &(*directory_)[index]; } bool Minidump::ReadBytes(void* bytes, size_t count) { // Can't check valid_ because Read needs to call this method before // validity can be determined. The only member that this method // depends on is mFD, and an unset or invalid fd may generate an // error but should not cause a crash. ssize_t bytes_read = read(fd_, bytes, count); if (static_cast(bytes_read) != count) return false; return true; } bool Minidump::SeekSet(off_t offset) { // Can't check valid_ because Read needs to call this method before // validity can be determined. The only member that this method // depends on is mFD, and an unset or invalid fd may generate an // error but should not cause a crash. off_t sought = lseek(fd_, offset, SEEK_SET); if (sought != offset) return false; return true; } string* Minidump::ReadString(off_t offset) { if (!valid_) return NULL; if (!SeekSet(offset)) return NULL; u_int32_t bytes; if (!ReadBytes(&bytes, sizeof(bytes))) return NULL; if (swap_) Swap(&bytes); if (bytes % 2 != 0) return NULL; unsigned int utf16_words = bytes / 2; // TODO(mmentovai): verify rational size! vector string_utf16(utf16_words); if (!ReadBytes(&string_utf16[0], bytes)) return NULL; return UTF16ToUTF8(string_utf16, swap_); } bool Minidump::SeekToStreamType(u_int32_t stream_type, u_int32_t* stream_length) { if (!valid_ || !stream_length) return false; MinidumpStreamMap::const_iterator iterator = stream_map_->find(stream_type); if (iterator == stream_map_->end()) { // This stream type didn't exist in the directory. return false; } MinidumpStreamInfo info = iterator->second; if (info.stream_index >= header_.stream_count) return false; MDRawDirectory* directory_entry = &(*directory_)[info.stream_index]; if (!SeekSet(directory_entry->location.rva)) return false; *stream_length = directory_entry->location.data_size; return true; } template T* Minidump::GetStream(T** stream) { // stream is a garbage parameter that's present only to account for C++'s // inability to overload a method based solely on its return type. if (!stream) return NULL; *stream = NULL; if (!valid_) return NULL; u_int32_t stream_type = T::kStreamType; MinidumpStreamMap::iterator iterator = stream_map_->find(stream_type); if (iterator == stream_map_->end()) { // This stream type didn't exist in the directory. return NULL; } // Get a pointer so that the stored stream field can be altered. MinidumpStreamInfo* info = &iterator->second; if (info->stream) { // This cast is safe because info.stream is only populated by this // method, and there is a direct correlation between T and stream_type. *stream = static_cast(info->stream); return *stream; } u_int32_t stream_length; if (!SeekToStreamType(stream_type, &stream_length)) return NULL; auto_ptr new_stream(new T(this)); if (!new_stream->Read(stream_length)) return NULL; *stream = new_stream.release(); info->stream = *stream; return *stream; } } // namespace google_airbag