From f7566bd447f628d77152dc28fb70ab232c342b86 Mon Sep 17 00:00:00 2001 From: "thestig@chromium.org" Date: Wed, 24 Apr 2013 21:18:44 +0000 Subject: [PATCH] Add an option to not handle DWARF inter-compilation unit references in Linux dump_syms. This saves a lot of memory for dump_syms. Review URL: https://breakpad.appspot.com/565002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1163 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/common/dwarf_cu_to_module.cc | 98 ++++++++++---- src/common/dwarf_cu_to_module.h | 64 ++++++--- src/common/dwarf_cu_to_module_unittest.cc | 152 +++++++++++++++------- src/common/linux/dump_symbols.cc | 53 ++++---- src/common/linux/dump_symbols.h | 14 +- src/common/linux/dump_symbols_unittest.cc | 18 ++- src/tools/linux/dump_syms/dump_syms.cc | 30 +++-- 7 files changed, 300 insertions(+), 129 deletions(-) diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc index a8b0a29b..9f892290 100644 --- a/src/common/dwarf_cu_to_module.cc +++ b/src/common/dwarf_cu_to_module.cc @@ -91,7 +91,7 @@ struct DwarfCUToModule::Specification { // An abstract origin -- base definition of an inline function. struct AbstractOrigin { AbstractOrigin() : name() {} - AbstractOrigin(const string& name) : name(name) {} + explicit AbstractOrigin(const string& name) : name(name) {} string name; }; @@ -128,14 +128,43 @@ struct DwarfCUToModule::FilePrivate { AbstractOriginByOffset origins; }; -DwarfCUToModule::FileContext::FileContext(const string &filename_arg, - Module *module_arg) - : filename(filename_arg), module(module_arg) { - file_private = new FilePrivate(); +DwarfCUToModule::FileContext::FileContext(const string &filename, + Module *module, + bool handle_inter_cu_refs) + : filename_(filename), + module_(module), + handle_inter_cu_refs_(handle_inter_cu_refs) { + file_private_ = new FilePrivate(); } DwarfCUToModule::FileContext::~FileContext() { - delete file_private; + delete file_private_; +} + +void DwarfCUToModule::FileContext::AddSectionToSectionMap( + const string& name, const char* contents, uint64 length) { + section_map_[name] = std::make_pair(contents, length); +} + +void DwarfCUToModule::FileContext::ClearSectionMapForTest() { + section_map_.clear(); +} + +const dwarf2reader::SectionMap& +DwarfCUToModule::FileContext::section_map() const { + return section_map_; +} + +void DwarfCUToModule::FileContext::ClearSpecifications() { + if (!handle_inter_cu_refs_) + file_private_->specifications.clear(); +} + +bool DwarfCUToModule::FileContext::IsUnhandledInterCUReference( + uint64 offset, uint64 compilation_unit_start) const { + if (handle_inter_cu_refs_) + return false; + return offset < compilation_unit_start; } // Information global to the particular compilation unit we're @@ -145,11 +174,13 @@ struct DwarfCUToModule::CUContext { CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg) : file_context(file_context_arg), reporter(reporter_arg), - language(Language::CPlusPlus) { } + language(Language::CPlusPlus) {} + ~CUContext() { for (vector::iterator it = functions.begin(); - it != functions.end(); it++) + it != functions.end(); ++it) { delete *it; + } }; // The DWARF-bearing file into which this CU was incorporated. @@ -276,14 +307,19 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( uint64 data) { switch (attr) { case dwarf2reader::DW_AT_specification: { + FileContext *file_context = cu_context_->file_context; + if (file_context->IsUnhandledInterCUReference( + data, cu_context_->reporter->cu_offset())) { + cu_context_->reporter->UnhandledInterCUReference(offset_, data); + break; + } // Find the Specification to which this attribute refers, and // set specification_ appropriately. We could do more processing // here, but it's better to leave the real work to our // EndAttribute member function, at which point we know we have // seen all the DIE's attributes. - FileContext *file_context = cu_context_->file_context; - SpecificationByOffset *specifications - = &file_context->file_private->specifications; + SpecificationByOffset *specifications = + &file_context->file_private_->specifications; SpecificationByOffset::iterator spec = specifications->find(data); if (spec != specifications->end()) { specification_ = &spec->second; @@ -303,7 +339,7 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) { pair::iterator, bool> result = - cu_context_->file_context->file_private->common_strings.insert(str); + cu_context_->file_context->file_private_->common_strings.insert(str); return *result.first; } @@ -365,15 +401,14 @@ string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() { // If this DIE was marked as a declaration, record its names in the // specification table. if (declaration_) { - FileContext *file_context = cu_context_->file_context; Specification spec; - if (qualified_name) + if (qualified_name) { spec.qualified_name = *qualified_name; - else { + } else { spec.enclosing_name = *enclosing_name; spec.unqualified_name = *unqualified_name; } - file_context->file_private->specifications[offset_] = spec; + cu_context_->file_context->file_private_->specifications[offset_] = spec; } if (qualified_name) @@ -457,10 +492,10 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeReference( enum DwarfAttribute attr, enum DwarfForm form, uint64 data) { - switch(attr) { + switch (attr) { case dwarf2reader::DW_AT_abstract_origin: { const AbstractOriginByOffset& origins = - cu_context_->file_context->file_private->origins; + cu_context_->file_context->file_private_->origins; AbstractOriginByOffset::const_iterator origin = origins.find(data); if (origin != origins.end()) { abstract_origin_ = &(origin->second); @@ -516,7 +551,7 @@ void DwarfCUToModule::FuncHandler::Finish() { } } else if (inline_) { AbstractOrigin origin(name_); - cu_context_->file_context->file_private->origins[offset_] = origin; + cu_context_->file_context->file_private_->origins[offset_] = origin; } } @@ -628,10 +663,19 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) { filename_.c_str(), offset); } +void DwarfCUToModule::WarningReporter::UnhandledInterCUReference( + uint64 offset, uint64 target) { + CUHeading(); + fprintf(stderr, "%s: warning: the DIE at offset 0x%llx has a " + "DW_FORM_ref_addr attribute with an inter-CU reference to " + "0x%llx, but inter-CU reference handling is turned off.\n", + filename_.c_str(), offset, target); +} + DwarfCUToModule::DwarfCUToModule(FileContext *file_context, LineToModuleHandler *line_reader, WarningReporter *reporter) - : line_reader_(line_reader), has_source_line_info_(false) { + : line_reader_(line_reader), has_source_line_info_(false) { cu_context_ = new CUContext(file_context, reporter); child_context_ = new DIEContext(); } @@ -723,7 +767,7 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) { // 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. @@ -741,7 +785,7 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) { void DwarfCUToModule::ReadSourceLines(uint64 offset) { const dwarf2reader::SectionMap §ion_map - = cu_context_->file_context->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 "__" @@ -759,7 +803,7 @@ void DwarfCUToModule::ReadSourceLines(uint64 offset) { return; } line_reader_->ReadProgram(section_start + offset, section_length - offset, - cu_context_->file_context->module, &lines_); + cu_context_->file_context->module_, &lines_); } namespace { @@ -926,7 +970,7 @@ void DwarfCUToModule::AssignLinesToFunctions() { // 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); + assert(func || line); if (func && line) next_transition = std::min(func->address, line->address); else if (func) @@ -984,12 +1028,14 @@ void DwarfCUToModule::Finish() { // Add our functions, which now have source lines assigned to them, // to module_. - cu_context_->file_context->module->AddFunctions(functions->begin(), - functions->end()); + 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(); + + cu_context_->file_context->ClearSpecifications(); } bool DwarfCUToModule::StartCompilationUnit(uint64 offset, diff --git a/src/common/dwarf_cu_to_module.h b/src/common/dwarf_cu_to_module.h index 85453316..4f466ace 100644 --- a/src/common/dwarf_cu_to_module.h +++ b/src/common/dwarf_cu_to_module.h @@ -65,7 +65,6 @@ using dwarf2reader::DwarfTag; 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 @@ -73,23 +72,52 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler { // 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); + // compilation unit we process in that file. Set HANDLE_INTER_CU_REFS + // to true to handle debugging symbols with DW_FORM_ref_addr entries. + class FileContext { + public: + FileContext(const string &filename, + Module *module, + bool handle_inter_cu_refs); ~FileContext(); + // Add CONTENTS of size LENGTH to the section map as NAME. + void AddSectionToSectionMap(const string& name, + const char* contents, + uint64 length); + + // Clear the section map for testing. + void ClearSectionMapForTest(); + + const dwarf2reader::SectionMap& section_map() const; + + private: + friend class DwarfCUToModule; + + // Clears all the Specifications if HANDLE_INTER_CU_REFS_ is false. + void ClearSpecifications(); + + // Given an OFFSET and a CU that starts at COMPILATION_UNIT_START, returns + // true if this is an inter-compilation unit reference that is not being + // handled. + bool IsUnhandledInterCUReference(uint64 offset, + uint64 compilation_unit_start) const; + // The name of this file, for use in error messages. - string filename; + const 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; + dwarf2reader::SectionMap section_map_; // The Module to which we're contributing definitions. - Module *module; + Module *module_; + + // True if we are handling references between compilation units. + const bool handle_inter_cu_refs_; // Inter-compilation unit data used internally by the handlers. - FilePrivate *file_private; + FilePrivate *file_private_; }; // An abstract base class for handlers that handle DWARF line data @@ -170,9 +198,17 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler { // link. virtual void UnnamedFunction(uint64 offset); + // The DW_FORM_ref_addr at OFFSET to TARGET was not handled because + // FilePrivate did not retain the inter-CU specification data. + virtual void UnhandledInterCUReference(uint64 offset, uint64 target); + + uint64 cu_offset() const { + return cu_offset_; + } + protected: - string filename_; - uint64 cu_offset_; + const string filename_; + const uint64 cu_offset_; string cu_name_; bool printed_cu_header_; bool printed_unpaired_header_; @@ -218,13 +254,11 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler { bool StartRootDIE(uint64 offset, enum DwarfTag tag); private: - // Used internally by the handler. Full definitions are in // dwarf_cu_to_module.cc. - struct FilePrivate; - struct Specification; struct CUContext; struct DIEContext; + struct Specification; class GenericDIEHandler; class FuncHandler; class NamedScopeHandler; @@ -234,7 +268,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler { // 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 @@ -275,6 +309,6 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler { vector lines_; }; -} // namespace google_breakpad +} // namespace google_breakpad #endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__ diff --git a/src/common/dwarf_cu_to_module_unittest.cc b/src/common/dwarf_cu_to_module_unittest.cc index 81e629b0..5f61a58e 100644 --- a/src/common/dwarf_cu_to_module_unittest.cc +++ b/src/common/dwarf_cu_to_module_unittest.cc @@ -81,6 +81,7 @@ class MockWarningReporter: public DwarfCUToModule::WarningReporter { MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function)); MOCK_METHOD1(UncoveredLine, void(const Module::Line &line)); MOCK_METHOD1(UnnamedFunction, void(uint64 offset)); + MOCK_METHOD2(UnhandledInterCUReference, void(uint64 offset, uint64 target)); }; // A fixture class including all the objects needed to handle a @@ -88,7 +89,6 @@ class MockWarningReporter: public DwarfCUToModule::WarningReporter { // for doing common kinds of setup and tests. class CUFixtureBase { public: - // If we have: // // vector lines; @@ -108,7 +108,8 @@ class CUFixtureBase { // in which case calling l2m with some line vector will append lines. class AppendLinesFunctor { public: - AppendLinesFunctor(const vector *lines) : lines_(lines) { } + explicit AppendLinesFunctor( + const vector *lines) : lines_(lines) { } void operator()(const char *program, uint64 length, Module *module, vector *lines) { lines->insert(lines->end(), lines_->begin(), lines_->end()); @@ -119,7 +120,7 @@ class CUFixtureBase { CUFixtureBase() : module_("module-name", "module-os", "module-arch", "module-id"), - file_context_("dwarf-filename", &module_), + file_context_("dwarf-filename", &module_, true), language_(dwarf2reader::DW_LANG_none), language_signed_(false), appender_(&lines_), @@ -137,6 +138,7 @@ class CUFixtureBase { EXPECT_CALL(reporter_, UncoveredFunction(_)).Times(0); EXPECT_CALL(reporter_, UncoveredLine(_)).Times(0); EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(0); + EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(0); // By default, expect the line program reader not to be invoked. We // may override this in StartCU. @@ -145,8 +147,9 @@ class CUFixtureBase { // The handler will consult this section map to decide what to // pass to our line reader. - file_context_.section_map[".debug_line"] = make_pair(dummy_line_program_, - dummy_line_size_); + file_context_.AddSectionToSectionMap(".debug_line", + dummy_line_program_, + dummy_line_size_); } // Add a line with the given address, size, filename, and line @@ -182,7 +185,7 @@ class CUFixtureBase { // 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 @@ -190,7 +193,7 @@ class CUFixtureBase { // attribute. DIEHandler *StartSpecifiedDIE(DIEHandler *parent, DwarfTag tag, uint64 specification, const char *name = NULL); - + // Define a function as a child of PARENT with the given name, address, and // size. If high_pc_form is DW_FORM_addr then the DW_AT_high_pc attribute // will be written as an address; otherwise it will be written as the @@ -246,7 +249,7 @@ class CUFixtureBase { // 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); @@ -283,7 +286,7 @@ class CUFixtureBase { AppendLinesFunctor appender_; static const char dummy_line_program_[]; static const size_t dummy_line_size_; - + MockWarningReporter reporter_; DwarfCUToModule root_handler_; @@ -299,8 +302,8 @@ class CUFixtureBase { }; const char CUFixtureBase::dummy_line_program_[] = "lots of fun data"; -const size_t CUFixtureBase::dummy_line_size_ = - sizeof (CUFixtureBase::dummy_line_program_); +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) { @@ -320,7 +323,7 @@ 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 + // Have it add the lines passed to PushLine. Otherwise, leave the // initial expectation (no calls) in force. if (!lines_.empty()) EXPECT_CALL(line_reader_, @@ -396,7 +399,7 @@ DIEHandler *CUFixtureBase::StartNamedDIE(DIEHandler *parent, delete handler; return NULL; } - + return handler; } @@ -420,7 +423,7 @@ DIEHandler *CUFixtureBase::StartSpecifiedDIE(DIEHandler *parent, delete handler; return NULL; } - + return handler; } @@ -541,7 +544,7 @@ void CUFixtureBase::AbstractInstanceDIE(DIEHandler *parent, void CUFixtureBase::DefineInlineInstanceDIE(DIEHandler *parent, const string &name, - uint64 origin, + uint64 origin, Module::Address address, Module::Address size) { dwarf2reader::DIEHandler *func @@ -708,7 +711,7 @@ TEST_F(SimpleCU, IrrelevantNamedScopeChildren) { // 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); + DwarfCUToModule::FileContext fc("dwarf-filename", &m, true); // Kludge: satisfy reporter_'s expectation. reporter_.SetCUName("compilation-unit-name"); @@ -864,34 +867,33 @@ TEST_P(FuncLinePairing, Pairing) { StartCU(); DefineFunction(&root_handler_, "function1", - s.functions[0].start, + s.functions[0].start, s.functions[0].end - s.functions[0].start, NULL); DefineFunction(&root_handler_, "function2", - s.functions[1].start, + s.functions[1].start, s.functions[1].end - s.functions[1].start, NULL); root_handler_.Finish(); TestFunctionCount(2); TestFunction(0, "function1", - s.functions[0].start, + 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, + 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].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, + 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(); @@ -913,7 +915,7 @@ TEST_F(FuncLinePairing, FuncsNoLines) { StartCU(); DefineFunction(&root_handler_, "function1", 0x127da12ffcf5c51fULL, 0x1000U, - NULL); + NULL); root_handler_.Finish(); TestFunctionCount(1); @@ -1064,11 +1066,11 @@ TEST_P(CXXQualifiedNames, FuncInEnclosureInNamespace) { PushLine(10, 1, "line-file", 69819327); StartCU(); - DIEHandler *namespace_handler + DIEHandler *namespace_handler = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, "Namespace"); EXPECT_TRUE(namespace_handler != NULL); - DIEHandler *enclosure_handler = StartNamedDIE(namespace_handler, tag, + DIEHandler *enclosure_handler = StartNamedDIE(namespace_handler, tag, "Enclosure"); EXPECT_TRUE(enclosure_handler != NULL); DefineFunction(enclosure_handler, "function", 10, 1, NULL); @@ -1127,10 +1129,10 @@ const LanguageAndQualifiedName LanguageAndQualifiedNameCases[] = { { dwarf2reader::DW_LANG_Mips_Assembler, NULL } }; -class QualifiedForLanguage: - public CUFixtureBase, - public TestWithParam { }; - +class QualifiedForLanguage + : public CUFixtureBase, + public TestWithParam { }; + INSTANTIATE_TEST_CASE_P(LanguageAndQualifiedName, QualifiedForLanguage, ValuesIn(LanguageAndQualifiedNameCases)); @@ -1254,7 +1256,7 @@ TEST_F(Specifications, FunctionDeclarationParent) { } DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, - 0x0e0e877c8404544aULL, "definition-name", + 0x0e0e877c8404544aULL, "definition-name", 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); root_handler_.Finish(); @@ -1287,14 +1289,14 @@ TEST_F(Specifications, NamedScopeDeclarationParent) { = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, 0x419bb1d12f9a73a2ULL, "class-definition-name"); ASSERT_TRUE(class_handler != NULL); - DefineFunction(class_handler, "function", + DefineFunction(class_handler, "function", 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, NULL); class_handler->Finish(); delete class_handler; } root_handler_.Finish(); - + TestFunctionCount(1); TestFunction(0, "space_A::class-definition-name::function", 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); @@ -1341,14 +1343,14 @@ TEST_F(Specifications, LongChain) { // class_H definition // func_I declaration // func_I definition - // - // So: + // + // 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 { @@ -1429,7 +1431,7 @@ TEST_F(Specifications, LongChain) { TEST_F(Specifications, InterCU) { Module m("module-name", "module-os", "module-arch", "module-id"); - DwarfCUToModule::FileContext fc("dwarf-filename", &m); + DwarfCUToModule::FileContext fc("dwarf-filename", &m, true); EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); MockLineToModuleHandler lr; EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0); @@ -1449,7 +1451,7 @@ TEST_F(Specifications, InterCU) { 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_); @@ -1486,6 +1488,63 @@ TEST_F(Specifications, InterCU) { EXPECT_STREQ("class_A::member_func_B", functions[0]->name.c_str()); } +TEST_F(Specifications, UnhandledInterCU) { + Module m("module-name", "module-os", "module-arch", "module-id"); + DwarfCUToModule::FileContext fc("dwarf-filename", &m, false); + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + MockLineToModuleHandler lr; + EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0); + + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + // First CU. Declares class_A. + { + DwarfCUToModule root1_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root1_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root1_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ProcessStrangeAttributes(&root1_handler); + ASSERT_TRUE(root1_handler.EndAttributes()); + DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL, + dwarf2reader::DW_TAG_class_type, "class_A", ""); + root1_handler.Finish(); + } + + // Second CU. Defines class_A, declares member_func_B. + { + DwarfCUToModule root2_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root2_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ASSERT_TRUE(root2_handler.EndAttributes()); + EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(1); + DIEHandler *class_A_handler + = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type, + 0xb8fbfdd5f0b26fceULL); + DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, + dwarf2reader::DW_TAG_subprogram, "member_func_B", ""); + class_A_handler->Finish(); + delete class_A_handler; + root2_handler.Finish(); + } + + // Third CU. Defines member_func_B. + { + DwarfCUToModule root3_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root3_handler.StartRootDIE(1, + dwarf2reader::DW_TAG_compile_unit)); + ASSERT_TRUE(root3_handler.EndAttributes()); + EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(1); + EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(1); + DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram, + 0xb01fef8b380bd1a2ULL, "", + 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL); + root3_handler.Finish(); + } +} + TEST_F(Specifications, BadOffset) { PushLine(0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL, "line-file", 56636272); EXPECT_CALL(reporter_, UnknownSpecification(_, 0x2be953efa6f9a996ULL)) @@ -1554,8 +1613,9 @@ TEST_F(Specifications, PreferSpecificationParents) { StartCU(); { - dwarf2reader::DIEHandler *declaration_class_handler - = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "declaration-class"); + 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", ""); @@ -1603,7 +1663,7 @@ 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(); + file_context_.ClearSectionMapForTest(); StartCU(); root_handler_.Finish(); @@ -1667,7 +1727,7 @@ struct Reporter: public Test { line.file = &file; line.number = 93400201; } - + DwarfCUToModule::WarningReporter reporter; Module::Function function; Module::File file; @@ -1714,7 +1774,7 @@ TEST_F(Reporter, UncoveredLineEnabled) { TEST_F(Reporter, UnnamedFunction) { reporter.UnnamedFunction(0x90c0baff9dedb2d9ULL); -} +} // Would be nice to also test: // - overlapping lines, functions diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index eec5373c..517edee3 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -72,6 +72,7 @@ // This namespace contains helper functions. namespace { +using google_breakpad::DumpOptions; using google_breakpad::DwarfCFIToModule; using google_breakpad::DwarfCUToModule; using google_breakpad::DwarfLineToModule; @@ -216,6 +217,7 @@ template bool LoadDwarf(const string& dwarf_filename, const typename ElfClass::Ehdr* elf_header, const bool big_endian, + bool handle_inter_cu_refs, Module* module) { typedef typename ElfClass::Shdr Shdr; @@ -224,7 +226,9 @@ bool LoadDwarf(const string& dwarf_filename, dwarf2reader::ByteReader byte_reader(endianness); // Construct a context for this file. - DwarfCUToModule::FileContext file_context(dwarf_filename, module); + DwarfCUToModule::FileContext file_context(dwarf_filename, + module, + handle_inter_cu_refs); // Build a map of the ELF file's sections. const Shdr* sections = @@ -238,14 +242,16 @@ bool LoadDwarf(const string& dwarf_filename, section->sh_name; const char* contents = GetOffset(elf_header, section->sh_offset); - uint64 length = section->sh_size; - file_context.section_map[name] = std::make_pair(contents, length); + file_context.AddSectionToSectionMap(name, contents, section->sh_size); } // Parse all the compilation units in the .debug_info section. DumperLineToModule line_to_module(&byte_reader); - std::pair debug_info_section - = file_context.section_map[".debug_info"]; + dwarf2reader::SectionMap::const_iterator debug_info_entry = + file_context.section_map().find(".debug_info"); + assert(debug_info_entry != file_context.section_map().end()); + const std::pair& debug_info_section = + debug_info_entry->second; // This should never have been called if the file doesn't have a // .debug_info section. assert(debug_info_section.first); @@ -258,7 +264,7 @@ bool LoadDwarf(const string& dwarf_filename, // Make a Dwarf2Handler that drives the DIEHandler. dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); // Make a DWARF parser for the compilation unit at OFFSET. - dwarf2reader::CompilationUnit reader(file_context.section_map, + dwarf2reader::CompilationUnit reader(file_context.section_map(), offset, &byte_reader, &die_dispatcher); @@ -521,7 +527,7 @@ bool LoadSymbols(const string& obj_file, const typename ElfClass::Ehdr* elf_header, const bool read_gnu_debug_link, LoadSymbolsInfo* info, - SymbolData symbol_data, + const DumpOptions& options, Module* module) { typedef typename ElfClass::Addr Addr; typedef typename ElfClass::Phdr Phdr; @@ -542,7 +548,7 @@ bool LoadSymbols(const string& obj_file, bool found_debug_info_section = false; bool found_usable_info = false; - if (symbol_data != ONLY_CFI) { + if (options.symbol_data != ONLY_CFI) { #ifndef NO_STABS_SUPPORT // Look for STABS debugging information, and load it if present. const Shdr* stab_section = @@ -573,13 +579,15 @@ bool LoadSymbols(const string& obj_file, found_debug_info_section = true; found_usable_info = true; info->LoadedSection(".debug_info"); - if (!LoadDwarf(obj_file, elf_header, big_endian, module)) + if (!LoadDwarf(obj_file, elf_header, big_endian, + options.handle_inter_cu_refs, module)) { fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " "DWARF debugging information\n", obj_file.c_str()); + } } } - if (symbol_data != NO_CFI) { + if (options.symbol_data != NO_CFI) { // Dwarf Call Frame Information (CFI) is actually independent from // the other DWARF debugging information, and can be used alone. const Shdr* dwarf_cfi_section = @@ -655,7 +663,7 @@ bool LoadSymbols(const string& obj_file, obj_file.c_str()); } } else { - if (symbol_data != ONLY_CFI) { + if (options.symbol_data != ONLY_CFI) { // The caller doesn't want to consult .gnu_debuglink. // See if there are export symbols available. const Shdr* dynsym_section = @@ -753,7 +761,7 @@ template bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, const string& obj_filename, const std::vector& debug_dirs, - SymbolData symbol_data, + const DumpOptions& options, Module** out_module) { typedef typename ElfClass::Ehdr Ehdr; typedef typename ElfClass::Shdr Shdr; @@ -788,7 +796,7 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, scoped_ptr module(new Module(name, os, architecture, id)); if (!LoadSymbols(obj_filename, big_endian, elf_header, !debug_dirs.empty(), &info, - symbol_data, module.get())) { + options, module.get())) { const string debuglink_file = info.debuglink_file(); if (debuglink_file.empty()) return false; @@ -827,7 +835,7 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, if (!LoadSymbols(debuglink_file, debug_big_endian, debug_elf_header, false, &info, - symbol_data, module.get())) { + options, module.get())) { return false; } } @@ -844,9 +852,8 @@ namespace google_breakpad { bool ReadSymbolDataInternal(const uint8_t* obj_file, const string& obj_filename, const std::vector& debug_dirs, - SymbolData symbol_data, + const DumpOptions& options, Module** module) { - if (!IsValidElf(obj_file)) { fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str()); return false; @@ -856,12 +863,12 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file, if (elfclass == ELFCLASS32) { return ReadSymbolDataElfClass( reinterpret_cast(obj_file), obj_filename, debug_dirs, - symbol_data, module); + options, module); } if (elfclass == ELFCLASS64) { return ReadSymbolDataElfClass( reinterpret_cast(obj_file), obj_filename, debug_dirs, - symbol_data, module); + options, module); } return false; @@ -869,20 +876,20 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file, bool WriteSymbolFile(const string &obj_file, const std::vector& debug_dirs, - SymbolData symbol_data, + const DumpOptions& options, std::ostream &sym_stream) { Module* module; - if (!ReadSymbolData(obj_file, debug_dirs, symbol_data, &module)) + if (!ReadSymbolData(obj_file, debug_dirs, options, &module)) return false; - bool result = module->Write(sym_stream, symbol_data); + bool result = module->Write(sym_stream, options.symbol_data); delete module; return result; } bool ReadSymbolData(const string& obj_file, const std::vector& debug_dirs, - SymbolData symbol_data, + const DumpOptions& options, Module** module) { MmapWrapper map_wrapper; void* elf_header = NULL; @@ -890,7 +897,7 @@ bool ReadSymbolData(const string& obj_file, return false; return ReadSymbolDataInternal(reinterpret_cast(elf_header), - obj_file, debug_dirs, symbol_data, module); + obj_file, debug_dirs, options, module); } } // namespace google_breakpad diff --git a/src/common/linux/dump_symbols.h b/src/common/linux/dump_symbols.h index f3c92cd5..636bb72f 100644 --- a/src/common/linux/dump_symbols.h +++ b/src/common/linux/dump_symbols.h @@ -46,6 +46,16 @@ namespace google_breakpad { class Module; +struct DumpOptions { + DumpOptions(SymbolData symbol_data, bool handle_inter_cu_refs) + : symbol_data(symbol_data), + handle_inter_cu_refs(handle_inter_cu_refs) { + } + + SymbolData symbol_data; + bool handle_inter_cu_refs; +}; + // Find all the debugging information in OBJ_FILE, an ELF executable // or shared library, and write it to SYM_STREAM in the Breakpad symbol // file format. @@ -54,7 +64,7 @@ class Module; // SYMBOL_DATA allows limiting the type of symbol data written. bool WriteSymbolFile(const string &obj_file, const std::vector& debug_dirs, - SymbolData symbol_data, + const DumpOptions& options, std::ostream &sym_stream); // As above, but simply return the debugging information in MODULE @@ -62,7 +72,7 @@ bool WriteSymbolFile(const string &obj_file, // Module object and must delete it when finished. bool ReadSymbolData(const string& obj_file, const std::vector& debug_dirs, - SymbolData symbol_data, + const DumpOptions& options, Module** module); } // namespace google_breakpad diff --git a/src/common/linux/dump_symbols_unittest.cc b/src/common/linux/dump_symbols_unittest.cc index 6f5aef2e..3f86dbe6 100644 --- a/src/common/linux/dump_symbols_unittest.cc +++ b/src/common/linux/dump_symbols_unittest.cc @@ -40,25 +40,24 @@ #include #include "breakpad_googletest_includes.h" +#include "common/linux/dump_symbols.h" #include "common/linux/synth_elf.h" #include "common/module.h" #include "common/using_std_string.h" namespace google_breakpad { + bool ReadSymbolDataInternal(const uint8_t* obj_file, const string& obj_filename, const std::vector& debug_dir, - SymbolData symbol_data, + const DumpOptions& options, Module** module); -} using google_breakpad::synth_elf::ELF; using google_breakpad::synth_elf::StringTable; using google_breakpad::synth_elf::SymbolTable; using google_breakpad::test_assembler::kLittleEndian; using google_breakpad::test_assembler::Section; -using google_breakpad::Module; -using google_breakpad::ReadSymbolDataInternal; using std::stringstream; using std::vector; using ::testing::Test; @@ -83,10 +82,11 @@ TEST_F(DumpSymbols, Invalid) { Elf32_Ehdr header; memset(&header, 0, sizeof(header)); Module* module; + DumpOptions options(ALL_SYMBOL_DATA, true); EXPECT_FALSE(ReadSymbolDataInternal(reinterpret_cast(&header), "foo", vector(), - ALL_SYMBOL_DATA, + options, &module)); } @@ -115,10 +115,11 @@ TEST_F(DumpSymbols, SimplePublic32) { GetElfContents(elf); Module* module; + DumpOptions options(ALL_SYMBOL_DATA, true); EXPECT_TRUE(ReadSymbolDataInternal(elfdata, "foo", vector(), - ALL_SYMBOL_DATA, + options, &module)); stringstream s; @@ -154,10 +155,11 @@ TEST_F(DumpSymbols, SimplePublic64) { GetElfContents(elf); Module* module; + DumpOptions options(ALL_SYMBOL_DATA, true); EXPECT_TRUE(ReadSymbolDataInternal(elfdata, "foo", vector(), - ALL_SYMBOL_DATA, + options, &module)); stringstream s; @@ -166,3 +168,5 @@ TEST_F(DumpSymbols, SimplePublic64) { "PUBLIC 1000 0 superfunc\n", s.str()); } + +} // namespace google_breakpad diff --git a/src/tools/linux/dump_syms/dump_syms.cc b/src/tools/linux/dump_syms/dump_syms.cc index f58952ed..c8ade33a 100644 --- a/src/tools/linux/dump_syms/dump_syms.cc +++ b/src/tools/linux/dump_syms/dump_syms.cc @@ -43,33 +43,43 @@ int usage(const char* self) { "[directories-for-debug-file]\n\n", self); fprintf(stderr, "Options:\n"); fprintf(stderr, " -c Do not generate CFI section\n"); + fprintf(stderr, " -r Do not handle inter-compilation unit references\n"); return 1; } int main(int argc, char **argv) { - if (argc < 2 || argc > 4) + if (argc < 2) return usage(argv[0]); bool cfi = true; - int binary_index = 1; - if (strcmp("-c", argv[1]) == 0) { - cfi = false; - ++binary_index; + bool handle_inter_cu_refs = true; + int arg_index = 1; + while (arg_index < argc && strlen(argv[arg_index]) > 0 && + argv[arg_index][0] == '-') { + if (strcmp("-c", argv[arg_index]) == 0) { + cfi = false; + } else if (strcmp("-r", argv[arg_index]) == 0) { + handle_inter_cu_refs = false; + } else { + return usage(argv[0]); + } + ++arg_index; } - if (!cfi && argc == 2) + if (arg_index == argc) return usage(argv[0]); - const char *binary; + const char* binary; std::vector debug_dirs; - binary = argv[binary_index]; - for (int debug_dir_index = binary_index + 1; + binary = argv[arg_index]; + for (int debug_dir_index = arg_index + 1; debug_dir_index < argc; ++debug_dir_index) { debug_dirs.push_back(argv[debug_dir_index]); } SymbolData symbol_data = cfi ? ALL_SYMBOL_DATA : NO_CFI; - if (!WriteSymbolFile(binary, debug_dirs, symbol_data, std::cout)) { + google_breakpad::DumpOptions options(symbol_data, handle_inter_cu_refs); + if (!WriteSymbolFile(binary, debug_dirs, options, std::cout)) { fprintf(stderr, "Failed to write symbol file.\n"); return 1; }