michael@0: // -*- mode: C++ -*- michael@0: michael@0: // Copyright (c) 2010, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // Original author: Jim Blandy michael@0: michael@0: // Mock classes for writing stackwalker tests, shared amongst architectures. michael@0: michael@0: #ifndef PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ michael@0: #define PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "common/using_std_string.h" michael@0: #include "google_breakpad/common/breakpad_types.h" michael@0: #include "google_breakpad/processor/code_module.h" michael@0: #include "google_breakpad/processor/code_modules.h" michael@0: #include "google_breakpad/processor/memory_region.h" michael@0: #include "google_breakpad/processor/symbol_supplier.h" michael@0: #include "google_breakpad/processor/system_info.h" michael@0: michael@0: class MockMemoryRegion: public google_breakpad::MemoryRegion { michael@0: public: michael@0: MockMemoryRegion(): base_address_(0) { } michael@0: michael@0: // Set this region's address and contents. If we have placed an michael@0: // instance of this class in a test fixture class, individual tests michael@0: // can use this to provide the region's contents. michael@0: void Init(uint64_t base_address, const string &contents) { michael@0: base_address_ = base_address; michael@0: contents_ = contents; michael@0: } michael@0: michael@0: uint64_t GetBase() const { return base_address_; } michael@0: uint32_t GetSize() const { return contents_.size(); } michael@0: michael@0: bool GetMemoryAtAddress(uint64_t address, uint8_t *value) const { michael@0: return GetMemoryLittleEndian(address, value); michael@0: } michael@0: bool GetMemoryAtAddress(uint64_t address, uint16_t *value) const { michael@0: return GetMemoryLittleEndian(address, value); michael@0: } michael@0: bool GetMemoryAtAddress(uint64_t address, uint32_t *value) const { michael@0: return GetMemoryLittleEndian(address, value); michael@0: } michael@0: bool GetMemoryAtAddress(uint64_t address, uint64_t *value) const { michael@0: return GetMemoryLittleEndian(address, value); michael@0: } michael@0: michael@0: private: michael@0: // Fetch a little-endian value from ADDRESS in contents_ whose size michael@0: // is BYTES, and store it in *VALUE. Return true on success. michael@0: template michael@0: bool GetMemoryLittleEndian(uint64_t address, ValueType *value) const { michael@0: if (address < base_address_ || michael@0: address - base_address_ + sizeof(ValueType) > contents_.size()) michael@0: return false; michael@0: ValueType v = 0; michael@0: int start = address - base_address_; michael@0: // The loop condition is odd, but it's correct for size_t. michael@0: for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--) michael@0: v = (v << 8) | static_cast(contents_[start + i]); michael@0: *value = v; michael@0: return true; michael@0: } michael@0: michael@0: uint64_t base_address_; michael@0: string contents_; michael@0: }; michael@0: michael@0: class MockCodeModule: public google_breakpad::CodeModule { michael@0: public: michael@0: MockCodeModule(uint64_t base_address, uint64_t size, michael@0: const string &code_file, const string &version) michael@0: : base_address_(base_address), size_(size), code_file_(code_file) { } michael@0: michael@0: uint64_t base_address() const { return base_address_; } michael@0: uint64_t size() const { return size_; } michael@0: string code_file() const { return code_file_; } michael@0: string code_identifier() const { return code_file_; } michael@0: string debug_file() const { return code_file_; } michael@0: string debug_identifier() const { return code_file_; } michael@0: string version() const { return version_; } michael@0: const google_breakpad::CodeModule *Copy() const { michael@0: abort(); // Tests won't use this. michael@0: } michael@0: michael@0: private: michael@0: uint64_t base_address_; michael@0: uint64_t size_; michael@0: string code_file_; michael@0: string version_; michael@0: }; michael@0: michael@0: class MockCodeModules: public google_breakpad::CodeModules { michael@0: public: michael@0: typedef google_breakpad::CodeModule CodeModule; michael@0: typedef google_breakpad::CodeModules CodeModules; michael@0: michael@0: void Add(const MockCodeModule *module) { michael@0: modules_.push_back(module); michael@0: } michael@0: michael@0: unsigned int module_count() const { return modules_.size(); } michael@0: michael@0: const CodeModule *GetModuleForAddress(uint64_t address) const { michael@0: for (ModuleVector::const_iterator i = modules_.begin(); michael@0: i != modules_.end(); i++) { michael@0: const MockCodeModule *module = *i; michael@0: if (module->base_address() <= address && michael@0: address - module->base_address() < module->size()) michael@0: return module; michael@0: } michael@0: return NULL; michael@0: }; michael@0: michael@0: const CodeModule *GetMainModule() const { return modules_[0]; } michael@0: michael@0: const CodeModule *GetModuleAtSequence(unsigned int sequence) const { michael@0: return modules_.at(sequence); michael@0: } michael@0: michael@0: const CodeModule *GetModuleAtIndex(unsigned int index) const { michael@0: return modules_.at(index); michael@0: } michael@0: michael@0: const CodeModules *Copy() const { abort(); } // Tests won't use this. michael@0: michael@0: private: michael@0: typedef std::vector ModuleVector; michael@0: ModuleVector modules_; michael@0: }; michael@0: michael@0: class MockSymbolSupplier: public google_breakpad::SymbolSupplier { michael@0: public: michael@0: typedef google_breakpad::CodeModule CodeModule; michael@0: typedef google_breakpad::SystemInfo SystemInfo; michael@0: MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule *module, michael@0: const SystemInfo *system_info, michael@0: string *symbol_file)); michael@0: MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule *module, michael@0: const SystemInfo *system_info, michael@0: string *symbol_file, michael@0: string *symbol_data)); michael@0: MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule *module, michael@0: const SystemInfo *system_info, michael@0: string *symbol_file, michael@0: char **symbol_data)); michael@0: MOCK_METHOD1(FreeSymbolData, void(const CodeModule *module)); michael@0: michael@0: // Copies the passed string contents into a newly allocated buffer. michael@0: // The newly allocated buffer will be freed during destruction. michael@0: char* CopySymbolDataAndOwnTheCopy(const std::string &info) { michael@0: unsigned int buffer_size = info.size() + 1; michael@0: char *symbol_data = new char [buffer_size]; michael@0: strcpy(symbol_data, info.c_str()); michael@0: symbol_data_to_free_.push_back(symbol_data); michael@0: return symbol_data; michael@0: } michael@0: michael@0: virtual ~MockSymbolSupplier() { michael@0: for (SymbolDataVector::const_iterator i = symbol_data_to_free_.begin(); michael@0: i != symbol_data_to_free_.end(); i++) { michael@0: char* symbol_data = *i; michael@0: delete [] symbol_data; michael@0: } michael@0: } michael@0: michael@0: private: michael@0: // List of symbol data to be freed upon destruction michael@0: typedef std::vector SymbolDataVector; michael@0: SymbolDataVector symbol_data_to_free_; michael@0: }; michael@0: michael@0: #endif // PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_