// 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 // dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule. #include #include #include #include #include "breakpad_googletest_includes.h" #include "common/dwarf_cu_to_module.h" #include "common/using_std_string.h" using std::make_pair; using std::vector; using dwarf2reader::DIEHandler; using dwarf2reader::DwarfTag; using dwarf2reader::DwarfAttribute; using dwarf2reader::DwarfForm; using dwarf2reader::DwarfInline; using dwarf2reader::RootDIEHandler; using google_breakpad::DwarfCUToModule; using google_breakpad::Module; using ::testing::_; using ::testing::AtMost; using ::testing::Invoke; using ::testing::Return; using ::testing::Test; using ::testing::TestWithParam; using ::testing::Values; using ::testing::ValuesIn; // Mock classes. class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler { public: MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir)); MOCK_METHOD8(ReadProgram, void(const uint8_t* program, uint64_t length, const uint8_t* string_section, uint64_t string_section_length, const uint8_t* line_string_section, uint64_t line_string_section_length, Module* module, vector* lines)); }; class MockWarningReporter: public DwarfCUToModule::WarningReporter { public: MockWarningReporter(const string& filename, uint64_t cu_offset) : DwarfCUToModule::WarningReporter(filename, cu_offset) { } MOCK_METHOD1(SetCUName, void(const string& name)); MOCK_METHOD2(UnknownSpecification, void(uint64_t offset, uint64_t target)); MOCK_METHOD2(UnknownAbstractOrigin, void(uint64_t offset, uint64_t target)); MOCK_METHOD1(MissingSection, void(const string& section_name)); MOCK_METHOD1(BadLineInfoOffset, void(uint64_t offset)); MOCK_METHOD1(UncoveredFunction, void(const Module::Function& function)); MOCK_METHOD1(UncoveredLine, void(const Module::Line& line)); MOCK_METHOD1(UnnamedFunction, void(uint64_t offset)); MOCK_METHOD1(DemangleError, void(const string& input)); MOCK_METHOD2(UnhandledInterCUReference, void(uint64_t offset, uint64_t target)); }; // 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 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 // MockLineToModuleHandler like this: // // MockLineToModuleHandler l2m; // EXPECT_CALL(l2m, ReadProgram(_,_,_,_)) // .WillOnce(DoAll(Invoke(appender), Return())); // // in which case calling l2m with some line vector will append lines. class AppendLinesFunctor { public: explicit AppendLinesFunctor( const vector* lines) : lines_(lines) { } void operator()(const uint8_t* program, uint64_t length, const uint8_t* string_section, uint64_t string_section_length, const uint8_t* line_string_section, uint64_t line_string_section_length, Module *module, vector* lines) { lines->insert(lines->end(), lines_->begin(), lines_->end()); } private: const vector* lines_; }; CUFixtureBase() : module_("module-name", "module-os", "module-arch", "module-id"), file_context_("dwarf-filename", &module_, true), language_(dwarf2reader::DW_LANG_none), language_signed_(false), appender_(&lines_), reporter_("dwarf-filename", 0xcf8f9bb6443d29b5LL), root_handler_(&file_context_, &line_reader_, /* ranges_reader */ nullptr, &reporter_), functions_filled_(false) { // 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); EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(0); // By default, expect the line program reader not to be invoked. We // may override this in StartCU. EXPECT_CALL(line_reader_, StartCompilationUnit(_)).Times(0); EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_,_,_,_,_)).Times(0); // The handler will consult this section map to decide what to // pass to our line reader. file_context_.AddSectionToSectionMap(".debug_line", 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 LineToModuleHandler. Call this before calling // StartCU. void PushLine(Module::Address address, Module::Address size, const string& filename, int line_number); // Use LANGUAGE for the compilation unit. More precisely, arrange // for StartCU to pass the compilation unit's root DIE a // DW_AT_language attribute whose value is LANGUAGE. void SetLanguage(dwarf2reader::DwarfLanguage language) { language_ = language; } // If SIGNED true, have StartCU report DW_AT_language as a signed // attribute; if false, have it report it as unsigned. void SetLanguageSigned(bool is_signed) { language_signed_ = is_signed; } // Call the handler this.root_handler_'s StartCompilationUnit and // StartRootDIE member functions, passing it appropriate attributes as // determined by prior calls to PushLine and SetLanguage. Leave // this.root_handler_ ready to hear about children: call // this.root_handler_.EndAttributes, but not this.root_handler_.Finish. void StartCU(); // Have HANDLER process some strange attribute/form/value triples. void ProcessStrangeAttributes(dwarf2reader::DIEHandler* handler); // Start a child DIE of PARENT with the given tag and name. Leave // the handler ready to hear about children: call EndAttributes, but // not Finish. DIEHandler* StartNamedDIE(DIEHandler* parent, DwarfTag tag, const string& name); // Start a child DIE of PARENT with the given tag and a // DW_AT_specification attribute whose value is SPECIFICATION. Leave // the handler ready to hear about children: call EndAttributes, but // not Finish. If NAME is non-zero, use it as the DW_AT_name // attribute. DIEHandler* StartSpecifiedDIE(DIEHandler* parent, DwarfTag tag, uint64_t 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 // function's 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, const char* mangled_name, DwarfForm high_pc_form = dwarf2reader::DW_FORM_addr); // 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_t offset, DwarfTag tag, const string& name, const string& mangled_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_t 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_t offset, DwarfInline type, uint64_t 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_t 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_comp_dir attribute that // indicates the path that this compilation unit was compiled in. string compilation_dir_; // 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 lines_; // Mock line program reader. MockLineToModuleHandler line_reader_; AppendLinesFunctor appender_; static const uint8_t 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 functions_; // True if we have filled the above vector with this.module_'s function list. bool functions_filled_; }; const uint8_t 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 (!compilation_dir_.empty()) EXPECT_CALL(line_reader_, StartCompilationUnit(compilation_dir_)).Times(1); // 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_, ReadProgram(&dummy_line_program_[0], dummy_line_size_, _,_,_,_, &module_, _)) .Times(AtMost(1)) .WillOnce(DoAll(Invoke(appender_), Return())); ASSERT_TRUE(root_handler_ .StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44, 0x4241b4f33720dd5cULL, 3)); { ASSERT_TRUE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, dwarf2reader::DW_TAG_compile_unit)); } root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, "compilation-unit-name"); if (!compilation_dir_.empty()) root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_comp_dir, dwarf2reader::DW_FORM_strp, compilation_dir_); if (!lines_.empty()) root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, dwarf2reader::DW_FORM_ref4, 0); if (language_ != dwarf2reader::DW_LANG_none) { if (language_signed_) root_handler_.ProcessAttributeSigned(dwarf2reader::DW_AT_language, dwarf2reader::DW_FORM_sdata, language_); else root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_language, dwarf2reader::DW_FORM_udata, language_); } ASSERT_TRUE(root_handler_.EndAttributes()); } void CUFixtureBase::ProcessStrangeAttributes( dwarf2reader::DIEHandler* handler) { handler->ProcessAttributeUnsigned((DwarfAttribute) 0xf560dead, (DwarfForm) 0x4106e4db, 0xa592571997facda1ULL); handler->ProcessAttributeSigned((DwarfAttribute) 0x85380095, (DwarfForm) 0x0f16fe87, 0x12602a4e3bf1f446LL); handler->ProcessAttributeReference((DwarfAttribute) 0xf7f7480f, (DwarfForm) 0x829e038a, 0x50fddef44734fdecULL); static const uint8_t buffer[10] = "frobynode"; handler->ProcessAttributeBuffer((DwarfAttribute) 0xa55ffb51, (DwarfForm) 0x2f43b041, buffer, sizeof(buffer)); handler->ProcessAttributeString((DwarfAttribute) 0x2f43b041, (DwarfForm) 0x895ffa23, "strange string"); } DIEHandler* CUFixtureBase::StartNamedDIE(DIEHandler* parent, DwarfTag tag, const string& name) { dwarf2reader::DIEHandler* handler = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag); if (!handler) return NULL; handler->ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, name); ProcessStrangeAttributes(handler); if (!handler->EndAttributes()) { handler->Finish(); delete handler; return NULL; } return handler; } DIEHandler* CUFixtureBase::StartSpecifiedDIE(DIEHandler* parent, DwarfTag tag, uint64_t specification, const char* name) { dwarf2reader::DIEHandler* handler = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag); if (!handler) return NULL; if (name) handler->ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, name); handler->ProcessAttributeReference(dwarf2reader::DW_AT_specification, dwarf2reader::DW_FORM_ref4, specification); if (!handler->EndAttributes()) { handler->Finish(); delete handler; return NULL; } return handler; } void CUFixtureBase::DefineFunction(dwarf2reader::DIEHandler* parent, const string& name, Module::Address address, Module::Address size, const char* mangled_name, DwarfForm high_pc_form) { dwarf2reader::DIEHandler* func = parent->FindChildHandler(0xe34797c7e68590a8LL, dwarf2reader::DW_TAG_subprogram); ASSERT_TRUE(func != NULL); func->ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, name); func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, dwarf2reader::DW_FORM_addr, address); Module::Address high_pc = size; if (high_pc_form == dwarf2reader::DW_FORM_addr) { high_pc += address; } func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, high_pc_form, high_pc); if (mangled_name) func->ProcessAttributeString(dwarf2reader::DW_AT_MIPS_linkage_name, dwarf2reader::DW_FORM_strp, mangled_name); ProcessStrangeAttributes(func); EXPECT_TRUE(func->EndAttributes()); func->Finish(); delete func; } void CUFixtureBase::DeclarationDIE(DIEHandler* parent, uint64_t offset, DwarfTag tag, const string& name, const string& mangled_name) { dwarf2reader::DIEHandler* die = parent->FindChildHandler(offset, tag); ASSERT_TRUE(die != NULL); if (!name.empty()) die->ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, name); if (!mangled_name.empty()) die->ProcessAttributeString(dwarf2reader::DW_AT_MIPS_linkage_name, dwarf2reader::DW_FORM_strp, mangled_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_t specification, const string& name, Module::Address address, Module::Address size) { dwarf2reader::DIEHandler* die = parent->FindChildHandler(0x6ccfea031a9e6cc9ULL, tag); ASSERT_TRUE(die != NULL); die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, dwarf2reader::DW_FORM_ref4, specification); if (!name.empty()) die->ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, name); if (size) { die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, dwarf2reader::DW_FORM_addr, address); die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, dwarf2reader::DW_FORM_addr, address + size); } EXPECT_TRUE(die->EndAttributes()); die->Finish(); delete die; } void CUFixtureBase::AbstractInstanceDIE(DIEHandler* parent, uint64_t offset, DwarfInline type, uint64_t specification, const string& name, DwarfForm form) { dwarf2reader::DIEHandler* die = parent->FindChildHandler(offset, dwarf2reader::DW_TAG_subprogram); ASSERT_TRUE(die != NULL); if (specification != 0ULL) die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, dwarf2reader::DW_FORM_ref4, specification); if (form == dwarf2reader::DW_FORM_sdata) { die->ProcessAttributeSigned(dwarf2reader::DW_AT_inline, form, type); } else { die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_inline, form, type); } if (!name.empty()) die->ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, name); EXPECT_TRUE(die->EndAttributes()); die->Finish(); delete die; } void CUFixtureBase::DefineInlineInstanceDIE(DIEHandler* parent, const string& name, uint64_t origin, Module::Address address, Module::Address size) { dwarf2reader::DIEHandler* func = parent->FindChildHandler(0x11c70f94c6e87ccdLL, dwarf2reader::DW_TAG_subprogram); ASSERT_TRUE(func != NULL); if (!name.empty()) { func->ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, name); } func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, dwarf2reader::DW_FORM_addr, address); func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, dwarf2reader::DW_FORM_addr, address + size); func->ProcessAttributeReference(dwarf2reader::DW_AT_abstract_origin, dwarf2reader::DW_FORM_ref4, origin); ProcessStrangeAttributes(func); EXPECT_TRUE(func->EndAttributes()); func->Finish(); delete func; } void CUFixtureBase::FillFunctions() { if (functions_filled_) return; module_.GetFunctions(&functions_, functions_.end()); sort(functions_.begin(), functions_.end(), Module::Function::CompareByAddress); functions_filled_ = true; } void CUFixtureBase::TestFunctionCount(size_t expected) { FillFunctions(); ASSERT_EQ(expected, functions_.size()); } void CUFixtureBase::TestFunction(int i, const string& name, Module::Address address, Module::Address size) { FillFunctions(); ASSERT_LT((size_t) i, functions_.size()); Module::Function* function = functions_[i]; EXPECT_EQ(name, function->name); EXPECT_EQ(address, function->address); EXPECT_EQ(size, function->ranges[0].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,e) TRACE(DefineFunction((a),(b),(c),(d),(e))) // (DefineFunction) instead of DefineFunction to avoid macro expansion. #define DefineFunction6(a,b,c,d,e,f) \ TRACE((DefineFunction)((a),(b),(c),(d),(e),(f))) #define DeclarationDIE(a,b,c,d,e) TRACE(DeclarationDIE((a),(b),(c),(d),(e))) #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, CompilationDir) { compilation_dir_ = "/src/build/"; StartCU(); root_handler_.Finish(); } TEST_F(SimpleCU, OneFunc) { PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); StartCU(); DefineFunction(&root_handler_, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, NULL); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); TestLineCount(0, 1); TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); } // As above, only DW_AT_high_pc is a length rather than an address. TEST_F(SimpleCU, OneFuncHighPcIsLength) { PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); StartCU(); DefineFunction6(&root_handler_, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, NULL, dwarf2reader::DW_FORM_udata); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); TestLineCount(0, 1); TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); } TEST_F(SimpleCU, MangledName) { PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); StartCU(); DefineFunction(&root_handler_, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "_ZN1n1fEi"); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "n::f(int)", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); } TEST_F(SimpleCU, IrrelevantRootChildren) { StartCU(); EXPECT_FALSE(root_handler_ .FindChildHandler(0x7db32bff4e2dcfb1ULL, dwarf2reader::DW_TAG_lexical_block)); } TEST_F(SimpleCU, IrrelevantNamedScopeChildren) { StartCU(); DIEHandler* class_A_handler = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); EXPECT_TRUE(class_A_handler != NULL); EXPECT_FALSE(class_A_handler ->FindChildHandler(0x02e55999b865e4e9ULL, dwarf2reader::DW_TAG_lexical_block)); delete class_A_handler; } // Verify that FileContexts can safely be deleted unused. TEST_F(SimpleCU, UnusedFileContext) { Module m("module-name", "module-os", "module-arch", "module-id"); DwarfCUToModule::FileContext fc("dwarf-filename", &m, true); // 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, "", 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); } TEST_F(SimpleCU, UnnamedFunction) { EXPECT_CALL(reporter_, UnnamedFunction(0xe34797c7e68590a8LL)) .WillOnce(Return()); PushLine(0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL, "line-file", 14044850); StartCU(); DefineFunction(&root_handler_, "", 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL, NULL); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "", 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 { }; 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, NULL); DefineFunction(&root_handler_, "function2", 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].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, NULL); 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, NULL); DefineFunction(&root_handler_, "function2", 20, 2, NULL); 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, NULL); // five-byte gap between functions, covered by line 63351048. // This should not elicit a warning. DefineFunction(&root_handler_, "function2", 20, 10, NULL); 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, NULL); DefineFunction(&root_handler_, "function2", 0xfffffffffffffffaULL, 5, NULL); 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, NULL); 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, NULL); DefineFunction(&root_handler_, "function2", 13, 1, NULL); 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 { }; 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, NULL); DefineFunction(enclosure_handler, "func_C", 20, 1, NULL); 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, NULL); 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, NULL); 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 { }; 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, NULL); 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, NULL); 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, MangledName) { // Language defaults to C++, so no need to set it here. PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); StartCU(); DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, dwarf2reader::DW_TAG_subprogram, "declaration-name", "_ZN1C1fEi"); DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, 0xcd3c51b946fb1eeeLL, "", 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "C::f(int)", 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); } TEST_F(Specifications, MangledNameSwift) { // Swift mangled names should pass through untouched. SetLanguage(dwarf2reader::DW_LANG_Swift); PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); StartCU(); const string kName = "_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si"; DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, dwarf2reader::DW_TAG_subprogram, "declaration-name", kName); DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, 0xcd3c51b946fb1eeeLL, "", 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, kName, 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); } TEST_F(Specifications, MangledNameRust) { SetLanguage(dwarf2reader::DW_LANG_Rust); PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); StartCU(); const string kName = "_ZN14rustc_demangle8demangle17h373defa94bffacdeE"; DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, dwarf2reader::DW_TAG_subprogram, "declaration-name", kName); DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, 0xcd3c51b946fb1eeeLL, "", 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, #ifndef HAVE_RUST_DEMANGLE // Rust mangled names should pass through untouched if not // using rust-demangle. kName, #else // If rust-demangle is available this should be properly // demangled. "rustc_demangle::demangle", #endif 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); } // 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, NULL); class_handler->Finish(); delete class_handler; } root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "space_A::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); } // An inline function in a namespace should correctly derive its // name from its abstract origin, and not just the namespace name. TEST_F(Specifications, InlineFunctionInNamespace) { PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); StartCU(); DIEHandler* space_handler = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, "Namespace"); ASSERT_TRUE(space_handler != NULL); AbstractInstanceDIE(space_handler, 0x1e8dac5d507ed7abULL, dwarf2reader::DW_INL_inlined, 0LL, "func-name"); DefineInlineInstanceDIE(space_handler, "", 0x1e8dac5d507ed7abULL, 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); space_handler->Finish(); delete space_handler; root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "Namespace::func-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); } TEST_F(Specifications, InterCU) { Module m("module-name", "module-os", "module-arch", "module-id"); DwarfCUToModule::FileContext fc("dwarf-filename", &m, true); 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, nullptr, &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, nullptr, &reporter_); ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3)); ASSERT_TRUE(root2_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit)); ASSERT_TRUE(root2_handler.EndAttributes()); DIEHandler* class_A_handler = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type, 0xb8fbfdd5f0b26fceULL); DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, dwarf2reader::DW_TAG_subprogram, "member_func_B", ""); class_A_handler->Finish(); delete class_A_handler; root2_handler.Finish(); } // Third CU. Defines member_func_B. { DwarfCUToModule root3_handler(&fc, &lr, nullptr, &reporter_); ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3)); ASSERT_TRUE(root3_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit)); ASSERT_TRUE(root3_handler.EndAttributes()); DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram, 0xb01fef8b380bd1a2ULL, "", 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL); root3_handler.Finish(); } vector 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, 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, nullptr, &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, nullptr, &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, nullptr, &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)) .WillOnce(Return()); StartCU(); DeclarationDIE(&root_handler_, 0xefd7f7752c27b7e4ULL, dwarf2reader::DW_TAG_subprogram, "", ""); DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, 0x2be953efa6f9a996ULL, "function", 0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL); root_handler_.Finish(); } TEST_F(Specifications, FunctionDefinitionHasOwnName) { PushLine(0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL, "line-file", 56792403); StartCU(); DeclarationDIE(&root_handler_, 0xc34ff4786cae78bdULL, dwarf2reader::DW_TAG_subprogram, "declaration-name", ""); DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, 0xc34ff4786cae78bdULL, "definition-name", 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "definition-name", 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); } TEST_F(Specifications, ClassDefinitionHasOwnName) { PushLine(0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL, "line-file", 57119241); StartCU(); DeclarationDIE(&root_handler_, 0xd0fe467ec2f1a58cULL, dwarf2reader::DW_TAG_class_type, "class-declaration-name", ""); dwarf2reader::DIEHandler* class_definition = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, 0xd0fe467ec2f1a58cULL, "class-definition-name"); ASSERT_TRUE(class_definition); DeclarationDIE(class_definition, 0x6d028229c15623dbULL, dwarf2reader::DW_TAG_subprogram, "function-declaration-name", ""); class_definition->Finish(); delete class_definition; DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, 0x6d028229c15623dbULL, "function-definition-name", 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "class-definition-name::function-definition-name", 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); } // DIEs that cite a specification should prefer the specification's // parents over their own when choosing qualified names. In this test, // we take the name from our definition but the enclosing scope name // from our declaration. I don't see why they'd ever be different, but // we want to verify what DwarfCUToModule is looking at. TEST_F(Specifications, PreferSpecificationParents) { PushLine(0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL, "line-file", 79488694); StartCU(); { dwarf2reader::DIEHandler* declaration_class_handler = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "declaration-class"); DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL, dwarf2reader::DW_TAG_subprogram, "function-declaration", ""); declaration_class_handler->Finish(); delete declaration_class_handler; } { dwarf2reader::DIEHandler* definition_class_handler = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "definition-class"); DefinitionDIE(definition_class_handler, dwarf2reader::DW_TAG_subprogram, 0x9ddb35517455ef7aULL, "function-definition", 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); definition_class_handler->Finish(); delete definition_class_handler; } root_handler_.Finish(); TestFunctionCount(1); TestFunction(0, "declaration-class::function-definition", 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); } class CUErrors: public CUFixtureBase, public Test { }; TEST_F(CUErrors, BadStmtList) { EXPECT_CALL(reporter_, BadLineInfoOffset(dummy_line_size_ + 10)).Times(1); ASSERT_TRUE(root_handler_ .StartCompilationUnit(0xc591d5b037543d7cULL, 0x11, 0xcd, 0x2d7d19546cf6590cULL, 3)); ASSERT_TRUE(root_handler_.StartRootDIE(0xae789dc102cfca54ULL, dwarf2reader::DW_TAG_compile_unit)); root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_strp, "compilation-unit-name"); root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, dwarf2reader::DW_FORM_ref4, dummy_line_size_ + 10); root_handler_.EndAttributes(); root_handler_.Finish(); } TEST_F(CUErrors, NoLineSection) { EXPECT_CALL(reporter_, MissingSection(".debug_line")).Times(1); PushLine(0x88507fb678052611ULL, 0x42c8e9de6bbaa0faULL, "line-file", 64472290); // Delete the entry for .debug_line added by the fixture class's constructor. file_context_.ClearSectionMapForTest(); StartCU(); root_handler_.Finish(); } TEST_F(CUErrors, BadDwarfVersion1) { // Kludge: satisfy reporter_'s expectation. reporter_.SetCUName("compilation-unit-name"); ASSERT_FALSE(root_handler_ .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, 0xc9de224ccb99ac3eULL, 1)); } TEST_F(CUErrors, GoodDwarfVersion2) { // Kludge: satisfy reporter_'s expectation. reporter_.SetCUName("compilation-unit-name"); ASSERT_TRUE(root_handler_ .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, 0xc9de224ccb99ac3eULL, 2)); } TEST_F(CUErrors, GoodDwarfVersion3) { // Kludge: satisfy reporter_'s expectation. reporter_.SetCUName("compilation-unit-name"); ASSERT_TRUE(root_handler_ .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, 0xc9de224ccb99ac3eULL, 3)); } TEST_F(CUErrors, BadCURootDIETag) { // Kludge: satisfy reporter_'s expectation. reporter_.SetCUName("compilation-unit-name"); ASSERT_TRUE(root_handler_ .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, 0xc9de224ccb99ac3eULL, 3)); ASSERT_FALSE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, dwarf2reader::DW_TAG_subprogram)); } // Tests for DwarfCUToModule::Reporter. These just produce (or fail to // produce) output, so their results need to be checked by hand. struct Reporter: public Test { Reporter() : reporter("filename", 0x123456789abcdef0ULL), function("function name", 0x19c45c30770c1eb0ULL), file("source file name") { reporter.SetCUName("compilation-unit-name"); Module::Range range(0x19c45c30770c1eb0ULL, 0x89808a5bdfa0a6a3ULL); function.ranges.push_back(range); function.parameter_size = 0x6a329f18683dcd51ULL; 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