summaryrefslogtreecommitdiffstats
path: root/breakpad
diff options
context:
space:
mode:
Diffstat (limited to 'breakpad')
-rw-r--r--breakpad/breakpad.gyp8
-rw-r--r--breakpad/pending/src/common/dwarf_cu_to_module.cc1027
-rw-r--r--breakpad/pending/src/common/dwarf_cu_to_module.h276
-rw-r--r--breakpad/pending/src/common/dwarf_cu_to_module_unittest.cc1865
-rw-r--r--breakpad/pending/src/common/module.cc286
5 files changed, 3460 insertions, 2 deletions
diff --git a/breakpad/breakpad.gyp b/breakpad/breakpad.gyp
index a8c30a5..6bebbe5 100644
--- a/breakpad/breakpad.gyp
+++ b/breakpad/breakpad.gyp
@@ -104,6 +104,10 @@
{
'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',
],
@@ -112,10 +116,10 @@
'src/common/dwarf/dwarf2reader.cc',
'src/common/dwarf/bytereader.cc',
'src/common/dwarf_cfi_to_module.cc',
- 'src/common/dwarf_cu_to_module.cc',
+ 'pending/src/common/dwarf_cu_to_module.cc',
'src/common/dwarf_line_to_module.cc',
'src/common/language.cc',
- 'src/common/module.cc',
+ 'pending/src/common/module.cc',
'src/common/mac/dump_syms.mm',
'src/common/mac/file_id.cc',
'src/common/mac/macho_id.cc',
diff --git a/breakpad/pending/src/common/dwarf_cu_to_module.cc b/breakpad/pending/src/common/dwarf_cu_to_module.cc
new file mode 100644
index 0000000..9acdd1f
--- /dev/null
+++ b/breakpad/pending/src/common/dwarf_cu_to_module.cc
@@ -0,0 +1,1027 @@
+// 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,
+ const AttributeList &attrs);
+
+ 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,
+ const AttributeList &attrs) {
+ 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,
+ LineToModuleFunctor *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,
+ const AttributeList &attrs) {
+ 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 &section_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_)(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,
+ const AttributeList& attrs) {
+ // 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
new file mode 100644
index 0000000..3a997ad
--- /dev/null
+++ b/breakpad/pending/src/common/dwarf_cu_to_module.h
@@ -0,0 +1,276 @@
+// -*- 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::AttributeList;
+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 functors 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 LineToModuleFunctor {
+ public:
+ LineToModuleFunctor() { }
+ virtual ~LineToModuleFunctor() { }
+
+ // 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 operator()(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 &section_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,
+ LineToModuleFunctor *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,
+ const AttributeList &attrs);
+
+ // 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,
+ const AttributeList& attrs);
+
+ 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.
+ LineToModuleFunctor *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
new file mode 100644
index 0000000..83a28e0
--- /dev/null
+++ b/breakpad/pending/src/common/dwarf_cu_to_module_unittest.cc
@@ -0,0 +1,1865 @@
+// 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::AttributeList;
+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 &section_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();
+
+ // Add some strange attributes/form pairs to the end of ATTRS.
+ void PushBackStrangeAttributes(dwarf2reader::AttributeList *attrs);
+
+ // Have HANDLER process some strange attribute/form/value triples.
+ // These will match those promised by PushBackStrangeAttributes.
+ 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));
+ {
+ dwarf2reader::AttributeList attrs;
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ if (!lines_.empty())
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_stmt_list,
+ dwarf2reader::DW_FORM_ref4));
+ if (language_ != dwarf2reader::DW_LANG_none)
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_language,
+ language_signed_
+ ? dwarf2reader::DW_FORM_sdata
+ : dwarf2reader::DW_FORM_udata));
+ ASSERT_TRUE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL,
+ dwarf2reader::DW_TAG_compile_unit,
+ attrs));
+ }
+ 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::PushBackStrangeAttributes(
+ dwarf2reader::AttributeList *attrs) {
+ attrs->push_back(make_pair((DwarfAttribute) 0xf560dead,
+ (DwarfForm) 0x4106e4db));
+ attrs->push_back(make_pair((DwarfAttribute) 0x85380095,
+ (DwarfForm) 0x0f16fe87));
+ attrs->push_back(make_pair((DwarfAttribute) 0xf7f7480f,
+ (DwarfForm) 0x829e038a));
+ attrs->push_back(make_pair((DwarfAttribute) 0xa55ffb51,
+ (DwarfForm) 0x2f43b041));
+ attrs->push_back(make_pair((DwarfAttribute) 0x2fde304a,
+ (DwarfForm) 0x895ffa23));
+}
+
+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::AttributeList attrs;
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ PushBackStrangeAttributes(&attrs);
+ dwarf2reader::DIEHandler *handler
+ = parent->FindChildHandler(incrementing_offset_++, tag, attrs);
+ 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::AttributeList attrs;
+ if (name)
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_specification,
+ dwarf2reader::DW_FORM_ref4));
+ dwarf2reader::DIEHandler *handler
+ = parent->FindChildHandler(incrementing_offset_++, tag, attrs);
+ 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::AttributeList func_attrs;
+ func_attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ func_attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc,
+ dwarf2reader::DW_FORM_addr));
+ func_attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc,
+ dwarf2reader::DW_FORM_addr));
+ PushBackStrangeAttributes(&func_attrs);
+ dwarf2reader::DIEHandler *func
+ = parent->FindChildHandler(incrementing_offset_++,
+ dwarf2reader::DW_TAG_subprogram,
+ func_attrs);
+ 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::AttributeList attrs;
+ if (!name.empty())
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_declaration,
+ dwarf2reader::DW_FORM_flag));
+ dwarf2reader::DIEHandler *die = parent->FindChildHandler(offset, tag, attrs);
+ 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::AttributeList attrs;
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_specification,
+ dwarf2reader::DW_FORM_ref4));
+ if (!name.empty())
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ if (size) {
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc,
+ dwarf2reader::DW_FORM_addr));
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc,
+ dwarf2reader::DW_FORM_addr));
+ }
+ dwarf2reader::DIEHandler *die
+ = parent->FindChildHandler(0x6ccfea031a9e6cc9ULL, tag, attrs);
+ 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::AttributeList attrs;
+ if (specification != 0ULL)
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_specification,
+ dwarf2reader::DW_FORM_ref4));
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_inline, form));
+ if (!name.empty())
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ dwarf2reader::DIEHandler *die
+ = parent->FindChildHandler(offset, dwarf2reader::DW_TAG_subprogram, attrs);
+ 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::AttributeList func_attrs;
+ if (!name.empty())
+ func_attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ func_attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc,
+ dwarf2reader::DW_FORM_addr));
+ func_attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc,
+ dwarf2reader::DW_FORM_addr));
+ func_attrs.push_back(make_pair(dwarf2reader::DW_AT_abstract_origin,
+ dwarf2reader::DW_FORM_ref4));
+ PushBackStrangeAttributes(&func_attrs);
+ dwarf2reader::DIEHandler *func
+ = parent->FindChildHandler(0x11c70f94c6e87ccdLL,
+ dwarf2reader::DW_TAG_subprogram,
+ func_attrs);
+ 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();
+ dwarf2reader::AttributeList no_attrs;
+ EXPECT_FALSE(root_handler_
+ .FindChildHandler(0x7db32bff4e2dcfb1ULL,
+ dwarf2reader::DW_TAG_lexical_block, no_attrs));
+}
+
+TEST_F(SimpleCU, IrrelevantNamedScopeChildren) {
+ StartCU();
+ dwarf2reader::AttributeList no_attrs;
+ 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,
+ no_attrs));
+ 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 &param = 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 &param = 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);
+ dwarf2reader::AttributeList no_attrs;
+
+ // 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));
+ dwarf2reader::AttributeList attrs;
+ PushBackStrangeAttributes(&attrs);
+ ASSERT_TRUE(root1_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit,
+ attrs));
+ 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,
+ no_attrs));
+ 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,
+ no_attrs));
+ 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));
+ dwarf2reader::AttributeList attrs;
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp));
+ attrs.push_back(make_pair(dwarf2reader::DW_AT_stmt_list,
+ dwarf2reader::DW_FORM_ref4));
+ ASSERT_TRUE(root_handler_.StartRootDIE(0xae789dc102cfca54ULL,
+ dwarf2reader::DW_TAG_compile_unit,
+ attrs));
+ 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));
+
+ dwarf2reader::AttributeList no_attrs;
+ ASSERT_FALSE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL,
+ dwarf2reader::DW_TAG_subprogram,
+ no_attrs));
+}
+
+// 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
new file mode 100644
index 0000000..6b83021
--- /dev/null
+++ b/breakpad/pending/src/common/module.cc
@@ -0,0 +1,286 @@
+// 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>
+
+// 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>
+
+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) {
+ stream << "MODULE " << os_ << " " << architecture_ << " "
+ << id_ << " " << name_ << endl;
+ if (!stream.good())
+ return ReportError();
+
+ 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();
+ }
+
+ // 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