Index: Makefile.in =================================================================== --- Makefile.in (revision 234) +++ Makefile.in (working copy) @@ -72,7 +72,9 @@ src/processor/minidump_processor_unittest$(EXEEXT) \ src/processor/pathname_stripper_unittest$(EXEEXT) \ src/processor/postfix_evaluator_unittest$(EXEEXT) \ - src/processor/range_map_unittest$(EXEEXT) $(am__EXEEXT_1) + src/processor/range_map_unittest$(EXEEXT) \ + src/processor/module_serialize_unittest$(EXEEXT) \ + $(am__EXEEXT_1) @SELFTEST_TRUE@am__append_1 = \ @SELFTEST_TRUE@ src/processor/stackwalker_selftest @@ -107,8 +109,9 @@ am__dirstamp = $(am__leading_dot)dirstamp am_src_libbreakpad_la_OBJECTS = src/processor/basic_code_modules.lo \ src/processor/basic_source_line_resolver.lo \ - src/processor/call_stack.lo src/processor/logging.lo \ - src/processor/minidump.lo src/processor/minidump_processor.lo \ + src/processor/call_stack.lo src/processor/disk_module_cache.lo \ + src/processor/logging.lo src/processor/minidump.lo \ + src/processor/minidump_processor.lo \ src/processor/pathname_stripper.lo \ src/processor/process_state.lo \ src/processor/simple_symbol_supplier.lo \ @@ -169,8 +172,9 @@ src_processor_minidump_stackwalk_DEPENDENCIES = \ src/processor/basic_code_modules.lo \ src/processor/basic_source_line_resolver.lo \ - src/processor/call_stack.lo src/processor/logging.lo \ - src/processor/minidump.lo src/processor/minidump_processor.lo \ + src/processor/call_stack.lo src/processor/disk_module_cache.lo \ + src/processor/logging.lo src/processor/minidump.lo \ + src/processor/minidump_processor.lo \ src/processor/pathname_stripper.lo \ src/processor/process_state.lo \ src/processor/simple_symbol_supplier.lo \ @@ -179,6 +183,15 @@ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo +am_src_processor_module_serialize_unittest_OBJECTS = \ + src/processor/module_serialize_unittest.$(OBJEXT) +src_processor_module_serialize_unittest_OBJECTS = \ + $(am_src_processor_module_serialize_unittest_OBJECTS) +src_processor_module_serialize_unittest_DEPENDENCIES = \ + src/processor/basic_code_modules.lo \ + src/processor/basic_source_line_resolver.lo \ + src/processor/call_stack.lo src/processor/logging.lo \ + src/processor/pathname_stripper.lo am_src_processor_pathname_stripper_unittest_OBJECTS = \ src/processor/pathname_stripper_unittest.$(OBJEXT) src_processor_pathname_stripper_unittest_OBJECTS = \ @@ -240,6 +253,7 @@ $(src_processor_minidump_dump_SOURCES) \ $(src_processor_minidump_processor_unittest_SOURCES) \ $(src_processor_minidump_stackwalk_SOURCES) \ + $(src_processor_module_serialize_unittest_SOURCES) \ $(src_processor_pathname_stripper_unittest_SOURCES) \ $(src_processor_postfix_evaluator_unittest_SOURCES) \ $(src_processor_range_map_unittest_SOURCES) \ @@ -251,6 +265,7 @@ $(src_processor_minidump_dump_SOURCES) \ $(src_processor_minidump_processor_unittest_SOURCES) \ $(src_processor_minidump_stackwalk_SOURCES) \ + $(src_processor_module_serialize_unittest_SOURCES) \ $(src_processor_pathname_stripper_unittest_SOURCES) \ $(src_processor_postfix_evaluator_unittest_SOURCES) \ $(src_processor_range_map_unittest_SOURCES) \ @@ -401,6 +416,7 @@ src/google_breakpad/processor/minidump_processor.h \ src/google_breakpad/processor/process_state.h \ src/google_breakpad/processor/source_line_resolver_interface.h \ + src/google_breakpad/processor/source_line_resolver_module_cache_interface.h \ src/google_breakpad/processor/stack_frame.h \ src/google_breakpad/processor/stack_frame_cpu.h \ src/google_breakpad/processor/stackwalker.h \ @@ -415,6 +431,8 @@ src/processor/call_stack.cc \ src/processor/contained_range_map-inl.h \ src/processor/contained_range_map.h \ + src/processor/disk_module_cache.cc \ + src/processor/disk_module_cache.h \ src/processor/linked_ptr.h \ src/processor/logging.h \ src/processor/logging.cc \ @@ -508,6 +526,16 @@ src/processor/logging.lo \ src/processor/pathname_stripper.lo +src_processor_module_serialize_unittest_SOURCES = \ + src/processor/module_serialize_unittest.cc + +src_processor_module_serialize_unittest_LDADD = \ + src/processor/basic_code_modules.lo \ + src/processor/basic_source_line_resolver.lo \ + src/processor/call_stack.lo \ + src/processor/logging.lo \ + src/processor/pathname_stripper.lo + src_processor_stackwalker_selftest_SOURCES = \ src/processor/stackwalker_selftest.cc @@ -541,6 +569,7 @@ src/processor/basic_code_modules.lo \ src/processor/basic_source_line_resolver.lo \ src/processor/call_stack.lo \ + src/processor/disk_module_cache.lo \ src/processor/logging.lo \ src/processor/minidump.lo \ src/processor/minidump_processor.lo \ @@ -778,6 +807,8 @@ src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/call_stack.lo: src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/disk_module_cache.lo: src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/logging.lo: src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/minidump.lo: src/processor/$(am__dirstamp) \ @@ -883,6 +914,12 @@ src/processor/minidump_stackwalk$(EXEEXT): $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_DEPENDENCIES) src/processor/$(am__dirstamp) @rm -f src/processor/minidump_stackwalk$(EXEEXT) $(CXXLINK) $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_LDADD) $(LIBS) +src/processor/module_serialize_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/module_serialize_unittest$(EXEEXT): $(src_processor_module_serialize_unittest_OBJECTS) $(src_processor_module_serialize_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/module_serialize_unittest$(EXEEXT) + $(CXXLINK) $(src_processor_module_serialize_unittest_OBJECTS) $(src_processor_module_serialize_unittest_LDADD) $(LIBS) src/processor/pathname_stripper_unittest.$(OBJEXT): \ src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) @@ -919,6 +956,8 @@ -rm -f src/processor/call_stack.$(OBJEXT) -rm -f src/processor/call_stack.lo -rm -f src/processor/contained_range_map_unittest.$(OBJEXT) + -rm -f src/processor/disk_module_cache.$(OBJEXT) + -rm -f src/processor/disk_module_cache.lo -rm -f src/processor/logging.$(OBJEXT) -rm -f src/processor/logging.lo -rm -f src/processor/minidump.$(OBJEXT) @@ -928,6 +967,7 @@ -rm -f src/processor/minidump_processor.lo -rm -f src/processor/minidump_processor_unittest.$(OBJEXT) -rm -f src/processor/minidump_stackwalk.$(OBJEXT) + -rm -f src/processor/module_serialize_unittest.$(OBJEXT) -rm -f src/processor/pathname_stripper.$(OBJEXT) -rm -f src/processor/pathname_stripper.lo -rm -f src/processor/pathname_stripper_unittest.$(OBJEXT) @@ -958,12 +998,14 @@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/basic_source_line_resolver_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/call_stack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/contained_range_map_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/disk_module_cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/logging.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_dump.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_stackwalk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/module_serialize_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/pathname_stripper.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/pathname_stripper_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/postfix_evaluator_unittest.Po@am__quote@ Index: src/processor/range_map.h =================================================================== --- src/processor/range_map.h (revision 234) +++ src/processor/range_map.h (working copy) @@ -43,10 +43,8 @@ #include - namespace google_breakpad { - template class RangeMap { public: @@ -92,6 +90,8 @@ // initially created. void Clear(); + bool Equals(const RangeMap &other) const; + private: class Range { public: @@ -101,6 +101,10 @@ AddressType base() const { return base_; } EntryType entry() const { return entry_; } + //XXX: I really wanted this to be Equals, but + // I can't seem to get that to work properly. + bool operator==(const Range &other) const; + private: // The base address of the range. The high address does not need to // be stored, because RangeMap uses it as the key to the map. @@ -117,6 +121,11 @@ // Maps the high address of each range to a EntryType. AddressToRangeMap map_; + + friend class ModuleSerializer; + + //template + //friend bool Equals(const T &a, const T &b); }; Index: src/processor/address_map-inl.h =================================================================== --- src/processor/address_map-inl.h (revision 234) +++ src/processor/address_map-inl.h (working copy) @@ -39,6 +39,7 @@ #include #include "processor/address_map.h" +#include "processor/equals.h" #include "processor/logging.h" namespace google_breakpad { @@ -87,6 +88,12 @@ map_.clear(); } +template +bool AddressMap::Equals( + const AddressMap &other) const { + return google_breakpad::Equals(map_, other.map_); +} + } // namespace google_breakpad #endif // PROCESSOR_ADDRESS_MAP_INL_H__ Index: src/processor/range_map-inl.h =================================================================== --- src/processor/range_map-inl.h (revision 234) +++ src/processor/range_map-inl.h (working copy) @@ -40,12 +40,12 @@ #include #include "processor/range_map.h" +#include "processor/equals.h" #include "processor/logging.h" namespace google_breakpad { - template bool RangeMap::StoreRange(const AddressType &base, const AddressType &size, @@ -203,7 +203,18 @@ map_.clear(); } +template +bool RangeMap::Equals( + const RangeMap &other) const { + return google_breakpad::Equals(map_, other.map_); +} +template +bool RangeMap::Range::operator==(const Range &other) const { + return google_breakpad::Equals(base_, other.base_) && + google_breakpad::Equals(entry_, other.entry_); +} + } // namespace google_breakpad Index: src/processor/contained_range_map.h =================================================================== --- src/processor/contained_range_map.h (revision 234) +++ src/processor/contained_range_map.h (working copy) @@ -102,6 +102,8 @@ // empty state when called on the root node. void Clear(); + bool Equals(const ContainedRangeMap &other) const; + private: // AddressToRangeMap stores pointers. This makes reparenting simpler in // StoreRange, because it doesn't need to copy entire objects. @@ -136,6 +138,8 @@ // address. This is a pointer to avoid allocating map structures for // leaf nodes, where they are not needed. AddressToRangeMap *map_; + + friend class ModuleSerializer; }; Index: src/processor/disk_module_cache.h =================================================================== --- src/processor/disk_module_cache.h (revision 0) +++ src/processor/disk_module_cache.h (revision 0) @@ -0,0 +1,74 @@ +// Copyright (c) 2007, 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. + +// DiskModuleCache implements SourceLineResolverModuleCacheInterface, +// storing the cached objects on disk. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_DISK_MODULE_CACHE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_DISK_MODULE_CACHE_H__ + +#include "google_breakpad/processor/source_line_resolver_module_cache_interface.h" + +namespace google_breakpad { + +using std::string; +using std::istream; +using std::ostream; + +class DiskModuleCache : public SourceLineResolverModuleCacheInterface { + public: + DiskModuleCache(string cache_directory); + virtual ~DiskModuleCache() {} + + // Retrieve the symbol data associated with a module + virtual bool GetModuleData(const string &symbol_file, + istream **data_stream); + + // Get a stream to which the data associated with a module + // can be stored + virtual bool BeginSetModuleData(const string &symbol_file, + ostream **data_stream); + // Finish setting the data associated with this module, + // data should already have been written to the stream + virtual bool EndSetModuleData(const string &symbol_file, + ostream **data_stream); + + private: + string cache_directory_; + // Given a symbol file, map to a cache entry on disk + string MapToCacheEntry(const string &symbol_file); + // Given a file path, ensure that all directories in the path exist. + bool EnsurePathExists(const string &cache_entry); + // determine if this path is a directory + bool IsDir(const string &path); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_DISK_MODULE_CACHE_H__ Index: src/processor/minidump_stackwalk.cc =================================================================== --- src/processor/minidump_stackwalk.cc (revision 234) +++ src/processor/minidump_stackwalk.cc (working copy) @@ -45,6 +45,7 @@ #include "google_breakpad/processor/minidump_processor.h" #include "google_breakpad/processor/process_state.h" #include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/disk_module_cache.h" #include "processor/logging.h" #include "processor/pathname_stripper.h" #include "processor/scoped_ptr.h" @@ -55,6 +56,7 @@ using std::string; using std::vector; using google_breakpad::BasicSourceLineResolver; +using google_breakpad::DiskModuleCache; using google_breakpad::CallStack; using google_breakpad::CodeModule; using google_breakpad::CodeModules; @@ -438,7 +440,8 @@ symbol_supplier.reset(new SimpleSymbolSupplier(symbol_paths)); } - BasicSourceLineResolver resolver; + DiskModuleCache disk_cache("/tmp/module_cache/"); + BasicSourceLineResolver resolver(&disk_cache); MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver); // Process the minidump. Index: src/processor/basic_source_line_resolver.cc =================================================================== --- src/processor/basic_source_line_resolver.cc (revision 234) +++ src/processor/basic_source_line_resolver.cc (working copy) @@ -33,7 +33,9 @@ #include #include #include +#include +#include "processor/equals.h" #include "processor/address_map-inl.h" #include "processor/contained_range_map-inl.h" #include "processor/range_map-inl.h" @@ -61,6 +63,13 @@ , source_file_id(file_id) , line(source_line) { } + bool operator==(const Line &other) const { + return address == other.address && + size == other.size && + source_file_id == other.source_file_id && + line == other.line; + } + MemAddr address; MemAddr size; int source_file_id; @@ -85,6 +94,16 @@ RangeMap< MemAddr, linked_ptr > lines; }; +// doesn't really belong here, but I can't put it anywhere else... +inline bool Equals(const BasicSourceLineResolver::Function &a, + const BasicSourceLineResolver::Function &b) +{ + return a.name == b.name && + a.address == b.address && + a.size == b.size && + Equals(a.lines, b.lines); +} + struct BasicSourceLineResolver::PublicSymbol { PublicSymbol(const string& set_name, MemAddr set_address, @@ -93,11 +112,17 @@ address(set_address), parameter_size(set_parameter_size) {} + bool operator==(const PublicSymbol &other) const { + return name == other.name && + address == other.address && + parameter_size == other.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 + // is set to the size of the parameters passed to the function on the // stack, if known. int parameter_size; }; @@ -116,8 +141,11 @@ // any returned StackFrameInfo object. StackFrameInfo* LookupAddress(StackFrame *frame) const; + bool Equals(const Module &other) const; + private: friend class BasicSourceLineResolver; + #ifdef BSLR_NO_HASH_MAP typedef map FileMap; #else // BSLR_NO_HASH_MAP @@ -177,11 +205,395 @@ // as certain types. ContainedRangeMap< MemAddr, linked_ptr > stack_info_[STACK_INFO_LAST]; + + friend class ModuleSerializer; }; -BasicSourceLineResolver::BasicSourceLineResolver() : modules_(new ModuleMap) { + +class ModuleSerializer { + public: + ModuleSerializer(std::istream *stream) : instream_(stream), + outstream_(NULL) {} + ModuleSerializer(std::ostream *stream) : instream_(NULL), + outstream_(stream) {} + + bool Serialize(const BasicSourceLineResolver::Module &module) { + if (!outstream_) + return false; + + outstream_->seekp(0, std::ios_base::beg); + Write(SERIALIZE_FORMAT); + Write(module.files_); + Write(module.functions_); + Write(module.public_symbols_); + for (int i = BasicSourceLineResolver::Module::STACK_INFO_FPO; + i < BasicSourceLineResolver::Module::STACK_INFO_LAST; i++) + Write(module.stack_info_[i]); + return true; + } + + bool Deserialize(BasicSourceLineResolver::Module &module) { + if (!instream_) + return false; + + instream_->seekg(0, std::ios_base::beg); + //TODO: implement me + int format; + Read(format); + if (format != SERIALIZE_FORMAT) + return false; + + Read(module.files_); + Read(module.functions_); + Read(module.public_symbols_); + for (int i = BasicSourceLineResolver::Module::STACK_INFO_FPO; + i < BasicSourceLineResolver::Module::STACK_INFO_LAST; i++) + Read(module.stack_info_[i]); + + return true; + } + + private: + // Increment this if changing the serializing format + static const int SERIALIZE_FORMAT = 1; + + /* + doesn't really make sense to have this, it just hides cases where + there ought to be specific serializers. + template + void Write(const T &data) { + outstream_->write(reinterpret_cast(&data), sizeof(data)); + } + */ + + template + void Read(T &data) { + instream_->read(reinterpret_cast(&data), sizeof(data)); + } + + void Write(const size_t data) { + outstream_->write(reinterpret_cast(&data), sizeof(size_t)); + } + + void Write(const u_int32_t data) { + outstream_->write(reinterpret_cast(&data), sizeof(u_int32_t)); + } + + void Write(const int data) { + outstream_->write(reinterpret_cast(&data), sizeof(int)); + } + + // Serialize strings as a length + character array (not null terminated) + // pad out to sizeof(int) + void Write(const string &data) { + if (data.empty()) { + Write((size_t)0); + return; + } + + int extra = sizeof(int) - (data.size() % sizeof(int)); + Write(data.size() + extra); + outstream_->write(data.c_str(), data.size()); + char c = '\0'; + for (int i=0; iwrite(&c, 1); + } + + void Read(string &data) { + size_t length; + Read(length); + + if (length > 0) { + vector string_bytes(length); + instream_->read(&string_bytes[0], length); + data = &string_bytes[0]; + } + } + + template + void Write(const map &data) { + Write(data.size()); + for (typename map::const_iterator it = data.begin(); + it != data.end(); it++) { + Write(it->first); + Write(it->second); + } + } + + template + void Read(map &data) { + size_t length; + Read(length); + if (length == 0) { + BPLOG(INFO) << "Empty map"; + return; + } + + for (int i=0; i + void Write(const hash_map &data) { + Write(data.size()); + for (typename hash_map::const_iterator it = data.begin(); + it != data.end(); it++) { + Write(it->first); + Write(it->second); + } + } + + template + void Read(hash_map &data) { + size_t length; + Read(length); + if (length == 0) + return; + + data.resize(length); + + for (int i=0; i + void Write(const RangeMap &data) { + Write(data.map_); + } + + template + void Read(RangeMap &data) { + // Can't instantiate Range(), so manually read in the + // AddressMap here. + size_t length; + Read(length); + if (length == 0) + return; + + for (int i=0; i::Range(range_address, entry))); + } + } + + template + void Write(const typename RangeMap::Range &data) { + Write(data.base_); + Write(data.entry_); + } + + template + void Write(const AddressMap &data) { + Write(data.map_); + } + + template + void Read(AddressMap &data) { + Read(data.map_); + } + + template + void Write(const ContainedRangeMap &data) { + Write(data.base_); + Write(data.entry_); + if (data.map_) { + // write this as a marker + Write((size_t)1); + Write(*data.map_); + } + else { + Write((size_t)0); + } + } + + template + void Read(ContainedRangeMap &data) { + // sort of evil, but that's ok + AddressType *base_ptr = const_cast(&data.base_); + Read(*base_ptr); + EntryType *entry_ptr = const_cast(&data.entry_); + Read(*entry_ptr); + + size_t marker; + Read(marker); + if (marker == 1) { + data.map_ = new typename ContainedRangeMap::AddressToRangeMap(); + Read(*data.map_); + } + } + + template + void Write(const ContainedRangeMap * const &data) { + BPLOG(INFO) << "Write(ContainedRangeMap *)"; + if (data) { + // write this as a marker + Write((size_t)1); + Write(*data); + } + else { + Write((size_t)0); + } + } + + template + void Read(ContainedRangeMap *&data) { + size_t marker; + Read(marker); + if (marker == 1) { + data = new ContainedRangeMap(); + Read(*data); + } + else { + data = NULL; + } + } + + void Write(const BasicSourceLineResolver::Function &data) { + Write(data.name); + Write(data.address); + Write(data.size); + Write(data.parameter_size); + Write(data.lines); + } + + // Read this as a pointer because there's no zero-argument + // constructor. + void Read(BasicSourceLineResolver::Function *&data) { + string name; + SourceLineResolverInterface::MemAddr address; + SourceLineResolverInterface::MemAddr size; + int parameter_size; + + Read(name); + Read(address); + Read(size); + Read(parameter_size); + data = new BasicSourceLineResolver::Function(name, address, size, + parameter_size); + Read(data->lines); + } + + void Write(const BasicSourceLineResolver::PublicSymbol &data) { + Write(data.name); + Write(data.address); + Write(data.parameter_size); + } + + // Read this as a pointer because there's no zero-argument + // constructor. + void Read(BasicSourceLineResolver::PublicSymbol *&data) { + string name; + SourceLineResolverInterface::MemAddr address; + int parameter_size; + Read(name); + Read(address); + Read(parameter_size); + data = new BasicSourceLineResolver::PublicSymbol(name, address, + parameter_size); + } + + void Write(const BasicSourceLineResolver::Line &data) { + Write(data.address); + Write(data.size); + Write(data.source_file_id); + Write(data.line); + } + + // Read this as a pointer because there's no zero-argument + // constructor. + void Read(BasicSourceLineResolver::Line *&data) { + SourceLineResolverInterface::MemAddr address; + SourceLineResolverInterface::MemAddr size; + int source_file_id; + int line; + + data = new BasicSourceLineResolver::Line(address, size, source_file_id, + line); + } + + void Write(const StackFrameInfo &data) { + Write(data.valid); + Write(data.prolog_size); + Write(data.epilog_size); + Write(data.parameter_size); + Write(data.saved_register_size); + Write(data.local_size); + Write(data.max_stack_size); + Write(data.allocates_base_pointer); + Write(data.program_string); + } + + // Just for consistency with the above methods. + void Read(StackFrameInfo *&data) { + data = new StackFrameInfo(); + Read(data->valid); + Read(data->prolog_size); + Read(data->epilog_size); + Read(data->parameter_size); + Read(data->saved_register_size); + Read(data->local_size); + Read(data->max_stack_size); + Read(data->allocates_base_pointer); + Read(data->program_string); + } + + template + void Write(const linked_ptr &data) { + if (data.get()) { + // write this as a marker + Write((size_t)1); + Write(*data); + } + else { + Write((size_t)0); + } + } + + template + void Read(linked_ptr &data) { + size_t marker; + Read(marker); + if (marker == 1) { + T *thing; + // Read will allocate the pointer + Read(thing); + data.reset(thing); + } + } + + std::istream *instream_; + std::ostream *outstream_; +}; + +BasicSourceLineResolver::BasicSourceLineResolver() : + modules_(new ModuleMap), + module_cache_(NULL) { } +BasicSourceLineResolver::BasicSourceLineResolver(SourceLineResolverModuleCacheInterface *module_cache) : + modules_(new ModuleMap), + module_cache_(module_cache) { +} + BasicSourceLineResolver::~BasicSourceLineResolver() { ModuleMap::iterator it; for (it = modules_->begin(); it != modules_->end(); ++it) { @@ -202,11 +614,34 @@ map_file; Module *module = new Module(module_name); - if (!module->LoadMap(map_file)) { - delete module; - return false; + + // First see if we have a cache, and if so, + // if the cache contains this module + istream *instream; + if (module_cache_ && + module_cache_->GetModuleData(map_file, &instream)) { + ModuleSerializer serializer(instream); + serializer.Deserialize(*module); + delete instream; } + else { + // Otherwise load from file + if (!module->LoadMap(map_file)) { + delete module; + return false; + } + // If we have a cache, then store the result + ostream *outstream; + if (module_cache_ && + module_cache_->BeginSetModuleData(map_file, &outstream)) { + ModuleSerializer serializer(outstream); + serializer.Serialize(*module); + module_cache_->EndSetModuleData(map_file, &outstream); + delete outstream; + } + } + modules_->insert(make_pair(module_name, module)); return true; } @@ -402,6 +837,23 @@ return frame_info.release(); } +bool BasicSourceLineResolver::Module::Equals(const Module &other) const { + return + google_breakpad::Equals(files_, other.files_) && + google_breakpad::Equals(functions_, other.functions_) && + google_breakpad::Equals(public_symbols_, other.public_symbols_) && + google_breakpad::Equals(stack_info_[STACK_INFO_FPO], + other.stack_info_[STACK_INFO_FPO]) && + google_breakpad::Equals(stack_info_[STACK_INFO_TRAP], + other.stack_info_[STACK_INFO_TRAP]) && + google_breakpad::Equals(stack_info_[STACK_INFO_TSS], + other.stack_info_[STACK_INFO_TSS]) && + google_breakpad::Equals(stack_info_[STACK_INFO_STANDARD], + other.stack_info_[STACK_INFO_STANDARD]) && + google_breakpad::Equals(stack_info_[STACK_INFO_FRAME_DATA], + other.stack_info_[STACK_INFO_FRAME_DATA]); +} + // static bool BasicSourceLineResolver::Module::Tokenize(char *line, int max_tokens, vector *tokens) { @@ -611,4 +1063,43 @@ } #endif // BSLR_NO_HASH_MAP +// static +bool BasicSourceLineResolver::ModuleRoundTripTest(const string &map_file) { + Module *module = new Module("test"); + if (!module->LoadMap(map_file)) { + BPLOG(ERROR) << "Failed to load map file!"; + return false; + } + + if (!module->Equals(*module)) { // sanity check + BPLOG(ERROR) << "Failed sanity check!"; + return false; + } + + std::stringstream memory_stream(std::ios::in | std::ios::out | + std::ios::binary); + memory_stream.exceptions(std::ios::eofbit | std::ios::failbit | + std::ios::badbit); + ModuleSerializer serializer(static_cast(&memory_stream)); + if (!serializer.Serialize(*module)) { + BPLOG(ERROR) << "Failed to serialize Module!"; + return false; + } + + BPLOG(INFO) << "Serialized " << memory_stream.tellp() << " bytes."; + + Module *new_module = new Module("test"); + ModuleSerializer deserializer(static_cast(&memory_stream)); + if (!deserializer.Deserialize(*new_module)) { + BPLOG(ERROR) << "Failed to deserialize Module!"; + return false; + } + + if (!module->Equals(*new_module)) { + BPLOG(ERROR) << "Deserialized module not equivalent to original!"; + return false; + } + return true; +} + } // namespace google_breakpad Index: src/processor/module_serialize_unittest.cc =================================================================== --- src/processor/module_serialize_unittest.cc (revision 0) +++ src/processor/module_serialize_unittest.cc (revision 0) @@ -0,0 +1,60 @@ +// Copyright (c) 2007, 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_serialize_unittest.cc: Unit tests for +// BasicSourceLineResolver::Module serialization. +// +// Author: Ted Mielczarek + +#include +#include "stdio.h" + +#include "processor/logging.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + char symbol_test_files[][1024] = { + "src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym", + "src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1/test_app.sym" }; + + for (int i=0; i < sizeof(symbol_test_files) / sizeof(symbol_test_files[0]); + i++) { + BPLOG(INFO) << "Testing round trip serialize for symbol file " + << symbol_test_files[i]; + if (!google_breakpad::BasicSourceLineResolver::ModuleRoundTripTest(symbol_test_files[i])) { + BPLOG(ERROR) << "FAILED: module round trip test for symbol file " + << symbol_test_files[i]; + return 1; + } + } + + return 0; +} Index: src/processor/stack_frame_info.h =================================================================== --- src/processor/stack_frame_info.h (revision 234) +++ src/processor/stack_frame_info.h (working copy) @@ -100,6 +100,18 @@ program_string.erase(); } + bool operator==(const StackFrameInfo &other) const { + return valid == other.valid && + prolog_size == other.prolog_size && + epilog_size == other.epilog_size && + parameter_size == other.parameter_size && + saved_register_size == other.saved_register_size && + local_size == other.local_size && + max_stack_size == other.max_stack_size && + allocates_base_pointer == other.allocates_base_pointer && + program_string == other.program_string; + } + // Identifies which fields in the structure are valid. This is of // type Validity, but it is defined as an int because it's not // possible to OR values into an enumerated type. Users must check Index: src/processor/equals.h =================================================================== --- src/processor/equals.h (revision 0) +++ src/processor/equals.h (revision 0) @@ -0,0 +1,199 @@ +// Copyright (c) 2007, 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. + +// equals.h: Templatized functions for checking equivalence. +// +// Use these instead of operator== so that we can check for equivalence +// of objects stored as pointers inside linked_ptrs. Equals knows +// how to compare certain objects (RangeMap, AddressMap, ContainedRangeMap), +// and special-cases linked_ptr as well. It defaults to operator== for +// everything else. +// +// Author: Ted Mielczarek + +#ifndef GOOGLE_BREAKPAD_EQUALS_H_ +#define GOOGLE_BREAKPAD_EQUALS_H_ + +#include + +#ifdef __SUNPRO_CC +#define BSLR_NO_HASH_MAP +#endif // __SUNPRO_CC + +#ifndef BSLR_NO_HASH_MAP +#include +#endif + +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "processor/linked_ptr.h" +#include "processor/range_map.h" +#include "processor/address_map.h" +#include "processor/contained_range_map.h" +#include "processor/logging.h" + +namespace google_breakpad { + +using std::map; +#ifndef BSLR_NO_HASH_MAP +using __gnu_cxx::hash_map; +#endif + +// In the general case, just use operator== +// This should handle base types. +template +inline bool Equals(const T &a, const T &b) +{ + return a == b; +} + +// for linked_ptr, we want to compare the objects +// pointed at, not the raw pointers +template +bool Equals(const linked_ptr &a, const linked_ptr &b) +{ + if (a.get() && b.get()) + return Equals(*a, *b); + + // if one is NULL, just compare pointers + return a == b; +} + +// for std::map/hash_map, we want to call Equals on each component of +// each pair +#ifndef BSLR_NO_HASH_MAP +template +bool Equals(const hash_map &a, const hash_map &b) +{ + // simple check for equivalent size first + if (a.size() != b.size()) + return false; + + typename hash_map::const_iterator it, found; + for (it = a.begin(); it != a.end(); it++) { + // if b doesn't contain this entry, then + // just get out + found = b.find(it->first); + if (found == b.end()) + return false; + + if (!Equals(it->second, found->second)) + return false; + } + return true; +} +#endif // BSLR_NO_HASH_MAP + +template +void Display(const T &a, const T &b) +{ + BPLOG(INFO) << "Display with unknown types"; + throw a; +} + +template +void Display(ContainedRangeMap * const &a, + ContainedRangeMap * const &b) +{ + BPLOG(INFO) << "Display: " << a << ", " << b; +} + +template +bool Equals(const map &a, const map &b) +{ + // simple check for equivalent size first + if (a.size() != b.size()) { + BPLOG(ERROR) << "Equals(map): " << a.size() << " != " << b.size(); + return false; + } + + typename map::const_iterator it, found; + for (it = a.begin(); it != a.end(); it++) { + // if b doesn't contain this entry, then + // just get out + found = b.find(it->first); + if (found == b.end()) { + BPLOG(ERROR) << "Equals(map): " << it->first << " not in map b!"; + return false; + } + + if (!Equals(it->second, found->second)) { + BPLOG(ERROR) << "Equals(map): it->second != found->second"; + Display(it->second, found->second); + return false; + } + } + return true; +} + +// For certain classes we know about, use their Equals method. +// Note: a few variations of this are in other places: +// processor/basic_source_line_resolver.cc + +template +inline bool Equals(const RangeMap &a, + const RangeMap &b) +{ + return a.Equals(b); +} + +template +inline bool Equals(const typename RangeMap::Range &a, + const typename RangeMap::Range &b) +{ + return a.Equals(b); +} + +template +inline bool Equals(const AddressMap &a, + const AddressMap &b) +{ + return a.Equals(b); +} + +template +inline bool Equals(const ContainedRangeMap &a, + const ContainedRangeMap &b) +{ + return a.Equals(b); +} + +template +inline bool Equals(ContainedRangeMap * const &a, + ContainedRangeMap * const &b) +{ + if (a && b) + return a->Equals(*b); + + // if one is NULL, just compare pointer values + return a == b; +} + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_EQUALS_H_ Index: src/processor/contained_range_map-inl.h =================================================================== --- src/processor/contained_range_map-inl.h (revision 234) +++ src/processor/contained_range_map-inl.h (working copy) @@ -40,6 +40,7 @@ #include #include "processor/contained_range_map.h" +#include "processor/equals.h" #include "processor/logging.h" @@ -185,7 +186,24 @@ } } +template +bool ContainedRangeMap::Equals( + const ContainedRangeMap &other) const +{ + bool equal; + if (map_ && other.map_) { + equal = google_breakpad::Equals(*map_, *other.map_); + } + else { + // otherwise if they're both NULL they're equal + equal = map_ == other.map_; + } + return equal && + google_breakpad::Equals(base_, other.base_) && + google_breakpad::Equals(entry_, other.entry_); +} + } // namespace google_breakpad Index: src/processor/disk_module_cache.cc =================================================================== --- src/processor/disk_module_cache.cc (revision 0) +++ src/processor/disk_module_cache.cc (revision 0) @@ -0,0 +1,173 @@ +// Copyright (c) 2007, 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. + +#include +#include +#include + +#include +#include + +#include "processor/disk_module_cache.h" +#include "processor/logging.h" + +using std::string; +using std::ostream; +using std::ofstream; +using std::istream; +using std::ifstream; +using std::ios; + + +namespace google_breakpad { + +DiskModuleCache::DiskModuleCache(string cache_directory) : + cache_directory_(cache_directory) +{ + // ensure trailing slash in cache directory + if (cache_directory_[cache_directory_.length()] != '/') + cache_directory.append("/"); +} + +bool DiskModuleCache::GetModuleData(const string &symbol_file, + istream **data_stream) +{ + if (!data_stream) + return false; + + string cache_file = MapToCacheEntry(symbol_file); + if (cache_file.empty()) + return false; + + BPLOG(INFO) << "Loading cached copy of symbol file " << symbol_file + << " from " << cache_file; + + *data_stream = new ifstream(cache_file.c_str(), + ios::in | ios::binary); + if (!**data_stream) { + delete *data_stream; + *data_stream = NULL; + return false; + } + return true; +} + +bool DiskModuleCache::BeginSetModuleData(const string &symbol_file, + ostream **data_stream) +{ + if (!data_stream) + return false; + + string cache_file = MapToCacheEntry(symbol_file); + if (!EnsurePathExists(cache_file.substr(0, cache_file.rfind('/')))) + return false; + + *data_stream = new ofstream(cache_file.c_str(), + ios::out | ios::binary | ios::trunc); + if (!**data_stream) { + delete *data_stream; + *data_stream = NULL; + return false; + } + return true; +} + +bool DiskModuleCache::EndSetModuleData(const string &symbol_file, + ostream **data_stream) +{ + ofstream *file_stream = dynamic_cast(*data_stream); + if (!file_stream) + return false; + + file_stream->close(); + return true; +} + +// We assume that symbol_file is in the Microsoft Symbol Server +// format, /path/debug_file/IDENTIFIER/debug_file.sym. We map +// this to /cache/path/debug_file/IDENTIFIER/debug_file.symcache. +// NOTE: this assumes unix style paths! +string DiskModuleCache::MapToCacheEntry(const string &symbol_file) +{ + string::size_type pos = string::npos; + + // we want the last three components in the path + for (int i=0; i<3; i++) { + pos = symbol_file.rfind('/', pos); + if (pos == string::npos) + return ""; + // back up one character so we don't find this slash again + pos--; + } + + string cache_file(cache_directory_ + + symbol_file.substr(pos + 2)); + pos = cache_file.length() - 4; + if (cache_file.compare(pos, 4, ".sym") == 0) + cache_file.replace(pos, 4, ".symcache"); + + return cache_file; +} + +bool DiskModuleCache::EnsurePathExists(const string &path) +{ + if (IsDir(path)) + return true; + + if (mkdir(path.c_str(), 0755) == -1) { + // we handle ENOENT here + if (errno != ENOENT) + return false; + + string::size_type pos = path.rfind('/'); + if (pos == string::npos) + // not enough path left? + return false; + + if (!EnsurePathExists(path.substr(0, pos))) + return false; + + // all previous directories should exist at this point + return mkdir(path.c_str(), 0755) != -1; + } + else { + return true; + } +} + +bool DiskModuleCache::IsDir(const string &path) +{ + struct stat st; + if (stat(path.c_str(), &st) != 0) + return false; + + return (st.st_mode & S_IFDIR) == S_IFDIR; +} + +} // namespace google_breakpad Index: src/processor/address_map.h =================================================================== --- src/processor/address_map.h (revision 234) +++ src/processor/address_map.h (working copy) @@ -64,6 +64,8 @@ // initially created. void Clear(); + bool Equals(const AddressMap &other) const; + private: // Convenience types. typedef std::map AddressToEntryMap; @@ -72,6 +74,8 @@ // Maps the address of each entry to an EntryType. AddressToEntryMap map_; + + friend class ModuleSerializer; }; } // namespace google_breakpad Index: src/google_breakpad/processor/basic_source_line_resolver.h =================================================================== --- src/google_breakpad/processor/basic_source_line_resolver.h (revision 234) +++ src/google_breakpad/processor/basic_source_line_resolver.h (working copy) @@ -46,6 +46,7 @@ #endif // BSLR_NO_HASH_MAP #include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/source_line_resolver_module_cache_interface.h" namespace google_breakpad { @@ -56,9 +57,12 @@ using __gnu_cxx::hash_map; #endif // BSLR_NO_HASH_MAP +class ModuleSerializer; + class BasicSourceLineResolver : public SourceLineResolverInterface { public: BasicSourceLineResolver(); + BasicSourceLineResolver(SourceLineResolverModuleCacheInterface *); virtual ~BasicSourceLineResolver(); // SourceLineResolverInterface methods, see source_line_resolver_interface.h @@ -73,6 +77,8 @@ virtual StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const; + static bool ModuleRoundTripTest(const string &map_file); + private: template class MemAddrMap; struct Line; @@ -97,10 +103,16 @@ typedef hash_map ModuleMap; #endif // BSLR_NO_HASH_MAP ModuleMap *modules_; + SourceLineResolverModuleCacheInterface *module_cache_; // Disallow unwanted copy ctor and assignment operator BasicSourceLineResolver(const BasicSourceLineResolver&); void operator=(const BasicSourceLineResolver&); + + friend bool Equals(const BasicSourceLineResolver::Function &a, + const BasicSourceLineResolver::Function &b); + + friend class ModuleSerializer; }; } // namespace google_breakpad Index: src/google_breakpad/processor/source_line_resolver_module_cache_interface.h =================================================================== --- src/google_breakpad/processor/source_line_resolver_module_cache_interface.h (revision 0) +++ src/google_breakpad/processor/source_line_resolver_module_cache_interface.h (revision 0) @@ -0,0 +1,71 @@ +// Copyright (c) 2007, 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. + +// Abstract interface to store and retrieve cached data for module +// symbols. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_MODULE_CACHE_INTERFACE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_MODULE_CACHE_INTERFACE_H__ + +#include +#include +#include + +namespace google_breakpad { + +using std::string; +using std::istream; +using std::ostream; + +class SourceLineResolverModuleCacheInterface { + public: + virtual ~SourceLineResolverModuleCacheInterface() {} + + // Retrieve the symbol data associated with a module + virtual bool GetModuleData(const string &symbol_file, + istream **data_stream) = 0; + + // Get a stream to which the data associated with a module + // can be stored + virtual bool BeginSetModuleData(const string &symbol_file, + ostream **data_stream) = 0; + // Finish setting the data associated with this module, + // data should already have been written to the stream + virtual bool EndSetModuleData(const string &symbol_file, + ostream **data_stream) = 0; + + protected: + // SourceLineResolverModuleCacheInterface cannot be instantiated + // except by subclasses + SourceLineResolverModuleCacheInterface() {} +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_MODULE_CACHE_INTERFACE_H__ Index: Makefile.am =================================================================== --- Makefile.am (revision 234) +++ Makefile.am (working copy) @@ -62,6 +62,7 @@ src/google_breakpad/processor/minidump_processor.h \ src/google_breakpad/processor/process_state.h \ src/google_breakpad/processor/source_line_resolver_interface.h \ + src/google_breakpad/processor/source_line_resolver_module_cache_interface.h \ src/google_breakpad/processor/stack_frame.h \ src/google_breakpad/processor/stack_frame_cpu.h \ src/google_breakpad/processor/stackwalker.h \ @@ -76,6 +77,8 @@ src/processor/call_stack.cc \ src/processor/contained_range_map-inl.h \ src/processor/contained_range_map.h \ + src/processor/disk_module_cache.cc \ + src/processor/disk_module_cache.h \ src/processor/linked_ptr.h \ src/processor/logging.h \ src/processor/logging.cc \ @@ -117,7 +120,8 @@ src/processor/minidump_processor_unittest \ src/processor/pathname_stripper_unittest \ src/processor/postfix_evaluator_unittest \ - src/processor/range_map_unittest + src/processor/range_map_unittest \ + src/processor/module_serialize_unittest if SELFTEST check_PROGRAMS += \ @@ -185,6 +189,15 @@ src/processor/logging.lo \ src/processor/pathname_stripper.lo +src_processor_module_serialize_unittest_SOURCES = \ + src/processor/module_serialize_unittest.cc +src_processor_module_serialize_unittest_LDADD = \ + src/processor/basic_code_modules.lo \ + src/processor/basic_source_line_resolver.lo \ + src/processor/call_stack.lo \ + src/processor/logging.lo \ + src/processor/pathname_stripper.lo + src_processor_stackwalker_selftest_SOURCES = \ src/processor/stackwalker_selftest.cc src_processor_stackwalker_selftest_LDADD = \ @@ -218,6 +231,7 @@ src/processor/basic_code_modules.lo \ src/processor/basic_source_line_resolver.lo \ src/processor/call_stack.lo \ + src/processor/disk_module_cache.lo \ src/processor/logging.lo \ src/processor/minidump.lo \ src/processor/minidump_processor.lo \