diff options
Diffstat (limited to 'src/processor/minidump.cc')
-rw-r--r-- | src/processor/minidump.cc | 390 |
1 files changed, 283 insertions, 107 deletions
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index ed2e2df5..9ffbc1d0 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -54,6 +54,8 @@ typedef SSIZE_T ssize_t; #include "processor/range_map-inl.h" #include "google_airbag/processor/minidump.h" +#include "processor/basic_code_module.h" +#include "processor/basic_code_modules.h" #include "processor/scoped_ptr.h" @@ -999,11 +1001,11 @@ void MinidumpThreadList::Print() { MinidumpModule::MinidumpModule(Minidump* minidump) : MinidumpObject(minidump), + module_valid_(false), module_(), name_(NULL), cv_record_(NULL), - misc_record_(NULL), - debug_filename_(NULL) { + misc_record_(NULL) { } @@ -1011,7 +1013,6 @@ MinidumpModule::~MinidumpModule() { delete name_; delete cv_record_; delete misc_record_; - delete debug_filename_; } @@ -1023,9 +1024,8 @@ bool MinidumpModule::Read() { cv_record_ = NULL; delete misc_record_; misc_record_ = NULL; - delete debug_filename_; - debug_filename_ = NULL; + module_valid_ = false; valid_ = false; if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) @@ -1062,24 +1062,239 @@ bool MinidumpModule::Read() { if (module_.size_of_image == 0 || high_address < module_.base_of_image) return false; + module_valid_ = true; + return true; +} + + +bool MinidumpModule::ReadAuxiliaryData() { + if (!module_valid_) + return false; + + // Each module must have a name. + name_ = minidump_->ReadString(module_.module_name_rva); + if (!name_) + return false; + + // CodeView and miscellaneous debug records are only required if the + // module indicates that they exist. + if (module_.cv_record.data_size && !GetCVRecord()) + return false; + + if (module_.misc_record.data_size && !GetMiscRecord()) + return false; + valid_ = true; return true; } -const string* MinidumpModule::GetName() { +string MinidumpModule::code_file() const { if (!valid_) - return NULL; + return ""; + + return *name_; +} - if (!name_) - name_ = minidump_->ReadString(module_.module_name_rva); - return name_; +string MinidumpModule::code_identifier() const { + if (!valid_) + return ""; + + MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo(); + if (!minidump_system_info) + return ""; + + const MDRawSystemInfo *raw_system_info = minidump_system_info->system_info(); + if (!raw_system_info) + return ""; + + string identifier; + + switch (raw_system_info->platform_id) { + case MD_OS_WIN32_NT: + case MD_OS_WIN32_WINDOWS: { + char identifier_string[17]; + snprintf(identifier_string, sizeof(identifier_string), "%08x%x", + module_.time_date_stamp, module_.size_of_image); + identifier = identifier_string; + break; + } + + case MD_OS_MAC_OS_X: { + // TODO(mmentovai): support uuid extension if present, otherwise fall + // back to version (from LC_ID_DYLIB?), otherwise fall back to something + // else. + identifier = "id"; + break; + } + + default: { + // Without knowing what OS generated the dump, we can't generate a good + // identifier. Return an empty string, signalling failure. + break; + } + } + + return identifier; } -const u_int8_t* MinidumpModule::GetCVRecord() { +string MinidumpModule::debug_file() const { + if (!valid_) + return ""; + + string file; + // Prefer the CodeView record if present. + const MDCVInfoPDB70* cv_record_70 = + reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]); + if (cv_record_70) { + if (cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE) { + // GetCVRecord guarantees pdb_file_name is null-terminated. + file = reinterpret_cast<const char*>(cv_record_70->pdb_file_name); + } else if (cv_record_70->cv_signature == MD_CVINFOPDB20_SIGNATURE) { + // It's actually a MDCVInfoPDB20 structure. + const MDCVInfoPDB20* cv_record_20 = + reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]); + + // GetCVRecord guarantees pdb_file_name is null-terminated. + file = reinterpret_cast<const char*>(cv_record_20->pdb_file_name); + } + + // If there's a CodeView record but it doesn't match a known signature, + // try the miscellaneous record - but it's suspicious because + // GetCVRecord shouldn't have accepted a CodeView record that doesn't + // match a known signature. + } + + if (file.empty()) { + // No usable CodeView record. Try the miscellaneous debug record. + const MDImageDebugMisc* misc_record = + reinterpret_cast<const MDImageDebugMisc *>(&(*misc_record_)[0]); + if (misc_record) { + if (!misc_record->unicode) { + // If it's not Unicode, just stuff it into the string. It's unclear + // if misc_record->data is 0-terminated, so use an explicit size. + file = string( + reinterpret_cast<const char*>(misc_record->data), + module_.misc_record.data_size - sizeof(MDImageDebugMisc)); + } else { + // There's a misc_record but it encodes the debug filename in UTF-16. + // (Actually, because miscellaneous records are so old, it's probably + // UCS-2.) Convert it to UTF-8 for congruity with the other strings + // that this method (and all other methods in the Minidump family) + // return. + + unsigned int bytes = + module_.misc_record.data_size - sizeof(MDImageDebugMisc); + if (bytes % 2 == 0) { + unsigned int utf16_words = bytes / 2; + + // UTF16ToUTF8 expects a vector<u_int16_t>, so create a temporary one + // and copy the UTF-16 data into it. + vector<u_int16_t> string_utf16(utf16_words); + if (utf16_words) + memcpy(&string_utf16[0], &misc_record->data, bytes); + + // GetMiscRecord already byte-swapped the data[] field if it contains + // UTF-16, so pass false as the swap argument. + scoped_ptr<string> new_file(UTF16ToUTF8(string_utf16, false)); + file = *new_file; + } + } + } + } + + return file; +} + + +string MinidumpModule::debug_identifier() const { + if (!valid_) + return ""; + + string identifier; + + // Use the CodeView record if present. + const MDCVInfoPDB70* cv_record_70 = + reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]); + if (cv_record_70) { + if (cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE) { + char identifier_string[41]; + snprintf(identifier_string, sizeof(identifier_string), + "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%X", + cv_record_70->signature.data1, + cv_record_70->signature.data2, + cv_record_70->signature.data3, + cv_record_70->signature.data4[0], + cv_record_70->signature.data4[1], + cv_record_70->signature.data4[2], + cv_record_70->signature.data4[3], + cv_record_70->signature.data4[4], + cv_record_70->signature.data4[5], + cv_record_70->signature.data4[6], + cv_record_70->signature.data4[7], + cv_record_70->age); + identifier = identifier_string; + } else if (cv_record_70->cv_signature == MD_CVINFOPDB20_SIGNATURE) { + // It's actually a MDCVInfoPDB20 structure. + const MDCVInfoPDB20* cv_record_20 = + reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]); + + char identifier_string[17]; + snprintf(identifier_string, sizeof(identifier_string), + "%08x%x", cv_record_20->signature, cv_record_20->age); + identifier = identifier_string; + } + } + + // TODO(mmentovai): if there's no CodeView record, there might be a + // miscellaneous debug record. It only carries a filename, though, and no + // identifier. I'm not sure what the right thing to do for the identifier + // is in that case, but I don't expect to find many modules without a + // CodeView record (or some other Airbag extension structure in place of + // a CodeView record). Treat it as an error (empty identifier) for now. + + // TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier(). + + return identifier; +} + + +string MinidumpModule::version() const { if (!valid_) + return ""; + + string version; + + if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE && + module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) { + char version_string[24]; + snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u", + module_.version_info.file_version_hi >> 16, + module_.version_info.file_version_hi & 0xffff, + module_.version_info.file_version_lo >> 16, + module_.version_info.file_version_lo & 0xffff); + version = version_string; + } + + // TODO(mmentovai): possibly support other struct types in place of + // the one used with MD_VSFIXEDFILEINFO_SIGNATURE. We can possibly use + // a different structure that better represents versioning facilities on + // Mac OS X and Linux, instead of forcing them to adhere to the dotted + // quad of 16-bit ints that Windows uses. + + return version; +} + + +const CodeModule* MinidumpModule::Copy() const { + return new BasicCodeModule(this); +} + + +const u_int8_t* MinidumpModule::GetCVRecord() { + if (!module_valid_) return NULL; if (!cv_record_) { @@ -1157,7 +1372,7 @@ const u_int8_t* MinidumpModule::GetCVRecord() { const MDImageDebugMisc* MinidumpModule::GetMiscRecord() { - if (!valid_) + if (!module_valid_) return NULL; if (!misc_record_) { @@ -1216,83 +1431,6 @@ const MDImageDebugMisc* MinidumpModule::GetMiscRecord() { } -// 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<const MDCVInfoPDB70*>(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<const char*>(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<const MDCVInfoPDB20*>(cv_record_70); - - // GetCVRecord guarantees pdb_file_name is null-terminated. - debug_filename_ = new string( - reinterpret_cast<const char*>(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<const char*>(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<u_int16_t>, so create a temporary one and - // copy the UTF-16 data into it. - vector<u_int16_t> string_utf16(utf16_words); - if (utf16_words) - memcpy(&string_utf16[0], &misc_record->data, bytes); - - // GetMiscRecord already byte-swapped the data[] field if it contains - // UTF-16, so pass false as the swap argument. - debug_filename_ = UTF16ToUTF8(string_utf16, false); - } - - return debug_filename_; -} - - void MinidumpModule::Print() { if (!valid_) return; @@ -1340,11 +1478,9 @@ void MinidumpModule::Print() { 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"); + printf(" (code_file) = \"%s\"\n", code_file().c_str()); + printf(" (code_identifier) = \"%s\"\n", + code_identifier().c_str()); const MDCVInfoPDB70* cv_record = reinterpret_cast<const MDCVInfoPDB70*>(GetCVRecord()); @@ -1405,13 +1541,10 @@ void MinidumpModule::Print() { 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(" (debug_file) = \"%s\"\n", debug_file().c_str()); + printf(" (debug_identifier) = \"%s\"\n", + debug_identifier().c_str()); + printf(" (version) = \"%s\"\n", version().c_str()); printf("\n"); } @@ -1471,6 +1604,20 @@ bool MinidumpModuleList::Read(u_int32_t expected_size) { // Assume that the file offset is correct after the last read. if (!module->Read()) return false; + } + + // Loop through the module list once more to read additional data and + // build the range map. This is done in a second pass because + // MinidumpModule::ReadAuxiliaryData seeks around, and if it were + // included in the loop above, additional seeks would be needed where + // none are now to read contiguous data. + for (unsigned int module_index = 0; + module_index < module_count; + ++module_index) { + MinidumpModule* module = &(*modules)[module_index]; + + if (!module->ReadAuxiliaryData()) + return false; u_int64_t base_address = module->base_address(); u_int64_t module_size = module->size(); @@ -1491,27 +1638,56 @@ bool MinidumpModuleList::Read(u_int32_t expected_size) { } -MinidumpModule* MinidumpModuleList::GetModuleAtIndex(unsigned int index) - const { - if (!valid_ || index >= module_count_) +const MinidumpModule* MinidumpModuleList::GetModuleForAddress( + u_int64_t address) const { + if (!valid_) return NULL; - return &(*modules_)[index]; + unsigned int module_index; + if (!range_map_->RetrieveRange(address, &module_index, NULL, NULL)) + return NULL; + + return GetModuleAtIndex(module_index); } -MinidumpModule* MinidumpModuleList::GetModuleForAddress(u_int64_t address) { +const MinidumpModule* MinidumpModuleList::GetMainModule() const { if (!valid_) return NULL; + // The main code module is the first one present in a minidump file's + // MDRawModuleList. + return GetModuleAtSequence(0); +} + + +const MinidumpModule* MinidumpModuleList::GetModuleAtSequence( + unsigned int sequence) const { + if (!valid_ || sequence >= module_count_) + return NULL; + unsigned int module_index; - if (!range_map_->RetrieveRange(address, &module_index, NULL, NULL)) + if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index, NULL, NULL)) return NULL; return GetModuleAtIndex(module_index); } +const MinidumpModule* MinidumpModuleList::GetModuleAtIndex( + unsigned int index) const { + if (!valid_ || index >= module_count_) + return NULL; + + return &(*modules_)[index]; +} + + +const CodeModules* MinidumpModuleList::Copy() const { + return new BasicCodeModules(this); +} + + void MinidumpModuleList::Print() { if (!valid_) return; |