From dd5067f391baee2561404f8e2915429b3d638ff7 Mon Sep 17 00:00:00 2001 From: jimblandy Date: Wed, 10 Feb 2010 17:55:24 +0000 Subject: [PATCH] Linux DWARF reader: Follow DW_AT_abstract_origin links to find function names. Without this patch, debugging information like the following will produce FUNC records with no names, because the dumper (correctly) ignores the DW_TAG_subprogram DIEs that lack DW_AT_low_pc/DW_AT_high_pc attributes, but won't follow the DW_AT_abstract_origin link from the DIE that does have code addresses to find its name. <1><168>: Abbrev Number: 5 (DW_TAG_class_type) <169> DW_AT_name : Foo <2><183>: Abbrev Number: 7 (DW_TAG_subprogram) <185> DW_AT_name : Foo <18b> DW_AT_declaration : 1 <1><1b7>: Abbrev Number: 12 (DW_TAG_subprogram) <1b8> DW_AT_specification: <0x183> <1bc> DW_AT_inline : 2 (declared as inline but ignored) <1><1dc>: Abbrev Number: 16 (DW_TAG_subprogram) <1dd> DW_AT_abstract_origin: <0x1b7> <1e1> DW_AT_low_pc : 0x8048578 <1e5> DW_AT_high_pc : 0x8048588 a=dmuir, r=jimblandy git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@520 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/common/dwarf/dwarf2enums.h | 8 + src/common/linux/dwarf_cu_to_module.cc | 92 +++++++++- src/common/linux/dwarf_cu_to_module.h | 6 +- .../linux/dwarf_cu_to_module_unittest.cc | 161 ++++++++++++++++++ src/tools/linux/dump_syms/Makefile | 3 + 5 files changed, 267 insertions(+), 3 deletions(-) diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h index a7c1a8d9..9e6c0278 100644 --- a/src/common/dwarf/dwarf2enums.h +++ b/src/common/dwarf/dwarf2enums.h @@ -522,5 +522,13 @@ enum DwarfLanguage DW_LANG_Upc =0x8765 // Unified Parallel C }; +// Inline codes. These are values for DW_AT_inline. +enum DwarfInline { + DW_INL_not_inlined =0x0, + DW_INL_inlined =0x1, + DW_INL_declared_not_inlined =0x2, + DW_INL_declared_inlined =0x3, +}; + } // namespace dwarf2reader #endif // COMMON_DWARF_DWARF2ENUMS_H__ diff --git a/src/common/linux/dwarf_cu_to_module.cc b/src/common/linux/dwarf_cu_to_module.cc index 3c339870..88cea940 100644 --- a/src/common/linux/dwarf_cu_to_module.cc +++ b/src/common/linux/dwarf_cu_to_module.cc @@ -68,6 +68,16 @@ struct DwarfCUToModule::Specification { string unqualified_name; }; +// An abstract origin -- base definition of an inline function. +struct AbstractOrigin { + AbstractOrigin() : name() {} + AbstractOrigin(const string& name) : name(name) {} + + string name; +}; + +typedef map AbstractOriginByOffset; + // Data global to the DWARF-bearing file that is private to the // DWARF-to-Module process. struct DwarfCUToModule::FilePrivate { @@ -75,6 +85,8 @@ struct DwarfCUToModule::FilePrivate { // Specifications describing those DIEs. Specification references can // cross compilation unit boundaries. SpecificationByOffset specifications; + + AbstractOriginByOffset origins; }; DwarfCUToModule::FileContext::FileContext(const string &filename_arg, @@ -230,7 +242,7 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( // don't think any producers we care about ever emit such // things. cu_context_->reporter->UnknownSpecification(offset_, data); - } + } break; } default: break; @@ -287,10 +299,17 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { FuncHandler(CUContext *cu_context, DIEContext *parent_context, uint64 offset) : GenericDIEHandler(cu_context, parent_context, offset), - low_pc_(0), high_pc_(0) { } + low_pc_(0), high_pc_(0), abstract_origin_(NULL), 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(); @@ -299,6 +318,8 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { // specification_, parent_context_. Computed in EndAttributes. string name_; uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc + const AbstractOrigin* abstract_origin_; + bool inline_; }; void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( @@ -306,6 +327,16 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( enum DwarfForm form, uint64 data) { switch (attr) { + case dwarf2reader::DW_AT_inline: + switch(data) { + case dwarf2reader::DW_INL_inlined: + case dwarf2reader::DW_INL_declared_not_inlined: + case dwarf2reader::DW_INL_declared_inlined: + inline_ = true; break; + default: + break; + } + break; case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break; case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break; default: @@ -314,9 +345,54 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( } } +void DwarfCUToModule::FuncHandler::ProcessAttributeSigned( + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + switch (attr) { + case dwarf2reader::DW_AT_inline: + switch(data) { + case dwarf2reader::DW_INL_inlined: + case dwarf2reader::DW_INL_declared_not_inlined: + case dwarf2reader::DW_INL_declared_inlined: + inline_ = true; break; + default: + break; + } + break; + default: + break; + } +} + +void DwarfCUToModule::FuncHandler::ProcessAttributeReference( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch(attr) { + case dwarf2reader::DW_AT_abstract_origin: { + const AbstractOriginByOffset& origins = + cu_context_->file_context->file_private->origins; + AbstractOriginByOffset::const_iterator origin = origins.find(data); + if (origin != origins.end()) { + abstract_origin_ = &(origin->second); + } else { + cu_context_->reporter->UnknownAbstractOrigin(offset_, data); + } + break; + } + default: + GenericDIEHandler::ProcessAttributeReference(attr, form, data); + break; + } +} + bool DwarfCUToModule::FuncHandler::EndAttributes() { // Compute our name, and record a specification, if appropriate. name_ = ComputeQualifiedName(); + if (name_.empty() && abstract_origin_) { + name_ = abstract_origin_->name; + } return true; } @@ -334,6 +410,9 @@ void DwarfCUToModule::FuncHandler::Finish() { func->size = high_pc_ - low_pc_; func->parameter_size = 0; cu_context_->functions.push_back(func); + } else if (inline_) { + AbstractOrigin origin(name_); + cu_context_->file_context->file_private->origins[offset_] = origin; } } @@ -391,6 +470,15 @@ void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64 offset, 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", + filename_.c_str(), offset, target); +} + void DwarfCUToModule::WarningReporter::MissingSection(const string &name) { CUHeading(); fprintf(stderr, "%s: warning: couldn't find DWARF '%s' section\n", diff --git a/src/common/linux/dwarf_cu_to_module.h b/src/common/linux/dwarf_cu_to_module.h index 9b33af9a..af330cff 100644 --- a/src/common/linux/dwarf_cu_to_module.h +++ b/src/common/linux/dwarf_cu_to_module.h @@ -130,7 +130,11 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler { // haven't processed yet, or that wasn't marked as a declaration, // at TARGET. virtual void UnknownSpecification(uint64 offset, uint64 target); - + + // A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we + // haven't processed yet, or that wasn't marked as inline, at TARGET. + virtual void UnknownAbstractOrigin(uint64 offset, uint64 target); + // We were unable to find the DWARF section named SECTION_NAME. virtual void MissingSection(const string §ion_name); diff --git a/src/common/linux/dwarf_cu_to_module_unittest.cc b/src/common/linux/dwarf_cu_to_module_unittest.cc index d9e4ac0a..4e9062cf 100644 --- a/src/common/linux/dwarf_cu_to_module_unittest.cc +++ b/src/common/linux/dwarf_cu_to_module_unittest.cc @@ -43,6 +43,7 @@ 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; @@ -74,6 +75,7 @@ class MockWarningReporter: public DwarfCUToModule::WarningReporter { : DwarfCUToModule::WarningReporter(filename, cu_offset) { } MOCK_METHOD1(SetCUName, void(const string &name)); MOCK_METHOD2(UnknownSpecification, void(uint64 offset, uint64 target)); + MOCK_METHOD2(UnknownAbstractOrigin, void(uint64 offset, uint64 target)); MOCK_METHOD1(MissingSection, void(const string §ion_name)); MOCK_METHOD1(BadLineInfoOffset, void(uint64 offset)); MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function)); @@ -128,6 +130,7 @@ class CUFixtureBase { // 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); @@ -210,6 +213,22 @@ class CUFixtureBase { 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. @@ -520,6 +539,83 @@ void CUFixtureBase::DefinitionDIE(DIEHandler *parent, 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; @@ -629,6 +725,53 @@ TEST_F(Simple, UnusedFileContext) { reporter_.SetCUName("compilation-unit-name"); } +TEST_F(Simple, 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(Simple, 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); +} + +TEST_F(Simple, UnknownAbstractOrigin) { + EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, 1ULL)).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, "", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + // An address range. struct Range { Module::Address start, end; @@ -1114,6 +1257,24 @@ TEST_F(Specifications, NamedScopeDeclarationParent) { 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 diff --git a/src/tools/linux/dump_syms/Makefile b/src/tools/linux/dump_syms/Makefile index fd12a96d..b02731b6 100644 --- a/src/tools/linux/dump_syms/Makefile +++ b/src/tools/linux/dump_syms/Makefile @@ -50,6 +50,9 @@ COVERAGE= # SOME_ENV_VAR=value valgrind ./some-test-executable test-args TEST_WRAPPER= +# Arguments to pass to the test programs. +TEST_ARGS= + ### Variables used internally by this Makefile. # The top of the breakpad source tree.