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: // module_comparer.cc: ModuleComparer implementation. michael@0: // See module_comparer.h for documentation. michael@0: // michael@0: // Author: lambxsy@google.com (Siyang Xie) michael@0: michael@0: #include "processor/module_comparer.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "common/scoped_ptr.h" michael@0: #include "processor/basic_code_module.h" michael@0: #include "processor/logging.h" michael@0: michael@0: #define ASSERT_TRUE(condition) \ michael@0: if (!(condition)) { \ michael@0: BPLOG(ERROR) << "FAIL: " << #condition << " @ " \ michael@0: << __FILE__ << ":" << __LINE__; \ michael@0: return false; \ michael@0: } michael@0: michael@0: #define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition)) michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: bool ModuleComparer::Compare(const string &symbol_data) { michael@0: scoped_ptr basic_module(new BasicModule("test_module")); michael@0: scoped_ptr fast_module(new FastModule("test_module")); michael@0: michael@0: // Load symbol data into basic_module michael@0: scoped_array buffer(new char[symbol_data.size() + 1]); michael@0: strcpy(buffer.get(), symbol_data.c_str()); michael@0: ASSERT_TRUE(basic_module->LoadMapFromMemory(buffer.get())); michael@0: buffer.reset(); michael@0: michael@0: // Serialize BasicSourceLineResolver::Module. michael@0: unsigned int serialized_size = 0; michael@0: scoped_array serialized_data( michael@0: serializer_.Serialize(*(basic_module.get()), &serialized_size)); michael@0: ASSERT_TRUE(serialized_data.get()); michael@0: BPLOG(INFO) << "Serialized size = " << serialized_size << " Bytes"; michael@0: michael@0: // Load FastSourceLineResolver::Module using serialized data. michael@0: ASSERT_TRUE(fast_module->LoadMapFromMemory(serialized_data.get())); michael@0: michael@0: // Compare FastSourceLineResolver::Module with michael@0: // BasicSourceLineResolver::Module. michael@0: ASSERT_TRUE(CompareModule(basic_module.get(), fast_module.get())); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Traversal the content of module and do comparison michael@0: bool ModuleComparer::CompareModule(const BasicModule *basic_module, michael@0: const FastModule *fast_module) const { michael@0: // Compare name_. michael@0: ASSERT_TRUE(basic_module->name_ == fast_module->name_); michael@0: michael@0: // Compare files_: michael@0: { michael@0: BasicModule::FileMap::const_iterator iter1 = basic_module->files_.begin(); michael@0: FastModule::FileMap::iterator iter2 = fast_module->files_.begin(); michael@0: while (iter1 != basic_module->files_.end() michael@0: && iter2 != fast_module->files_.end()) { michael@0: ASSERT_TRUE(iter1->first == iter2.GetKey()); michael@0: string tmp(iter2.GetValuePtr()); michael@0: ASSERT_TRUE(iter1->second == tmp); michael@0: ++iter1; michael@0: ++iter2; michael@0: } michael@0: ASSERT_TRUE(iter1 == basic_module->files_.end()); michael@0: ASSERT_TRUE(iter2 == fast_module->files_.end()); michael@0: } michael@0: michael@0: // Compare functions_: michael@0: { michael@0: RangeMap >::MapConstIterator iter1; michael@0: StaticRangeMap::MapConstIterator iter2; michael@0: iter1 = basic_module->functions_.map_.begin(); michael@0: iter2 = fast_module->functions_.map_.begin(); michael@0: while (iter1 != basic_module->functions_.map_.end() michael@0: && iter2 != fast_module->functions_.map_.end()) { michael@0: ASSERT_TRUE(iter1->first == iter2.GetKey()); michael@0: ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base()); michael@0: ASSERT_TRUE(CompareFunction( michael@0: iter1->second.entry().get(), iter2.GetValuePtr()->entryptr())); michael@0: ++iter1; michael@0: ++iter2; michael@0: } michael@0: ASSERT_TRUE(iter1 == basic_module->functions_.map_.end()); michael@0: ASSERT_TRUE(iter2 == fast_module->functions_.map_.end()); michael@0: } michael@0: michael@0: // Compare public_symbols_: michael@0: { michael@0: AddressMap >::MapConstIterator iter1; michael@0: StaticAddressMap::MapConstIterator iter2; michael@0: iter1 = basic_module->public_symbols_.map_.begin(); michael@0: iter2 = fast_module->public_symbols_.map_.begin(); michael@0: while (iter1 != basic_module->public_symbols_.map_.end() michael@0: && iter2 != fast_module->public_symbols_.map_.end()) { michael@0: ASSERT_TRUE(iter1->first == iter2.GetKey()); michael@0: ASSERT_TRUE(ComparePubSymbol( michael@0: iter1->second.get(), iter2.GetValuePtr())); michael@0: ++iter1; michael@0: ++iter2; michael@0: } michael@0: ASSERT_TRUE(iter1 == basic_module->public_symbols_.map_.end()); michael@0: ASSERT_TRUE(iter2 == fast_module->public_symbols_.map_.end()); michael@0: } michael@0: michael@0: // Compare windows_frame_info_[]: michael@0: for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i) { michael@0: ASSERT_TRUE(CompareCRM(&(basic_module->windows_frame_info_[i]), michael@0: &(fast_module->windows_frame_info_[i]))); michael@0: } michael@0: michael@0: // Compare cfi_initial_rules_: michael@0: { michael@0: RangeMap::MapConstIterator iter1; michael@0: StaticRangeMap::MapConstIterator iter2; michael@0: iter1 = basic_module->cfi_initial_rules_.map_.begin(); michael@0: iter2 = fast_module->cfi_initial_rules_.map_.begin(); michael@0: while (iter1 != basic_module->cfi_initial_rules_.map_.end() michael@0: && iter2 != fast_module->cfi_initial_rules_.map_.end()) { michael@0: ASSERT_TRUE(iter1->first == iter2.GetKey()); michael@0: ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base()); michael@0: string tmp(iter2.GetValuePtr()->entryptr()); michael@0: ASSERT_TRUE(iter1->second.entry() == tmp); michael@0: ++iter1; michael@0: ++iter2; michael@0: } michael@0: ASSERT_TRUE(iter1 == basic_module->cfi_initial_rules_.map_.end()); michael@0: ASSERT_TRUE(iter2 == fast_module->cfi_initial_rules_.map_.end()); michael@0: } michael@0: michael@0: // Compare cfi_delta_rules_: michael@0: { michael@0: map::const_iterator iter1; michael@0: StaticMap::iterator iter2; michael@0: iter1 = basic_module->cfi_delta_rules_.begin(); michael@0: iter2 = fast_module->cfi_delta_rules_.begin(); michael@0: while (iter1 != basic_module->cfi_delta_rules_.end() michael@0: && iter2 != fast_module->cfi_delta_rules_.end()) { michael@0: ASSERT_TRUE(iter1->first == iter2.GetKey()); michael@0: string tmp(iter2.GetValuePtr()); michael@0: ASSERT_TRUE(iter1->second == tmp); michael@0: ++iter1; michael@0: ++iter2; michael@0: } michael@0: ASSERT_TRUE(iter1 == basic_module->cfi_delta_rules_.end()); michael@0: ASSERT_TRUE(iter2 == fast_module->cfi_delta_rules_.end()); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool ModuleComparer::CompareFunction(const BasicFunc *basic_func, michael@0: const FastFunc *fast_func_raw) const { michael@0: FastFunc* fast_func = new FastFunc(); michael@0: fast_func->CopyFrom(fast_func_raw); michael@0: ASSERT_TRUE(basic_func->name == fast_func->name); michael@0: ASSERT_TRUE(basic_func->address == fast_func->address); michael@0: ASSERT_TRUE(basic_func->size == fast_func->size); michael@0: michael@0: // compare range map of lines: michael@0: RangeMap >::MapConstIterator iter1; michael@0: StaticRangeMap::MapConstIterator iter2; michael@0: iter1 = basic_func->lines.map_.begin(); michael@0: iter2 = fast_func->lines.map_.begin(); michael@0: while (iter1 != basic_func->lines.map_.end() michael@0: && iter2 != fast_func->lines.map_.end()) { michael@0: ASSERT_TRUE(iter1->first == iter2.GetKey()); michael@0: ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base()); michael@0: ASSERT_TRUE(CompareLine(iter1->second.entry().get(), michael@0: iter2.GetValuePtr()->entryptr())); michael@0: ++iter1; michael@0: ++iter2; michael@0: } michael@0: ASSERT_TRUE(iter1 == basic_func->lines.map_.end()); michael@0: ASSERT_TRUE(iter2 == fast_func->lines.map_.end()); michael@0: michael@0: delete fast_func; michael@0: return true; michael@0: } michael@0: michael@0: bool ModuleComparer::CompareLine(const BasicLine *basic_line, michael@0: const FastLine *fast_line_raw) const { michael@0: FastLine *fast_line = new FastLine; michael@0: fast_line->CopyFrom(fast_line_raw); michael@0: michael@0: ASSERT_TRUE(basic_line->address == fast_line->address); michael@0: ASSERT_TRUE(basic_line->size == fast_line->size); michael@0: ASSERT_TRUE(basic_line->source_file_id == fast_line->source_file_id); michael@0: ASSERT_TRUE(basic_line->line == fast_line->line); michael@0: michael@0: delete fast_line; michael@0: return true; michael@0: } michael@0: michael@0: bool ModuleComparer::ComparePubSymbol(const BasicPubSymbol* basic_ps, michael@0: const FastPubSymbol* fastps_raw) const { michael@0: FastPubSymbol *fast_ps = new FastPubSymbol; michael@0: fast_ps->CopyFrom(fastps_raw); michael@0: ASSERT_TRUE(basic_ps->name == fast_ps->name); michael@0: ASSERT_TRUE(basic_ps->address == fast_ps->address); michael@0: ASSERT_TRUE(basic_ps->parameter_size == fast_ps->parameter_size); michael@0: delete fast_ps; michael@0: return true; michael@0: } michael@0: michael@0: bool ModuleComparer::CompareWFI(const WindowsFrameInfo& wfi1, michael@0: const WindowsFrameInfo& wfi2) const { michael@0: ASSERT_TRUE(wfi1.type_ == wfi2.type_); michael@0: ASSERT_TRUE(wfi1.valid == wfi2.valid); michael@0: ASSERT_TRUE(wfi1.prolog_size == wfi2.prolog_size); michael@0: ASSERT_TRUE(wfi1.epilog_size == wfi2.epilog_size); michael@0: ASSERT_TRUE(wfi1.parameter_size == wfi2.parameter_size); michael@0: ASSERT_TRUE(wfi1.saved_register_size == wfi2.saved_register_size); michael@0: ASSERT_TRUE(wfi1.local_size == wfi2.local_size); michael@0: ASSERT_TRUE(wfi1.max_stack_size == wfi2.max_stack_size); michael@0: ASSERT_TRUE(wfi1.allocates_base_pointer == wfi2.allocates_base_pointer); michael@0: ASSERT_TRUE(wfi1.program_string == wfi2.program_string); michael@0: return true; michael@0: } michael@0: michael@0: // Compare ContainedRangeMap michael@0: bool ModuleComparer::CompareCRM( michael@0: const ContainedRangeMap >* basic_crm, michael@0: const StaticContainedRangeMap* fast_crm) const { michael@0: ASSERT_TRUE(basic_crm->base_ == fast_crm->base_); michael@0: michael@0: if (!basic_crm->entry_.get() || !fast_crm->entry_ptr_) { michael@0: // empty entry: michael@0: ASSERT_TRUE(!basic_crm->entry_.get() && !fast_crm->entry_ptr_); michael@0: } else { michael@0: WFI newwfi; michael@0: newwfi.CopyFrom(fast_resolver_->CopyWFI(fast_crm->entry_ptr_)); michael@0: ASSERT_TRUE(CompareWFI(*(basic_crm->entry_.get()), newwfi)); michael@0: } michael@0: michael@0: if ((!basic_crm->map_ || basic_crm->map_->empty()) michael@0: || fast_crm->map_.empty()) { michael@0: ASSERT_TRUE((!basic_crm->map_ || basic_crm->map_->empty()) michael@0: && fast_crm->map_.empty()); michael@0: } else { michael@0: ContainedRangeMap >::MapConstIterator iter1; michael@0: StaticContainedRangeMap::MapConstIterator iter2; michael@0: iter1 = basic_crm->map_->begin(); michael@0: iter2 = fast_crm->map_.begin(); michael@0: while (iter1 != basic_crm->map_->end() michael@0: && iter2 != fast_crm->map_.end()) { michael@0: ASSERT_TRUE(iter1->first == iter2.GetKey()); michael@0: StaticContainedRangeMap *child = michael@0: new StaticContainedRangeMap( michael@0: reinterpret_cast(iter2.GetValuePtr())); michael@0: ASSERT_TRUE(CompareCRM(iter1->second, child)); michael@0: delete child; michael@0: ++iter1; michael@0: ++iter2; michael@0: } michael@0: ASSERT_TRUE(iter1 == basic_crm->map_->end()); michael@0: ASSERT_TRUE(iter2 == fast_crm->map_.end()); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace google_breakpad