aboutsummaryrefslogtreecommitdiff
path: root/src/processor
diff options
context:
space:
mode:
authormmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-12-05 22:52:28 +0000
committermmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-12-05 22:52:28 +0000
commitdb3342a10ec30902aa9018b80e1d9a40bd01c487 (patch)
tree933903715ae6d5d5f1b7827e0612314162b0aecf /src/processor
parentDon't use CRT in exception handler code (#86). r=bryner (diff)
downloadbreakpad-db3342a10ec30902aa9018b80e1d9a40bd01c487.tar.xz
Module API (#32). r=waylonis, bryner
- Introduces a standard API for dealing with modules. MinidumpModule is now a concrete implementation of this API. Code may interact with single modules using the CodeModule interface, and collections of modules using its container, the CodeModules interface. - CodeModule is used directly by SymbolSupplier implementations and SourceLineResolver. Reliance on the specific implementation in MinidumpModule has been eliminated. - Module lists are now added to ProcessState objects. Module references in each stack frame are now pointers to objects in these module lists. - The sample minidump_stackwalk tool prints the module list after printing all threads' stacks. http://groups.google.com/group/airbag-dev/browse_frm/thread/a9c0550edde54cf8 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@74 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor')
-rw-r--r--src/processor/basic_code_module.h95
-rw-r--r--src/processor/basic_code_modules.cc112
-rw-r--r--src/processor/basic_code_modules.h85
-rw-r--r--src/processor/minidump.cc390
-rw-r--r--src/processor/minidump_processor.cc18
-rw-r--r--src/processor/minidump_processor_unittest.cc45
-rw-r--r--src/processor/minidump_stackwalk.cc35
-rw-r--r--src/processor/postfix_evaluator_unittest.cc35
-rw-r--r--src/processor/process_state.cc3
-rw-r--r--src/processor/range_map-inl.h29
-rw-r--r--src/processor/range_map.h14
-rw-r--r--src/processor/range_map_unittest.cc77
-rw-r--r--src/processor/simple_symbol_supplier.cc56
-rw-r--r--src/processor/simple_symbol_supplier.h31
-rw-r--r--src/processor/source_line_resolver.cc24
-rw-r--r--src/processor/source_line_resolver_unittest.cc56
-rw-r--r--src/processor/stackwalker.cc15
-rw-r--r--src/processor/stackwalker_ppc.cc4
-rw-r--r--src/processor/stackwalker_ppc.h8
-rw-r--r--src/processor/stackwalker_selftest.cc35
-rw-r--r--src/processor/stackwalker_x86.cc4
-rw-r--r--src/processor/stackwalker_x86.h7
-rw-r--r--src/processor/testdata/minidump2.dump.out91
-rw-r--r--src/processor/testdata/minidump2.stackwalk.out15
24 files changed, 1029 insertions, 255 deletions
diff --git a/src/processor/basic_code_module.h b/src/processor/basic_code_module.h
new file mode 100644
index 00000000..b05a4a76
--- /dev/null
+++ b/src/processor/basic_code_module.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// basic_code_module.h: Carries information about code modules that are loaded
+// into a process.
+//
+// This is a basic concrete implementation of CodeModule. It cannot be
+// instantiated directly, only based on other objects that implement
+// the CodeModule interface. It exists to provide a CodeModule implementation
+// a place to store information when the life of the original object (such as
+// a MinidumpModule) cannot be guaranteed.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_BASIC_CODE_MODULE_H__
+#define PROCESSOR_BASIC_CODE_MODULE_H__
+
+#include <string>
+
+#include "google_airbag/processor/code_module.h"
+
+namespace google_airbag {
+
+using std::string;
+
+class BasicCodeModule : public CodeModule {
+ public:
+ // Creates a new BasicCodeModule given any existing CodeModule
+ // implementation. This is useful to make a copy of the data relevant to
+ // the CodeModule interface without requiring all of the resources that
+ // other CodeModule implementations may require.
+ explicit BasicCodeModule(const CodeModule *that)
+ : base_address_(that->base_address()),
+ size_(that->size()),
+ code_file_(that->code_file()),
+ code_identifier_(that->code_identifier()),
+ debug_file_(that->debug_file()),
+ debug_identifier_(that->debug_identifier()),
+ version_(that->version()) {}
+ virtual ~BasicCodeModule() {}
+
+ // See code_module.h for descriptions of these methods and the associated
+ // members.
+ virtual u_int64_t base_address() const { return base_address_; }
+ virtual u_int64_t size() const { return size_; }
+ virtual string code_file() const { return code_file_; }
+ virtual string code_identifier() const { return code_identifier_; }
+ virtual string debug_file() const { return debug_file_; }
+ virtual string debug_identifier() const { return debug_identifier_; }
+ virtual string version() const { return version_; }
+ virtual const CodeModule* Copy() const { return new BasicCodeModule(this); }
+
+ private:
+ u_int64_t base_address_;
+ u_int64_t size_;
+ string code_file_;
+ string code_identifier_;
+ string debug_file_;
+ string debug_identifier_;
+ string version_;
+
+ // Disallow copy constructor and assignment operator.
+ BasicCodeModule(const BasicCodeModule &that);
+ void operator=(const BasicCodeModule &that);
+};
+
+} // namespace google_airbag
+
+#endif // PROCESSOR_BASIC_CODE_MODULE_H__
diff --git a/src/processor/basic_code_modules.cc b/src/processor/basic_code_modules.cc
new file mode 100644
index 00000000..82395109
--- /dev/null
+++ b/src/processor/basic_code_modules.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// basic_code_modules.cc: Contains all of the CodeModule objects that
+// were loaded into a single process.
+//
+// See basic_code_modules.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include <cassert>
+
+#include "processor/basic_code_modules.h"
+#include "google_airbag/processor/code_module.h"
+#include "processor/linked_ptr.h"
+#include "processor/range_map-inl.h"
+
+namespace google_airbag {
+
+BasicCodeModules::BasicCodeModules(const CodeModules *that)
+ : main_address_(0),
+ map_(new RangeMap<u_int64_t, linked_ptr<const CodeModule> >()) {
+ assert(that);
+
+ const CodeModule *main_module = that->GetMainModule();
+ if (main_module)
+ main_address_ = main_module->base_address();
+
+ unsigned int count = that->module_count();
+ for (unsigned int module_sequence = 0;
+ module_sequence < count;
+ ++module_sequence) {
+ // Make a copy of the module and insert it into the map. Use
+ // GetModuleAtIndex because ordering is unimportant when slurping the
+ // entire list, and GetModuleAtIndex may be faster than
+ // GetModuleAtSequence.
+ const CodeModule *module = that->GetModuleAtIndex(module_sequence)->Copy();
+ map_->StoreRange(module->base_address(), module->size(),
+ linked_ptr<const CodeModule>(module));
+ }
+}
+
+BasicCodeModules::~BasicCodeModules() {
+ delete map_;
+}
+
+unsigned int BasicCodeModules::module_count() const {
+ return map_->GetCount();
+}
+
+const CodeModule* BasicCodeModules::GetModuleForAddress(
+ u_int64_t address) const {
+ linked_ptr<const CodeModule> module;
+ if (!map_->RetrieveRange(address, &module, NULL, NULL))
+ return NULL;
+
+ return module.get();
+}
+
+const CodeModule* BasicCodeModules::GetMainModule() const {
+ return GetModuleForAddress(main_address_);
+}
+
+const CodeModule* BasicCodeModules::GetModuleAtSequence(
+ unsigned int sequence) const {
+ linked_ptr<const CodeModule> module;
+ if (!map_->RetrieveRangeAtIndex(sequence, &module, NULL, NULL))
+ return NULL;
+
+ return module.get();
+}
+
+const CodeModule* BasicCodeModules::GetModuleAtIndex(
+ unsigned int index) const {
+ // This class stores everything in a RangeMap, without any more-efficient
+ // way to walk the list of CodeModule objects. Implement GetModuleAtIndex
+ // using GetModuleAtSequence, which meets all of the requirements, and
+ // in addition, guarantees ordering.
+ return GetModuleAtSequence(index);
+}
+
+const CodeModules* BasicCodeModules::Copy() const {
+ return new BasicCodeModules(this);
+}
+
+} // namespace google_airbag
diff --git a/src/processor/basic_code_modules.h b/src/processor/basic_code_modules.h
new file mode 100644
index 00000000..73bccdfd
--- /dev/null
+++ b/src/processor/basic_code_modules.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// basic_code_modules.h: Contains all of the CodeModule objects that
+// were loaded into a single process.
+//
+// This is a basic concrete implementation of CodeModules. It cannot be
+// instantiated directly, only based on other objects that implement
+// the CodeModules interface. It exists to provide a CodeModules
+// implementation a place to store information when the life of the original
+// object (such as a MinidumpModuleList) cannot be guaranteed.
+//
+// Author: Mark Mentovai
+
+#ifndef PROCESSOR_BASIC_CODE_MODULES_H__
+#define PROCESSOR_BASIC_CODE_MODULES_H__
+
+#include "google_airbag/processor/code_modules.h"
+
+namespace google_airbag {
+
+template<typename T> class linked_ptr;
+template<typename AddressType, typename EntryType> class RangeMap;
+
+class BasicCodeModules : public CodeModules {
+ public:
+ // Creates a new BasicCodeModules object given any existing CodeModules
+ // implementation. This is useful to make a copy of the data relevant to
+ // the CodeModules and CodeModule interfaces without requiring all of the
+ // resources that other implementations may require. A copy will be
+ // made of each contained CodeModule using CodeModule::Copy.
+ explicit BasicCodeModules(const CodeModules *that);
+
+ virtual ~BasicCodeModules();
+
+ // See code_modules.h for descriptions of these methods.
+ virtual unsigned int module_count() const;
+ virtual const CodeModule* GetModuleForAddress(u_int64_t address) const;
+ virtual const CodeModule* GetMainModule() const;
+ virtual const CodeModule* GetModuleAtSequence(unsigned int sequence) const;
+ virtual const CodeModule* GetModuleAtIndex(unsigned int index) const;
+ virtual const CodeModules* Copy() const;
+
+ private:
+ // The base address of the main module.
+ u_int64_t main_address_;
+
+ // The map used to contain each CodeModule, keyed by each CodeModule's
+ // address range.
+ RangeMap<u_int64_t, linked_ptr<const CodeModule> > *map_;
+
+ // Disallow copy constructor and assignment operator.
+ BasicCodeModules(const BasicCodeModules &that);
+ void operator=(const BasicCodeModules &that);
+};
+
+} // namespace google_airbag
+
+#endif // PROCESSOR_BASIC_CODE_MODULES_H__
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;
diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc
index f9f8066a..d2c44159 100644
--- a/src/processor/minidump_processor.cc
+++ b/src/processor/minidump_processor.cc
@@ -75,6 +75,14 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
&dump, &process_state->crash_address_);
}
+ MinidumpModuleList *module_list = dump.GetModuleList();
+
+ // Put a copy of the module list into ProcessState object. This is not
+ // necessarily a MinidumpModuleList, but it adheres to the CodeModules
+ // interface, which is all that ProcessState needs to expose.
+ if (module_list)
+ process_state->modules_ = module_list->Copy();
+
MinidumpThreadList *threads = dump.GetThreadList();
if (!threads) {
return NULL;
@@ -137,10 +145,18 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
return NULL;
}
+ // Use process_state->modules_ instead of module_list, because the
+ // |modules| argument will be used to populate the |module| fields in
+ // the returned StackFrame objects, which will be placed into the
+ // returned ProcessState object. module_list's lifetime is only as
+ // long as the Minidump object: it will be deleted when this function
+ // returns. process_state->modules_ is owned by the ProcessState object
+ // (just like the StackFrame objects), and is much more suitable for this
+ // task.
scoped_ptr<Stackwalker> stackwalker(
Stackwalker::StackwalkerForCPU(context,
thread_memory,
- dump.GetModuleList(),
+ process_state->modules_,
supplier_));
if (!stackwalker.get()) {
return NULL;
diff --git a/src/processor/minidump_processor_unittest.cc b/src/processor/minidump_processor_unittest.cc
index 9166be92..f4b7b3c0 100644
--- a/src/processor/minidump_processor_unittest.cc
+++ b/src/processor/minidump_processor_unittest.cc
@@ -32,7 +32,8 @@
#include <string>
#include "google_airbag/processor/call_stack.h"
-#include "google_airbag/processor/minidump.h"
+#include "google_airbag/processor/code_module.h"
+#include "google_airbag/processor/code_modules.h"
#include "google_airbag/processor/minidump_processor.h"
#include "google_airbag/processor/process_state.h"
#include "google_airbag/processor/stack_frame.h"
@@ -43,7 +44,7 @@ namespace {
using std::string;
using google_airbag::CallStack;
-using google_airbag::MinidumpModule;
+using google_airbag::CodeModule;
using google_airbag::MinidumpProcessor;
using google_airbag::ProcessState;
using google_airbag::scoped_ptr;
@@ -55,15 +56,17 @@ using google_airbag::SymbolSupplier;
return false; \
}
+#define ASSERT_FALSE(cond) ASSERT_TRUE(!(cond))
+
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
class TestSymbolSupplier : public SymbolSupplier {
public:
- virtual string GetSymbolFile(MinidumpModule *module);
+ virtual string GetSymbolFile(const CodeModule *module);
};
-string TestSymbolSupplier::GetSymbolFile(MinidumpModule *module) {
- if (*(module->GetName()) == "c:\\test_app.exe") {
+string TestSymbolSupplier::GetSymbolFile(const CodeModule *module) {
+ if (module && module->code_file() == "c:\\test_app.exe") {
// The funny-looking pathname is so that the symbol file can also be
// reached by a SimpleSymbolSupplier.
return string(getenv("srcdir") ? getenv("srcdir") : ".") +
@@ -92,38 +95,54 @@ static bool RunTests() {
ASSERT_EQ(state->crash_address(), 0x45);
ASSERT_EQ(state->threads()->size(), 1);
ASSERT_EQ(state->requesting_thread(), 0);
+
CallStack *stack = state->threads()->at(0);
ASSERT_TRUE(stack);
ASSERT_EQ(stack->frames()->size(), 4);
- ASSERT_EQ(stack->frames()->at(0)->module_base, 0x400000);
- ASSERT_EQ(stack->frames()->at(0)->module_name, "c:\\test_app.exe");
+ ASSERT_TRUE(stack->frames()->at(0)->module);
+ ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000);
+ ASSERT_EQ(stack->frames()->at(0)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(0)->function_name, "CrashFunction()");
ASSERT_EQ(stack->frames()->at(0)->source_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack->frames()->at(0)->source_line, 51);
- ASSERT_EQ(stack->frames()->at(1)->module_base, 0x400000);
- ASSERT_EQ(stack->frames()->at(1)->module_name, "c:\\test_app.exe");
+ ASSERT_TRUE(stack->frames()->at(1)->module);
+ ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000);
+ ASSERT_EQ(stack->frames()->at(1)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack->frames()->at(1)->source_line, 56);
// This comes from the CRT
- ASSERT_EQ(stack->frames()->at(2)->module_base, 0x400000);
- ASSERT_EQ(stack->frames()->at(2)->module_name, "c:\\test_app.exe");
+ ASSERT_TRUE(stack->frames()->at(2)->module);
+ ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000);
+ ASSERT_EQ(stack->frames()->at(2)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup");
ASSERT_EQ(stack->frames()->at(2)->source_file_name,
"f:\\rtm\\vctools\\crt_bld\\self_x86\\crt\\src\\crt0.c");
ASSERT_EQ(stack->frames()->at(2)->source_line, 318);
// No debug info available for kernel32.dll
- ASSERT_EQ(stack->frames()->at(3)->module_base, 0x7c800000);
- ASSERT_EQ(stack->frames()->at(3)->module_name,
+ ASSERT_TRUE(stack->frames()->at(3)->module);
+ ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000);
+ ASSERT_EQ(stack->frames()->at(3)->module->code_file(),
"C:\\WINDOWS\\system32\\kernel32.dll");
ASSERT_TRUE(stack->frames()->at(3)->function_name.empty());
ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
+ ASSERT_EQ(state->modules()->module_count(), 13);
+ ASSERT_TRUE(state->modules()->GetMainModule());
+ ASSERT_EQ(state->modules()->GetMainModule()->code_file(), "c:\\test_app.exe");
+ ASSERT_FALSE(state->modules()->GetModuleForAddress(0));
+ ASSERT_EQ(state->modules()->GetMainModule(),
+ state->modules()->GetModuleForAddress(0x400000));
+ ASSERT_EQ(state->modules()->GetModuleForAddress(0x7c801234)->debug_file(),
+ "kernel32.pdb");
+ ASSERT_EQ(state->modules()->GetModuleForAddress(0x77d43210)->version(),
+ "5.1.2600.2622");
+
return true;
}
diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc
index 4fbee425..62bfd555 100644
--- a/src/processor/minidump_stackwalk.cc
+++ b/src/processor/minidump_stackwalk.cc
@@ -37,6 +37,8 @@
#include <string>
#include "google_airbag/processor/call_stack.h"
+#include "google_airbag/processor/code_module.h"
+#include "google_airbag/processor/code_modules.h"
#include "google_airbag/processor/minidump.h"
#include "google_airbag/processor/minidump_processor.h"
#include "google_airbag/processor/process_state.h"
@@ -49,6 +51,8 @@ namespace {
using std::string;
using google_airbag::CallStack;
+using google_airbag::CodeModule;
+using google_airbag::CodeModules;
using google_airbag::MinidumpModule;
using google_airbag::MinidumpProcessor;
using google_airbag::PathnameStripper;
@@ -90,8 +94,8 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
const StackFrame *frame = stack->frames()->at(frame_index);
printf("%2d ", frame_index);
- if (!frame->module_name.empty()) {
- printf("%s", PathnameStripper::File(frame->module_name).c_str());
+ if (frame->module) {
+ printf("%s", PathnameStripper::File(frame->module->code_file()).c_str());
if (!frame->function_name.empty()) {
printf("!%s", frame->function_name.c_str());
if (!frame->source_file_name.empty()) {
@@ -104,7 +108,7 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
printf(" + 0x%llx", frame->instruction - frame->function_base);
}
} else {
- printf(" + 0x%llx", frame->instruction - frame->module_base);
+ printf(" + 0x%llx", frame->instruction - frame->module->base_address());
}
} else {
printf("0x%llx", frame->instruction);
@@ -147,6 +151,29 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
}
}
+static void PrintModules(const CodeModules *modules) {
+ if (!modules)
+ return;
+
+ printf("\n");
+ printf("Loaded modules:\n");
+
+ u_int64_t main_address = modules->GetMainModule()->base_address();
+
+ unsigned int module_count = modules->module_count();
+ for (unsigned int module_sequence = 0;
+ module_sequence < module_count;
+ ++module_sequence) {
+ const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
+ u_int64_t base_address = module->base_address();
+ printf("0x%08llx - 0x%08llx %s %s%s\n",
+ base_address, base_address + module->size() - 1,
+ PathnameStripper::File(module->code_file()).c_str(),
+ module->version().empty() ? "???" : module->version().c_str(),
+ module->base_address() == main_address ? " (main)" : "");
+ }
+}
+
// Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if
// non-empty, is the base directory of a symbol storage area, laid out in
// the format required by SimpleSymbolSupplier. If such a storage area
@@ -217,6 +244,8 @@ static bool PrintMinidumpProcess(const string &minidump_file,
}
}
+ PrintModules(process_state->modules());
+
return true;
}
diff --git a/src/processor/postfix_evaluator_unittest.cc b/src/processor/postfix_evaluator_unittest.cc
index 9fac632d..2d16e5c9 100644
--- a/src/processor/postfix_evaluator_unittest.cc
+++ b/src/processor/postfix_evaluator_unittest.cc
@@ -1,16 +1,31 @@
-// Copyright (C) 2006 Google Inc.
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
//
-// 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
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
//
-// 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.
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// postfix_evaluator_unittest.cc: Unit tests for PostfixEvaluator.
//
diff --git a/src/processor/process_state.cc b/src/processor/process_state.cc
index b7c2239e..895ee5f7 100644
--- a/src/processor/process_state.cc
+++ b/src/processor/process_state.cc
@@ -35,6 +35,7 @@
#include "google_airbag/processor/process_state.h"
#include "google_airbag/processor/call_stack.h"
+#include "google_airbag/processor/code_modules.h"
namespace google_airbag {
@@ -44,6 +45,8 @@ ProcessState::~ProcessState() {
++iterator) {
delete *iterator;
}
+
+ delete modules_;
}
} // namespace google_airbag
diff --git a/src/processor/range_map-inl.h b/src/processor/range_map-inl.h
index 74ce8513..2ead2e65 100644
--- a/src/processor/range_map-inl.h
+++ b/src/processor/range_map-inl.h
@@ -142,6 +142,35 @@ bool RangeMap<AddressType, EntryType>::RetrieveNearestRange(
template<typename AddressType, typename EntryType>
+bool RangeMap<AddressType, EntryType>::RetrieveRangeAtIndex(
+ int index, EntryType *entry,
+ AddressType *entry_base, AddressType *entry_size) const {
+ if (!entry || index >= GetCount())
+ return false;
+
+ // Walk through the map. Although it's ordered, it's not a vector, so it
+ // can't be addressed directly by index.
+ MapConstIterator iterator = map_.begin();
+ for (int this_index = 0; this_index < index; ++this_index)
+ ++iterator;
+
+ *entry = iterator->second.entry();
+ if (entry_base)
+ *entry_base = iterator->first;
+ if (entry_size)
+ *entry_size = iterator->first - iterator->second.base() + 1;
+
+ return true;
+}
+
+
+template<typename AddressType, typename EntryType>
+int RangeMap<AddressType, EntryType>::GetCount() const {
+ return map_.size();
+}
+
+
+template<typename AddressType, typename EntryType>
void RangeMap<AddressType, EntryType>::Clear() {
map_.clear();
}
diff --git a/src/processor/range_map.h b/src/processor/range_map.h
index 301b82e5..72a96a96 100644
--- a/src/processor/range_map.h
+++ b/src/processor/range_map.h
@@ -75,6 +75,20 @@ class RangeMap {
AddressType *entry_base, AddressType *entry_size)
const;
+ // Treating all ranges as a list ordered by the address spaces that they
+ // occupy, locates the range at the index specified by index. Returns
+ // false if index is larger than the number of ranges stored, or if another
+ // parameter error occurs. entry_base and entry_size, if non-NULL, are set
+ // to the base and size of the entry's range.
+ //
+ // RetrieveRangeAtIndex is not optimized for speedy operation.
+ bool RetrieveRangeAtIndex(int index, EntryType *entry,
+ AddressType *entry_base, AddressType *entry_size)
+ const;
+
+ // Returns the number of ranges stored in the RangeMap.
+ int GetCount() const;
+
// Empties the range map, restoring it to the state it was when it was
// initially created.
void Clear();
diff --git a/src/processor/range_map_unittest.cc b/src/processor/range_map_unittest.cc
index cff5b0a9..b9657f64 100644
--- a/src/processor/range_map_unittest.cc
+++ b/src/processor/range_map_unittest.cc
@@ -243,6 +243,71 @@ static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
}
+// Test RetrieveRangeAtIndex, which is supposed to return objects in order
+// according to their addresses. This test is performed by looping through
+// the map, calling RetrieveRangeAtIndex for all possible indices in sequence,
+// and verifying that each call returns a different object than the previous
+// call, and that ranges are returned with increasing base addresses. Returns
+// false if the test fails.
+static bool RetrieveIndexTest(TestMap *range_map, int set) {
+ linked_ptr<CountedObject> object;
+ CountedObject *last_object = NULL;
+ AddressType last_base = 0;
+
+ int object_count = range_map->GetCount();
+ for (int object_index = 0; object_index < object_count; ++object_index) {
+ AddressType base;
+ if (!range_map->RetrieveRangeAtIndex(object_index, &object, &base, NULL)) {
+ fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, "
+ "expected success, observed failure\n",
+ set, object_index);
+ return false;
+ }
+
+ if (!object.get()) {
+ fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, "
+ "expected object, observed NULL\n",
+ set, object_index);
+ return false;
+ }
+
+ // It's impossible to do these comparisons unless there's a previous
+ // object to compare against.
+ if (last_object) {
+ // The object must be different from the last one.
+ if (object->id() == last_object->id()) {
+ fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, "
+ "expected different objects, observed same objects (%d)\n",
+ set, object_index, object->id());
+ return false;
+ }
+
+ // Each object must have a base greater than the previous object's base.
+ if (base <= last_base) {
+ fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, "
+ "expected different bases, observed same bases (%d)\n",
+ set, object_index, base);
+ return false;
+ }
+ }
+
+ last_object = object.get();
+ last_base = base;
+ }
+
+ // Make sure that RetrieveRangeAtIndex doesn't allow lookups at indices that
+ // are too high.
+ if (range_map->RetrieveRangeAtIndex(object_count, &object, NULL, NULL)) {
+ fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d (too large), "
+ "expected failure, observed success\n",
+ set, object_count);
+ return false;
+ }
+
+ return true;
+}
+
+
// RunTests runs a series of test sets.
static bool RunTests() {
// These tests will be run sequentially. The first set of tests exercises
@@ -373,6 +438,15 @@ static bool RunTests() {
return false;
}
+ // The RangeMap's own count of objects should also match.
+ if (range_map->GetCount() != stored_count) {
+ fprintf(stderr, "FAILED: stored object count doesn't match GetCount, "
+ "expected %d, observed %d\n",
+ stored_count, range_map->GetCount());
+
+ return false;
+ }
+
// Run the RetrieveRange test
for (unsigned int range_test_index = 0;
range_test_index < range_test_count;
@@ -382,6 +456,9 @@ static bool RunTests() {
return false;
}
+ if (!RetrieveIndexTest(range_map.get(), range_test_set_index))
+ return false;
+
// Clear the map between test sets. If this is the final test set,
// delete the map instead to test destruction.
if (range_test_set_index < range_test_set_count - 1)
diff --git a/src/processor/simple_symbol_supplier.cc b/src/processor/simple_symbol_supplier.cc
index 3b59739e..32b94342 100644
--- a/src/processor/simple_symbol_supplier.cc
+++ b/src/processor/simple_symbol_supplier.cc
@@ -34,61 +34,45 @@
// Author: Mark Mentovai
#include "processor/simple_symbol_supplier.h"
-#include "google_airbag/processor/minidump.h"
+#include "google_airbag/processor/code_module.h"
#include "processor/pathname_stripper.h"
namespace google_airbag {
-string SimpleSymbolSupplier::GetSymbolFileAtPath(MinidumpModule *module,
+string SimpleSymbolSupplier::GetSymbolFileAtPath(const CodeModule *module,
const string &root_path) {
- // For now, only support modules that have GUIDs - which means
- // MDCVInfoPDB70.
-
if (!module)
return "";
- const MDCVInfoPDB70 *cv_record =
- reinterpret_cast<const MDCVInfoPDB70*>(module->GetCVRecord());
- if (!cv_record)
- return "";
-
- if (cv_record->cv_signature != MD_CVINFOPDB70_SIGNATURE)
- return "";
-
// Start with the base path.
string path = root_path;
- // Append the pdb file name as a directory name.
+ // Append the debug (pdb) file name as a directory name.
path.append("/");
- string pdb_file_name = PathnameStripper::File(
- reinterpret_cast<const char *>(cv_record->pdb_file_name));
- path.append(pdb_file_name);
+ string debug_file_name = PathnameStripper::File(module->debug_file());
+ if (debug_file_name.empty())
+ return "";
+ path.append(debug_file_name);
- // Append the uuid and age as a directory name.
+ // Append the identifier as a directory name.
path.append("/");
- char uuid_age_string[43];
- snprintf(uuid_age_string, sizeof(uuid_age_string),
- "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%X",
- cv_record->signature.data1, cv_record->signature.data2,
- cv_record->signature.data3,
- cv_record->signature.data4[0], cv_record->signature.data4[1],
- cv_record->signature.data4[2], cv_record->signature.data4[3],
- cv_record->signature.data4[4], cv_record->signature.data4[5],
- cv_record->signature.data4[6], cv_record->signature.data4[7],
- cv_record->age);
- path.append(uuid_age_string);
+ string identifier = module->debug_identifier();
+ if (identifier.empty())
+ return "";
+ path.append(identifier);
- // Transform the pdb file name into one ending in .sym. If the existing
+ // Transform the debug file name into one ending in .sym. If the existing
// name ends in .pdb, strip the .pdb. Otherwise, add .sym to the non-.pdb
// name.
path.append("/");
- string pdb_file_extension = pdb_file_name.substr(pdb_file_name.size() - 4);
- transform(pdb_file_extension.begin(), pdb_file_extension.end(),
- pdb_file_extension.begin(), tolower);
- if (pdb_file_extension == ".pdb") {
- path.append(pdb_file_name.substr(0, pdb_file_name.size() - 4));
+ string debug_file_extension =
+ debug_file_name.substr(debug_file_name.size() - 4);
+ transform(debug_file_extension.begin(), debug_file_extension.end(),
+ debug_file_extension.begin(), tolower);
+ if (debug_file_extension == ".pdb") {
+ path.append(debug_file_name.substr(0, debug_file_name.size() - 4));
} else {
- path.append(pdb_file_name);
+ path.append(debug_file_name);
}
path.append(".sym");
diff --git a/src/processor/simple_symbol_supplier.h b/src/processor/simple_symbol_supplier.h
index f9f52af1..30485d24 100644
--- a/src/processor/simple_symbol_supplier.h
+++ b/src/processor/simple_symbol_supplier.h
@@ -33,14 +33,16 @@
// that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is
// created with a base directory, which is the root for all symbol files.
// Each symbol file contained therien has a directory entry in the base
-// directory with a name identical to the corresponding pdb file. Within
-// each of these directories, there are subdirectories named for the uuid and
-// age of each pdb file. The uuid is presented in hexadecimal form, with
-// uppercase characters and no dashes. The age is appended to it in
-// hexadecimal form, without any separators. Within that subdirectory,
+// directory with a name identical to the corresponding debugging file (pdb).
+// Within each of these directories, there are subdirectories named for the
+// debugging file's identifier. For recent pdb files, this is a concatenation
+// of the pdb's uuid and age, presented in hexadecimal form, using uppercase
+// characters and no dashes or separators. Within that subdirectory,
// SimpleSymbolSupplier expects to find the symbol file, which is named
-// identically to the pdb file, but with a .sym extension. This sample
-// hierarchy is rooted at the "symbols" base directory:
+// identically to the debug file, but with a .sym extension. If the original
+// debug file had a name ending in .pdb, the .pdb extension will be replaced
+// with .sym. This sample hierarchy is rooted at the "symbols" base
+// directory:
//
// symbols
// symbols/test_app.pdb
@@ -59,9 +61,11 @@
// SimpleSymbolServer, provided that the pdb files are transformed to dumped
// format using a tool such as dump_syms, and given a .sym extension.
//
-// SimpleSymbolSupplier presently only supports symbol files that have
-// the MSVC 7.0 CodeView record format. See MDCVInfoPDB70 in
-// minidump_format.h.
+// SimpleSymbolSupplier supports any debugging file which can be identified
+// by a CodeModule object's debug_file and debug_identifier accessors. The
+// expected ultimate source of these CodeModule objects are MinidumpModule
+// objects; it is this class that is responsible for assigning appropriate
+// values for debug_file and debug_identifier.
//
// Author: Mark Mentovai
@@ -76,7 +80,7 @@ namespace google_airbag {
using std::string;
-class MinidumpModule;
+class CodeModule;
class SimpleSymbolSupplier : public SymbolSupplier {
public:
@@ -88,12 +92,13 @@ class SimpleSymbolSupplier : public SymbolSupplier {
// Returns the path to the symbol file for the given module. See the
// description above.
- virtual string GetSymbolFile(MinidumpModule *module) {
+ virtual string GetSymbolFile(const CodeModule *module) {
return GetSymbolFileAtPath(module, path_);
}
protected:
- string GetSymbolFileAtPath(MinidumpModule *module, const string &root_path);
+ string GetSymbolFileAtPath(const CodeModule *module,
+ const string &root_path);
private:
string path_;
diff --git a/src/processor/source_line_resolver.cc b/src/processor/source_line_resolver.cc
index 66e31288..1ebc1bf4 100644
--- a/src/processor/source_line_resolver.cc
+++ b/src/processor/source_line_resolver.cc
@@ -39,6 +39,7 @@
#include "processor/range_map-inl.h"
#include "processor/source_line_resolver.h"
+#include "google_airbag/processor/code_module.h"
#include "google_airbag/processor/stack_frame.h"
#include "processor/linked_ptr.h"
#include "processor/scoped_ptr.h"
@@ -111,7 +112,7 @@ class SourceLineResolver::Module {
// returned. If no additional information is available, returns NULL.
// A NULL return value is not an error. The caller takes ownership of
// any returned StackFrameInfo object.
- StackFrameInfo* LookupAddress(MemAddr address, StackFrame *frame) const;
+ StackFrameInfo* LookupAddress(StackFrame *frame) const;
private:
friend class SourceLineResolver;
@@ -206,10 +207,11 @@ bool SourceLineResolver::HasModule(const string &module_name) const {
StackFrameInfo* SourceLineResolver::FillSourceLineInfo(
StackFrame *frame) const {
- ModuleMap::const_iterator it = modules_->find(frame->module_name);
- if (it != modules_->end()) {
- return it->second->LookupAddress(frame->instruction - frame->module_base,
- frame);
+ if (frame->module) {
+ ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
+ if (it != modules_->end()) {
+ return it->second->LookupAddress(frame);
+ }
}
return NULL;
}
@@ -273,8 +275,10 @@ bool SourceLineResolver::Module::LoadMap(const string &map_file) {
return true;
}
-StackFrameInfo* SourceLineResolver::Module::LookupAddress(
- MemAddr address, StackFrame *frame) const {
+StackFrameInfo* SourceLineResolver::Module::LookupAddress(StackFrame *frame)
+ const {
+ MemAddr address = frame->instruction - frame->module->base_address();
+
linked_ptr<StackFrameInfo> retrieved_info;
// Check for debugging info first, before any possible early returns.
//
@@ -318,7 +322,7 @@ StackFrameInfo* SourceLineResolver::Module::LookupAddress(
parameter_size = func->parameter_size;
frame->function_name = func->name;
- frame->function_base = frame->module_base + function_base;
+ frame->function_base = frame->module->base_address() + function_base;
linked_ptr<Line> line;
MemAddr line_base;
@@ -328,7 +332,7 @@ StackFrameInfo* SourceLineResolver::Module::LookupAddress(
frame->source_file_name = files_.find(line->source_file_id)->second;
}
frame->source_line = line->line;
- frame->source_line_base = frame->module_base + line_base;
+ frame->source_line_base = frame->module->base_address() + line_base;
}
} else if (public_symbols_.Retrieve(address,
&public_symbol, &public_address) &&
@@ -336,7 +340,7 @@ StackFrameInfo* SourceLineResolver::Module::LookupAddress(
parameter_size = public_symbol->parameter_size;
frame->function_name = public_symbol->name;
- frame->function_base = frame->module_base + public_address;
+ frame->function_base = frame->module->base_address() + public_address;
} else {
// No FUNC or PUBLIC data available.
return frame_info.release();
diff --git a/src/processor/source_line_resolver_unittest.cc b/src/processor/source_line_resolver_unittest.cc
index e2ea0fee..38de9e2c 100644
--- a/src/processor/source_line_resolver_unittest.cc
+++ b/src/processor/source_line_resolver_unittest.cc
@@ -30,6 +30,7 @@
#include <cstdio>
#include <string>
#include "processor/source_line_resolver.h"
+#include "google_airbag/processor/code_module.h"
#include "google_airbag/processor/stack_frame.h"
#include "processor/linked_ptr.h"
#include "processor/scoped_ptr.h"
@@ -48,12 +49,33 @@
namespace {
using std::string;
+using google_airbag::CodeModule;
using google_airbag::linked_ptr;
using google_airbag::scoped_ptr;
using google_airbag::SourceLineResolver;
using google_airbag::StackFrame;
using google_airbag::StackFrameInfo;
+class TestCodeModule : public CodeModule {
+ public:
+ TestCodeModule(string code_file) : code_file_(code_file) {}
+ virtual ~TestCodeModule() {}
+
+ virtual u_int64_t base_address() const { return 0; }
+ virtual u_int64_t size() const { return 0x4000; }
+ virtual string code_file() const { return code_file_; }
+ virtual string code_identifier() const { return ""; }
+ virtual string debug_file() const { return ""; }
+ virtual string debug_identifier() const { return ""; }
+ virtual string version() const { return ""; }
+ virtual const CodeModule* Copy() const {
+ return new TestCodeModule(code_file_);
+ }
+
+ private:
+ string code_file_;
+};
+
static bool VerifyEmpty(const StackFrame &frame) {
ASSERT_TRUE(frame.function_name.empty());
ASSERT_TRUE(frame.source_file_name.empty());
@@ -63,6 +85,7 @@ static bool VerifyEmpty(const StackFrame &frame) {
static void ClearSourceLineInfo(StackFrame *frame) {
frame->function_name.clear();
+ frame->module = NULL;
frame->source_file_name.clear();
frame->source_line = 0;
}
@@ -77,13 +100,28 @@ static bool RunTests() {
ASSERT_TRUE(resolver.LoadModule("module2", testdata_dir + "/module2.out"));
ASSERT_TRUE(resolver.HasModule("module2"));
+ TestCodeModule module1("module1");
+
StackFrame frame;
frame.instruction = 0x1000;
- frame.module_name = "module1";
+ frame.module = NULL;
scoped_ptr<StackFrameInfo> frame_info(resolver.FillSourceLineInfo(&frame));
+ ASSERT_FALSE(frame.module);
+ ASSERT_TRUE(frame.function_name.empty());
+ ASSERT_EQ(frame.function_base, 0);
+ ASSERT_TRUE(frame.source_file_name.empty());
+ ASSERT_EQ(frame.source_line, 0);
+ ASSERT_EQ(frame.source_line_base, 0);
+
+ frame.module = &module1;
+ frame_info.reset(resolver.FillSourceLineInfo(&frame));
ASSERT_EQ(frame.function_name, "Function1_1");
+ ASSERT_TRUE(frame.module);
+ ASSERT_EQ(frame.module->code_file(), "module1");
+ ASSERT_EQ(frame.function_base, 0x1000);
ASSERT_EQ(frame.source_file_name, "file1_1.cc");
ASSERT_EQ(frame.source_line, 44);
+ ASSERT_EQ(frame.source_line_base, 0x1000);
ASSERT_TRUE(frame_info.get());
ASSERT_FALSE(frame_info->allocates_base_pointer);
ASSERT_EQ(frame_info->program_string,
@@ -91,6 +129,7 @@ static bool RunTests() {
ClearSourceLineInfo(&frame);
frame.instruction = 0x800;
+ frame.module = &module1;
frame_info.reset(resolver.FillSourceLineInfo(&frame));
ASSERT_TRUE(VerifyEmpty(frame));
ASSERT_FALSE(frame_info.get());
@@ -113,28 +152,33 @@ static bool RunTests() {
ASSERT_FALSE(frame_info->allocates_base_pointer);
ASSERT_FALSE(frame_info->program_string.empty());
- frame.instruction = 0x2180;
- frame.module_name = "module2";
+ TestCodeModule module2("module2");
+
+ frame.instruction = 0x2181;
+ frame.module = &module2;
frame_info.reset(resolver.FillSourceLineInfo(&frame));
ASSERT_EQ(frame.function_name, "Function2_2");
+ ASSERT_EQ(frame.function_base, 0x2170);
+ ASSERT_TRUE(frame.module);
+ ASSERT_EQ(frame.module->code_file(), "module2");
ASSERT_EQ(frame.source_file_name, "file2_2.cc");
ASSERT_EQ(frame.source_line, 21);
+ ASSERT_EQ(frame.source_line_base, 0x2180);
ASSERT_TRUE(frame_info.get());
ASSERT_EQ(frame_info->prolog_size, 1);
frame.instruction = 0x216f;
- frame.module_name = "module2";
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_1");
ClearSourceLineInfo(&frame);
frame.instruction = 0x219f;
- frame.module_name = "module2";
+ frame.module = &module2;
resolver.FillSourceLineInfo(&frame);
ASSERT_TRUE(frame.function_name.empty());
frame.instruction = 0x21a0;
- frame.module_name = "module2";
+ frame.module = &module2;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_2");
diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc
index 78f17363..4e4a6b9f 100644
--- a/src/processor/stackwalker.cc
+++ b/src/processor/stackwalker.cc
@@ -36,6 +36,8 @@
#include "google_airbag/processor/stackwalker.h"
#include "google_airbag/processor/call_stack.h"
+#include "google_airbag/processor/code_module.h"
+#include "google_airbag/processor/code_modules.h"
#include "google_airbag/processor/minidump.h"
#include "google_airbag/processor/stack_frame.h"
#include "google_airbag/processor/symbol_supplier.h"
@@ -49,7 +51,7 @@
namespace google_airbag {
-Stackwalker::Stackwalker(MemoryRegion *memory, MinidumpModuleList *modules,
+Stackwalker::Stackwalker(MemoryRegion *memory, const CodeModules *modules,
SymbolSupplier *supplier)
: memory_(memory), modules_(modules), supplier_(supplier) {
}
@@ -80,15 +82,14 @@ CallStack* Stackwalker::Walk() {
// Resolve the module information, if a module map was provided.
if (modules_) {
- MinidumpModule *module =
+ const CodeModule *module =
modules_->GetModuleForAddress(frame->instruction);
if (module) {
- frame->module_name = *(module->GetName());
- frame->module_base = module->base_address();
- if (!resolver.HasModule(frame->module_name) && supplier_) {
+ frame->module = module;
+ if (!resolver.HasModule(frame->module->code_file()) && supplier_) {
string symbol_file = supplier_->GetSymbolFile(module);
if (!symbol_file.empty()) {
- resolver.LoadModule(frame->module_name, symbol_file);
+ resolver.LoadModule(frame->module->code_file(), symbol_file);
}
}
frame_info.reset(resolver.FillSourceLineInfo(frame.get()));
@@ -114,7 +115,7 @@ CallStack* Stackwalker::Walk() {
// static
Stackwalker* Stackwalker::StackwalkerForCPU(MinidumpContext *context,
MemoryRegion *memory,
- MinidumpModuleList *modules,
+ const CodeModules *modules,
SymbolSupplier *supplier) {
Stackwalker *cpu_stackwalker = NULL;
diff --git a/src/processor/stackwalker_ppc.cc b/src/processor/stackwalker_ppc.cc
index a958f492..49b62f8c 100644
--- a/src/processor/stackwalker_ppc.cc
+++ b/src/processor/stackwalker_ppc.cc
@@ -36,7 +36,7 @@
#include "processor/stackwalker_ppc.h"
#include "google_airbag/processor/call_stack.h"
-#include "google_airbag/processor/minidump.h"
+#include "google_airbag/processor/memory_region.h"
#include "google_airbag/processor/stack_frame_cpu.h"
namespace google_airbag {
@@ -44,7 +44,7 @@ namespace google_airbag {
StackwalkerPPC::StackwalkerPPC(const MDRawContextPPC *context,
MemoryRegion *memory,
- MinidumpModuleList *modules,
+ const CodeModules *modules,
SymbolSupplier *supplier)
: Stackwalker(memory, modules, supplier),
context_(context) {
diff --git a/src/processor/stackwalker_ppc.h b/src/processor/stackwalker_ppc.h
index 10b6a7a5..bccc9dc2 100644
--- a/src/processor/stackwalker_ppc.h
+++ b/src/processor/stackwalker_ppc.h
@@ -45,19 +45,17 @@
namespace google_airbag {
-class MinidumpContext;
-class MinidumpModuleList;
-
+class CodeModules;
class StackwalkerPPC : public Stackwalker {
public:
- // context is a MinidumpContext object that gives access to ppc-specific
+ // context is a ppc context object that gives access to ppc-specific
// register state corresponding to the innermost called frame to be
// included in the stack. The other arguments are passed directly through
// to the base Stackwalker constructor.
StackwalkerPPC(const MDRawContextPPC *context,
MemoryRegion *memory,
- MinidumpModuleList *modules,
+ const CodeModules *modules,
SymbolSupplier *supplier);
private:
diff --git a/src/processor/stackwalker_selftest.cc b/src/processor/stackwalker_selftest.cc
index 2534cfd5..877a8f3d 100644
--- a/src/processor/stackwalker_selftest.cc
+++ b/src/processor/stackwalker_selftest.cc
@@ -1,16 +1,31 @@
-// Copyright (C) 2006 Google Inc.
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
//
-// 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
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
//
-// 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.
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// stackwalker_selftest.cc: Tests StackwalkerX86 or StackwalkerPPC using the
// running process' stack as test data, if running on an x86 or ppc and
diff --git a/src/processor/stackwalker_x86.cc b/src/processor/stackwalker_x86.cc
index 61f42a10..1e3390f5 100644
--- a/src/processor/stackwalker_x86.cc
+++ b/src/processor/stackwalker_x86.cc
@@ -38,7 +38,7 @@
#include "processor/stackwalker_x86.h"
#include "google_airbag/processor/call_stack.h"
-#include "google_airbag/processor/minidump.h"
+#include "google_airbag/processor/memory_region.h"
#include "google_airbag/processor/stack_frame_cpu.h"
#include "processor/linked_ptr.h"
#include "processor/stack_frame_info.h"
@@ -48,7 +48,7 @@ namespace google_airbag {
StackwalkerX86::StackwalkerX86(const MDRawContextX86 *context,
MemoryRegion *memory,
- MinidumpModuleList *modules,
+ const CodeModules *modules,
SymbolSupplier *supplier)
: Stackwalker(memory, modules, supplier),
context_(context) {
diff --git a/src/processor/stackwalker_x86.h b/src/processor/stackwalker_x86.h
index 707aa94b..fe2a7808 100644
--- a/src/processor/stackwalker_x86.h
+++ b/src/processor/stackwalker_x86.h
@@ -45,19 +45,18 @@
namespace google_airbag {
-class MinidumpContext;
-class MinidumpModuleList;
+class CodeModules;
class StackwalkerX86 : public Stackwalker {
public:
- // context is a MinidumpContext object that gives access to x86-specific
+ // context is an x86 context object that gives access to x86-specific
// register state corresponding to the innermost called frame to be
// included in the stack. The other arguments are passed directly through
// to the base Stackwalker constructor.
StackwalkerX86(const MDRawContextX86 *context,
MemoryRegion *memory,
- MinidumpModuleList *modules,
+ const CodeModules *modules,
SymbolSupplier *supplier);
private:
diff --git a/src/processor/testdata/minidump2.dump.out b/src/processor/testdata/minidump2.dump.out
index 492d5b5d..910f6256 100644
--- a/src/processor/testdata/minidump2.dump.out
+++ b/src/processor/testdata/minidump2.dump.out
@@ -200,13 +200,16 @@ MDRawModule
cv_record.rva = 0x132c
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "c:\test_app.exe"
+ (code_file) = "c:\test_app.exe"
+ (code_identifier) = "454fa44d2b000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = 8ddb7e9a-3657-4893-8d6e-b08b1dca31aa
(cv_record).age = 1
(cv_record).pdb_file_name = "c:\test_app.pdb"
(misc_record) = (null)
- (debug_filename) = "c:\test_app.pdb"
+ (debug_file) = "c:\test_app.pdb"
+ (debug_identifier) = "8DDB7E9A365748938D6EB08B1DCA31AA1"
+ (version) = ""
module[1]
MDRawModule
@@ -229,13 +232,16 @@ MDRawModule
cv_record.rva = 0x1354
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\ntdll.dll"
+ (code_file) = "C:\WINDOWS\system32\ntdll.dll"
+ (code_identifier) = "411096b4b0000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = 36515fb5-d043-45e4-91f6-72fa2e2878c0
(cv_record).age = 2
(cv_record).pdb_file_name = "ntdll.pdb"
(misc_record) = (null)
- (debug_filename) = "ntdll.pdb"
+ (debug_file) = "ntdll.pdb"
+ (debug_identifier) = "36515FB5D04345E491F672FA2E2878C02"
+ (version) = "5.1.2600.2180"
module[2]
MDRawModule
@@ -258,13 +264,16 @@ MDRawModule
cv_record.rva = 0x1376
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\kernel32.dll"
+ (code_file) = "C:\WINDOWS\system32\kernel32.dll"
+ (code_identifier) = "44ab9a84f4000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = bce8785c-57b4-4245-a669-896b6a19b954
(cv_record).age = 2
(cv_record).pdb_file_name = "kernel32.pdb"
(misc_record) = (null)
- (debug_filename) = "kernel32.pdb"
+ (debug_file) = "kernel32.pdb"
+ (debug_identifier) = "BCE8785C57B44245A669896B6A19B9542"
+ (version) = "5.1.2600.2945"
module[3]
MDRawModule
@@ -287,13 +296,16 @@ MDRawModule
cv_record.rva = 0x139b
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\ole32.dll"
+ (code_file) = "C:\WINDOWS\system32\ole32.dll"
+ (code_identifier) = "42e5be9313d000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = 683b65b2-46f4-4187-96d2-ee6d4c55eb11
(cv_record).age = 2
(cv_record).pdb_file_name = "ole32.pdb"
(misc_record) = (null)
- (debug_filename) = "ole32.pdb"
+ (debug_file) = "ole32.pdb"
+ (debug_identifier) = "683B65B246F4418796D2EE6D4C55EB112"
+ (version) = "5.1.2600.2726"
module[4]
MDRawModule
@@ -316,13 +328,16 @@ MDRawModule
cv_record.rva = 0x13bd
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\advapi32.dll"
+ (code_file) = "C:\WINDOWS\system32\advapi32.dll"
+ (code_identifier) = "411096a79b000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = 455d6c5f-184d-45bb-b5c5-f30f82975114
(cv_record).age = 2
(cv_record).pdb_file_name = "advapi32.pdb"
(misc_record) = (null)
- (debug_filename) = "advapi32.pdb"
+ (debug_file) = "advapi32.pdb"
+ (debug_identifier) = "455D6C5F184D45BBB5C5F30F829751142"
+ (version) = "5.1.2600.2180"
module[5]
MDRawModule
@@ -345,13 +360,16 @@ MDRawModule
cv_record.rva = 0x13e2
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\rpcrt4.dll"
+ (code_file) = "C:\WINDOWS\system32\rpcrt4.dll"
+ (code_identifier) = "411096ae91000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = bea45a72-1da1-41da-a3ba-86b3a2031153
(cv_record).age = 2
(cv_record).pdb_file_name = "rpcrt4.pdb"
(misc_record) = (null)
- (debug_filename) = "rpcrt4.pdb"
+ (debug_file) = "rpcrt4.pdb"
+ (debug_identifier) = "BEA45A721DA141DAA3BA86B3A20311532"
+ (version) = "5.1.2600.2180"
module[6]
MDRawModule
@@ -374,13 +392,16 @@ MDRawModule
cv_record.rva = 0x1405
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\gdi32.dll"
+ (code_file) = "C:\WINDOWS\system32\gdi32.dll"
+ (code_identifier) = "43b34feb47000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = c0ea66be-00a6-4bd7-aef7-9e443a91869c
(cv_record).age = 2
(cv_record).pdb_file_name = "gdi32.pdb"
(misc_record) = (null)
- (debug_filename) = "gdi32.pdb"
+ (debug_file) = "gdi32.pdb"
+ (debug_identifier) = "C0EA66BE00A64BD7AEF79E443A91869C2"
+ (version) = "5.1.2600.2818"
module[7]
MDRawModule
@@ -403,13 +424,16 @@ MDRawModule
cv_record.rva = 0x1427
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\user32.dll"
+ (code_file) = "C:\WINDOWS\system32\user32.dll"
+ (code_identifier) = "4226015990000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = ee2b714d-83a3-4c9d-8802-7621272f8326
(cv_record).age = 2
(cv_record).pdb_file_name = "user32.pdb"
(misc_record) = (null)
- (debug_filename) = "user32.pdb"
+ (debug_file) = "user32.pdb"
+ (debug_identifier) = "EE2B714D83A34C9D88027621272F83262"
+ (version) = "5.1.2600.2622"
module[8]
MDRawModule
@@ -432,13 +456,16 @@ MDRawModule
cv_record.rva = 0x144a
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\msvcrt.dll"
+ (code_file) = "C:\WINDOWS\system32\msvcrt.dll"
+ (code_identifier) = "4110975258000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = a678f3c3-0ded-426b-8390-32b996987e38
(cv_record).age = 1
(cv_record).pdb_file_name = "msvcrt.pdb"
(misc_record) = (null)
- (debug_filename) = "msvcrt.pdb"
+ (debug_file) = "msvcrt.pdb"
+ (debug_identifier) = "A678F3C30DED426B839032B996987E381"
+ (version) = "7.0.2600.2180"
module[9]
MDRawModule
@@ -461,13 +488,16 @@ MDRawModule
cv_record.rva = 0x146d
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\imm32.dll"
+ (code_file) = "C:\WINDOWS\system32\imm32.dll"
+ (code_identifier) = "411096ae1d000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = 2c17a49c-251b-4c8e-b9e2-ad13d7d9ea16
(cv_record).age = 2
(cv_record).pdb_file_name = "imm32.pdb"
(misc_record) = (null)
- (debug_filename) = "imm32.pdb"
+ (debug_file) = "imm32.pdb"
+ (debug_identifier) = "2C17A49C251B4C8EB9E2AD13D7D9EA162"
+ (version) = "5.1.2600.2180"
module[10]
MDRawModule
@@ -490,13 +520,16 @@ MDRawModule
cv_record.rva = 0x148f
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\dbghelp.dll"
+ (code_file) = "C:\WINDOWS\system32\dbghelp.dll"
+ (code_identifier) = "4110969aa1000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = 39559573-e21b-46f2-8e28-6923be9e6a76
(cv_record).age = 1
(cv_record).pdb_file_name = "dbghelp.pdb"
(misc_record) = (null)
- (debug_filename) = "dbghelp.pdb"
+ (debug_file) = "dbghelp.pdb"
+ (debug_identifier) = "39559573E21B46F28E286923BE9E6A761"
+ (version) = "5.1.2600.2180"
module[11]
MDRawModule
@@ -519,13 +552,16 @@ MDRawModule
cv_record.rva = 0x14b3
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\version.dll"
+ (code_file) = "C:\WINDOWS\system32\version.dll"
+ (code_identifier) = "411096b78000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = 180a90c4-0384-463e-82dd-c45b2c8ab76e
(cv_record).age = 2
(cv_record).pdb_file_name = "version.pdb"
(misc_record) = (null)
- (debug_filename) = "version.pdb"
+ (debug_file) = "version.pdb"
+ (debug_identifier) = "180A90C40384463E82DDC45B2C8AB76E2"
+ (version) = "5.1.2600.2180"
module[12]
MDRawModule
@@ -548,13 +584,16 @@ MDRawModule
cv_record.rva = 0x14d7
misc_record.data_size = 0
misc_record.rva = 0x0
- (module_name) = "C:\WINDOWS\system32\psapi.dll"
+ (code_file) = "C:\WINDOWS\system32\psapi.dll"
+ (code_identifier) = "411096cab000"
(cv_record).cv_signature = 0x53445352
(cv_record).signature = a5c3a1f9-689f-43d8-ad22-8a0929388970
(cv_record).age = 2
(cv_record).pdb_file_name = "psapi.pdb"
(misc_record) = (null)
- (debug_filename) = "psapi.pdb"
+ (debug_file) = "psapi.pdb"
+ (debug_identifier) = "A5C3A1F9689F43D8AD228A09293889702"
+ (version) = "5.1.2600.2180"
MinidumpMemoryList
region_count = 3
diff --git a/src/processor/testdata/minidump2.stackwalk.out b/src/processor/testdata/minidump2.stackwalk.out
index 82c42d73..c8fa3b8d 100644
--- a/src/processor/testdata/minidump2.stackwalk.out
+++ b/src/processor/testdata/minidump2.stackwalk.out
@@ -17,3 +17,18 @@ Thread 0 (crashed)
eip = 0x0040395c esp = 0x0012ff78 ebp = 0x0012ffc0
3 kernel32.dll!BaseProcessStart + 0x22
eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0
+
+Loaded modules:
+0x00400000 - 0x0042afff test_app.exe ??? (main)
+0x59a60000 - 0x59b00fff dbghelp.dll 5.1.2600.2180
+0x76390000 - 0x763acfff imm32.dll 5.1.2600.2180
+0x76bf0000 - 0x76bfafff psapi.dll 5.1.2600.2180
+0x774e0000 - 0x7761cfff ole32.dll 5.1.2600.2726
+0x77c00000 - 0x77c07fff version.dll 5.1.2600.2180
+0x77c10000 - 0x77c67fff msvcrt.dll 7.0.2600.2180
+0x77d40000 - 0x77dcffff user32.dll 5.1.2600.2622
+0x77dd0000 - 0x77e6afff advapi32.dll 5.1.2600.2180
+0x77e70000 - 0x77f00fff rpcrt4.dll 5.1.2600.2180
+0x77f10000 - 0x77f56fff gdi32.dll 5.1.2600.2818
+0x7c800000 - 0x7c8f3fff kernel32.dll 5.1.2600.2945
+0x7c900000 - 0x7c9affff ntdll.dll 5.1.2600.2180