diff options
Diffstat (limited to 'breakpad')
-rw-r--r-- | breakpad/breakpad.gyp | 8 | ||||
-rw-r--r-- | breakpad/pending/src/common/dwarf_cu_to_module.cc | 1023 | ||||
-rw-r--r-- | breakpad/pending/src/common/dwarf_cu_to_module.h | 279 | ||||
-rw-r--r-- | breakpad/pending/src/common/dwarf_cu_to_module_unittest.cc | 1763 | ||||
-rw-r--r-- | breakpad/pending/src/common/module.cc | 294 |
5 files changed, 2 insertions, 3365 deletions
diff --git a/breakpad/breakpad.gyp b/breakpad/breakpad.gyp index 8ef5e9a..ea137cc 100644 --- a/breakpad/breakpad.gyp +++ b/breakpad/breakpad.gyp @@ -206,18 +206,13 @@ { 'target_name': 'dump_syms', 'type': 'executable', - 'include_dirs++': [ - # ++ ensures this comes before src brought in from target_defaults. - 'pending/src', - ], 'include_dirs': [ 'src/common/mac', ], 'sources': [ - 'pending/src/common/dwarf_cu_to_module.cc', - 'pending/src/common/module.cc', 'src/common/dwarf/bytereader.cc', 'src/common/dwarf_cfi_to_module.cc', + 'src/common/dwarf_cu_to_module.cc', 'src/common/dwarf/dwarf2diehandler.cc', 'src/common/dwarf/dwarf2reader.cc', 'src/common/dwarf_line_to_module.cc', @@ -231,6 +226,7 @@ 'src/common/mac/macho_utilities.cc', 'src/common/mac/macho_walker.cc', 'src/common/md5.cc', + 'src/common/module.cc', 'src/common/stabs_reader.cc', 'src/common/stabs_to_module.cc', 'src/tools/mac/dump_syms/dump_syms_tool.mm', diff --git a/breakpad/pending/src/common/dwarf_cu_to_module.cc b/breakpad/pending/src/common/dwarf_cu_to_module.cc deleted file mode 100644 index 54f27bd..0000000 --- a/breakpad/pending/src/common/dwarf_cu_to_module.cc +++ /dev/null @@ -1,1023 +0,0 @@ -// 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. - -// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - -// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h. - -// For <inttypes.h> PRI* macros, before anything else might #include it. -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif /* __STDC_FORMAT_MACROS */ - -#include "common/dwarf_cu_to_module.h" - -#include <assert.h> -#include <inttypes.h> -#include <stdio.h> - -#include <algorithm> -#include <set> -#include <utility> - -#include "common/dwarf_line_to_module.h" - -namespace google_breakpad { - -using std::map; -using std::pair; -using std::set; -using std::vector; - -// This Function extends the base definition of Function to defer computation -// of a function's name until the entire compilation unit has been processed. -// specification_offset and abstract_origin_offset, if not 0, refer to an -// offset key in the DwarfCUToModule::FilePrivate::specifications map. -struct DwarfCUToModule::Function : public Module::Function { - Function() - : Module::Function(), - specification_offset(0), - abstract_origin_offset(0) { - } - - uint64_t specification_offset; - uint64_t abstract_origin_offset; -}; - -// Data provided by a DWARF specification DIE. -// -// In DWARF, the DIE for a definition may contain a DW_AT_specification -// attribute giving the offset of the corresponding declaration DIE, and -// the definition DIE may omit information given in the declaration. For -// example, it's common for a function's address range to appear only in -// its definition DIE, but its name to appear only in its declaration -// DIE. -// -// The dumper needs to be able to follow DW_AT_specification links to -// bring all this information together in a FUNC record. Conveniently, -// DIEs that are the target of such links have a DW_AT_declaration flag -// set, so we can identify them when we first see them, and record their -// contents for later reference. -// -// A Specification holds information gathered from a declaration DIE that -// we may need if we find a DW_AT_specification link pointing to it. -struct DwarfCUToModule::Specification { - // Compute qualified_name if it has not yet been computed, and return it. - // cu_context is used to provide access to the specifications map for - // further lookups on specification_offset and - // enclosing_name_specification_offset, as well as access to the - // WarningReporter. The offset argument is the offset at which the DIE that - // resulted in this object's creation was located, and is only used for - // warning reporting. - string QualifiedName(DwarfCUToModule::CUContext* cu_context, uint64 offset); - - // These fields contain values set by the DIE that resulted in the creation - // of this object. - string unqualified_name; - uint64 specification_offset; - bool declaration; - uint64 enclosing_name_specification_offset; - const Language* language; - - // The qualified name, empty until computed by QualifiedName. - string qualified_name; -}; - -// Data global to the DWARF-bearing file that is private to the -// DWARF-to-Module process. -struct DwarfCUToModule::FilePrivate { - // A set of strings used in this CU. Before storing a string in one of - // our data structures, insert it into this set, and then use the string - // from the set. - // - // Because std::string uses reference counting internally, simply using - // strings from this set, even if passed by value, assigned, or held - // directly in structures and containers (map<string, ...>, for example), - // causes those strings to share a single instance of each distinct piece - // of text. - set<string> common_strings; - - // A map from offsets of DIEs within the .debug_info section to - // Specifications describing those DIEs. Specification references can - // cross compilation unit boundaries. Pointers added to this map are owned - // by the FileContext that owns this FilePrivate. - SpecificationByOffset specifications; -}; - -DwarfCUToModule::FileContext::FileContext(const string &filename_arg, - Module *module_arg) - : filename(filename_arg), module(module_arg) { - file_private = new FilePrivate(); -} - -DwarfCUToModule::FileContext::~FileContext() { - for (SpecificationByOffset::iterator iterator = - file_private->specifications.begin(); - iterator != file_private->specifications.end(); - ++iterator) { - delete iterator->second; - } - - delete file_private; -} - -// Information global to the particular compilation unit we're -// parsing. This is for data shared across the CU's entire DIE tree, -// and parameters from the code invoking the CU parser. -struct DwarfCUToModule::CUContext { - CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg) - : file_context(file_context_arg), - reporter(reporter_arg), - language(Language::CPlusPlus) { } - ~CUContext() { - for (vector<Module::Function *>::iterator it = functions.begin(); - it != functions.end(); it++) - delete *it; - }; - - // The DWARF-bearing file into which this CU was incorporated. - FileContext *file_context; - - // For printing error messages. - WarningReporter *reporter; - - // The source language of this compilation unit. - const Language *language; - - // The functions defined in this compilation unit. We accumulate - // them here during parsing. Then, in DwarfCUToModule::Finish, we - // assign them lines and add them to file_context->module. - // - // Destroying this destroys all the functions this vector points to. - // - // This actually stores google_breakpad::DwarfCUToModule::Function*, a - // type derived from google_breakpad::Module::Function*. - vector<Module::Function *> functions; -}; - -string DwarfCUToModule::Specification::QualifiedName( - DwarfCUToModule::CUContext* cu_context, uint64 offset) { - SpecificationByOffset* specifications = - &cu_context->file_context->file_private->specifications; - - if (!qualified_name.empty()) { - return qualified_name; - } - - // Avoid infinite recursion in the presence of evil data. - qualified_name.assign("<in process>"); - - Specification* other = NULL; - if (specification_offset) { - SpecificationByOffset::iterator iterator = - specifications->find(specification_offset); - if (iterator != specifications->end()) { - other = iterator->second; - if (!other->declaration) { - other = NULL; - } - } - if (!other) { - cu_context->reporter->UnknownSpecification(offset, specification_offset); - } - } - - if (other) { - other->QualifiedName(cu_context, specification_offset); - } - - if (unqualified_name.empty() && other) { - unqualified_name = other->unqualified_name; - } - - if (other) { - enclosing_name_specification_offset = - other->enclosing_name_specification_offset; - } - - std::string enclosing_name; - Specification* enclosing = NULL; - if (enclosing_name_specification_offset) { - SpecificationByOffset::iterator iterator = - specifications->find(enclosing_name_specification_offset); - if (iterator != specifications->end()) { - enclosing = iterator->second; - } else { - cu_context->reporter->UnknownSpecification( - offset, enclosing_name_specification_offset); - } - } - if (enclosing) { - enclosing_name = - enclosing->QualifiedName(cu_context, - enclosing_name_specification_offset); - if (enclosing_name.empty() && language == Language::CPlusPlus) { - enclosing_name.assign("(anonymous namespace)"); - } - } - - qualified_name = - language->MakeQualifiedName(enclosing_name, unqualified_name); - - return qualified_name; -} - -// Information about the context of a particular DIE. This is for -// information that changes as we descend the tree towards the leaves: -// the containing classes/namespaces, etc. -struct DwarfCUToModule::DIEContext { - DIEContext() - : name_specification_offset(0) { - } - - // The fully-qualified name of the context. For example, for a - // tree like: - // - // DW_TAG_namespace Foo - // DW_TAG_class Bar - // DW_TAG_subprogram Baz - // - // in a C++ compilation unit, the DIEContext's name for the - // DW_TAG_subprogram DIE would be "Foo::Bar". The DIEContext's - // name for the DW_TAG_namespace DIE would be "". - uint64 name_specification_offset; -}; - -// An abstract base class for all the dumper's DIE handlers. -class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler { - public: - // Create a handler for the DIE at OFFSET whose compilation unit is - // described by CU_CONTEXT, and whose immediate context is described - // by PARENT_CONTEXT. - GenericDIEHandler(CUContext *cu_context, DIEContext *parent_context, - uint64 offset) - : cu_context_(cu_context), - parent_context_(parent_context), - offset_(offset), - declaration_(false), - specification_offset_(0) { } - - // Derived classes' ProcessAttributeUnsigned can defer to this to - // handle DW_AT_declaration, or simply not override it. - void ProcessAttributeUnsigned(enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data); - - // Derived classes' ProcessAttributeReference can defer to this to - // handle DW_AT_specification, or simply not override it. - void ProcessAttributeReference(enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data); - - // Derived classes' ProcessAttributeReference can defer to this to - // handle DW_AT_specification, or simply not override it. - void ProcessAttributeString(enum DwarfAttribute attr, - enum DwarfForm form, - const string &data); - - protected: - // Create and store a Specification for later use. Once all Specifications - // have been read, they can be used to determine the fully-qualified name - // of this DIE. Specifications may reference those that come later in the - // file, so these are all stored in a map and name computation is deferred - // until they've all been read. - // - // Use this from EndAttributes member functions, not ProcessAttribute* - // functions; only the former can be sure that all the DIE's attributes - // have been seen. - void RememberSpecification(); - - CUContext *cu_context_; - DIEContext *parent_context_; - uint64 offset_; - - // If this DIE has a DW_AT_declaration attribute, this is its value. - // It is false on DIEs with no DW_AT_declaration attribute. - bool declaration_; - - // If this DIE has a DW_AT_specification attribute, this is the - // offset that it refers to, which can ultimately be looked up in - // DwarfCUToModule::FilePrivate::specifications. Otherwise, this is 0. - uint64 specification_offset_; - - // The value of the DW_AT_name attribute, or the empty string if the - // DIE has no such attribute. - string name_attribute_; -}; - -void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned( - enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data) { - switch (attr) { - case dwarf2reader::DW_AT_declaration: declaration_ = (data != 0); break; - default: break; - } -} - -void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( - enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data) { - switch (attr) { - case dwarf2reader::DW_AT_specification: { - specification_offset_ = data; - break; - } - default: break; - } -} - -void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString( - enum DwarfAttribute attr, - enum DwarfForm form, - const string &data) { - switch (attr) { - case dwarf2reader::DW_AT_name: { - // Place the name in our global set of strings, and then use the - // string from the set. Even though the assignment looks like a copy, - // all the major std::string implementations use reference counting - // internally, so the effect is to have all our data structures share - // copies of strings whenever possible. - pair<set<string>::iterator, bool> result = - cu_context_->file_context->file_private->common_strings.insert(data); - name_attribute_ = *result.first; - break; - } - default: break; - } -} - -void DwarfCUToModule::GenericDIEHandler::RememberSpecification() { - Specification* specification = new Specification(); - - specification->unqualified_name = name_attribute_; - specification->specification_offset = specification_offset_; - specification->declaration = declaration_; - specification->enclosing_name_specification_offset = - parent_context_->name_specification_offset; - specification->language = cu_context_->language; - - FileContext *file_context = cu_context_->file_context; - - // Make sure it isn't there yet. - assert(file_context->file_private->specifications.find(offset_) == - file_context->file_private->specifications.end()); - - file_context->file_private->specifications[offset_] = specification; -} - -// A handler class for DW_TAG_subprogram DIEs. -class DwarfCUToModule::FuncHandler: public GenericDIEHandler { - public: - FuncHandler(CUContext *cu_context, DIEContext *parent_context, - uint64 offset) - : GenericDIEHandler(cu_context, parent_context, offset), - low_pc_(0), - high_pc_(0), - abstract_origin_offset_(0), - inline_(false) { - } - - void ProcessAttributeUnsigned(enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data); - void ProcessAttributeSigned(enum DwarfAttribute attr, - enum DwarfForm form, - int64 data); - void ProcessAttributeReference(enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data); - - bool EndAttributes(); - void Finish(); - - private: - // The fully-qualified name, as derived from name_attribute_, - // specification_, parent_context_. Computed in EndAttributes. - string name_; - uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc - uint64 abstract_origin_offset_; - bool inline_; -}; - -void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( - enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data) { - switch (attr) { - // If this attribute is present at all --- even if its value is - // DW_INL_not_inlined --- then GCC may cite it as someone else's - // DW_AT_abstract_origin attribute. - case dwarf2reader::DW_AT_inline: inline_ = true; break; - - case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break; - case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break; - default: - GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); - break; - } -} - -void DwarfCUToModule::FuncHandler::ProcessAttributeSigned( - enum DwarfAttribute attr, - enum DwarfForm form, - int64 data) { - switch (attr) { - // If this attribute is present at all --- even if its value is - // DW_INL_not_inlined --- then GCC may cite it as someone else's - // DW_AT_abstract_origin attribute. - case dwarf2reader::DW_AT_inline: inline_ = true; break; - - default: - break; - } -} - -void DwarfCUToModule::FuncHandler::ProcessAttributeReference( - enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data) { - switch(attr) { - case dwarf2reader::DW_AT_abstract_origin: { - abstract_origin_offset_ = data; - break; - } - default: - GenericDIEHandler::ProcessAttributeReference(attr, form, data); - break; - } -} - -bool DwarfCUToModule::FuncHandler::EndAttributes() { - RememberSpecification(); - return true; -} - -void DwarfCUToModule::FuncHandler::Finish() { - // Did we collect the information we need? Not all DWARF function - // entries have low and high addresses (for example, inlined - // functions that were never used), but all the ones we're - // interested in cover a non-empty range of bytes. - if (low_pc_ < high_pc_) { - // Create a Module::Function based on the data we've gathered, and - // add it to the functions_ list. - Function *func = new Function; - func->address = low_pc_; - func->size = high_pc_ - low_pc_; - func->parameter_size = 0; - func->specification_offset = offset_; - func->abstract_origin_offset = abstract_origin_offset_; - cu_context_->functions.push_back(func); - } -} - -// A handler for DIEs that contain functions and contribute a -// component to their names: namespaces, classes, etc. -class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler { - public: - NamedScopeHandler(CUContext *cu_context, DIEContext *parent_context, - uint64 offset) - : GenericDIEHandler(cu_context, parent_context, offset) { } - bool EndAttributes(); - DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag); - - private: - DIEContext child_context_; // A context for our children. -}; - -bool DwarfCUToModule::NamedScopeHandler::EndAttributes() { - child_context_.name_specification_offset = offset_; - RememberSpecification(); - return true; -} - -dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler( - uint64 offset, - enum DwarfTag tag) { - switch (tag) { - case dwarf2reader::DW_TAG_subprogram: - return new FuncHandler(cu_context_, &child_context_, offset); - case dwarf2reader::DW_TAG_namespace: - case dwarf2reader::DW_TAG_class_type: - case dwarf2reader::DW_TAG_structure_type: - case dwarf2reader::DW_TAG_union_type: - return new NamedScopeHandler(cu_context_, &child_context_, offset); - default: - return NULL; - } -} - -void DwarfCUToModule::WarningReporter::CUHeading() { - if (printed_cu_header_) - return; - fprintf(stderr, "%s: in compilation unit '%s' (offset 0x%llx):\n", - filename_.c_str(), cu_name_.c_str(), cu_offset_); - printed_cu_header_ = true; -} - -void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64 offset, - uint64 target) { - CUHeading(); - fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_specification" - " attribute referring to the DIE at offset 0x%llx, which either" - " was not marked as a declaration, or comes later in the file\n", - filename_.c_str(), offset, target); -} - -void DwarfCUToModule::WarningReporter::UnknownAbstractOrigin(uint64 offset, - uint64 target) { - CUHeading(); - fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_abstract_origin" - " attribute referring to the DIE at offset 0x%llx, which either" - " was not marked as an inline, or comes later in the file\n", - filename_.c_str(), offset, target); -} - -void DwarfCUToModule::WarningReporter::MissingSection(const string &name) { - CUHeading(); - fprintf(stderr, "%s: warning: couldn't find DWARF '%s' section\n", - filename_.c_str(), name.c_str()); -} - -void DwarfCUToModule::WarningReporter::BadLineInfoOffset(uint64 offset) { - CUHeading(); - fprintf(stderr, "%s: warning: line number data offset beyond end" - " of '.debug_line' section\n", - filename_.c_str()); -} - -void DwarfCUToModule::WarningReporter::UncoveredHeading() { - if (printed_unpaired_header_) - return; - CUHeading(); - fprintf(stderr, "%s: warning: skipping unpaired lines/functions:\n", - filename_.c_str()); - printed_unpaired_header_ = true; -} - -void DwarfCUToModule::WarningReporter::UncoveredFunction( - const Module::Function &function) { - if (!uncovered_warnings_enabled_) - return; - UncoveredHeading(); - fprintf(stderr, " function%s: %s\n", - function.size == 0 ? " (zero-length)" : "", - function.name.c_str()); -} - -void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) { - if (!uncovered_warnings_enabled_) - return; - UncoveredHeading(); - fprintf(stderr, " line%s: %s:%d at 0x%" PRIx64 "\n", - (line.size == 0 ? " (zero-length)" : ""), - line.file->name.c_str(), line.number, line.address); -} - -void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) { - CUHeading(); - fprintf(stderr, "%s: warning: function at offset 0x%" PRIx64 " has no name\n", - filename_.c_str(), offset); -} - -DwarfCUToModule::DwarfCUToModule(FileContext *file_context, - LineToModuleHandler *line_reader, - WarningReporter *reporter) - : line_reader_(line_reader), has_source_line_info_(false) { - cu_context_ = new CUContext(file_context, reporter); - child_context_ = new DIEContext(); -} - -DwarfCUToModule::~DwarfCUToModule() { - delete cu_context_; - delete child_context_; -} - -void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr, - enum DwarfForm form, - int64 data) { - switch (attr) { - case dwarf2reader::DW_AT_language: // source language of this CU - SetLanguage(static_cast<DwarfLanguage>(data)); - break; - default: - break; - } -} - -void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data) { - switch (attr) { - case dwarf2reader::DW_AT_stmt_list: // Line number information. - has_source_line_info_ = true; - source_line_offset_ = data; - break; - case dwarf2reader::DW_AT_language: // source language of this CU - SetLanguage(static_cast<DwarfLanguage>(data)); - break; - default: - break; - } -} - -void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr, - enum DwarfForm form, - const string &data) { - if (attr == dwarf2reader::DW_AT_name) - cu_context_->reporter->SetCUName(data); -} - -bool DwarfCUToModule::EndAttributes() { - return true; -} - -dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler( - uint64 offset, - enum DwarfTag tag) { - switch (tag) { - case dwarf2reader::DW_TAG_subprogram: - return new FuncHandler(cu_context_, child_context_, offset); - case dwarf2reader::DW_TAG_namespace: - case dwarf2reader::DW_TAG_class_type: - case dwarf2reader::DW_TAG_structure_type: - case dwarf2reader::DW_TAG_union_type: - return new NamedScopeHandler(cu_context_, child_context_, offset); - default: - return NULL; - } -} - -void DwarfCUToModule::SetLanguage(DwarfLanguage language) { - switch (language) { - case dwarf2reader::DW_LANG_Java: - cu_context_->language = Language::Java; - break; - - // DWARF has no generic language code for assembly language; this is - // what the GNU toolchain uses. - case dwarf2reader::DW_LANG_Mips_Assembler: - cu_context_->language = Language::Assembler; - break; - - // C++ covers so many cases that it probably has some way to cope - // with whatever the other languages throw at us. So make it the - // default. - // - // Objective C and Objective C++ seem to create entries for - // methods whose DW_AT_name values are already fully-qualified: - // "-[Classname method:]". These appear at the top level. - // - // DWARF data for C should never include namespaces or functions - // nested in struct types, but if it ever does, then C++'s - // notation is probably not a bad choice for that. - default: - case dwarf2reader::DW_LANG_ObjC: - case dwarf2reader::DW_LANG_ObjC_plus_plus: - case dwarf2reader::DW_LANG_C: - case dwarf2reader::DW_LANG_C89: - case dwarf2reader::DW_LANG_C99: - case dwarf2reader::DW_LANG_C_plus_plus: - cu_context_->language = Language::CPlusPlus; - break; - } -} - -void DwarfCUToModule::ReadSourceLines(uint64 offset) { - const dwarf2reader::SectionMap §ion_map - = cu_context_->file_context->section_map; - dwarf2reader::SectionMap::const_iterator map_entry - = section_map.find(".debug_line"); - // Mac OS X puts DWARF data in sections whose names begin with "__" - // instead of ".". - if (map_entry == section_map.end()) - map_entry = section_map.find("__debug_line"); - if (map_entry == section_map.end()) { - cu_context_->reporter->MissingSection(".debug_line"); - return; - } - const char *section_start = map_entry->second.first; - uint64 section_length = map_entry->second.second; - if (offset >= section_length) { - cu_context_->reporter->BadLineInfoOffset(offset); - return; - } - line_reader_->ReadProgram(section_start + offset, section_length - offset, - cu_context_->file_context->module, &lines_); -} - -namespace { -// Return true if ADDRESS falls within the range of ITEM. -template <class T> -inline bool within(const T &item, Module::Address address) { - // Because Module::Address is unsigned, and unsigned arithmetic - // wraps around, this will be false if ADDRESS falls before the - // start of ITEM, or if it falls after ITEM's end. - return address - item.address < item.size; -} -} - -void DwarfCUToModule::AssignLinesToFunctions() { - vector<Module::Function *> *functions = &cu_context_->functions; - WarningReporter *reporter = cu_context_->reporter; - - // This would be simpler if we assumed that source line entries - // don't cross function boundaries. However, there's no real reason - // to assume that (say) a series of function definitions on the same - // line wouldn't get coalesced into one line number entry. The - // DWARF spec certainly makes no such promises. - // - // So treat the functions and lines as peers, and take the trouble - // to compute their ranges' intersections precisely. In any case, - // the hair here is a constant factor for performance; the - // complexity from here on out is linear. - - // Put both our functions and lines in order by address. - sort(functions->begin(), functions->end(), - Function::CompareByAddress); - sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress); - - // The last line that we used any piece of. We use this only for - // generating warnings. - const Module::Line *last_line_used = NULL; - - // The last function and line we warned about --- so we can avoid - // doing so more than once. - const Module::Function *last_function_cited = NULL; - const Module::Line *last_line_cited = NULL; - - // Make a single pass through both vectors from lower to higher - // addresses, populating each Function's lines vector with lines - // from our lines_ vector that fall within the function's address - // range. - vector<Module::Function *>::iterator func_it = functions->begin(); - vector<Module::Line>::const_iterator line_it = lines_.begin(); - - Module::Address current; - - // Pointers to the referents of func_it and line_it, or NULL if the - // iterator is at the end of the sequence. - Module::Function *func; - const Module::Line *line; - - // Start current at the beginning of the first line or function, - // whichever is earlier. - if (func_it != functions->end() && line_it != lines_.end()) { - func = *func_it; - line = &*line_it; - current = std::min(func->address, line->address); - } else if (line_it != lines_.end()) { - func = NULL; - line = &*line_it; - current = line->address; - } else if (func_it != functions->end()) { - func = *func_it; - line = NULL; - current = (*func_it)->address; - } else { - return; - } - - while (func || line) { - // This loop has two invariants that hold at the top. - // - // First, at least one of the iterators is not at the end of its - // sequence, and those that are not refer to the earliest - // function or line that contains or starts after CURRENT. - // - // Note that every byte is in one of four states: it is covered - // or not covered by a function, and, independently, it is - // covered or not covered by a line. - // - // The second invariant is that CURRENT refers to a byte whose - // state is different from its predecessor, or it refers to the - // first byte in the address space. In other words, CURRENT is - // always the address of a transition. - // - // Note that, although each iteration advances CURRENT from one - // transition address to the next in each iteration, it might - // not advance the iterators. Suppose we have a function that - // starts with a line, has a gap, and then a second line, and - // suppose that we enter an iteration with CURRENT at the end of - // the first line. The next transition address is the start of - // the second line, after the gap, so the iteration should - // advance CURRENT to that point. At the head of that iteration, - // the invariants require that the line iterator be pointing at - // the second line. But this is also true at the head of the - // next. And clearly, the iteration must not change the function - // iterator. So neither iterator moves. - - // Assert the first invariant (see above). - assert(!func || current < func->address || within(*func, current)); - assert(!line || current < line->address || within(*line, current)); - - // The next transition after CURRENT. - Module::Address next_transition; - - // Figure out which state we're in, add lines or warn, and compute - // the next transition address. - if (func && current >= func->address) { - if (line && current >= line->address) { - // Covered by both a line and a function. - Module::Address func_left = func->size - (current - func->address); - Module::Address line_left = line->size - (current - line->address); - // This may overflow, but things work out. - next_transition = current + std::min(func_left, line_left); - Module::Line l = *line; - l.address = current; - l.size = next_transition - current; - func->lines.push_back(l); - last_line_used = line; - } else { - // Covered by a function, but no line. - if (func != last_function_cited) { - reporter->UncoveredFunction(*func); - last_function_cited = func; - } - if (line && within(*func, line->address)) - next_transition = line->address; - else - // If this overflows, we'll catch it below. - next_transition = func->address + func->size; - } - } else { - if (line && current >= line->address) { - // Covered by a line, but no function. - // - // If GCC emits padding after one function to align the start - // of the next, then it will attribute the padding - // instructions to the last source line of function (to reduce - // the size of the line number info), but omit it from the - // DW_AT_{low,high}_pc range given in .debug_info (since it - // costs nothing to be precise there). If we did use at least - // some of the line we're about to skip, and it ends at the - // start of the next function, then assume this is what - // happened, and don't warn. - if (line != last_line_cited - && !(func - && line == last_line_used - && func->address - line->address == line->size)) { - reporter->UncoveredLine(*line); - last_line_cited = line; - } - if (func && within(*line, func->address)) - next_transition = func->address; - else - // If this overflows, we'll catch it below. - next_transition = line->address + line->size; - } else { - // Covered by neither a function nor a line. By the invariant, - // both func and line begin after CURRENT. The next transition - // is the start of the next function or next line, whichever - // is earliest. - assert (func || line); - if (func && line) - next_transition = std::min(func->address, line->address); - else if (func) - next_transition = func->address; - else - next_transition = line->address; - } - } - - // If a function or line abuts the end of the address space, then - // next_transition may end up being zero, in which case we've completed - // our pass. Handle that here, instead of trying to deal with it in - // each place we compute next_transition. - if (!next_transition) - break; - - // Advance iterators as needed. If lines overlap or functions overlap, - // then we could go around more than once. We don't worry too much - // about what result we produce in that case, just as long as we don't - // hang or crash. - while (func_it != functions->end() - && next_transition >= (*func_it)->address - && !within(**func_it, next_transition)) - func_it++; - func = (func_it != functions->end()) ? *func_it : NULL; - while (line_it != lines_.end() - && next_transition >= line_it->address - && !within(*line_it, next_transition)) - line_it++; - line = (line_it != lines_.end()) ? &*line_it : NULL; - - // We must make progress. - assert(next_transition > current); - current = next_transition; - } -} - -void DwarfCUToModule::Finish() { - // Assembly language files have no function data, and that gives us - // no place to store our line numbers (even though the GNU toolchain - // will happily produce source line info for assembly language - // files). To avoid spurious warnings about lines we can't assign - // to functions, skip CUs in languages that lack functions. - if (!cu_context_->language->HasFunctions()) - return; - - // Read source line info, if we have any. - if (has_source_line_info_) - ReadSourceLines(source_line_offset_); - - vector<Module::Function *> *functions = &cu_context_->functions; - - for (vector<Module::Function*>::iterator iterator = functions->begin(); - iterator != functions->end(); - ++iterator) { - Function* function = static_cast<Function*>(*iterator); - - if (function->specification_offset) { - SpecificationByOffset::iterator specification_iterator = - cu_context_->file_context->file_private->specifications.find( - function->specification_offset); - if (specification_iterator != - cu_context_->file_context->file_private->specifications.end()) { - Specification* specification = specification_iterator->second; - function->name = specification->QualifiedName( - cu_context_, function->specification_offset); - } else { - cu_context_->reporter->UnknownSpecification( - 0, function->specification_offset); - } - } - - if (function->name.empty()) { - if (function->abstract_origin_offset) { - SpecificationByOffset::iterator abstract_origin_iterator = - cu_context_->file_context->file_private->specifications.find( - function->abstract_origin_offset); - if (abstract_origin_iterator != - cu_context_->file_context->file_private->specifications.end()) { - Specification* abstract_origin = abstract_origin_iterator->second; - function->name = abstract_origin->QualifiedName( - cu_context_, function->abstract_origin_offset); - } else { - cu_context_->reporter->UnknownAbstractOrigin( - 0, function->abstract_origin_offset); - } - } - } - - if (function->name.empty()) { - function->name.assign("<name omitted>"); - cu_context_->reporter->UnnamedFunction(function->specification_offset); - } - } - - // Dole out lines to the appropriate functions. - AssignLinesToFunctions(); - - // Add our functions, which now have source lines assigned to them, - // to module_. - cu_context_->file_context->module->AddFunctions(functions->begin(), - functions->end()); - - // Ownership of the function objects has shifted from cu_context to - // the Module. - functions->clear(); -} - -bool DwarfCUToModule::StartCompilationUnit(uint64 offset, - uint8 address_size, - uint8 offset_size, - uint64 cu_length, - uint8 dwarf_version) { - return dwarf_version >= 2; -} - -bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag) { - // We don't deal with partial compilation units (the only other tag - // likely to be used for root DIE). - return tag == dwarf2reader::DW_TAG_compile_unit; -} - -} // namespace google_breakpad diff --git a/breakpad/pending/src/common/dwarf_cu_to_module.h b/breakpad/pending/src/common/dwarf_cu_to_module.h deleted file mode 100644 index f5ca555..0000000 --- a/breakpad/pending/src/common/dwarf_cu_to_module.h +++ /dev/null @@ -1,279 +0,0 @@ -// -*- mode: c++ -*- - -// 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. - -// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - -// Add DWARF debugging information to a Breakpad symbol file. This -// file defines the DwarfCUToModule class, which accepts parsed DWARF -// data and populates a google_breakpad::Module with the results; the -// Module can then write its contents as a Breakpad symbol file. - -#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__ -#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__ - -#include <string> - -#include "common/language.h" -#include "common/module.h" -#include "common/dwarf/bytereader.h" -#include "common/dwarf/dwarf2diehandler.h" -#include "common/dwarf/dwarf2reader.h" - -namespace google_breakpad { - -using dwarf2reader::DwarfAttribute; -using dwarf2reader::DwarfForm; -using dwarf2reader::DwarfLanguage; -using dwarf2reader::DwarfTag; - -// Populate a google_breakpad::Module with DWARF debugging information. -// -// An instance of this class can be provided as a handler to a -// dwarf2reader::CompilationUnit DWARF parser. The handler uses the -// results of parsing to populate a google_breakpad::Module with -// source file, function, and source line information. -class DwarfCUToModule: public dwarf2reader::RootDIEHandler { - struct FilePrivate; - public: - - // Information global to the DWARF-bearing file we are processing, - // for use by DwarfCUToModule. Each DwarfCUToModule instance deals - // with a single compilation unit within the file, but information - // global to the whole file is held here. The client is responsible - // for filling it in appropriately (except for the 'file_private' - // field, which the constructor and destructor take care of), and - // then providing it to the DwarfCUToModule instance for each - // compilation unit we process in that file. - struct FileContext { - FileContext(const string &filename_arg, Module *module_arg); - ~FileContext(); - - // The name of this file, for use in error messages. - string filename; - - // A map of this file's sections, used for finding other DWARF - // sections that the .debug_info section may refer to. - dwarf2reader::SectionMap section_map; - - // The Module to which we're contributing definitions. - Module *module; - - // Inter-compilation unit data used internally by the handlers. - FilePrivate *file_private; - }; - - // An abstract base class for handlers that handle DWARF line data - // for DwarfCUToModule. DwarfCUToModule could certainly just use - // dwarf2reader::LineInfo itself directly, but decoupling things - // this way makes unit testing a little easier. - class LineToModuleHandler { - public: - LineToModuleHandler() { } - virtual ~LineToModuleHandler() { } - - // Called at the beginning of a new compilation unit, prior to calling - // ReadProgram(). compilation_dir will indicate the path that the - // current compilation unit was compiled in, consistent with the - // DW_AT_comp_dir DIE. - virtual void StartCompilationUnit(const string& compilation_dir) = 0; - - // Populate MODULE and LINES with source file names and code/line - // mappings, given a pointer to some DWARF line number data - // PROGRAM, and an overestimate of its size. Add no zero-length - // lines to LINES. - virtual void ReadProgram(const char *program, uint64 length, - Module *module, vector<Module::Line> *lines) = 0; - }; - - // The interface DwarfCUToModule uses to report warnings. The member - // function definitions for this class write messages to stderr, but - // you can override them if you'd like to detect or report these - // conditions yourself. - class WarningReporter { - public: - // Warn about problems in the DWARF file FILENAME, in the - // compilation unit at OFFSET. - WarningReporter(const string &filename, uint64 cu_offset) - : filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false), - printed_unpaired_header_(false), - uncovered_warnings_enabled_(false) { } - virtual ~WarningReporter() { } - - // Set the name of the compilation unit we're processing to NAME. - virtual void SetCUName(const string &name) { cu_name_ = name; } - - // Accessor and setter for uncovered_warnings_enabled_. - // UncoveredFunction and UncoveredLine only report a problem if that is - // true. By default, these warnings are disabled, because those - // conditions occur occasionally in healthy code. - virtual bool uncovered_warnings_enabled() const { - return uncovered_warnings_enabled_; - } - virtual void set_uncovered_warnings_enabled(bool value) { - uncovered_warnings_enabled_ = value; - } - - // A DW_AT_specification in the DIE at OFFSET refers to a DIE we - // haven't processed yet, or that wasn't marked as a declaration, - // at TARGET. - virtual void UnknownSpecification(uint64 offset, uint64 target); - - // A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we - // haven't processed yet, or that wasn't marked as inline, at TARGET. - virtual void UnknownAbstractOrigin(uint64 offset, uint64 target); - - // We were unable to find the DWARF section named SECTION_NAME. - virtual void MissingSection(const string §ion_name); - - // The CU's DW_AT_stmt_list offset OFFSET is bogus. - virtual void BadLineInfoOffset(uint64 offset); - - // FUNCTION includes code covered by no line number data. - virtual void UncoveredFunction(const Module::Function &function); - - // Line number NUMBER in LINE_FILE, of length LENGTH, includes code - // covered by no function. - virtual void UncoveredLine(const Module::Line &line); - - // The DW_TAG_subprogram DIE at OFFSET has no name specified directly - // in the DIE, nor via a DW_AT_specification or DW_AT_abstract_origin - // link. - virtual void UnnamedFunction(uint64 offset); - - protected: - string filename_; - uint64 cu_offset_; - string cu_name_; - bool printed_cu_header_; - bool printed_unpaired_header_; - bool uncovered_warnings_enabled_; - - private: - // Print a per-CU heading, once. - void CUHeading(); - // Print an unpaired function/line heading, once. - void UncoveredHeading(); - }; - - // Create a DWARF debugging info handler for a compilation unit - // within FILE_CONTEXT. This uses information received from the - // dwarf2reader::CompilationUnit DWARF parser to populate - // FILE_CONTEXT->module. Use LINE_READER to handle the compilation - // unit's line number data. Use REPORTER to report problems with the - // data we find. - DwarfCUToModule(FileContext *file_context, - LineToModuleHandler *line_reader, - WarningReporter *reporter); - ~DwarfCUToModule(); - - void ProcessAttributeSigned(enum DwarfAttribute attr, - enum DwarfForm form, - int64 data); - void ProcessAttributeUnsigned(enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data); - void ProcessAttributeString(enum DwarfAttribute attr, - enum DwarfForm form, - const string &data); - bool EndAttributes(); - DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag); - - // Assign all our source Lines to the Functions that cover their - // addresses, and then add them to module_. - void Finish(); - - bool StartCompilationUnit(uint64 offset, uint8 address_size, - uint8 offset_size, uint64 cu_length, - uint8 dwarf_version); - bool StartRootDIE(uint64 offset, enum DwarfTag tag); - - private: - - // Used internally by the handler. Full definitions are in - // dwarf_cu_to_module.cc. - struct FilePrivate; - struct Specification; - struct CUContext; - struct DIEContext; - struct Function; - class GenericDIEHandler; - class FuncHandler; - class NamedScopeHandler; - - // A map from section offsets to specifications. - typedef map<uint64, Specification*> SpecificationByOffset; - - // Set this compilation unit's source language to LANGUAGE. - void SetLanguage(DwarfLanguage language); - - // Read source line information at OFFSET in the .debug_line - // section. Record source files in module_, but record source lines - // in lines_; we apportion them to functions in - // AssignLinesToFunctions. - void ReadSourceLines(uint64 offset); - - // Assign the lines in lines_ to the individual line lists of the - // functions in functions_. (DWARF line information maps an entire - // compilation unit at a time, and gives no indication of which - // lines belong to which functions, beyond their addresses.) - void AssignLinesToFunctions(); - - // The only reason cu_context_ and child_context_ are pointers is - // that we want to keep their definitions private to - // dwarf_cu_to_module.cc, instead of listing them all here. They are - // owned by this DwarfCUToModule: the constructor sets them, and the - // destructor deletes them. - - // The functor to use to handle line number data. - LineToModuleHandler *line_reader_; - - // This compilation unit's context. - CUContext *cu_context_; - - // A context for our children. - DIEContext *child_context_; - - // True if this compilation unit has source line information. - bool has_source_line_info_; - - // The offset of this compilation unit's line number information in - // the .debug_line section. - uint64 source_line_offset_; - - // The line numbers we have seen thus far. We accumulate these here - // during parsing. Then, in Finish, we call AssignLinesToFunctions - // to dole them out to the appropriate functions. - vector<Module::Line> lines_; -}; - -} // namespace google_breakpad - -#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__ diff --git a/breakpad/pending/src/common/dwarf_cu_to_module_unittest.cc b/breakpad/pending/src/common/dwarf_cu_to_module_unittest.cc deleted file mode 100644 index 34fd728..0000000 --- a/breakpad/pending/src/common/dwarf_cu_to_module_unittest.cc +++ /dev/null @@ -1,1763 +0,0 @@ -// 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. - -// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - -// dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule. - -#include <vector> - -#include "breakpad_googletest_includes.h" -#include "common/dwarf_cu_to_module.h" - -using std::vector; - -using dwarf2reader::DIEHandler; -using dwarf2reader::DwarfTag; -using dwarf2reader::DwarfAttribute; -using dwarf2reader::DwarfForm; -using dwarf2reader::DwarfInline; -using dwarf2reader::RootDIEHandler; -using google_breakpad::DwarfCUToModule; -using google_breakpad::Module; - -using ::testing::_; -using ::testing::AtMost; -using ::testing::Invoke; -using ::testing::Return; -using ::testing::Test; -using ::testing::TestWithParam; -using ::testing::Values; -using ::testing::ValuesIn; - -// Mock classes. - -class MockLineToModuleFunctor: public DwarfCUToModule::LineToModuleFunctor { - public: - MOCK_METHOD4(mock_apply, void(const char *program, uint64 length, - Module *module, vector<Module::Line> *lines)); - void operator()(const char *program, uint64 length, - Module *module, vector<Module::Line> *lines) { - mock_apply(program, length, module, lines); - } -}; - -class MockWarningReporter: public DwarfCUToModule::WarningReporter { - public: - MockWarningReporter(const string &filename, uint64 cu_offset) - : DwarfCUToModule::WarningReporter(filename, cu_offset) { } - MOCK_METHOD1(SetCUName, void(const string &name)); - MOCK_METHOD2(UnknownSpecification, void(uint64 offset, uint64 target)); - MOCK_METHOD2(UnknownAbstractOrigin, void(uint64 offset, uint64 target)); - MOCK_METHOD1(MissingSection, void(const string §ion_name)); - MOCK_METHOD1(BadLineInfoOffset, void(uint64 offset)); - MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function)); - MOCK_METHOD1(UncoveredLine, void(const Module::Line &line)); - MOCK_METHOD1(UnnamedFunction, void(uint64 offset)); -}; - -// A fixture class including all the objects needed to handle a -// compilation unit, and their entourage. It includes member functions -// for doing common kinds of setup and tests. -class CUFixtureBase { - public: - - // If we have: - // - // vector<Module::Line> lines; - // AppendLinesFunctor appender(lines); - // - // then doing: - // - // appender(line_program, length, module, line_vector); - // - // will append lines to the end of line_vector. We can use this with - // MockLineToModuleFunctor like this: - // - // MockLineToModuleFunctor l2m; - // EXPECT_CALL(l2m, mock_apply(_,_,_,_)) - // .WillOnce(DoAll(Invoke(appender), Return())); - // - // in which case calling l2m with some line vector will append lines. - class AppendLinesFunctor { - public: - AppendLinesFunctor(const vector<Module::Line> *lines) : lines_(lines) { } - void operator()(const char *program, uint64 length, - Module *module, vector<Module::Line> *lines) { - lines->insert(lines->end(), lines_->begin(), lines_->end()); - } - private: - const vector<Module::Line> *lines_; - }; - - CUFixtureBase() - : module_("module-name", "module-os", "module-arch", "module-id"), - file_context_("dwarf-filename", &module_), - language_(dwarf2reader::DW_LANG_none), - language_signed_(false), - appender_(&lines_), - reporter_("dwarf-filename", 0xcf8f9bb6443d29b5LL), - root_handler_(&file_context_, &line_reader_, &reporter_), - functions_filled_(false), - incrementing_offset_(0x0000000000100000ULL) { - // By default, expect no warnings to be reported, and expect the - // compilation unit's name to be provided. The test can override - // these expectations. - EXPECT_CALL(reporter_, SetCUName("compilation-unit-name")).Times(1); - EXPECT_CALL(reporter_, UnknownSpecification(_, _)).Times(0); - EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, _)).Times(0); - EXPECT_CALL(reporter_, MissingSection(_)).Times(0); - EXPECT_CALL(reporter_, BadLineInfoOffset(_)).Times(0); - EXPECT_CALL(reporter_, UncoveredFunction(_)).Times(0); - EXPECT_CALL(reporter_, UncoveredLine(_)).Times(0); - EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(0); - - // By default, expect the line program reader not to be invoked. We - // may override this in StartCU. - EXPECT_CALL(line_reader_, mock_apply(_,_,_,_)).Times(0); - - // The handler will consult this section map to decide what to - // pass to our line reader. - file_context_.section_map[".debug_line"] = std::make_pair(dummy_line_program_, - dummy_line_size_); - } - - // Add a line with the given address, size, filename, and line - // number to the end of the statement list the handler will receive - // when it invokes its LineToModuleFunctor. Call this before calling - // StartCU. - void PushLine(Module::Address address, Module::Address size, - const string &filename, int line_number); - - // Use LANGUAGE for the compilation unit. More precisely, arrange - // for StartCU to pass the compilation unit's root DIE a - // DW_AT_language attribute whose value is LANGUAGE. - void SetLanguage(dwarf2reader::DwarfLanguage language) { - language_ = language; - } - - // If SIGNED true, have StartCU report DW_AT_language as a signed - // attribute; if false, have it report it as unsigned. - void SetLanguageSigned(bool is_signed) { language_signed_ = is_signed; } - - // Call the handler this.root_handler_'s StartCompilationUnit and - // StartRootDIE member functions, passing it appropriate attributes as - // determined by prior calls to PushLine and SetLanguage. Leave - // this.root_handler_ ready to hear about children: call - // this.root_handler_.EndAttributes, but not this.root_handler_.Finish. - void StartCU(); - - // Have HANDLER process some strange attribute/form/value triples. - void ProcessStrangeAttributes(dwarf2reader::DIEHandler *handler); - - // Start a child DIE of PARENT with the given tag and name. Leave - // the handler ready to hear about children: call EndAttributes, but - // not Finish. - DIEHandler *StartNamedDIE(DIEHandler *parent, DwarfTag tag, - const string &name); - - // Start a child DIE of PARENT with the given tag and a - // DW_AT_specification attribute whose value is SPECIFICATION. Leave - // the handler ready to hear about children: call EndAttributes, but - // not Finish. If NAME is non-zero, use it as the DW_AT_name - // attribute. - DIEHandler *StartSpecifiedDIE(DIEHandler *parent, DwarfTag tag, - uint64 offset, const char *name = NULL); - - // Define a function as a child of PARENT with the given name, - // address, and size. Call EndAttributes and Finish; one cannot - // define children of the defined function's DIE. - void DefineFunction(DIEHandler *parent, const string &name, - Module::Address address, Module::Address size); - - // Create a declaration DIE as a child of PARENT with the given - // offset, tag and name. If NAME is the empty string, don't provide - // a DW_AT_name attribute. Call EndAttributes and Finish. - void DeclarationDIE(DIEHandler *parent, uint64 offset, - DwarfTag tag, const string &name); - - // Create a definition DIE as a child of PARENT with the given tag - // that refers to the declaration DIE at offset SPECIFICATION as its - // specification. If NAME is non-empty, pass it as the DW_AT_name - // attribute. If SIZE is non-zero, record ADDRESS and SIZE as - // low_pc/high_pc attributes. - void DefinitionDIE(DIEHandler *parent, DwarfTag tag, - uint64 specification, const string &name, - Module::Address address = 0, Module::Address size = 0); - - // Create an inline DW_TAG_subprogram DIE as a child of PARENT. If - // SPECIFICATION is non-zero, then the DIE refers to the declaration DIE at - // offset SPECIFICATION as its specification. If Name is non-empty, pass it - // as the DW_AT_name attribute. - void AbstractInstanceDIE(DIEHandler *parent, uint64 offset, - DwarfInline type, uint64 specification, - const string &name, - DwarfForm form = dwarf2reader::DW_FORM_data1); - - // Create a DW_TAG_subprogram DIE as a child of PARENT that refers to - // ORIGIN in its DW_AT_abstract_origin attribute. If NAME is the empty - // string, don't provide a DW_AT_name attribute. - void DefineInlineInstanceDIE(DIEHandler *parent, const string &name, - uint64 origin, Module::Address address, - Module::Address size); - - // The following Test* functions should be called after calling - // this.root_handler_.Finish. After that point, no further calls - // should be made on the handler. - - // Test that the number of functions defined in the module this.module_ is - // equal to EXPECTED. - void TestFunctionCount(size_t expected); - - // Test that the I'th function (ordered by address) in the module - // this.module_ has the given name, address, and size, and that its - // parameter size is zero. - void TestFunction(int i, const string &name, - Module::Address address, Module::Address size); - - // Test that the number of source lines owned by the I'th function - // in the module this.module_ is equal to EXPECTED. - void TestLineCount(int i, size_t expected); - - // Test that the J'th line (ordered by address) of the I'th function - // (again, by address) has the given address, size, filename, and - // line number. - void TestLine(int i, int j, Module::Address address, Module::Address size, - const string &filename, int number); - - // Actual objects under test. - Module module_; - DwarfCUToModule::FileContext file_context_; - - // If this is not DW_LANG_none, we'll pass it as a DW_AT_language - // attribute to the compilation unit. This defaults to DW_LANG_none. - dwarf2reader::DwarfLanguage language_; - - // If this is true, report DW_AT_language as a signed value; if false, - // report it as an unsigned value. - bool language_signed_; - - // If this is not empty, we'll give the CU a DW_AT_stmt_list - // attribute that, when passed to line_reader_, adds these lines to the - // provided lines array. - vector<Module::Line> lines_; - - // Mock line program reader. - MockLineToModuleFunctor line_reader_; - AppendLinesFunctor appender_; - static const char dummy_line_program_[]; - static const size_t dummy_line_size_; - - MockWarningReporter reporter_; - DwarfCUToModule root_handler_; - - private: - // Fill functions_, if we haven't already. - void FillFunctions(); - - // If functions_filled_ is true, this is a table of functions we've - // extracted from module_, sorted by address. - vector<Module::Function *> functions_; - // True if we have filled the above vector with this.module_'s function list. - bool functions_filled_; - - // When a unique offset value is needed, grab one from here and increment - // it. - uint64 incrementing_offset_; -}; - -const char CUFixtureBase::dummy_line_program_[] = "lots of fun data"; -const size_t CUFixtureBase::dummy_line_size_ = - sizeof (CUFixtureBase::dummy_line_program_); - -void CUFixtureBase::PushLine(Module::Address address, Module::Address size, - const string &filename, int line_number) { - Module::Line l; - l.address = address; - l.size = size; - l.file = module_.FindFile(filename); - l.number = line_number; - lines_.push_back(l); -} - -void CUFixtureBase::StartCU() { - // If we have lines, make the line reader expect to be invoked at - // most once. (Hey, if the handler can pass its tests without - // bothering to read the line number data, that's great.) - // Have it add the lines passed to PushLine. Otherwise, leave the - // initial expectation (no calls) in force. - if (!lines_.empty()) - EXPECT_CALL(line_reader_, - mock_apply(&dummy_line_program_[0], dummy_line_size_, - &module_, _)) - .Times(AtMost(1)) - .WillOnce(DoAll(Invoke(appender_), Return())); - - ASSERT_TRUE(root_handler_ - .StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44, - 0x4241b4f33720dd5cULL, 3)); - { - ASSERT_TRUE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, - dwarf2reader::DW_TAG_compile_unit)); - } - root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - "compilation-unit-name"); - if (!lines_.empty()) - root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, - dwarf2reader::DW_FORM_ref4, - 0); - if (language_ != dwarf2reader::DW_LANG_none) { - if (language_signed_) - root_handler_.ProcessAttributeSigned(dwarf2reader::DW_AT_language, - dwarf2reader::DW_FORM_sdata, - language_); - else - root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_language, - dwarf2reader::DW_FORM_udata, - language_); - } - ASSERT_TRUE(root_handler_.EndAttributes()); -} - -void CUFixtureBase::ProcessStrangeAttributes( - dwarf2reader::DIEHandler *handler) { - handler->ProcessAttributeUnsigned((DwarfAttribute) 0xf560dead, - (DwarfForm) 0x4106e4db, - 0xa592571997facda1ULL); - handler->ProcessAttributeSigned((DwarfAttribute) 0x85380095, - (DwarfForm) 0x0f16fe87, - 0x12602a4e3bf1f446LL); - handler->ProcessAttributeReference((DwarfAttribute) 0xf7f7480f, - (DwarfForm) 0x829e038a, - 0x50fddef44734fdecULL); - static const char buffer[10] = "frobynode"; - handler->ProcessAttributeBuffer((DwarfAttribute) 0xa55ffb51, - (DwarfForm) 0x2f43b041, - buffer, sizeof(buffer)); - handler->ProcessAttributeString((DwarfAttribute) 0x2f43b041, - (DwarfForm) 0x895ffa23, - "strange string"); -} - -DIEHandler *CUFixtureBase::StartNamedDIE(DIEHandler *parent, - DwarfTag tag, - const string &name) { - dwarf2reader::DIEHandler *handler - = parent->FindChildHandler(incrementing_offset_++, tag); - if (!handler) - return NULL; - handler->ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - name); - ProcessStrangeAttributes(handler); - if (!handler->EndAttributes()) { - handler->Finish(); - delete handler; - return NULL; - } - - return handler; -} - -DIEHandler *CUFixtureBase::StartSpecifiedDIE(DIEHandler *parent, - DwarfTag tag, - uint64 specification, - const char *name) { - dwarf2reader::DIEHandler *handler - = parent->FindChildHandler(incrementing_offset_++, tag); - if (!handler) - return NULL; - if (name) - handler->ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - name); - handler->ProcessAttributeReference(dwarf2reader::DW_AT_specification, - dwarf2reader::DW_FORM_ref4, - specification); - if (!handler->EndAttributes()) { - handler->Finish(); - delete handler; - return NULL; - } - - return handler; -} - -void CUFixtureBase::DefineFunction(dwarf2reader::DIEHandler *parent, - const string &name, Module::Address address, - Module::Address size) { - dwarf2reader::DIEHandler *func - = parent->FindChildHandler(incrementing_offset_++, - dwarf2reader::DW_TAG_subprogram); - ASSERT_TRUE(func != NULL); - func->ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - name); - func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, - dwarf2reader::DW_FORM_addr, - address); - func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, - dwarf2reader::DW_FORM_addr, - address + size); - ProcessStrangeAttributes(func); - EXPECT_TRUE(func->EndAttributes()); - func->Finish(); - delete func; -} - -void CUFixtureBase::DeclarationDIE(DIEHandler *parent, uint64 offset, - DwarfTag tag, - const string &name) { - dwarf2reader::DIEHandler *die = parent->FindChildHandler(offset, tag); - ASSERT_TRUE(die != NULL); - if (!name.empty()) - die->ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - name); - die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_declaration, - dwarf2reader::DW_FORM_flag, - 1); - EXPECT_TRUE(die->EndAttributes()); - die->Finish(); - delete die; -} - -void CUFixtureBase::DefinitionDIE(DIEHandler *parent, - DwarfTag tag, - uint64 specification, - const string &name, - Module::Address address, - Module::Address size) { - dwarf2reader::DIEHandler *die - = parent->FindChildHandler(0x6ccfea031a9e6cc9ULL, tag); - ASSERT_TRUE(die != NULL); - die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, - dwarf2reader::DW_FORM_ref4, - specification); - if (!name.empty()) - die->ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - name); - if (size) { - die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, - dwarf2reader::DW_FORM_addr, - address); - die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, - dwarf2reader::DW_FORM_addr, - address + size); - } - EXPECT_TRUE(die->EndAttributes()); - die->Finish(); - delete die; -} - -void CUFixtureBase::AbstractInstanceDIE(DIEHandler *parent, - uint64 offset, - DwarfInline type, - uint64 specification, - const string &name, - DwarfForm form) { - dwarf2reader::DIEHandler *die - = parent->FindChildHandler(offset, dwarf2reader::DW_TAG_subprogram); - ASSERT_TRUE(die != NULL); - if (specification != 0ULL) - die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, - dwarf2reader::DW_FORM_ref4, - specification); - if (form == dwarf2reader::DW_FORM_sdata) { - die->ProcessAttributeSigned(dwarf2reader::DW_AT_inline, form, type); - } else { - die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_inline, form, type); - } - if (!name.empty()) - die->ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - name); - - EXPECT_TRUE(die->EndAttributes()); - die->Finish(); - delete die; -} - -void CUFixtureBase::DefineInlineInstanceDIE(DIEHandler *parent, - const string &name, - uint64 origin, - Module::Address address, - Module::Address size) { - dwarf2reader::DIEHandler *func - = parent->FindChildHandler(0x11c70f94c6e87ccdLL, - dwarf2reader::DW_TAG_subprogram); - ASSERT_TRUE(func != NULL); - if (!name.empty()) { - func->ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - name); - } - func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, - dwarf2reader::DW_FORM_addr, - address); - func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, - dwarf2reader::DW_FORM_addr, - address + size); - func->ProcessAttributeReference(dwarf2reader::DW_AT_abstract_origin, - dwarf2reader::DW_FORM_ref4, - origin); - ProcessStrangeAttributes(func); - EXPECT_TRUE(func->EndAttributes()); - func->Finish(); - delete func; -} - -void CUFixtureBase::FillFunctions() { - if (functions_filled_) - return; - module_.GetFunctions(&functions_, functions_.end()); - sort(functions_.begin(), functions_.end(), - Module::Function::CompareByAddress); - functions_filled_ = true; -} - -void CUFixtureBase::TestFunctionCount(size_t expected) { - FillFunctions(); - ASSERT_EQ(expected, functions_.size()); -} - -void CUFixtureBase::TestFunction(int i, const string &name, - Module::Address address, - Module::Address size) { - FillFunctions(); - ASSERT_LT((size_t) i, functions_.size()); - - Module::Function *function = functions_[i]; - EXPECT_EQ(name, function->name); - EXPECT_EQ(address, function->address); - EXPECT_EQ(size, function->size); - EXPECT_EQ(0U, function->parameter_size); -} - -void CUFixtureBase::TestLineCount(int i, size_t expected) { - FillFunctions(); - ASSERT_LT((size_t) i, functions_.size()); - - ASSERT_EQ(expected, functions_[i]->lines.size()); -} - -void CUFixtureBase::TestLine(int i, int j, - Module::Address address, Module::Address size, - const string &filename, int number) { - FillFunctions(); - ASSERT_LT((size_t) i, functions_.size()); - ASSERT_LT((size_t) j, functions_[i]->lines.size()); - - Module::Line *line = &functions_[i]->lines[j]; - EXPECT_EQ(address, line->address); - EXPECT_EQ(size, line->size); - EXPECT_EQ(filename, line->file->name.c_str()); - EXPECT_EQ(number, line->number); -} - -// Include caller locations for our test subroutines. -#define TRACE(call) do { SCOPED_TRACE("called from here"); call; } while (0) -#define PushLine(a,b,c,d) TRACE(PushLine((a),(b),(c),(d))) -#define SetLanguage(a) TRACE(SetLanguage(a)) -#define StartCU() TRACE(StartCU()) -#define DefineFunction(a,b,c,d) TRACE(DefineFunction((a),(b),(c),(d))) -#define DeclarationDIE(a,b,c,d) TRACE(DeclarationDIE((a),(b),(c),(d))) -#define DefinitionDIE(a,b,c,d,e,f) TRACE(DefinitionDIE((a),(b),(c),(d),(e),(f))) -#define TestFunctionCount(a) TRACE(TestFunctionCount(a)) -#define TestFunction(a,b,c,d) TRACE(TestFunction((a),(b),(c),(d))) -#define TestLineCount(a,b) TRACE(TestLineCount((a),(b))) -#define TestLine(a,b,c,d,e,f) TRACE(TestLine((a),(b),(c),(d),(e),(f))) - -class SimpleCU: public CUFixtureBase, public Test { -}; - -TEST_F(SimpleCU, OneFunc) { - PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); - - StartCU(); - DefineFunction(&root_handler_, "function1", - 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); - TestLineCount(0, 1); - TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", - 246571772); -} - -TEST_F(SimpleCU, IrrelevantRootChildren) { - StartCU(); - EXPECT_FALSE(root_handler_ - .FindChildHandler(0x7db32bff4e2dcfb1ULL, - dwarf2reader::DW_TAG_lexical_block)); -} - -TEST_F(SimpleCU, IrrelevantNamedScopeChildren) { - StartCU(); - DIEHandler *class_A_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); - EXPECT_TRUE(class_A_handler != NULL); - EXPECT_FALSE(class_A_handler - ->FindChildHandler(0x02e55999b865e4e9ULL, - dwarf2reader::DW_TAG_lexical_block)); - delete class_A_handler; -} - -// Verify that FileContexts can safely be deleted unused. -TEST_F(SimpleCU, UnusedFileContext) { - Module m("module-name", "module-os", "module-arch", "module-id"); - DwarfCUToModule::FileContext fc("dwarf-filename", &m); - - // Kludge: satisfy reporter_'s expectation. - reporter_.SetCUName("compilation-unit-name"); -} - -TEST_F(SimpleCU, InlineFunction) { - PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); - - StartCU(); - AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, - dwarf2reader::DW_INL_inlined, 0, "inline-name"); - DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, - 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "inline-name", - 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); -} - -TEST_F(SimpleCU, InlineFunctionSignedAttribute) { - PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); - - StartCU(); - AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, - dwarf2reader::DW_INL_inlined, 0, "inline-name", - dwarf2reader::DW_FORM_sdata); - DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, - 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "inline-name", - 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); -} - -// Any DIE with an DW_AT_inline attribute can be cited by -// DW_AT_abstract_origin attributes --- even if the value of the -// DW_AT_inline attribute is DW_INL_not_inlined. -TEST_F(SimpleCU, AbstractOriginNotInlined) { - PushLine(0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL, "line-file", 6111581); - - StartCU(); - AbstractInstanceDIE(&root_handler_, 0x93e9cdad52826b39ULL, - dwarf2reader::DW_INL_not_inlined, 0, "abstract-instance"); - DefineInlineInstanceDIE(&root_handler_, "", 0x93e9cdad52826b39ULL, - 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "abstract-instance", - 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL); -} - -TEST_F(SimpleCU, UnknownAbstractOrigin) { - EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, 1ULL)).WillOnce(Return()); - EXPECT_CALL(reporter_, UnnamedFunction(0x11c70f94c6e87ccdLL)) - .WillOnce(Return()); - PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); - - StartCU(); - AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, - dwarf2reader::DW_INL_inlined, 0, "inline-name"); - DefineInlineInstanceDIE(&root_handler_, "", 1ULL, - 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "<name omitted>", - 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); -} - -TEST_F(SimpleCU, UnnamedFunction) { - EXPECT_CALL(reporter_, UnnamedFunction(_)).WillOnce(Return()); - PushLine(0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL, "line-file", 14044850); - - StartCU(); - DefineFunction(&root_handler_, "", - 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "<name omitted>", - 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL); -} - -// An address range. -struct Range { - Module::Address start, end; -}; - -// Test data for pairing functions and lines. -struct Situation { - // Two function intervals, and two line intervals. - Range functions[2], lines[2]; - - // The number of lines we expect to be assigned to each of the - // functions, and the address ranges. - int paired_count[2]; - Range paired[2][2]; - - // The number of functions that are not entirely covered by lines, - // and vice versa. - int uncovered_functions, uncovered_lines; -}; - -#define PAIRING(func1_start, func1_end, func2_start, func2_end, \ - line1_start, line1_end, line2_start, line2_end, \ - func1_num_lines, func2_num_lines, \ - func1_line1_start, func1_line1_end, \ - func1_line2_start, func1_line2_end, \ - func2_line1_start, func2_line1_end, \ - func2_line2_start, func2_line2_end, \ - uncovered_functions, uncovered_lines) \ - { { { func1_start, func1_end }, { func2_start, func2_end } }, \ - { { line1_start, line1_end }, { line2_start, line2_end } }, \ - { func1_num_lines, func2_num_lines }, \ - { { { func1_line1_start, func1_line1_end }, \ - { func1_line2_start, func1_line2_end } }, \ - { { func2_line1_start, func2_line1_end }, \ - { func2_line2_start, func2_line2_end } } }, \ - uncovered_functions, uncovered_lines }, - -Situation situations[] = { -#include "common/testdata/func-line-pairing.h" -}; - -#undef PAIRING - -class FuncLinePairing: public CUFixtureBase, - public TestWithParam<Situation> { }; - -INSTANTIATE_TEST_CASE_P(AllSituations, FuncLinePairing, - ValuesIn(situations)); - -TEST_P(FuncLinePairing, Pairing) { - const Situation &s = GetParam(); - PushLine(s.lines[0].start, - s.lines[0].end - s.lines[0].start, - "line-file", 67636963); - PushLine(s.lines[1].start, - s.lines[1].end - s.lines[1].start, - "line-file", 67636963); - if (s.uncovered_functions) - EXPECT_CALL(reporter_, UncoveredFunction(_)) - .Times(s.uncovered_functions) - .WillRepeatedly(Return()); - if (s.uncovered_lines) - EXPECT_CALL(reporter_, UncoveredLine(_)) - .Times(s.uncovered_lines) - .WillRepeatedly(Return()); - - StartCU(); - DefineFunction(&root_handler_, "function1", - s.functions[0].start, - s.functions[0].end - s.functions[0].start); - DefineFunction(&root_handler_, "function2", - s.functions[1].start, - s.functions[1].end - s.functions[1].start); - root_handler_.Finish(); - - TestFunctionCount(2); - TestFunction(0, "function1", - s.functions[0].start, - s.functions[0].end - s.functions[0].start); - TestLineCount(0, s.paired_count[0]); - for (int i = 0; i < s.paired_count[0]; i++) - TestLine(0, i, s.paired[0][i].start, - s.paired[0][i].end - s.paired[0][i].start, - "line-file", 67636963); - TestFunction(1, "function2", - s.functions[1].start, - s.functions[1].end - s.functions[1].start); - TestLineCount(1, s.paired_count[1]); - for (int i = 0; i < s.paired_count[1]; i++) - TestLine(1, i, s.paired[1][i].start, - s.paired[1][i].end - s.paired[1][i].start, - "line-file", 67636963); -} - -TEST_F(FuncLinePairing, EmptyCU) { - - StartCU(); - root_handler_.Finish(); - - TestFunctionCount(0); -} - -TEST_F(FuncLinePairing, LinesNoFuncs) { - PushLine(40, 2, "line-file", 82485646); - EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); - - StartCU(); - root_handler_.Finish(); - - TestFunctionCount(0); -} - -TEST_F(FuncLinePairing, FuncsNoLines) { - EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); - - StartCU(); - DefineFunction(&root_handler_, "function1", 0x127da12ffcf5c51fULL, 0x1000U); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "function1", 0x127da12ffcf5c51fULL, 0x1000U); -} - -TEST_F(FuncLinePairing, GapThenFunction) { - PushLine(20, 2, "line-file-2", 174314698); - PushLine(10, 2, "line-file-1", 263008005); - - StartCU(); - DefineFunction(&root_handler_, "function1", 10, 2); - DefineFunction(&root_handler_, "function2", 20, 2); - root_handler_.Finish(); - - TestFunctionCount(2); - TestFunction(0, "function1", 10, 2); - TestLineCount(0, 1); - TestLine(0, 0, 10, 2, "line-file-1", 263008005); - TestFunction(1, "function2", 20, 2); - TestLineCount(1, 1); - TestLine(1, 0, 20, 2, "line-file-2", 174314698); -} - -// If GCC emits padding after one function to align the start of -// the next, then it will attribute the padding instructions to -// the last source line of function (to reduce the size of the -// line number info), but omit it from the DW_AT_{low,high}_pc -// range given in .debug_info (since it costs nothing to be -// precise there). If we did use at least some of the line -// we're about to skip, then assume this is what happened, and -// don't warn. -TEST_F(FuncLinePairing, GCCAlignmentStretch) { - PushLine(10, 10, "line-file", 63351048); - PushLine(20, 10, "line-file", 61661044); - - StartCU(); - DefineFunction(&root_handler_, "function1", 10, 5); - // five-byte gap between functions, covered by line 63351048. - // This should not elicit a warning. - DefineFunction(&root_handler_, "function2", 20, 10); - root_handler_.Finish(); - - TestFunctionCount(2); - TestFunction(0, "function1", 10, 5); - TestLineCount(0, 1); - TestLine(0, 0, 10, 5, "line-file", 63351048); - TestFunction(1, "function2", 20, 10); - TestLineCount(1, 1); - TestLine(1, 0, 20, 10, "line-file", 61661044); -} - -// Unfortunately, neither the DWARF parser's handler interface nor the -// DIEHandler interface is capable of expressing a function that abuts -// the end of the address space: the high_pc value looks like zero. - -TEST_F(FuncLinePairing, LineAtEndOfAddressSpace) { - PushLine(0xfffffffffffffff0ULL, 16, "line-file", 63351048); - EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); - - StartCU(); - DefineFunction(&root_handler_, "function1", 0xfffffffffffffff0ULL, 6); - DefineFunction(&root_handler_, "function2", 0xfffffffffffffffaULL, 5); - root_handler_.Finish(); - - TestFunctionCount(2); - TestFunction(0, "function1", 0xfffffffffffffff0ULL, 6); - TestLineCount(0, 1); - TestLine(0, 0, 0xfffffffffffffff0ULL, 6, "line-file", 63351048); - TestFunction(1, "function2", 0xfffffffffffffffaULL, 5); - TestLineCount(1, 1); - TestLine(1, 0, 0xfffffffffffffffaULL, 5, "line-file", 63351048); -} - -// A function with more than one uncovered area should only be warned -// about once. -TEST_F(FuncLinePairing, WarnOnceFunc) { - PushLine(20, 1, "line-file-2", 262951329); - PushLine(11, 1, "line-file-1", 219964021); - EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); - - StartCU(); - DefineFunction(&root_handler_, "function", 10, 11); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "function", 10, 11); - TestLineCount(0, 2); - TestLine(0, 0, 11, 1, "line-file-1", 219964021); - TestLine(0, 1, 20, 1, "line-file-2", 262951329); -} - -// A line with more than one uncovered area should only be warned -// about once. -TEST_F(FuncLinePairing, WarnOnceLine) { - PushLine(10, 20, "filename1", 118581871); - EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); - - StartCU(); - DefineFunction(&root_handler_, "function1", 11, 1); - DefineFunction(&root_handler_, "function2", 13, 1); - root_handler_.Finish(); - - TestFunctionCount(2); - TestFunction(0, "function1", 11, 1); - TestLineCount(0, 1); - TestLine(0, 0, 11, 1, "filename1", 118581871); - TestFunction(1, "function2", 13, 1); - TestLineCount(1, 1); - TestLine(1, 0, 13, 1, "filename1", 118581871); -} - -class CXXQualifiedNames: public CUFixtureBase, - public TestWithParam<DwarfTag> { }; - -INSTANTIATE_TEST_CASE_P(VersusEnclosures, CXXQualifiedNames, - Values(dwarf2reader::DW_TAG_class_type, - dwarf2reader::DW_TAG_structure_type, - dwarf2reader::DW_TAG_union_type, - dwarf2reader::DW_TAG_namespace)); - -TEST_P(CXXQualifiedNames, TwoFunctions) { - DwarfTag tag = GetParam(); - - SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); - PushLine(10, 1, "filename1", 69819327); - PushLine(20, 1, "filename2", 95115701); - - StartCU(); - DIEHandler *enclosure_handler = StartNamedDIE(&root_handler_, tag, - "Enclosure"); - EXPECT_TRUE(enclosure_handler != NULL); - DefineFunction(enclosure_handler, "func_B", 10, 1); - DefineFunction(enclosure_handler, "func_C", 20, 1); - enclosure_handler->Finish(); - delete enclosure_handler; - root_handler_.Finish(); - - TestFunctionCount(2); - TestFunction(0, "Enclosure::func_B", 10, 1); - TestFunction(1, "Enclosure::func_C", 20, 1); -} - -TEST_P(CXXQualifiedNames, FuncInEnclosureInNamespace) { - DwarfTag tag = GetParam(); - - SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); - PushLine(10, 1, "line-file", 69819327); - - StartCU(); - DIEHandler *namespace_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, - "Namespace"); - EXPECT_TRUE(namespace_handler != NULL); - DIEHandler *enclosure_handler = StartNamedDIE(namespace_handler, tag, - "Enclosure"); - EXPECT_TRUE(enclosure_handler != NULL); - DefineFunction(enclosure_handler, "function", 10, 1); - enclosure_handler->Finish(); - delete enclosure_handler; - namespace_handler->Finish(); - delete namespace_handler; - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "Namespace::Enclosure::function", 10, 1); -} - -TEST_F(CXXQualifiedNames, FunctionInClassInStructInNamespace) { - SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); - PushLine(10, 1, "filename1", 69819327); - - StartCU(); - DIEHandler *namespace_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, - "namespace_A"); - EXPECT_TRUE(namespace_handler != NULL); - DIEHandler *struct_handler - = StartNamedDIE(namespace_handler, dwarf2reader::DW_TAG_structure_type, - "struct_B"); - EXPECT_TRUE(struct_handler != NULL); - DIEHandler *class_handler - = StartNamedDIE(struct_handler, dwarf2reader::DW_TAG_class_type, - "class_C"); - DefineFunction(class_handler, "function_D", 10, 1); - class_handler->Finish(); - delete class_handler; - struct_handler->Finish(); - delete struct_handler; - namespace_handler->Finish(); - delete namespace_handler; - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "namespace_A::struct_B::class_C::function_D", 10, 1); -} - -struct LanguageAndQualifiedName { - dwarf2reader::DwarfLanguage language; - const char *name; -}; - -const LanguageAndQualifiedName LanguageAndQualifiedNameCases[] = { - { dwarf2reader::DW_LANG_none, "class_A::function_B" }, - { dwarf2reader::DW_LANG_C, "class_A::function_B" }, - { dwarf2reader::DW_LANG_C89, "class_A::function_B" }, - { dwarf2reader::DW_LANG_C99, "class_A::function_B" }, - { dwarf2reader::DW_LANG_C_plus_plus, "class_A::function_B" }, - { dwarf2reader::DW_LANG_Java, "class_A.function_B" }, - { dwarf2reader::DW_LANG_Cobol74, "class_A::function_B" }, - { dwarf2reader::DW_LANG_Mips_Assembler, NULL } -}; - -class QualifiedForLanguage: - public CUFixtureBase, - public TestWithParam<LanguageAndQualifiedName> { }; - -INSTANTIATE_TEST_CASE_P(LanguageAndQualifiedName, QualifiedForLanguage, - ValuesIn(LanguageAndQualifiedNameCases)); - -TEST_P(QualifiedForLanguage, MemberFunction) { - const LanguageAndQualifiedName ¶m = GetParam(); - - PushLine(10, 1, "line-file", 212966758); - SetLanguage(param.language); - - StartCU(); - DIEHandler *class_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - "class_A"); - DefineFunction(class_handler, "function_B", 10, 1); - class_handler->Finish(); - delete class_handler; - root_handler_.Finish(); - - if (param.name) { - TestFunctionCount(1); - TestFunction(0, param.name, 10, 1); - } else { - TestFunctionCount(0); - } -} - -TEST_P(QualifiedForLanguage, MemberFunctionSignedLanguage) { - const LanguageAndQualifiedName ¶m = GetParam(); - - PushLine(10, 1, "line-file", 212966758); - SetLanguage(param.language); - SetLanguageSigned(true); - - StartCU(); - DIEHandler *class_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - "class_A"); - DefineFunction(class_handler, "function_B", 10, 1); - class_handler->Finish(); - delete class_handler; - root_handler_.Finish(); - - if (param.name) { - TestFunctionCount(1); - TestFunction(0, param.name, 10, 1); - } else { - TestFunctionCount(0); - } -} - -class Specifications: public CUFixtureBase, public Test { }; - -TEST_F(Specifications, Function) { - PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); - - StartCU(); - DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, - dwarf2reader::DW_TAG_subprogram, "declaration-name"); - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0xcd3c51b946fb1eeeLL, "", - 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "declaration-name", - 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); -} - -TEST_F(Specifications, MemberFunction) { - PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691); - - StartCU(); - DIEHandler *class_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); - DeclarationDIE(class_handler, 0x7d83028c431406e8ULL, - dwarf2reader::DW_TAG_subprogram, "declaration-name"); - class_handler->Finish(); - delete class_handler; - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0x7d83028c431406e8ULL, "", - 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "class_A::declaration-name", - 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); -} - -// Specifications.MemberFunction, with DIEs in reverse order. -TEST_F(Specifications, MemberFunctionForwardReference) { - PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691); - - StartCU(); - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0x7d83028c431406e8ULL, "", - 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); - DIEHandler *class_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); - DeclarationDIE(class_handler, 0x7d83028c431406e8ULL, - dwarf2reader::DW_TAG_subprogram, "declaration-name"); - class_handler->Finish(); - delete class_handler; - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "class_A::declaration-name", - 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); -} - -// This case should gather the name from both the definition and the -// declaration's parent. -TEST_F(Specifications, FunctionDeclarationParent) { - PushLine(0x463c9ddf405be227ULL, 0x6a47774af5049680ULL, "line-file", 70254922); - - StartCU(); - { - DIEHandler *class_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - "class_A"); - ASSERT_TRUE(class_handler != NULL); - DeclarationDIE(class_handler, 0x0e0e877c8404544aULL, - dwarf2reader::DW_TAG_subprogram, "declaration-name"); - class_handler->Finish(); - delete class_handler; - } - - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0x0e0e877c8404544aULL, "definition-name", - 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); - - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "class_A::definition-name", - 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); -} - -// Named scopes should also gather enclosing name components from -// their declarations. -TEST_F(Specifications, NamedScopeDeclarationParent) { - PushLine(0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, "line-file", 77392604); - - StartCU(); - { - DIEHandler *space_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, - "space_A"); - ASSERT_TRUE(space_handler != NULL); - DeclarationDIE(space_handler, 0x419bb1d12f9a73a2ULL, - dwarf2reader::DW_TAG_class_type, "class-declaration-name"); - space_handler->Finish(); - delete space_handler; - } - - { - DIEHandler *class_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - 0x419bb1d12f9a73a2ULL, "class-definition-name"); - ASSERT_TRUE(class_handler != NULL); - DefineFunction(class_handler, "function", - 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); - class_handler->Finish(); - delete class_handler; - } - - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "space_A::class-definition-name::function", - 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); -} - -TEST_F(Specifications, UnnamedScopeDeclarationParent) { - PushLine(0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, "line-file", 77392604); - - StartCU(); - { - DIEHandler *space_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, - ""); - ASSERT_TRUE(space_handler != NULL); - DeclarationDIE(space_handler, 0x419bb1d12f9a73a2ULL, - dwarf2reader::DW_TAG_class_type, "class-declaration-name"); - space_handler->Finish(); - delete space_handler; - } - - { - DIEHandler *class_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - 0x419bb1d12f9a73a2ULL, "class-definition-name"); - ASSERT_TRUE(class_handler != NULL); - DefineFunction(class_handler, "function", - 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); - class_handler->Finish(); - delete class_handler; - } - - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "(anonymous namespace)::class-definition-name::function", - 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); -} - -// This test recreates bug 364. -TEST_F(Specifications, InlineFunction) { - PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); - - StartCU(); - DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, - dwarf2reader::DW_TAG_subprogram, "inline-name"); - AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, - dwarf2reader::DW_INL_inlined, 0xcd3c51b946fb1eeeLL, ""); - DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, - 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "inline-name", - 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); -} - -// Check name construction for a long chain containing each combination of: -// - struct, union, class, namespace -// - direct and definition -TEST_F(Specifications, LongChain) { - PushLine(0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL, "line-file", 21192926); - SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); - - StartCU(); - // The structure we're building here is: - // space_A full definition - // space_B declaration - // space_B definition - // struct_C full definition - // struct_D declaration - // struct_D definition - // union_E full definition - // union_F declaration - // union_F definition - // class_G full definition - // class_H declaration - // class_H definition - // func_I declaration - // func_I definition - // - // So: - // - space_A, struct_C, union_E, and class_G don't use specifications; - // - space_B, struct_D, union_F, and class_H do. - // - func_I uses a specification. - // - // The full name for func_I is thus: - // - // space_A::space_B::struct_C::struct_D::union_E::union_F:: - // class_G::class_H::func_I - { - DIEHandler *space_A_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, - "space_A"); - DeclarationDIE(space_A_handler, 0x2e111126496596e2ULL, - dwarf2reader::DW_TAG_namespace, "space_B"); - space_A_handler->Finish(); - delete space_A_handler; - } - - { - DIEHandler *space_B_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, - 0x2e111126496596e2ULL); - DIEHandler *struct_C_handler - = StartNamedDIE(space_B_handler, dwarf2reader::DW_TAG_structure_type, - "struct_C"); - DeclarationDIE(struct_C_handler, 0x20cd423bf2a25a4cULL, - dwarf2reader::DW_TAG_structure_type, "struct_D"); - struct_C_handler->Finish(); - delete struct_C_handler; - space_B_handler->Finish(); - delete space_B_handler; - } - - { - DIEHandler *struct_D_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_structure_type, - 0x20cd423bf2a25a4cULL); - DIEHandler *union_E_handler - = StartNamedDIE(struct_D_handler, dwarf2reader::DW_TAG_union_type, - "union_E"); - DeclarationDIE(union_E_handler, 0xe25c84805aa58c32ULL, - dwarf2reader::DW_TAG_union_type, "union_F"); - union_E_handler->Finish(); - delete union_E_handler; - struct_D_handler->Finish(); - delete struct_D_handler; - } - - { - DIEHandler *union_F_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_union_type, - 0xe25c84805aa58c32ULL); - DIEHandler *class_G_handler - = StartNamedDIE(union_F_handler, dwarf2reader::DW_TAG_class_type, - "class_G"); - DeclarationDIE(class_G_handler, 0xb70d960dcc173b6eULL, - dwarf2reader::DW_TAG_class_type, "class_H"); - class_G_handler->Finish(); - delete class_G_handler; - union_F_handler->Finish(); - delete union_F_handler; - } - - { - DIEHandler *class_H_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - 0xb70d960dcc173b6eULL); - DeclarationDIE(class_H_handler, 0x27ff829e3bf69f37ULL, - dwarf2reader::DW_TAG_subprogram, "func_I"); - class_H_handler->Finish(); - delete class_H_handler; - } - - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0x27ff829e3bf69f37ULL, "", - 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "space_A::space_B::struct_C::struct_D::union_E::union_F" - "::class_G::class_H::func_I", - 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); -} - -// Specifications.LongChain with DIEs in reverse order. -TEST_F(Specifications, LongChainForwardReference) { - PushLine(0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL, "line-file", 21192926); - SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); - - StartCU(); - - { - DIEHandler *class_H_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - 0xb70d960dcc173b6eULL); - DeclarationDIE(class_H_handler, 0x27ff829e3bf69f37ULL, - dwarf2reader::DW_TAG_subprogram, "func_I"); - class_H_handler->Finish(); - delete class_H_handler; - } - - { - DIEHandler *union_F_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_union_type, - 0xe25c84805aa58c32ULL); - DIEHandler *class_G_handler - = StartNamedDIE(union_F_handler, dwarf2reader::DW_TAG_class_type, - "class_G"); - DeclarationDIE(class_G_handler, 0xb70d960dcc173b6eULL, - dwarf2reader::DW_TAG_class_type, "class_H"); - class_G_handler->Finish(); - delete class_G_handler; - union_F_handler->Finish(); - delete union_F_handler; - } - - { - DIEHandler *struct_D_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_structure_type, - 0x20cd423bf2a25a4cULL); - DIEHandler *union_E_handler - = StartNamedDIE(struct_D_handler, dwarf2reader::DW_TAG_union_type, - "union_E"); - DeclarationDIE(union_E_handler, 0xe25c84805aa58c32ULL, - dwarf2reader::DW_TAG_union_type, "union_F"); - union_E_handler->Finish(); - delete union_E_handler; - struct_D_handler->Finish(); - delete struct_D_handler; - } - - { - DIEHandler *space_B_handler - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, - 0x2e111126496596e2ULL); - DIEHandler *struct_C_handler - = StartNamedDIE(space_B_handler, dwarf2reader::DW_TAG_structure_type, - "struct_C"); - DeclarationDIE(struct_C_handler, 0x20cd423bf2a25a4cULL, - dwarf2reader::DW_TAG_structure_type, "struct_D"); - struct_C_handler->Finish(); - delete struct_C_handler; - space_B_handler->Finish(); - delete space_B_handler; - } - - { - DIEHandler *space_A_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, - "space_A"); - DeclarationDIE(space_A_handler, 0x2e111126496596e2ULL, - dwarf2reader::DW_TAG_namespace, "space_B"); - space_A_handler->Finish(); - delete space_A_handler; - } - - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0x27ff829e3bf69f37ULL, "", - 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "space_A::space_B::struct_C::struct_D::union_E::union_F" - "::class_G::class_H::func_I", - 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); -} - -TEST_F(Specifications, InterCU) { - Module m("module-name", "module-os", "module-arch", "module-id"); - DwarfCUToModule::FileContext fc("dwarf-filename", &m); - EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); - MockLineToModuleFunctor lr; - EXPECT_CALL(lr, mock_apply(_,_,_,_)).Times(0); - - // Kludge: satisfy reporter_'s expectation. - reporter_.SetCUName("compilation-unit-name"); - - // First CU. Declares class_A. - { - DwarfCUToModule root1_handler(&fc, &lr, &reporter_); - ASSERT_TRUE(root1_handler.StartCompilationUnit(0, 1, 2, 3, 3)); - ASSERT_TRUE(root1_handler.StartRootDIE(1, - dwarf2reader::DW_TAG_compile_unit)); - ProcessStrangeAttributes(&root1_handler); - ASSERT_TRUE(root1_handler.EndAttributes()); - DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL, - dwarf2reader::DW_TAG_class_type, "class_A"); - root1_handler.Finish(); - } - - // Second CU. Defines class_A, declares member_func_B. - { - DwarfCUToModule root2_handler(&fc, &lr, &reporter_); - ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3)); - ASSERT_TRUE(root2_handler.StartRootDIE(1, - dwarf2reader::DW_TAG_compile_unit)); - ASSERT_TRUE(root2_handler.EndAttributes()); - DIEHandler *class_A_handler - = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type, - 0xb8fbfdd5f0b26fceULL); - DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, - dwarf2reader::DW_TAG_subprogram, "member_func_B"); - class_A_handler->Finish(); - delete class_A_handler; - root2_handler.Finish(); - } - - // Third CU. Defines member_func_B. - { - DwarfCUToModule root3_handler(&fc, &lr, &reporter_); - ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3)); - ASSERT_TRUE(root3_handler.StartRootDIE(1, - dwarf2reader::DW_TAG_compile_unit)); - ASSERT_TRUE(root3_handler.EndAttributes()); - DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram, - 0xb01fef8b380bd1a2ULL, "", - 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL); - root3_handler.Finish(); - } - - vector<Module::Function *> functions; - m.GetFunctions(&functions, functions.end()); - EXPECT_EQ(1U, functions.size()); - EXPECT_STREQ("class_A::member_func_B", functions[0]->name.c_str()); -} - -TEST_F(Specifications, BadOffset) { - PushLine(0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL, "line-file", 56636272); - EXPECT_CALL(reporter_, UnknownSpecification(_, 0x2be953efa6f9a996ULL)) - .WillOnce(Return()); - - StartCU(); - DeclarationDIE(&root_handler_, 0xefd7f7752c27b7e4ULL, - dwarf2reader::DW_TAG_subprogram, ""); - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0x2be953efa6f9a996ULL, "function", - 0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL); - root_handler_.Finish(); -} - -TEST_F(Specifications, FunctionDefinitionHasOwnName) { - PushLine(0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL, "line-file", 56792403); - - StartCU(); - DeclarationDIE(&root_handler_, 0xc34ff4786cae78bdULL, - dwarf2reader::DW_TAG_subprogram, "declaration-name"); - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0xc34ff4786cae78bdULL, "definition-name", - 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "definition-name", - 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); -} - -TEST_F(Specifications, ClassDefinitionHasOwnName) { - PushLine(0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL, "line-file", 57119241); - - StartCU(); - DeclarationDIE(&root_handler_, 0xd0fe467ec2f1a58cULL, - dwarf2reader::DW_TAG_class_type, "class-declaration-name"); - - dwarf2reader::DIEHandler *class_definition - = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - 0xd0fe467ec2f1a58cULL, "class-definition-name"); - ASSERT_TRUE(class_definition); - DeclarationDIE(class_definition, 0x6d028229c15623dbULL, - dwarf2reader::DW_TAG_subprogram, - "function-declaration-name"); - class_definition->Finish(); - delete class_definition; - - DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0x6d028229c15623dbULL, "function-definition-name", - 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); - - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "class-definition-name::function-definition-name", - 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); -} - -// DIEs that cite a specification should prefer the specification's -// parents over their own when choosing qualified names. In this test, -// we take the name from our definition but the enclosing scope name -// from our declaration. I don't see why they'd ever be different, but -// we want to verify what DwarfCUToModule is looking at. -TEST_F(Specifications, PreferSpecificationParents) { - PushLine(0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL, "line-file", 79488694); - - StartCU(); - { - dwarf2reader::DIEHandler *declaration_class_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "declaration-class"); - DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL, - dwarf2reader::DW_TAG_subprogram, "function-declaration"); - declaration_class_handler->Finish(); - delete declaration_class_handler; - } - { - dwarf2reader::DIEHandler *definition_class_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, - "definition-class"); - DefinitionDIE(definition_class_handler, dwarf2reader::DW_TAG_subprogram, - 0x9ddb35517455ef7aULL, "function-definition", - 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); - definition_class_handler->Finish(); - delete definition_class_handler; - } - root_handler_.Finish(); - - TestFunctionCount(1); - TestFunction(0, "declaration-class::function-definition", - 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); -} - -class CUErrors: public CUFixtureBase, public Test { }; - -TEST_F(CUErrors, BadStmtList) { - EXPECT_CALL(reporter_, BadLineInfoOffset(dummy_line_size_ + 10)).Times(1); - - ASSERT_TRUE(root_handler_ - .StartCompilationUnit(0xc591d5b037543d7cULL, 0x11, 0xcd, - 0x2d7d19546cf6590cULL, 3)); - ASSERT_TRUE(root_handler_.StartRootDIE(0xae789dc102cfca54ULL, - dwarf2reader::DW_TAG_compile_unit)); - root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, - dwarf2reader::DW_FORM_strp, - "compilation-unit-name"); - root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, - dwarf2reader::DW_FORM_ref4, - dummy_line_size_ + 10); - root_handler_.EndAttributes(); - root_handler_.Finish(); -} - -TEST_F(CUErrors, NoLineSection) { - EXPECT_CALL(reporter_, MissingSection(".debug_line")).Times(1); - PushLine(0x88507fb678052611ULL, 0x42c8e9de6bbaa0faULL, "line-file", 64472290); - // Delete the entry for .debug_line added by the fixture class's constructor. - file_context_.section_map.clear(); - - StartCU(); - root_handler_.Finish(); -} - -TEST_F(CUErrors, BadDwarfVersion1) { - // Kludge: satisfy reporter_'s expectation. - reporter_.SetCUName("compilation-unit-name"); - - ASSERT_FALSE(root_handler_ - .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, - 0xc9de224ccb99ac3eULL, 1)); -} - -TEST_F(CUErrors, GoodDwarfVersion2) { - // Kludge: satisfy reporter_'s expectation. - reporter_.SetCUName("compilation-unit-name"); - - ASSERT_TRUE(root_handler_ - .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, - 0xc9de224ccb99ac3eULL, 2)); -} - -TEST_F(CUErrors, GoodDwarfVersion3) { - // Kludge: satisfy reporter_'s expectation. - reporter_.SetCUName("compilation-unit-name"); - - ASSERT_TRUE(root_handler_ - .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, - 0xc9de224ccb99ac3eULL, 3)); -} - -TEST_F(CUErrors, BadCURootDIETag) { - // Kludge: satisfy reporter_'s expectation. - reporter_.SetCUName("compilation-unit-name"); - - ASSERT_TRUE(root_handler_ - .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, - 0xc9de224ccb99ac3eULL, 3)); - - ASSERT_FALSE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, - dwarf2reader::DW_TAG_subprogram)); -} - -// Tests for DwarfCUToModule::Reporter. These just produce (or fail to -// produce) output, so their results need to be checked by hand. -struct Reporter: public Test { - Reporter() - : reporter("filename", 0x123456789abcdef0ULL) { - reporter.SetCUName("compilation-unit-name"); - - function.name = "function name"; - function.address = 0x19c45c30770c1eb0ULL; - function.size = 0x89808a5bdfa0a6a3ULL; - function.parameter_size = 0x6a329f18683dcd51ULL; - - file.name = "source file name"; - - line.address = 0x3606ac6267aebeccULL; - line.size = 0x5de482229f32556aULL; - line.file = &file; - line.number = 93400201; - } - - DwarfCUToModule::WarningReporter reporter; - Module::Function function; - Module::File file; - Module::Line line; -}; - -TEST_F(Reporter, UnknownSpecification) { - reporter.UnknownSpecification(0x123456789abcdef1ULL, 0x323456789abcdef2ULL); -} - -TEST_F(Reporter, UnknownAbstractOrigin) { - reporter.UnknownAbstractOrigin(0x123456789abcdef1ULL, 0x323456789abcdef2ULL); -} - -TEST_F(Reporter, MissingSection) { - reporter.MissingSection("section name"); -} - -TEST_F(Reporter, BadLineInfoOffset) { - reporter.BadLineInfoOffset(0x123456789abcdef1ULL); -} - -TEST_F(Reporter, UncoveredFunctionDisabled) { - reporter.UncoveredFunction(function); - EXPECT_FALSE(reporter.uncovered_warnings_enabled()); -} - -TEST_F(Reporter, UncoveredFunctionEnabled) { - reporter.set_uncovered_warnings_enabled(true); - reporter.UncoveredFunction(function); - EXPECT_TRUE(reporter.uncovered_warnings_enabled()); -} - -TEST_F(Reporter, UncoveredLineDisabled) { - reporter.UncoveredLine(line); - EXPECT_FALSE(reporter.uncovered_warnings_enabled()); -} - -TEST_F(Reporter, UncoveredLineEnabled) { - reporter.set_uncovered_warnings_enabled(true); - reporter.UncoveredLine(line); - EXPECT_TRUE(reporter.uncovered_warnings_enabled()); -} - -TEST_F(Reporter, UnnamedFunction) { - reporter.UnnamedFunction(0x90c0baff9dedb2d9ULL); -} - -// Would be nice to also test: -// - overlapping lines, functions diff --git a/breakpad/pending/src/common/module.cc b/breakpad/pending/src/common/module.cc deleted file mode 100644 index cb4847c..0000000 --- a/breakpad/pending/src/common/module.cc +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright (c) 2011 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. - -// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - -// module.cc: Implement google_breakpad::Module. See module.h. - -#include "common/module.h" - -#include <assert.h> -#include <errno.h> -#include <stdio.h> -#include <string.h> - -#include <iostream> -#include <utility> - -namespace google_breakpad { - -using std::dec; -using std::endl; -using std::hex; - - -Module::Module(const string &name, const string &os, - const string &architecture, const string &id) : - name_(name), - os_(os), - architecture_(architecture), - id_(id), - load_address_(0) { } - -Module::~Module() { - for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) - delete it->second; - for (FunctionSet::iterator it = functions_.begin(); - it != functions_.end(); ++it) { - delete *it; - } - for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin(); - it != stack_frame_entries_.end(); ++it) { - delete *it; - } - for (ExternSet::iterator it = externs_.begin(); it != externs_.end(); ++it) - delete *it; -} - -void Module::SetLoadAddress(Address address) { - load_address_ = address; -} - -void Module::AddFunction(Function *function) { - std::pair<FunctionSet::iterator,bool> ret = functions_.insert(function); - if (!ret.second) { - // Free the duplicate that was not inserted because this Module - // now owns it. - delete function; - } -} - -void Module::AddFunctions(vector<Function *>::iterator begin, - vector<Function *>::iterator end) { - for (vector<Function *>::iterator it = begin; it != end; ++it) - AddFunction(*it); -} - -void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) { - stack_frame_entries_.push_back(stack_frame_entry); -} - -void Module::AddExtern(Extern *ext) { - std::pair<ExternSet::iterator,bool> ret = externs_.insert(ext); - if (!ret.second) { - // Free the duplicate that was not inserted because this Module - // now owns it. - delete ext; - } -} - -void Module::GetFunctions(vector<Function *> *vec, - vector<Function *>::iterator i) { - vec->insert(i, functions_.begin(), functions_.end()); -} - -void Module::GetExterns(vector<Extern *> *vec, - vector<Extern *>::iterator i) { - vec->insert(i, externs_.begin(), externs_.end()); -} - -Module::File *Module::FindFile(const string &name) { - // A tricky bit here. The key of each map entry needs to be a - // pointer to the entry's File's name string. This means that we - // can't do the initial lookup with any operation that would create - // an empty entry for us if the name isn't found (like, say, - // operator[] or insert do), because such a created entry's key will - // be a pointer the string passed as our argument. Since the key of - // a map's value type is const, we can't fix it up once we've - // created our file. lower_bound does the lookup without doing an - // insertion, and returns a good hint iterator to pass to insert. - // Our "destiny" is where we belong, whether we're there or not now. - FileByNameMap::iterator destiny = files_.lower_bound(&name); - if (destiny == files_.end() - || *destiny->first != name) { // Repeated string comparison, boo hoo. - File *file = new File; - file->name = name; - file->source_id = -1; - destiny = files_.insert(destiny, - FileByNameMap::value_type(&file->name, file)); - } - return destiny->second; -} - -Module::File *Module::FindFile(const char *name) { - string name_string = name; - return FindFile(name_string); -} - -Module::File *Module::FindExistingFile(const string &name) { - FileByNameMap::iterator it = files_.find(&name); - return (it == files_.end()) ? NULL : it->second; -} - -void Module::GetFiles(vector<File *> *vec) { - vec->clear(); - for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) - vec->push_back(it->second); -} - -void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) { - *vec = stack_frame_entries_; -} - -void Module::AssignSourceIds() { - // First, give every source file an id of -1. - for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); ++file_it) { - file_it->second->source_id = -1; - } - - // Next, mark all files actually cited by our functions' line number - // info, by setting each one's source id to zero. - for (FunctionSet::const_iterator func_it = functions_.begin(); - func_it != functions_.end(); ++func_it) { - Function *func = *func_it; - for (vector<Line>::iterator line_it = func->lines.begin(); - line_it != func->lines.end(); ++line_it) - line_it->file->source_id = 0; - } - - // Finally, assign source ids to those files that have been marked. - // We could have just assigned source id numbers while traversing - // the line numbers, but doing it this way numbers the files in - // lexicographical order by name, which is neat. - int next_source_id = 0; - for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); ++file_it) { - if (!file_it->second->source_id) - file_it->second->source_id = next_source_id++; - } -} - -bool Module::ReportError() { - fprintf(stderr, "error writing symbol file: %s\n", - strerror(errno)); - return false; -} - -bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) { - for (RuleMap::const_iterator it = rule_map.begin(); - it != rule_map.end(); ++it) { - if (it != rule_map.begin()) - stream << ' '; - stream << it->first << ": " << it->second; - } - return stream.good(); -} - -bool Module::Write(std::ostream &stream, SymbolData symbol_data) { - stream << "MODULE " << os_ << " " << architecture_ << " " - << id_ << " " << name_ << endl; - if (!stream.good()) - return ReportError(); - - if (symbol_data != ONLY_CFI) { - AssignSourceIds(); - - // Write out files. - for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); ++file_it) { - File *file = file_it->second; - if (file->source_id >= 0) { - stream << "FILE " << file->source_id << " " << file->name << endl; - if (!stream.good()) - return ReportError(); - } - } - - // Write out functions and their lines. - for (FunctionSet::const_iterator func_it = functions_.begin(); - func_it != functions_.end(); ++func_it) { - Function *func = *func_it; - assert(!func->name.empty()); - stream << "FUNC " << hex - << (func->address - load_address_) << " " - << func->size << " " - << func->parameter_size << " " - << func->name << dec << endl; - - if (!stream.good()) - return ReportError(); - for (vector<Line>::iterator line_it = func->lines.begin(); - line_it != func->lines.end(); ++line_it) { - stream << hex - << (line_it->address - load_address_) << " " - << line_it->size << " " - << dec - << line_it->number << " " - << line_it->file->source_id << endl; - if (!stream.good()) - return ReportError(); - } - } - - // Write out 'PUBLIC' records. - for (ExternSet::const_iterator extern_it = externs_.begin(); - extern_it != externs_.end(); ++extern_it) { - Extern *ext = *extern_it; - stream << "PUBLIC " << hex - << (ext->address - load_address_) << " 0 " - << ext->name << dec << endl; - if (!stream.good()) - return ReportError(); - } - } - - if (symbol_data != NO_CFI) { - // Write out 'STACK CFI INIT' and 'STACK CFI' records. - vector<StackFrameEntry *>::const_iterator frame_it; - for (frame_it = stack_frame_entries_.begin(); - frame_it != stack_frame_entries_.end(); ++frame_it) { - StackFrameEntry *entry = *frame_it; - stream << "STACK CFI INIT " << hex - << (entry->address - load_address_) << " " - << entry->size << " " << dec; - if (!stream.good() - || !WriteRuleMap(entry->initial_rules, stream)) - return ReportError(); - - stream << endl; - - // Write out this entry's delta rules as 'STACK CFI' records. - for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); - delta_it != entry->rule_changes.end(); ++delta_it) { - stream << "STACK CFI " << hex - << (delta_it->first - load_address_) << " " << dec; - if (!stream.good() - || !WriteRuleMap(delta_it->second, stream)) - return ReportError(); - - stream << endl; - } - } - } - - return true; -} - -} // namespace google_breakpad |