diff options
Diffstat (limited to 'src')
22 files changed, 982 insertions, 418 deletions
diff --git a/src/google_breakpad/processor/basic_source_line_resolver.h b/src/google_breakpad/processor/basic_source_line_resolver.h index fe93f4d2..e6c13532 100644 --- a/src/google_breakpad/processor/basic_source_line_resolver.h +++ b/src/google_breakpad/processor/basic_source_line_resolver.h @@ -27,59 +27,56 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// BasicSourceLineResolver implements SourceLineResolverInterface, using -// address map files produced by a compatible writer, e.g. PDBSourceLineWriter. +// basic_source_line_resolver.h: BasicSourceLineResolver is derived from +// SourceLineResolverBase, and is a concrete implementation of +// SourceLineResolverInterface, using address map files produced by a +// compatible writer, e.g. PDBSourceLineWriter. +// +// see "processor/source_line_resolver_base.h" +// and "source_line_resolver_interface.h" for more documentation. #ifndef GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ #define GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ #include <map> -#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/source_line_resolver_base.h" namespace google_breakpad { using std::string; using std::map; -class BasicSourceLineResolver : public SourceLineResolverInterface { +class BasicSourceLineResolver : public SourceLineResolverBase { public: BasicSourceLineResolver(); - virtual ~BasicSourceLineResolver(); - - // SourceLineResolverInterface methods, see source_line_resolver_interface.h - // for more details. - - // Adds a module to this resolver, returning true on success. - // The given map_file is read into memory, and its symbols will be - // retained until the BasicSourceLineResolver is destroyed. - virtual bool LoadModule(const CodeModule *module, const string &map_file); + virtual ~BasicSourceLineResolver() { } - // Exactly the same as above, except the given map_buffer is used - // for symbols. - virtual bool LoadModuleUsingMapBuffer(const CodeModule *module, - const string &map_buffer); - - void UnloadModule(const CodeModule *module); - virtual bool HasModule(const CodeModule *module); - virtual void FillSourceLineInfo(StackFrame *frame); - virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame); - virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame); + using SourceLineResolverBase::LoadModule; + using SourceLineResolverBase::LoadModuleUsingMapBuffer; + using SourceLineResolverBase::LoadModuleUsingMemoryBuffer; + using SourceLineResolverBase::UnloadModule; + using SourceLineResolverBase::HasModule; + using SourceLineResolverBase::FillSourceLineInfo; + using SourceLineResolverBase::FindWindowsFrameInfo; + using SourceLineResolverBase::FindCFIFrameInfo; private: - template<class T> class MemAddrMap; - struct Line; + // friend declarations: + friend class BasicModuleFactory; + + // Function derives from SourceLineResolverBase::Function. struct Function; - struct PublicSymbol; - struct File; - struct CompareString { - bool operator()(const string &s1, const string &s2) const; - }; + // Module implements SourceLineResolverBase::Module interface. class Module; - // All of the modules we've loaded - typedef map<string, Module*, CompareString> ModuleMap; - ModuleMap *modules_; + // Helper method. + virtual void DeleteDataAfterLoad(char *symbol_data); + // No-op helper methods. + virtual void DeleteDataUnload(const CodeModule *module) { } + virtual void ClearLocalMemory() { } + virtual void StoreDataBeforeLoad(const CodeModule *module, + char *symbol_data) { } // Disallow unwanted copy ctor and assignment operator BasicSourceLineResolver(const BasicSourceLineResolver&); diff --git a/src/google_breakpad/processor/network_source_line_resolver.h b/src/google_breakpad/processor/network_source_line_resolver.h index f2c7732d..f60ff701 100644 --- a/src/google_breakpad/processor/network_source_line_resolver.h +++ b/src/google_breakpad/processor/network_source_line_resolver.h @@ -37,8 +37,8 @@ // An implementation of the server side of the protocol is provided there // as NetworkSourceLineServer. -#ifndef GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H_ -#define GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H_ +#ifndef GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H__ #include <sys/socket.h> @@ -81,6 +81,8 @@ class NetworkSourceLineResolver : public SourceLineResolverInterface, virtual bool LoadModule(const CodeModule *module, const string &map_file); virtual bool LoadModuleUsingMapBuffer(const CodeModule *module, const string &map_buffer); + virtual bool LoadModuleUsingMemoryBuffer(const CodeModule *module, + char *memory_buffer); void UnloadModule(const CodeModule *module); @@ -104,6 +106,11 @@ class NetworkSourceLineResolver : public SourceLineResolverInterface, const SystemInfo *system_info, string *symbol_file, string *symbol_data); + // Similar as the above GetSymbolFile() method, see the comment above. + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data); private: int wait_milliseconds_; @@ -165,4 +172,4 @@ class NetworkSourceLineResolver : public SourceLineResolverInterface, } // namespace google_breakpad -#endif // GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H_ +#endif // GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H__ diff --git a/src/google_breakpad/processor/source_line_resolver_base.h b/src/google_breakpad/processor/source_line_resolver_base.h new file mode 100644 index 00000000..3d978877 --- /dev/null +++ b/src/google_breakpad/processor/source_line_resolver_base.h @@ -0,0 +1,117 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// source_line_resolver_base.h: SourceLineResolverBase, an (incomplete) +// implementation of SourceLineResolverInterface. It serves as a common base +// class for concrete implementations: FastSourceLineResolver and +// BasicSourceLineResolver. It is designed for refactoring that removes +// code redundancy in the two concrete source line resolver classes. +// +// See "google_breakpad/processor/source_line_resolver_interface.h" for more +// documentation. + +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ + +#include <map> +#include <string> + +#include "google_breakpad/processor/source_line_resolver_interface.h" + +namespace google_breakpad { + +using std::map; + +// Forward declaration. +// ModuleFactory is a simple factory interface for creating a Module instance +// at run-time. +class ModuleFactory; + +class SourceLineResolverBase : public SourceLineResolverInterface { + public: + // Read the symbol_data from a file with given file_name. + // The part of code was originally in BasicSourceLineResolver::Module's + // LoadMap() method. + static bool ReadSymbolFile(char **symbol_data, const string &file_name); + + protected: + // Users are not allowed create SourceLineResolverBase instance directly. + SourceLineResolverBase(ModuleFactory *module_factory); + virtual ~SourceLineResolverBase(); + + // Virtual methods inherited from SourceLineResolverInterface. + virtual bool LoadModule(const CodeModule *module, const string &map_file); + virtual bool LoadModuleUsingMapBuffer(const CodeModule *module, + const string &map_buffer); + virtual bool LoadModuleUsingMemoryBuffer(const CodeModule *module, + char *memory_buffer); + virtual void UnloadModule(const CodeModule *module); + virtual bool HasModule(const CodeModule *module); + virtual void FillSourceLineInfo(StackFrame *frame); + virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame); + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame); + + // Helper methods to manage C-String format symbol data. + virtual void StoreDataBeforeLoad(const CodeModule *module, char *symbol_data); + virtual void DeleteDataAfterLoad(char *symbol_data); + virtual void DeleteDataUnload(const CodeModule *module); + virtual void ClearLocalMemory(); + + // Nested structs and classes. + struct Line; + struct Function; + struct PublicSymbol; + struct CompareString { + bool operator()(const string &s1, const string &s2) const; + }; + // Module is an interface for an in-memory symbol file. + class Module; + class AutoFileCloser; + + // All of the modules we've loaded + typedef map<string, Module*, CompareString> ModuleMap; + ModuleMap *modules_; + + // Creates a concrete module at run-time. + ModuleFactory *module_factory_; + + private: + // ModuleFactory needs to have access to protected type Module. + friend class ModuleFactory; + + // Disallow unwanted copy ctor and assignment operator + SourceLineResolverBase(const SourceLineResolverBase&); + void operator=(const SourceLineResolverBase&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ diff --git a/src/google_breakpad/processor/source_line_resolver_interface.h b/src/google_breakpad/processor/source_line_resolver_interface.h index fa45d75f..bd6a12d6 100644 --- a/src/google_breakpad/processor/source_line_resolver_interface.h +++ b/src/google_breakpad/processor/source_line_resolver_interface.h @@ -64,6 +64,12 @@ class SourceLineResolverInterface { virtual bool LoadModuleUsingMapBuffer(const CodeModule *module, const string &map_buffer) = 0; + // Add an interface to load symbol using C-String data insteading string. + // This is useful in the optimization design for avoiding unnecessary copying + // of symbol data, in order to improve memory efficiency. + virtual bool LoadModuleUsingMemoryBuffer(const CodeModule *module, + char *memory_buffer) = 0; + // Request that the specified module be unloaded from this resolver. // A resolver may choose to ignore such a request. virtual void UnloadModule(const CodeModule *module) = 0; diff --git a/src/google_breakpad/processor/symbol_supplier.h b/src/google_breakpad/processor/symbol_supplier.h index 7b9d00e4..4a41688e 100644 --- a/src/google_breakpad/processor/symbol_supplier.h +++ b/src/google_breakpad/processor/symbol_supplier.h @@ -75,6 +75,15 @@ class SymbolSupplier { const SystemInfo *system_info, string *symbol_file, string *symbol_data) = 0; + + // Same as above, except places symbol data into symbol_data as C-string in + // dynamically allocated memory. Using C-string as type of symbol data enables + // passing data by pointer, and thus avoids unncessary copying of data (to + // improve memory efficiency). + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) = 0; }; } // namespace google_breakpad diff --git a/src/processor/basic_source_line_resolver.cc b/src/processor/basic_source_line_resolver.cc index 828f25e9..a795c4ae 100644 --- a/src/processor/basic_source_line_resolver.cc +++ b/src/processor/basic_source_line_resolver.cc @@ -26,6 +26,12 @@ // 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_source_line_resolver.cc: BasicSourceLineResolver implementation. +// +// See basic_source_line_resolver.h and basic_source_line_resolver_types.h +// for documentation. + #include <stdio.h> #include <string.h> @@ -37,17 +43,10 @@ #include <utility> #include <vector> -#include "processor/address_map-inl.h" -#include "processor/contained_range_map-inl.h" -#include "processor/range_map-inl.h" - #include "google_breakpad/processor/basic_source_line_resolver.h" -#include "google_breakpad/processor/code_module.h" -#include "google_breakpad/processor/stack_frame.h" -#include "processor/cfi_frame_info.h" -#include "processor/linked_ptr.h" -#include "processor/scoped_ptr.h" -#include "processor/windows_frame_info.h" +#include "processor/basic_source_line_resolver_types.h" +#include "processor/module_factory.h" + #include "processor/tokenize.h" using std::map; @@ -58,299 +57,33 @@ namespace google_breakpad { static const char *kWhitespace = " \r\n"; -struct BasicSourceLineResolver::Line { - Line(MemAddr addr, MemAddr code_size, int file_id, int source_line) - : address(addr) - , size(code_size) - , source_file_id(file_id) - , line(source_line) { } - - MemAddr address; - MemAddr size; - int source_file_id; - int line; -}; - -struct BasicSourceLineResolver::Function { - Function(const string &function_name, - MemAddr function_address, - MemAddr code_size, - int set_parameter_size) - : name(function_name), address(function_address), size(code_size), - parameter_size(set_parameter_size) { } - - string name; - MemAddr address; - MemAddr size; - - // The size of parameters passed to this function on the stack. - int parameter_size; - - RangeMap< MemAddr, linked_ptr<Line> > lines; -}; - -struct BasicSourceLineResolver::PublicSymbol { - PublicSymbol(const string& set_name, - MemAddr set_address, - int set_parameter_size) - : name(set_name), - address(set_address), - parameter_size(set_parameter_size) {} - - string name; - MemAddr address; - - // If the public symbol is used as a function entry point, parameter_size - // is set to the size of the parameters passed to the funciton on the - // stack, if known. - int parameter_size; -}; - -class BasicSourceLineResolver::Module { - public: - Module(const string &name) : name_(name) { } - - // Loads the given map file, returning true on success. Reads the - // map file into memory and calls LoadMapFromBuffer - bool LoadMap(const string &map_file); - - // Loads a map from the given buffer, returning true on success - bool LoadMapFromBuffer(const string &map_buffer); - - // Looks up the given relative address, and fills the StackFrame struct - // with the result. - void LookupAddress(StackFrame *frame) const; - - // If Windows stack walking information is available covering ADDRESS, - // return a WindowsFrameInfo structure describing it. If the information - // is not available, returns NULL. A NULL return value does not indicate - // an error. The caller takes ownership of any returned WindowsFrameInfo - // object. - WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const; - - // If CFI stack walking information is available covering ADDRESS, - // return a CFIFrameInfo structure describing it. If the information - // is not available, return NULL. The caller takes ownership of any - // returned CFIFrameInfo object. - CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const; - - private: - friend class BasicSourceLineResolver; - typedef map<int, string> FileMap; - - // Parses a file declaration - bool ParseFile(char *file_line); - - // Parses a function declaration, returning a new Function object. - Function* ParseFunction(char *function_line); - - // Parses a line declaration, returning a new Line object. - Line* ParseLine(char *line_line); - - // Parses a PUBLIC symbol declaration, storing it in public_symbols_. - // Returns false if an error occurs. - bool ParsePublicSymbol(char *public_line); - - // Parses a STACK WIN or STACK CFI frame info declaration, storing - // it in the appropriate table. - bool ParseStackInfo(char *stack_info_line); - - // Parses a STACK CFI record, storing it in cfi_frame_info_. - bool ParseCFIFrameInfo(char *stack_info_line); - - // Parse RULE_SET, a series of rules of the sort appearing in STACK - // CFI records, and store the given rules in FRAME_INFO. - bool ParseCFIRuleSet(const string &rule_set, CFIFrameInfo *frame_info) const; - - string name_; - FileMap files_; - RangeMap< MemAddr, linked_ptr<Function> > functions_; - AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_; - - // Each element in the array is a ContainedRangeMap for a type - // listed in WindowsFrameInfoTypes. These are split by type because - // there may be overlaps between maps of different types, but some - // information is only available as certain types. - ContainedRangeMap< MemAddr, linked_ptr<WindowsFrameInfo> > - windows_frame_info_[WindowsFrameInfo::STACK_INFO_LAST]; - - // DWARF CFI stack walking data. The Module stores the initial rule sets - // and rule deltas as strings, just as they appear in the symbol file: - // although the file may contain hundreds of thousands of STACK CFI - // records, walking a stack will only ever use a few of them, so it's - // best to delay parsing a record until it's actually needed. - - // STACK CFI INIT records: for each range, an initial set of register - // recovery rules. The RangeMap's itself gives the starting and ending - // addresses. - RangeMap<MemAddr, string> cfi_initial_rules_; - - // STACK CFI records: at a given address, the changes to the register - // recovery rules that take effect at that address. The map key is the - // starting address; the ending address is the key of the next entry in - // this map, or the end of the range as given by the cfi_initial_rules_ - // entry (which FindCFIFrameInfo looks up first). - map<MemAddr, string> cfi_delta_rules_; -}; - -BasicSourceLineResolver::BasicSourceLineResolver() : modules_(new ModuleMap) { -} - -BasicSourceLineResolver::~BasicSourceLineResolver() { - ModuleMap::iterator it; - for (it = modules_->begin(); it != modules_->end(); ++it) { - delete it->second; - } - delete modules_; -} - -bool BasicSourceLineResolver::LoadModule(const CodeModule *module, - const string &map_file) { - if (module == NULL) - return false; - - // Make sure we don't already have a module with the given name. - if (modules_->find(module->code_file()) != modules_->end()) { - BPLOG(INFO) << "Symbols for module " << module->code_file() - << " already loaded"; - return false; - } - - BPLOG(INFO) << "Loading symbols for module " << module->code_file() - << " from " << map_file; - - Module *basic_module = new Module(module->code_file()); - if (!basic_module->LoadMap(map_file)) { - delete basic_module; - return false; - } - - modules_->insert(make_pair(module->code_file(), basic_module)); - return true; -} - -bool BasicSourceLineResolver::LoadModuleUsingMapBuffer( - const CodeModule *module, - const string &map_buffer) { - if (!module) - return false; - - // Make sure we don't already have a module with the given name. - if (modules_->find(module->code_file()) != modules_->end()) { - BPLOG(INFO) << "Symbols for module " << module->code_file() - << " already loaded"; - return false; - } - - BPLOG(INFO) << "Loading symbols for module " << module->code_file() - << " from buffer"; - - Module *basic_module = new Module(module->code_file()); - if (!basic_module->LoadMapFromBuffer(map_buffer)) { - delete basic_module; - return false; - } - - modules_->insert(make_pair(module->code_file(), basic_module)); - return true; -} - -void BasicSourceLineResolver::UnloadModule(const CodeModule *module) -{ - if (!module) - return; - - ModuleMap::iterator iter = modules_->find(module->code_file()); - if (iter != modules_->end()) { - Module *basic_module = iter->second; - modules_->erase(iter); - delete basic_module; - } -} - -bool BasicSourceLineResolver::HasModule(const CodeModule *module) { - if (!module) - return false; - return modules_->find(module->code_file()) != modules_->end(); -} - -void BasicSourceLineResolver::FillSourceLineInfo(StackFrame *frame) { - if (frame->module) { - ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); - if (it != modules_->end()) { - it->second->LookupAddress(frame); - } - } -} - -WindowsFrameInfo *BasicSourceLineResolver::FindWindowsFrameInfo( - const StackFrame *frame) { - if (frame->module) { - ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); - if (it != modules_->end()) { - return it->second->FindWindowsFrameInfo(frame); - } - } - return NULL; -} +BasicSourceLineResolver::BasicSourceLineResolver() : + SourceLineResolverBase(new BasicModuleFactory) { } -CFIFrameInfo *BasicSourceLineResolver::FindCFIFrameInfo( - const StackFrame *frame) { - if (frame->module) { - ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); - if (it != modules_->end()) { - return it->second->FindCFIFrameInfo(frame); - } - } - return NULL; +void BasicSourceLineResolver::DeleteDataAfterLoad(char *symbol_data) { + // Always delete allocated memory after loading symbol. + delete symbol_data; } -class AutoFileCloser { - public: - AutoFileCloser(FILE *file) : file_(file) {} - ~AutoFileCloser() { - if (file_) - fclose(file_); - } - - private: - FILE *file_; -}; - -bool BasicSourceLineResolver::Module::LoadMapFromBuffer( - const string &map_buffer) { +bool BasicSourceLineResolver::Module::LoadMapFromMemory(char *memory_buffer) { linked_ptr<Function> cur_func; int line_number = 0; - const char *map_buffer_c_str = map_buffer.c_str(); char *save_ptr; - - // set up our input buffer as a c-style string so we - // can we use strtok() - // have to copy because modifying the result of string::c_str is not - // permitted - size_t map_buffer_length = strlen(map_buffer_c_str); + size_t map_buffer_length = strlen(memory_buffer); // If the length is 0, we can still pretend we have a symbol file. This is - // for scenarios that want to test symbol lookup, but don't necessarily care if - // certain modules do not have any information, like system libraries. + // for scenarios that want to test symbol lookup, but don't necessarily care + // if certain modules do not have any information, like system libraries. if (map_buffer_length == 0) { return true; } - scoped_array<char> map_buffer_chars(new char[map_buffer_length]); - if (map_buffer_chars == NULL) { - BPLOG(ERROR) << "Memory allocation of " << map_buffer_length << - " bytes failed"; - return false; + if (memory_buffer[map_buffer_length - 1] == '\n') { + memory_buffer[map_buffer_length - 1] = '\0'; } - strncpy(map_buffer_chars.get(), map_buffer_c_str, map_buffer_length); - - if (map_buffer_chars[map_buffer_length - 1] == '\n') { - map_buffer_chars[map_buffer_length - 1] = '\0'; - } char *buffer; - buffer = strtok_r(map_buffer_chars.get(), "\r\n", &save_ptr); + buffer = strtok_r(memory_buffer, "\r\n", &save_ptr); while (buffer != NULL) { ++line_number; @@ -413,68 +146,11 @@ bool BasicSourceLineResolver::Module::LoadMapFromBuffer( cur_func->lines.StoreRange(line->address, line->size, linked_ptr<Line>(line)); } - buffer = strtok_r(NULL, "\r\n", &save_ptr); } - return true; } -bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) { - struct stat buf; - int error_code = stat(map_file.c_str(), &buf); - if (error_code == -1) { - string error_string; - int error_code = ErrnoString(&error_string); - BPLOG(ERROR) << "Could not open " << map_file << - ", error " << error_code << ": " << error_string; - return false; - } - - off_t file_size = buf.st_size; - - // Allocate memory for file contents, plus a null terminator - // since we'll use strtok() on the contents. - char *file_buffer = new char[sizeof(char)*file_size + 1]; - - if (file_buffer == NULL) { - BPLOG(ERROR) << "Could not allocate memory for " << map_file; - return false; - } - - BPLOG(INFO) << "Opening " << map_file; - - FILE *f = fopen(map_file.c_str(), "rt"); - if (!f) { - string error_string; - int error_code = ErrnoString(&error_string); - BPLOG(ERROR) << "Could not open " << map_file << - ", error " << error_code << ": " << error_string; - delete [] file_buffer; - return false; - } - - AutoFileCloser closer(f); - - int items_read = 0; - - items_read = fread(file_buffer, 1, file_size, f); - - if (items_read != file_size) { - string error_string; - int error_code = ErrnoString(&error_string); - BPLOG(ERROR) << "Could not slurp " << map_file << - ", error " << error_code << ": " << error_string; - delete [] file_buffer; - return false; - } - file_buffer[file_size] = '\0'; - string map_buffer(file_buffer); - delete [] file_buffer; - - return LoadMapFromBuffer(map_buffer); -} - void BasicSourceLineResolver::Module::LookupAddress(StackFrame *frame) const { MemAddr address = frame->instruction - frame->module->base_address(); @@ -543,7 +219,7 @@ WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo( linked_ptr<Function> function; MemAddr function_base, function_size; if (functions_.RetrieveNearestRange(address, &function, - &function_base, &function_size) && + &function_base, &function_size) && address >= function_base && address - function_base < function_size) { result->parameter_size = function->parameter_size; result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE; @@ -558,7 +234,7 @@ WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo( (!function.get() || public_address > function_base)) { result->parameter_size = public_symbol->parameter_size; } - + return NULL; } @@ -596,13 +272,6 @@ CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo( return rules.release(); } -bool BasicSourceLineResolver::Module::ParseCFIRuleSet( - const string &rule_set, CFIFrameInfo *frame_info) const { - CFIFrameInfoParseHandler handler(frame_info); - CFIRuleParser parser(&handler); - return parser.Parse(rule_set); -} - bool BasicSourceLineResolver::Module::ParseFile(char *file_line) { // FILE <id> <filename> file_line += 5; // skip prefix @@ -762,7 +431,7 @@ bool BasicSourceLineResolver::Module::ParseCFIFrameInfo( // This record has the form "STACK INIT <address> <size> <rules...>". char *address_field = strtok_r(NULL, " \r\n", &cursor); if (!address_field) return false; - + char *size_field = strtok_r(NULL, " \r\n", &cursor); if (!size_field) return false; @@ -784,9 +453,4 @@ bool BasicSourceLineResolver::Module::ParseCFIFrameInfo( return true; } -bool BasicSourceLineResolver::CompareString::operator()( - const string &s1, const string &s2) const { - return strcmp(s1.c_str(), s2.c_str()) < 0; -} - } // namespace google_breakpad diff --git a/src/processor/basic_source_line_resolver_types.h b/src/processor/basic_source_line_resolver_types.h new file mode 100644 index 00000000..f40b15b1 --- /dev/null +++ b/src/processor/basic_source_line_resolver_types.h @@ -0,0 +1,159 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// basic_source_line_types.h: definition of nested classes/structs in +// BasicSourceLineResolver. It moves the definitions out of +// basic_source_line_resolver.cc, so that other classes could have access +// to these private nested types without including basic_source_line_resolver.cc +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__ +#define PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__ + +#include <map> +#include <string> + +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "processor/source_line_resolver_base_types.h" + +#include "processor/address_map-inl.h" +#include "processor/range_map-inl.h" +#include "processor/contained_range_map-inl.h" + +#include "processor/linked_ptr.h" +#include "processor/scoped_ptr.h" +#include "google_breakpad/processor/stack_frame.h" +#include "processor/cfi_frame_info.h" +#include "processor/windows_frame_info.h" + +namespace google_breakpad { + +struct +BasicSourceLineResolver::Function : public SourceLineResolverBase::Function { + Function(const string &function_name, + MemAddr function_address, + MemAddr code_size, + int set_parameter_size) : Base(function_name, + function_address, + code_size, + set_parameter_size), + lines() { } + RangeMap< MemAddr, linked_ptr<Line> > lines; + private: + typedef SourceLineResolverBase::Function Base; +}; + + +class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module { + public: + explicit Module(const string &name) : name_(name) { } + virtual ~Module() { } + + // Loads a map from the given buffer in char* type. + // Does NOT have ownership of memory_buffer. + virtual bool LoadMapFromMemory(char *memory_buffer); + + // Looks up the given relative address, and fills the StackFrame struct + // with the result. + virtual void LookupAddress(StackFrame *frame) const; + + // If Windows stack walking information is available covering ADDRESS, + // return a WindowsFrameInfo structure describing it. If the information + // is not available, returns NULL. A NULL return value does not indicate + // an error. The caller takes ownership of any returned WindowsFrameInfo + // object. + virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const; + + private: + // Friend declarations. + friend class BasicSourceLineResolver; + + typedef std::map<int, string> FileMap; + + // Parses a file declaration + bool ParseFile(char *file_line); + + // Parses a function declaration, returning a new Function object. + Function* ParseFunction(char *function_line); + + // Parses a line declaration, returning a new Line object. + Line* ParseLine(char *line_line); + + // Parses a PUBLIC symbol declaration, storing it in public_symbols_. + // Returns false if an error occurs. + bool ParsePublicSymbol(char *public_line); + + // Parses a STACK WIN or STACK CFI frame info declaration, storing + // it in the appropriate table. + bool ParseStackInfo(char *stack_info_line); + + // Parses a STACK CFI record, storing it in cfi_frame_info_. + bool ParseCFIFrameInfo(char *stack_info_line); + + string name_; + FileMap files_; + RangeMap< MemAddr, linked_ptr<Function> > functions_; + AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_; + + // Each element in the array is a ContainedRangeMap for a type + // listed in WindowsFrameInfoTypes. These are split by type because + // there may be overlaps between maps of different types, but some + // information is only available as certain types. + ContainedRangeMap< MemAddr, linked_ptr<WindowsFrameInfo> > + windows_frame_info_[WindowsFrameInfo::STACK_INFO_LAST]; + + // DWARF CFI stack walking data. The Module stores the initial rule sets + // and rule deltas as strings, just as they appear in the symbol file: + // although the file may contain hundreds of thousands of STACK CFI + // records, walking a stack will only ever use a few of them, so it's + // best to delay parsing a record until it's actually needed. + + // STACK CFI INIT records: for each range, an initial set of register + // recovery rules. The RangeMap's itself gives the starting and ending + // addresses. + RangeMap<MemAddr, string> cfi_initial_rules_; + + // STACK CFI records: at a given address, the changes to the register + // recovery rules that take effect at that address. The map key is the + // starting address; the ending address is the key of the next entry in + // this map, or the end of the range as given by the cfi_initial_rules_ + // entry (which FindCFIFrameInfo looks up first). + std::map<MemAddr, string> cfi_delta_rules_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__ diff --git a/src/processor/exploitability_unittest.cc b/src/processor/exploitability_unittest.cc index d365e610..b23c2de7 100644 --- a/src/processor/exploitability_unittest.cc +++ b/src/processor/exploitability_unittest.cc @@ -93,6 +93,11 @@ class TestSymbolSupplier : public SymbolSupplier { string *symbol_file, string *symbol_data); + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data); + // When set to true, causes the SymbolSupplier to return INTERRUPT void set_interrupt(bool interrupt) { interrupt_ = interrupt; } @@ -112,6 +117,14 @@ SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( return NOT_FOUND; } +SymbolSupplier::SymbolResult TestSymbolSupplier::GetCStringSymbolData( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) { + return GetSymbolFile(module, system_info, symbol_file); +} + SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( const CodeModule *module, const SystemInfo *system_info, diff --git a/src/processor/minidump_processor_unittest.cc b/src/processor/minidump_processor_unittest.cc index 8ceda743..ac0368d5 100644 --- a/src/processor/minidump_processor_unittest.cc +++ b/src/processor/minidump_processor_unittest.cc @@ -113,6 +113,11 @@ class TestSymbolSupplier : public SymbolSupplier { string *symbol_file, string *symbol_data); + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data); + // When set to true, causes the SymbolSupplier to return INTERRUPT void set_interrupt(bool interrupt) { interrupt_ = interrupt; } @@ -164,6 +169,25 @@ SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( return s; } +SymbolSupplier::SymbolResult TestSymbolSupplier::GetCStringSymbolData( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) { + string symbol_data_string; + SymbolSupplier::SymbolResult s = GetSymbolFile(module, + system_info, + symbol_file, + &symbol_data_string); + if (s == FOUND) { + unsigned int size = symbol_data_string.size() + 1; + *symbol_data = reinterpret_cast<char*>(operator new(size)); + strcpy(*symbol_data, symbol_data_string.c_str()); + } + + return s; +} + // A mock symbol supplier that always returns NOT_FOUND; one current // use for testing the processor's caching of symbol lookups. class MockSymbolSupplier : public SymbolSupplier { @@ -176,6 +200,10 @@ class MockSymbolSupplier : public SymbolSupplier { const SystemInfo*, string*, string*)); + MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule*, + const SystemInfo*, + string*, + char**)); }; class MinidumpProcessorTest : public ::testing::Test { @@ -217,11 +245,11 @@ TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) { string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + "/src/processor/testdata/minidump2.dmp"; ProcessState state; - EXPECT_CALL(supplier, GetSymbolFile( + EXPECT_CALL(supplier, GetCStringSymbolData( Property(&google_breakpad::CodeModule::code_file, "c:\\test_app.exe"), _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND)); - EXPECT_CALL(supplier, GetSymbolFile( + EXPECT_CALL(supplier, GetCStringSymbolData( Property(&google_breakpad::CodeModule::code_file, Ne("c:\\test_app.exe")), _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND)); @@ -232,11 +260,11 @@ TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) { // We need to verify that across minidumps, the processor will refetch // symbol files, even with the same symbol supplier. - EXPECT_CALL(supplier, GetSymbolFile( + EXPECT_CALL(supplier, GetCStringSymbolData( Property(&google_breakpad::CodeModule::code_file, "c:\\test_app.exe"), _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND)); - EXPECT_CALL(supplier, GetSymbolFile( + EXPECT_CALL(supplier, GetCStringSymbolData( Property(&google_breakpad::CodeModule::code_file, Ne("c:\\test_app.exe")), _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND)); diff --git a/src/processor/module_factory.h b/src/processor/module_factory.h new file mode 100644 index 00000000..6065493d --- /dev/null +++ b/src/processor/module_factory.h @@ -0,0 +1,62 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// module_factory.h: ModuleFactory a factory that provides +// an interface for creating a Module and deferring instantiation to subclasses +// BasicModuleFactory and FastModuleFactory. + +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_MODULE_FACTORY_H__ +#define PROCESSOR_MODULE_FACTORY_H__ + +#include "processor/source_line_resolver_base_types.h" +#include "processor/basic_source_line_resolver_types.h" + +namespace google_breakpad { + +class ModuleFactory { + public: + virtual ~ModuleFactory() { }; + virtual SourceLineResolverBase::Module* + CreateModule(const string &name) const = 0; +}; + +class BasicModuleFactory : public ModuleFactory { + public: + virtual ~BasicModuleFactory() { } + virtual BasicSourceLineResolver::Module* + CreateModule(const string &name) const { + return new BasicSourceLineResolver::Module(name); + } +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_MODULE_FACTORY_H__ diff --git a/src/processor/network_source_line_resolver.cc b/src/processor/network_source_line_resolver.cc index f342c7a4..86c939f9 100644 --- a/src/processor/network_source_line_resolver.cc +++ b/src/processor/network_source_line_resolver.cc @@ -97,6 +97,13 @@ bool NetworkSourceLineResolver::LoadModuleUsingMapBuffer( return true; } +bool NetworkSourceLineResolver::LoadModuleUsingMemoryBuffer( + const CodeModule *module, + char *memory_buffer) { + // see above + return true; +} + void NetworkSourceLineResolver::UnloadModule(const CodeModule *module) { // no-op } @@ -331,6 +338,18 @@ NetworkSourceLineResolver::GetSymbolFile(const CodeModule *module, return GetSymbolFile(module, system_info, symbol_file); } +SymbolSupplier::SymbolResult +NetworkSourceLineResolver::GetCStringSymbolData( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) { + if (symbol_data) + delete *symbol_data; + + return GetSymbolFile(module, system_info, symbol_file); +} + bool NetworkSourceLineResolver::SendMessageGetResponse( const binarystream &message, binarystream &response) { diff --git a/src/processor/network_source_line_server_unittest.cc b/src/processor/network_source_line_server_unittest.cc index 5e969ccf..50eb8a04 100644 --- a/src/processor/network_source_line_server_unittest.cc +++ b/src/processor/network_source_line_server_unittest.cc @@ -89,6 +89,10 @@ public: const SystemInfo *system_info, string *symbol_file, string *symbol_data)); + MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data)); }; class MockSourceLineResolver : public SourceLineResolverInterface { @@ -100,6 +104,8 @@ class MockSourceLineResolver : public SourceLineResolverInterface { const string &map_file)); MOCK_METHOD2(LoadModuleUsingMapBuffer, bool(const CodeModule *module, const string &map_buffer)); + MOCK_METHOD2(LoadModuleUsingMemoryBuffer, bool(const CodeModule *module, + char *memory_buffer)); MOCK_METHOD1(UnloadModule, void(const CodeModule *module)); MOCK_METHOD1(HasModule, bool(const CodeModule *module)); MOCK_METHOD1(FillSourceLineInfo, void(StackFrame *frame)); diff --git a/src/processor/simple_symbol_supplier.cc b/src/processor/simple_symbol_supplier.cc index 544a42cb..df77d72c 100644 --- a/src/processor/simple_symbol_supplier.cc +++ b/src/processor/simple_symbol_supplier.cc @@ -36,6 +36,7 @@ #include "processor/simple_symbol_supplier.h" #include <assert.h> +#include <string.h> #include <sys/types.h> #include <sys/stat.h> @@ -93,6 +94,26 @@ SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFile( return s; } +SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetCStringSymbolData( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) { + assert(symbol_data); + + string symbol_data_string; + SymbolSupplier::SymbolResult s = + GetSymbolFile(module, system_info, symbol_file, &symbol_data_string); + + if (s == FOUND) { + unsigned int size = symbol_data_string.size() + 1; + *symbol_data = reinterpret_cast<char*>(operator new(size)); + memcpy(*symbol_data, symbol_data_string.c_str(), size - 1); + (*symbol_data)[size - 1] = '\0'; + } + return s; +} + SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPathFromRoot( const CodeModule *module, const SystemInfo *system_info, const string &root_path, string *symbol_file) { diff --git a/src/processor/simple_symbol_supplier.h b/src/processor/simple_symbol_supplier.h index dff6a512..753bd0cd 100644 --- a/src/processor/simple_symbol_supplier.h +++ b/src/processor/simple_symbol_supplier.h @@ -110,6 +110,12 @@ class SimpleSymbolSupplier : public SymbolSupplier { const SystemInfo *system_info, string *symbol_file, string *symbol_data); + + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data); + protected: SymbolResult GetSymbolFileAtPathFromRoot(const CodeModule *module, const SystemInfo *system_info, diff --git a/src/processor/source_line_resolver_base.cc b/src/processor/source_line_resolver_base.cc new file mode 100644 index 00000000..14f6ec6d --- /dev/null +++ b/src/processor/source_line_resolver_base.cc @@ -0,0 +1,283 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// source_line_resolver_base.cc: Implementation of SourceLineResolverBase. +// +// See source_line_resolver_base.h and source_line_resolver_base_types.h for +// more documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> + +#include <map> +#include <utility> + +#include "google_breakpad/processor/source_line_resolver_base.h" +#include "processor/source_line_resolver_base_types.h" +#include "processor/module_factory.h" + +using std::map; +using std::make_pair; + +namespace google_breakpad { + +SourceLineResolverBase::SourceLineResolverBase( + ModuleFactory *module_factory) + : modules_(new ModuleMap), + module_factory_(module_factory) { +} + +SourceLineResolverBase::~SourceLineResolverBase() { + ModuleMap::iterator it; + // Iterate through ModuleMap and delete all loaded modules. + for (it = modules_->begin(); it != modules_->end(); ++it) { + // Delete individual module. + delete it->second; + } + // Delete the map of modules. + delete modules_; + delete module_factory_; + + // Helper method to be specified by subclasses. + ClearLocalMemory(); +} + +// Helper methods to be specified by subclasses. +void SourceLineResolverBase::StoreDataBeforeLoad(const CodeModule *module, + char *symbol_data) { } +void SourceLineResolverBase::DeleteDataAfterLoad(char *symbol_data) { } +void SourceLineResolverBase::DeleteDataUnload(const CodeModule *module) { } +void SourceLineResolverBase::ClearLocalMemory() { } + + +bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data, + const string &map_file) { + if (symbol_data == NULL) { + BPLOG(ERROR) << "Could not Read file into Null memory pointer"; + return false; + } + + struct stat buf; + int error_code = stat(map_file.c_str(), &buf); + if (error_code == -1) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not open " << map_file << + ", error " << error_code << ": " << error_string; + return false; + } + + off_t file_size = buf.st_size; + + // Allocate memory for file contents, plus a null terminator + // since we may use strtok() on the contents. + *symbol_data = reinterpret_cast<char*>(operator new(file_size + 1)); + + if (*symbol_data == NULL) { + BPLOG(ERROR) << "Could not allocate memory for " << map_file; + return false; + } + + BPLOG(INFO) << "Opening " << map_file; + + FILE *f = fopen(map_file.c_str(), "rt"); + if (!f) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not open " << map_file << + ", error " << error_code << ": " << error_string; + delete (*symbol_data); + *symbol_data = NULL; + return false; + } + + AutoFileCloser closer(f); + + int items_read = 0; + + items_read = fread(*symbol_data, 1, file_size, f); + + if (items_read != file_size) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not slurp " << map_file << + ", error " << error_code << ": " << error_string; + delete (*symbol_data); + *symbol_data = NULL; + return false; + } + + (*symbol_data)[file_size] = '\0'; + return true; +} + +bool SourceLineResolverBase::LoadModule(const CodeModule *module, + const string &map_file) { + if (module == NULL) + return false; + + // Make sure we don't already have a module with the given name. + if (modules_->find(module->code_file()) != modules_->end()) { + BPLOG(INFO) << "Symbols for module " << module->code_file() + << " already loaded"; + return false; + } + + BPLOG(INFO) << "Loading symbols for module " << module->code_file() + << " from " << map_file; + + char *memory_buffer; + if (!ReadSymbolFile(&memory_buffer, map_file)) + return false; + + BPLOG(INFO) << "Read symbol file " << map_file << " succeeded"; + + // Invoke helper method, let the concrete subclass decides its own action. + StoreDataBeforeLoad(module, memory_buffer); + + return LoadModuleUsingMemoryBuffer(module, memory_buffer); +} + +bool SourceLineResolverBase::LoadModuleUsingMapBuffer( + const CodeModule *module, const string &map_buffer) { + char *memory_buffer = reinterpret_cast<char*>( + operator new(map_buffer.size() + 1)); + if (memory_buffer == NULL) + return false; + + // Can't use strcpy, as the data may contain '\0's before the end. + memcpy(memory_buffer, map_buffer.c_str(), map_buffer.size()); + memory_buffer[map_buffer.size()] = '\0'; + + // Invoke helper method, let the concrete subclass decides its own action. + StoreDataBeforeLoad(module, memory_buffer); + + return LoadModuleUsingMemoryBuffer(module, memory_buffer); +} + +bool SourceLineResolverBase::LoadModuleUsingMemoryBuffer( + const CodeModule *module, char *memory_buffer) { + if (!module) { + // Invoke helper method, let the concrete subclass decides its own action. + DeleteDataAfterLoad(memory_buffer); + return false; + } + + // Make sure we don't already have a module with the given name. + if (modules_->find(module->code_file()) != modules_->end()) { + BPLOG(INFO) << "Symbols for module " << module->code_file() + << " already loaded"; + DeleteDataAfterLoad(memory_buffer); + return false; + } + + BPLOG(INFO) << "Loading symbols for module " << module->code_file() + << " from buffer"; + + Module *basic_module = module_factory_->CreateModule(module->code_file()); + + // Ownership of memory is NOT transfered to Module::LoadMapFromMemory(). + if (!basic_module->LoadMapFromMemory(memory_buffer)) { + delete basic_module; + DeleteDataAfterLoad(memory_buffer); + return false; + } + + modules_->insert(make_pair(module->code_file(), basic_module)); + DeleteDataAfterLoad(memory_buffer); + return true; +} + +void SourceLineResolverBase::UnloadModule(const CodeModule *code_module) { + if (!code_module) + return; + + ModuleMap::iterator iter = modules_->find(code_module->code_file()); + if (iter != modules_->end()) { + Module *symbol_module = iter->second; + delete symbol_module; + modules_->erase(iter); + } + + DeleteDataUnload(code_module); +} + +bool SourceLineResolverBase::HasModule(const CodeModule *module) { + if (!module) + return false; + return modules_->find(module->code_file()) != modules_->end(); +} + +void SourceLineResolverBase::FillSourceLineInfo(StackFrame *frame) { + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + it->second->LookupAddress(frame); + } + } +} + +WindowsFrameInfo *SourceLineResolverBase::FindWindowsFrameInfo( + const StackFrame *frame) { + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + return it->second->FindWindowsFrameInfo(frame); + } + } + return NULL; +} + +CFIFrameInfo *SourceLineResolverBase::FindCFIFrameInfo( + const StackFrame *frame) { + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + return it->second->FindCFIFrameInfo(frame); + } + } + return NULL; +} + +bool SourceLineResolverBase::CompareString::operator()( + const string &s1, const string &s2) const { + return strcmp(s1.c_str(), s2.c_str()) < 0; +} + +bool SourceLineResolverBase::Module::ParseCFIRuleSet( + const string &rule_set, CFIFrameInfo *frame_info) const { + CFIFrameInfoParseHandler handler(frame_info); + CFIRuleParser parser(&handler); + return parser.Parse(rule_set); +} + +} // namespace google_breakpad diff --git a/src/processor/source_line_resolver_base_types.h b/src/processor/source_line_resolver_base_types.h new file mode 100644 index 00000000..5b099f1a --- /dev/null +++ b/src/processor/source_line_resolver_base_types.h @@ -0,0 +1,149 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// source_line_resolver_base_types.h: definition of nested classes/structs in +// SourceLineResolverBase. It moves the definitions out of +// source_line_resolver_base.cc, so that other classes may have access +// to these private nested types without including source_line_resolver_base.cc +// In addition, Module is defined as a pure abstract class to be implemented by +// each concrete source line resolver class. +// +// See source_line_resolver_base.h for more documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include <stdio.h> + +#include <map> +#include <string> + +#include "google_breakpad/processor/source_line_resolver_base.h" +#include "google_breakpad/processor/stack_frame.h" +#include "processor/cfi_frame_info.h" +#include "processor/windows_frame_info.h" + +#ifndef PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__ +#define PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__ + +namespace google_breakpad { + +class SourceLineResolverBase::AutoFileCloser { + public: + explicit AutoFileCloser(FILE *file) : file_(file) {} + ~AutoFileCloser() { + if (file_) + fclose(file_); + } + + private: + FILE *file_; +}; + +struct SourceLineResolverBase::Line { + Line() { } + Line(MemAddr addr, MemAddr code_size, int file_id, int source_line) + : address(addr) + , size(code_size) + , source_file_id(file_id) + , line(source_line) { } + + MemAddr address; + MemAddr size; + int32_t source_file_id; + int32_t line; +}; + +struct SourceLineResolverBase::Function { + Function() { } + Function(const string &function_name, + MemAddr function_address, + MemAddr code_size, + int set_parameter_size) + : name(function_name), address(function_address), size(code_size), + parameter_size(set_parameter_size) { } + + string name; + MemAddr address; + MemAddr size; + + // The size of parameters passed to this function on the stack. + int32_t parameter_size; +}; + +struct SourceLineResolverBase::PublicSymbol { + PublicSymbol() { } + PublicSymbol(const string& set_name, + MemAddr set_address, + int set_parameter_size) + : name(set_name), + address(set_address), + parameter_size(set_parameter_size) {} + + string name; + MemAddr address; + + // If the public symbol is used as a function entry point, parameter_size + // is set to the size of the parameters passed to the funciton on the + // stack, if known. + int32_t parameter_size; +}; + +class SourceLineResolverBase::Module { + public: + virtual ~Module() { }; + // Loads a map from the given buffer in char* type. + // Does NOT take ownership of memory_buffer (the caller, source line resolver, + // is the owner of memory_buffer). + virtual bool LoadMapFromMemory(char *memory_buffer) = 0; + + // Looks up the given relative address, and fills the StackFrame struct + // with the result. + virtual void LookupAddress(StackFrame *frame) const = 0; + + // If Windows stack walking information is available covering ADDRESS, + // return a WindowsFrameInfo structure describing it. If the information + // is not available, returns NULL. A NULL return value does not indicate + // an error. The caller takes ownership of any returned WindowsFrameInfo + // object. + virtual WindowsFrameInfo * + FindWindowsFrameInfo(const StackFrame *frame) const = 0; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const = 0; + protected: + virtual bool ParseCFIRuleSet(const string &rule_set, + CFIFrameInfo *frame_info) const; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__ diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc index 39eb65ec..296697cb 100644 --- a/src/processor/stackwalker.cc +++ b/src/processor/stackwalker.cc @@ -97,15 +97,18 @@ bool Stackwalker::Walk(CallStack *stack) { no_symbol_modules_.find( module->code_file()) == no_symbol_modules_.end() && supplier_) { - string symbol_data, symbol_file; + string symbol_file; + char *symbol_data; SymbolSupplier::SymbolResult symbol_result = - supplier_->GetSymbolFile(module, system_info_, - &symbol_file, &symbol_data); + supplier_->GetCStringSymbolData(module, + system_info_, + &symbol_file, + &symbol_data); switch (symbol_result) { case SymbolSupplier::FOUND: - resolver_->LoadModuleUsingMapBuffer(frame->module, - symbol_data); + resolver_->LoadModuleUsingMemoryBuffer(frame->module, + symbol_data); break; case SymbolSupplier::NOT_FOUND: no_symbol_modules_.insert(module->code_file()); @@ -207,13 +210,14 @@ bool Stackwalker::InstructionAddressSeemsValid(u_int64_t address) { } if (!resolver_->HasModule(module)) { - string symbol_data, symbol_file; + string symbol_file; + char *symbol_data; SymbolSupplier::SymbolResult symbol_result = - supplier_->GetSymbolFile(module, system_info_, - &symbol_file, &symbol_data); + supplier_->GetCStringSymbolData(module, system_info_, + &symbol_file, &symbol_data); if (symbol_result != SymbolSupplier::FOUND || - !resolver_->LoadModuleUsingMapBuffer(module, + !resolver_->LoadModuleUsingMemoryBuffer(module, symbol_data)) { // we don't have symbols, but we're inside a loaded module return true; diff --git a/src/processor/stackwalker_amd64_unittest.cc b/src/processor/stackwalker_amd64_unittest.cc index 6fd3bea4..758c06db 100644 --- a/src/processor/stackwalker_amd64_unittest.cc +++ b/src/processor/stackwalker_amd64_unittest.cc @@ -85,15 +85,18 @@ class StackwalkerAMD64Fixture { // By default, none of the modules have symbol info; call // SetModuleSymbols to override this. - EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _)) + EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _)) .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); } // Set the Breakpad symbol information that supplier should return for // MODULE to INFO. void SetModuleSymbols(MockCodeModule *module, const string &info) { - EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _)) - .WillRepeatedly(DoAll(SetArgumentPointee<3>(info), + unsigned int buffer_size = info.size() + 1; + char *buffer = reinterpret_cast<char*>(operator new(buffer_size)); + strcpy(buffer, info.c_str()); + EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer), Return(MockSymbolSupplier::FOUND))); } diff --git a/src/processor/stackwalker_arm_unittest.cc b/src/processor/stackwalker_arm_unittest.cc index ad7aeb31..637435b1 100644 --- a/src/processor/stackwalker_arm_unittest.cc +++ b/src/processor/stackwalker_arm_unittest.cc @@ -87,15 +87,18 @@ class StackwalkerARMFixture { // By default, none of the modules have symbol info; call // SetModuleSymbols to override this. - EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _)) + EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _)) .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); } // Set the Breakpad symbol information that supplier should return for // MODULE to INFO. void SetModuleSymbols(MockCodeModule *module, const string &info) { - EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _)) - .WillRepeatedly(DoAll(SetArgumentPointee<3>(info), + unsigned int buffer_size = info.size() + 1; + char *buffer = reinterpret_cast<char*>(operator new(buffer_size)); + strcpy(buffer, info.c_str()); + EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer), Return(MockSymbolSupplier::FOUND))); } diff --git a/src/processor/stackwalker_unittest_utils.h b/src/processor/stackwalker_unittest_utils.h index a7bff3e8..2da71bc0 100644 --- a/src/processor/stackwalker_unittest_utils.h +++ b/src/processor/stackwalker_unittest_utils.h @@ -170,6 +170,10 @@ class MockSymbolSupplier: public google_breakpad::SymbolSupplier { const SystemInfo *system_info, std::string *symbol_file, std::string *symbol_data)); + MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + std::string *symbol_file, + char **symbol_data)); }; #endif // PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ diff --git a/src/processor/stackwalker_x86_unittest.cc b/src/processor/stackwalker_x86_unittest.cc index c0890a1e..4b32cac9 100644 --- a/src/processor/stackwalker_x86_unittest.cc +++ b/src/processor/stackwalker_x86_unittest.cc @@ -86,15 +86,18 @@ class StackwalkerX86Fixture { // By default, none of the modules have symbol info; call // SetModuleSymbols to override this. - EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _)) + EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _)) .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); } // Set the Breakpad symbol information that supplier should return for // MODULE to INFO. void SetModuleSymbols(MockCodeModule *module, const string &info) { - EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _)) - .WillRepeatedly(DoAll(SetArgumentPointee<3>(info), + unsigned int buffer_size = info.size() + 1; + char *buffer = reinterpret_cast<char*>(operator new(buffer_size)); + strcpy(buffer, info.c_str()); + EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer), Return(MockSymbolSupplier::FOUND))); } diff --git a/src/processor/windows_frame_info.h b/src/processor/windows_frame_info.h index 79e74638..067f3cfd 100644 --- a/src/processor/windows_frame_info.h +++ b/src/processor/windows_frame_info.h @@ -39,6 +39,7 @@ #define PROCESSOR_WINDOWS_FRAME_INFO_H__ #include <string.h> +#include <stdlib.h> #include <string> #include <vector> |