diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc index d129fba4..e3bd9414 100644 --- a/src/common/dwarf_cu_to_module.cc +++ b/src/common/dwarf_cu_to_module.cc @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -58,6 +59,7 @@ using std::map; using std::pair; using std::sort; using std::vector; +using std::unique_ptr; // Data provided by a DWARF specification DIE. // @@ -98,6 +100,71 @@ struct AbstractOrigin { typedef map AbstractOriginByOffset; +using InlineOriginByOffset = map; + +class InlineOriginMap { + public: + Module::InlineOrigin* GetOrCreateInlineOrigin(uint64_t offset, + const string& name) { + uint64_t specification_offset = references_[offset]; + if (inline_origins_.find(specification_offset) != inline_origins_.end()) { + if (inline_origins_[specification_offset]->name == "") { + inline_origins_[specification_offset]->name = name; + } + return inline_origins_[specification_offset]; + } + inline_origins_[specification_offset] = new Module::InlineOrigin(name); + return inline_origins_[specification_offset]; + } + + // offset is the offset of a DW_TAG_subprogram. specification_offset is the + // value of its DW_AT_specification or equals to offset if DW_AT_specification + // doesn't exist in that DIE. + void SetReference(uint64_t offset, uint64_t specification_offset) { + // If we haven't seen this doesn't exist in reference map, always add it. + if (references_.find(offset) == references_.end()) { + references_[offset] = specification_offset; + return; + } + // If offset equals specification_offset and offset exists in references_, + // there is no need to update the references_ map. This early return is + // necessary because the call to erase in following if will remove the entry + // of specification_offset in inline_origins_. + // If specification_offset equals to references_[offset], it might be + // duplicate debug info. + if (offset == specification_offset || + specification_offset == references_[offset]) + return; + + // Fix up mapping in inline_origins_. + auto remove = inline_origins_.find(references_[offset]); + if (remove != inline_origins_.end()) { + inline_origins_[specification_offset] = remove->second; + inline_origins_.erase(remove); + } + references_[offset] = specification_offset; + } + + void AssignFilesToInlineOrigins(vector& inline_origin_offsets, + Module::File* file) { + for (uint64_t offset : inline_origin_offsets) + if (references_.find(offset) != references_.end()) { + auto origin = inline_origins_.find(references_[offset]); + if (origin != inline_origins_.end()) + origin->second->file = file; + } + } + + private: + // A map from a DW_TAG_subprogram's offset to the DW_TAG_subprogram. + InlineOriginByOffset inline_origins_; + + // A map from a DW_TAG_subprogram's offset to the offset of its specification + // or abstract origin subprogram. The set of values in this map should always + // be the same set of keys in inline_origins_. + map references_; +}; + // Data global to the DWARF-bearing file that is private to the // DWARF-to-Module process. struct DwarfCUToModule::FilePrivate { @@ -130,6 +197,8 @@ struct DwarfCUToModule::FilePrivate { // Keep a list of forward references from DW_AT_abstract_origin and // DW_AT_specification attributes so names can be fixed up. std::map forward_ref_die_to_func; + + InlineOriginMap inline_origin_map; }; DwarfCUToModule::FileContext::FileContext(const string& filename, @@ -272,6 +341,9 @@ struct DwarfCUToModule::CUContext { // A map of function pointers to the its forward specification DIE's offset. map spec_function_offsets; + + // From file index to vector of subprogram's offset in this CU. + map> inline_origins; }; // Information about the context of a particular DIE. This is for @@ -304,7 +376,8 @@ class DwarfCUToModule::GenericDIEHandler: public DIEHandler { offset_(offset), declaration_(false), specification_(NULL), - forward_ref_die_offset_(0) { } + abstract_origin_(NULL), + forward_ref_die_offset_(0), specification_offset_(0) { } // Derived classes' ProcessAttributeUnsigned can defer to this to // handle DW_AT_declaration, or simply not override it. @@ -356,11 +429,19 @@ class DwarfCUToModule::GenericDIEHandler: public DIEHandler { // Otherwise, this is NULL. Specification* specification_; + // If this DIE has a DW_AT_abstract_origin attribute, this is the + // AbstractOrigin structure for the DIE the attribute refers to. + // Otherwise, this is NULL. + const AbstractOrigin* abstract_origin_; + // If this DIE has a DW_AT_specification or DW_AT_abstract_origin and it is a // forward reference, no Specification will be available. Track the reference // to be fixed up when the DIE is parsed. uint64_t forward_ref_die_offset_; + // The root offset of Specification or abstract origin. + uint64_t specification_offset_; + // The value of the DW_AT_name attribute, or the empty string if the // DIE has no such attribute. string name_attribute_; @@ -412,6 +493,21 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( } else { cu_context_->reporter->UnknownSpecification(offset_, data); } + specification_offset_ = data; + break; + } + case DW_AT_abstract_origin: { + const AbstractOriginByOffset& origins = + cu_context_->file_context->file_private_->origins; + AbstractOriginByOffset::const_iterator origin = origins.find(data); + if (origin != origins.end()) { + abstract_origin_ = &(origin->second); + } else if (data > offset_) { + forward_ref_die_offset_ = data; + } else { + cu_context_->reporter->UnknownAbstractOrigin(offset_, data); + } + specification_offset_ = data; break; } default: break; @@ -519,6 +615,163 @@ string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() { return return_value; } +static bool IsEmptyRange(const vector& ranges) { + uint64_t size = accumulate(ranges.cbegin(), ranges.cend(), 0, + [](uint64_t total, Module::Range entry) { + return total + entry.size; + } + ); + + return size == 0; +} + + +// A handler for DW_TAG_inlined_subroutine DIEs. +class DwarfCUToModule::InlineHandler : public GenericDIEHandler { + public: + InlineHandler(CUContext* cu_context, + DIEContext* parent_context, + uint64_t offset, + int inline_nest_level, + vector>& inlines) + : GenericDIEHandler(cu_context, parent_context, offset), + low_pc_(0), + high_pc_(0), + high_pc_form_(DW_FORM_addr), + ranges_form_(DW_FORM_sec_offset), + ranges_data_(0), + call_site_line_(0), + inline_nest_level_(inline_nest_level), + inlines_(inlines) {} + + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data); + DIEHandler* FindChildHandler(uint64_t offset, enum DwarfTag tag); + bool EndAttributes(); + void Finish(); + + private: + // The fully-qualified name, as derived from name_attribute_, + // specification_, parent_context_. Computed in EndAttributes. + string name_; + uint64_t low_pc_; // DW_AT_low_pc + uint64_t high_pc_; // DW_AT_high_pc + DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address. + DwarfForm ranges_form_; // DW_FORM_sec_offset or DW_FORM_rnglistx + uint64_t ranges_data_; // DW_AT_ranges + int call_site_line_; + int inline_nest_level_; + // A vector of inlines in the same nest level. It's owned by its parent + // function/inline. At Finish(), add this inline into the vector. + vector>& inlines_; + // A vector of child inlines. + vector> child_inlines_; +}; + +void DwarfCUToModule::InlineHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + switch (attr) { + case DW_AT_low_pc: + low_pc_ = data; + break; + case DW_AT_high_pc: + high_pc_form_ = form; + high_pc_ = data; + break; + case DW_AT_ranges: + ranges_data_ = data; + ranges_form_ = form; + break; + case DW_AT_call_line: + call_site_line_ = data; + break; + default: + GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); + break; + } +} + +DIEHandler* DwarfCUToModule::InlineHandler::FindChildHandler( + uint64_t offset, + enum DwarfTag tag) { + switch (tag) { + case DW_TAG_inlined_subroutine: + return new InlineHandler(cu_context_, new DIEContext(), offset, + inline_nest_level_ + 1, child_inlines_); + default: + return NULL; + } +} + +bool DwarfCUToModule::InlineHandler::EndAttributes() { + if (abstract_origin_) + name_ = abstract_origin_->name; + if (name_.empty()) { + // We haven't seen the abstract origin yet, which might appears later and we + // will fix the name after calling + // InlineOriginMap::GetOrCreateInlineOrigin with right name. + name_ = ""; + } + return true; +} + +void DwarfCUToModule::InlineHandler::Finish() { + vector ranges; + + if (low_pc_ && high_pc_) { + if (high_pc_form_ != DW_FORM_addr && + high_pc_form_ != DW_FORM_GNU_addr_index && + high_pc_form_ != DW_FORM_addrx && + high_pc_form_ != DW_FORM_addrx1 && + high_pc_form_ != DW_FORM_addrx2 && + high_pc_form_ != DW_FORM_addrx3 && + high_pc_form_ != DW_FORM_addrx4) { + high_pc_ += low_pc_; + } + + Module::Range range(low_pc_, high_pc_ - low_pc_); + ranges.push_back(range); + } else { + RangesHandler* ranges_handler = cu_context_->ranges_handler; + if (ranges_handler) { + RangeListReader::CURangesInfo cu_info; + if (cu_context_->AssembleRangeListInfo(&cu_info)) { + if (!ranges_handler->ReadRanges(ranges_form_, ranges_data_, + &cu_info, &ranges)) { + ranges.clear(); + cu_context_->reporter->MalformedRangeList(ranges_data_); + } + } else { + cu_context_->reporter->MissingRanges(); + } + } + } + + // Malformed DWARF may omit the name, but all Module::Functions must + // have names. + // If we have a forward reference to a DW_AT_specification or + // DW_AT_abstract_origin, then don't warn, the name will be fixed up + // later + if (name_.empty() && forward_ref_die_offset_ == 0) + cu_context_->reporter->UnnamedFunction(offset_); + + // Every DW_TAG_inlined_subroutine should have a DW_AT_abstract_origin. + assert(specification_offset_ != 0); + + cu_context_->file_context->file_private_->inline_origin_map.SetReference( + specification_offset_, specification_offset_); + Module::InlineOrigin* origin = + cu_context_->file_context->file_private_->inline_origin_map + .GetOrCreateInlineOrigin(specification_offset_, name_); + unique_ptr in = std::make_unique( + origin, ranges, call_site_line_, inline_nest_level_, + std::move(child_inlines_)); + inlines_.push_back(std::move(in)); +} + // A handler class for DW_TAG_subprogram DIEs. class DwarfCUToModule::FuncHandler: public GenericDIEHandler { public: @@ -527,7 +780,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { : GenericDIEHandler(cu_context, parent_context, offset), low_pc_(0), high_pc_(0), high_pc_form_(DW_FORM_addr), ranges_form_(DW_FORM_sec_offset), ranges_data_(0), - abstract_origin_(NULL), inline_(false) { } + decl_file_data_(UINT64_MAX), inline_(false) { } void ProcessAttributeUnsigned(enum DwarfAttribute attr, enum DwarfForm form, @@ -535,10 +788,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { void ProcessAttributeSigned(enum DwarfAttribute attr, enum DwarfForm form, int64_t data); - void ProcessAttributeReference(enum DwarfAttribute attr, - enum DwarfForm form, - uint64_t data); - + DIEHandler* FindChildHandler(uint64_t offset, enum DwarfTag tag); bool EndAttributes(); void Finish(); @@ -550,8 +800,10 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address. DwarfForm ranges_form_; // DW_FORM_sec_offset or DW_FORM_rnglistx uint64_t ranges_data_; // DW_AT_ranges - const AbstractOrigin* abstract_origin_; + // DW_AT_decl_file, value of UINT64_MAX means undefined. + uint64_t decl_file_data_; bool inline_; + vector> child_inlines_; }; void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( @@ -573,7 +825,9 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( ranges_data_ = data; ranges_form_ = form; break; - + case DW_AT_decl_file: + decl_file_data_ = data; + break; default: GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); break; @@ -595,27 +849,15 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeSigned( } } -void DwarfCUToModule::FuncHandler::ProcessAttributeReference( - enum DwarfAttribute attr, - enum DwarfForm form, - uint64_t data) { - switch (attr) { - case DW_AT_abstract_origin: { - const AbstractOriginByOffset& origins = - cu_context_->file_context->file_private_->origins; - AbstractOriginByOffset::const_iterator origin = origins.find(data); - if (origin != origins.end()) { - abstract_origin_ = &(origin->second); - } else if (data > offset_) { - forward_ref_die_offset_ = data; - } else { - cu_context_->reporter->UnknownAbstractOrigin(offset_, data); - } - break; - } +DIEHandler* DwarfCUToModule::FuncHandler::FindChildHandler( + uint64_t offset, + enum DwarfTag tag) { + switch (tag) { + case DW_TAG_inlined_subroutine: + return new InlineHandler(cu_context_, new DIEContext(), offset, 0, + child_inlines_); default: - GenericDIEHandler::ProcessAttributeReference(attr, form, data); - break; + return NULL; } } @@ -628,16 +870,6 @@ bool DwarfCUToModule::FuncHandler::EndAttributes() { return true; } -static bool IsEmptyRange(const vector& ranges) { - uint64_t size = accumulate(ranges.cbegin(), ranges.cend(), 0, - [](uint64_t total, Module::Range entry) { - return total + entry.size; - } - ); - - return size == 0; -} - void DwarfCUToModule::FuncHandler::Finish() { vector ranges; @@ -683,11 +915,12 @@ void DwarfCUToModule::FuncHandler::Finish() { } } + bool empty_range = IsEmptyRange(ranges); // Did we collect the information we need? Not all DWARF function // entries are non-empty (for example, inlined functions that were never // used), but all the ones we're interested in cover a non-empty range of // bytes. - if (!IsEmptyRange(ranges)) { + if (!empty_range) { low_pc_ = ranges.front().address; // Malformed DWARF may omit the name, but all Module::Functions must @@ -721,11 +954,27 @@ void DwarfCUToModule::FuncHandler::Finish() { cu_context_->spec_function_offsets[cu_context_->functions.back()] = forward_ref_die_offset_; } + + cu_context_->functions.back()->inlines.swap(child_inlines_); } } else if (inline_) { AbstractOrigin origin(name_); cu_context_->file_context->file_private_->origins[offset_] = origin; } + + // Only keep track of DW_TAG_subprogram which have the attributes we are + // interested. + if (!empty_range || inline_ || decl_file_data_ != UINT64_MAX) { + uint64_t offset = + specification_offset_ != 0 ? specification_offset_ : offset_; + cu_context_->file_context->file_private_->inline_origin_map.SetReference( + offset_, offset); + cu_context_->file_context->file_private_->inline_origin_map + .GetOrCreateInlineOrigin(offset_, + name_.empty() ? "" : name_); + if (decl_file_data_ != UINT64_MAX) + cu_context_->inline_origins[decl_file_data_].push_back(offset_); + } } // A handler for DIEs that contain functions and contribute a @@ -1041,7 +1290,7 @@ void DwarfCUToModule::ReadSourceLines(uint64_t offset) { line_section_start, line_section_length, string_section_start, string_section_length, line_string_section_start, line_string_section_length, - cu_context_->file_context->module_, &lines_); + cu_context_->file_context->module_, &lines_, &files_); } namespace { @@ -1300,6 +1549,14 @@ void DwarfCUToModule::AssignLinesToFunctions() { } } +void DwarfCUToModule::AssignFilesToInlines() { + for (auto iter : files_) { + cu_context_->file_context->file_private_->inline_origin_map + .AssignFilesToInlineOrigins(cu_context_->inline_origins[iter.first], + iter.second); + } +} + void DwarfCUToModule::Finish() { // Assembly language files have no function data, and that gives us // no place to store our line numbers (even though the GNU toolchain @@ -1318,6 +1575,8 @@ void DwarfCUToModule::Finish() { // Dole out lines to the appropriate functions. AssignLinesToFunctions(); + AssignFilesToInlines(); + // Add our functions, which now have source lines assigned to them, // to module_, and remove duplicate functions. for (Module::Function* func : *functions) diff --git a/src/common/dwarf_cu_to_module.h b/src/common/dwarf_cu_to_module.h index 1320ccc2..99dcd879 100644 --- a/src/common/dwarf_cu_to_module.h +++ b/src/common/dwarf_cu_to_module.h @@ -156,7 +156,8 @@ class DwarfCUToModule: public RootDIEHandler { uint64_t string_section_length, const uint8_t* line_string_section, uint64_t line_string_length, - Module* module, vector* lines) = 0; + Module* module, vector* lines, + map* files) = 0; }; // The interface DwarfCUToModule uses to report warnings. The member @@ -289,6 +290,7 @@ class DwarfCUToModule: public RootDIEHandler { struct Specification; class GenericDIEHandler; class FuncHandler; + class InlineHandler; class NamedScopeHandler; // A map from section offsets to specifications. @@ -309,6 +311,8 @@ class DwarfCUToModule: public RootDIEHandler { // lines belong to which functions, beyond their addresses.) void AssignLinesToFunctions(); + void AssignFilesToInlines(); + // The only reason cu_context_ and child_context_ are pointers is // that we want to keep their definitions private to // dwarf_cu_to_module.cc, instead of listing them all here. They are @@ -335,6 +339,9 @@ class DwarfCUToModule: public RootDIEHandler { // during parsing. Then, in Finish, we call AssignLinesToFunctions // to dole them out to the appropriate functions. vector lines_; + + // The map from file index to File* in this CU. + std::map files_; }; } // namespace google_breakpad diff --git a/src/common/dwarf_cu_to_module_unittest.cc b/src/common/dwarf_cu_to_module_unittest.cc index 2ce69d73..cb943ae3 100644 --- a/src/common/dwarf_cu_to_module_unittest.cc +++ b/src/common/dwarf_cu_to_module_unittest.cc @@ -67,12 +67,13 @@ using ::testing::ValuesIn; 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, + MOCK_METHOD9(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)); + Module* module, vector* lines, + std::map* files)); }; class MockWarningReporter: public DwarfCUToModule::WarningReporter { @@ -122,7 +123,8 @@ class CUFixtureBase { uint64_t string_section_length, const uint8_t* line_string_section, uint64_t line_string_section_length, - Module *module, vector* lines) { + Module *module, vector* lines, + std::map* files) { lines->insert(lines->end(), lines_->begin(), lines_->end()); } private: @@ -155,7 +157,7 @@ class CUFixtureBase { // 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); + EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_,_,_,_,_,_)).Times(0); // The handler will consult this section map to decide what to // pass to our line reader. @@ -341,7 +343,7 @@ void CUFixtureBase::StartCU() { EXPECT_CALL(line_reader_, ReadProgram(&dummy_line_program_[0], dummy_line_size_, _,_,_,_, - &module_, _)) + &module_, _,_)) .Times(AtMost(1)) .WillOnce(DoAll(Invoke(appender_), Return())); ASSERT_TRUE(root_handler_ @@ -1517,7 +1519,7 @@ TEST_F(Specifications, InterCU) { DwarfCUToModule::FileContext fc("dwarf-filename", &m, true); EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); MockLineToModuleHandler lr; - EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_)).Times(0); + EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_,_)).Times(0); // Kludge: satisfy reporter_'s expectation. reporter_.SetCUName("compilation-unit-name"); @@ -1576,7 +1578,7 @@ TEST_F(Specifications, UnhandledInterCU) { DwarfCUToModule::FileContext fc("dwarf-filename", &m, false); EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); MockLineToModuleHandler lr; - EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_)).Times(0); + EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_,_)).Times(0); // Kludge: satisfy reporter_'s expectation. reporter_.SetCUName("compilation-unit-name"); diff --git a/src/common/dwarf_line_to_module.cc b/src/common/dwarf_line_to_module.cc index fe808c08..83bb8f15 100644 --- a/src/common/dwarf_line_to_module.cc +++ b/src/common/dwarf_line_to_module.cc @@ -100,7 +100,7 @@ void DwarfLineToModule::DefineFile(const string& name, int32_t file_num, // Find a Module::File object of the given name, and add it to the // file table. - files_[file_num] = module_->FindFile(full_name); + (*files_)[file_num] = module_->FindFile(full_name); } void DwarfLineToModule::AddLine(uint64_t address, uint64_t length, @@ -122,7 +122,7 @@ void DwarfLineToModule::AddLine(uint64_t address, uint64_t length, } // Find the source file being referred to. - Module::File *file = files_[file_num]; + Module::File *file = (*files_)[file_num]; if (!file) { if (!warned_bad_file_number_) { fprintf(stderr, "warning: DWARF line number data refers to " diff --git a/src/common/dwarf_line_to_module.h b/src/common/dwarf_line_to_module.h index 8e7a0b0d..da2c5f0e 100644 --- a/src/common/dwarf_line_to_module.h +++ b/src/common/dwarf_line_to_module.h @@ -120,16 +120,19 @@ class DwarfLineToModule: public LineInfoHandler { // end of the address space, we clip it. It's up to our client to // sort out which lines belong to which functions; we don't add them // to any particular function in MODULE ourselves. - DwarfLineToModule(Module *module, const string& compilation_dir, - vector* lines) + DwarfLineToModule(Module* module, + const string& compilation_dir, + vector* lines, + std::map* files) : module_(module), compilation_dir_(compilation_dir), lines_(lines), + files_(files), highest_file_number_(-1), omitted_line_end_(0), warned_bad_file_number_(false), warned_bad_directory_number_(false) { } - + ~DwarfLineToModule() { } void DefineDir(const string& name, uint32_t dir_num); @@ -167,12 +170,12 @@ class DwarfLineToModule: public LineInfoHandler { DirectoryTable directories_; // A table mapping file numbers to Module::File pointers. - FileTable files_; + FileTable* files_; // The highest file number we've seen so far, or -1 if we've seen // none. Used for dynamically defined file numbers. int32_t highest_file_number_; - + // This is the ending address of the last line we omitted, or zero if we // didn't omit the previous line. It is zero before we have received any // AddLine calls. diff --git a/src/common/dwarf_line_to_module_unittest.cc b/src/common/dwarf_line_to_module_unittest.cc index 90b6570d..34cb02ed 100644 --- a/src/common/dwarf_line_to_module_unittest.cc +++ b/src/common/dwarf_line_to_module_unittest.cc @@ -45,7 +45,8 @@ using google_breakpad::Module; TEST(SimpleModule, One) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineFile("file1", 0x30bf0f27, 0, 0, 0); h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, @@ -66,7 +67,8 @@ TEST(SimpleModule, One) { TEST(SimpleModule, Many) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineDir("directory1", 0x838299ab); h.DefineDir("directory2", 0xf85de023); @@ -126,7 +128,8 @@ TEST(SimpleModule, Many) { TEST(Filenames, Absolute) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineDir("directory1", 1); h.DefineFile("/absolute", 1, 1, 0, 0); @@ -144,7 +147,8 @@ TEST(Filenames, Absolute) { TEST(Filenames, Relative) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineDir("directory1", 1); h.DefineFile("relative", 1, 1, 0, 0); @@ -162,7 +166,8 @@ TEST(Filenames, Relative) { TEST(Filenames, StrangeFile) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineDir("directory1", 1); h.DefineFile("", 1, 1, 0, 0); @@ -175,7 +180,8 @@ TEST(Filenames, StrangeFile) { TEST(Filenames, StrangeDirectory) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineDir("", 1); h.DefineFile("file1", 1, 1, 0, 0); @@ -188,7 +194,8 @@ TEST(Filenames, StrangeDirectory) { TEST(Filenames, StrangeDirectoryAndFile) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineDir("", 1); h.DefineFile("", 1, 1, 0, 0); @@ -203,7 +210,8 @@ TEST(Filenames, StrangeDirectoryAndFile) { TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "src/build", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "src/build", &lines, &cu_files); h.DefineDir("Dir", 1); h.DefineFile("File", 1, 0, 0, 0); @@ -219,7 +227,8 @@ TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) { TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "src/build", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "src/build", &lines, &cu_files); h.DefineDir("Dir", 1); h.DefineFile("File", 1, 1, 0, 0); @@ -235,7 +244,8 @@ TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) { TEST(Filenames, IncludeDirectoryAbsolute) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "src/build", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "src/build", &lines, &cu_files); h.DefineDir("/Dir", 1); h.DefineFile("File", 1, 1, 0, 0); @@ -251,7 +261,8 @@ TEST(Filenames, IncludeDirectoryAbsolute) { TEST(ModuleErrors, DirectoryZero) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineDir("directory0", 0); // should be ignored h.DefineFile("relative", 1, 0, 0, 0); @@ -267,7 +278,8 @@ TEST(ModuleErrors, DirectoryZero) { TEST(ModuleErrors, BadFileNumber) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineFile("relative", 1, 0, 0, 0); h.AddLine(1, 1, 2, 0, 0); // bad file number @@ -281,7 +293,8 @@ TEST(ModuleErrors, BadFileNumber) { TEST(ModuleErrors, BadDirectoryNumber) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineDir("directory1", 1); h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number @@ -296,7 +309,8 @@ TEST(ModuleErrors, BadDirectoryNumber) { TEST(ModuleErrors, EmptyLine) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineFile("filename1", 1, 0, 0, 0); h.AddLine(1, 0, 1, 0, 0); @@ -309,7 +323,8 @@ TEST(ModuleErrors, EmptyLine) { TEST(ModuleErrors, BigLine) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineFile("filename1", 1, 0, 0, 0); h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0); @@ -326,7 +341,8 @@ TEST(ModuleErrors, BigLine) { TEST(Omitted, DroppedThenGood) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineFile("filename1", 1, 0, 0, 0); h.AddLine(0, 10, 1, 83816211, 0); // should be omitted @@ -339,7 +355,8 @@ TEST(Omitted, DroppedThenGood) { TEST(Omitted, GoodThenDropped) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineFile("filename1", 1, 0, 0, 0); h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded @@ -352,7 +369,8 @@ TEST(Omitted, GoodThenDropped) { TEST(Omitted, Mix1) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineFile("filename1", 1, 0, 0, 0); h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded @@ -373,7 +391,8 @@ TEST(Omitted, Mix1) { TEST(Omitted, Mix2) { Module m("name", "os", "architecture", "id"); vector lines; - DwarfLineToModule h(&m, "/", &lines); + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); h.DefineFile("filename1", 1, 0, 0, 0); h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 5909d6ba..92a260ba 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -260,13 +260,16 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler { void StartCompilationUnit(const string& compilation_dir) { compilation_dir_ = compilation_dir; } - void ReadProgram(const uint8_t* program, uint64_t length, + void ReadProgram(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, std::vector* lines) { - DwarfLineToModule handler(module, compilation_dir_, lines); + Module* module, + std::vector* lines, + std::map* files) { + DwarfLineToModule handler(module, compilation_dir_, lines, files); google_breakpad::LineInfo parser(program, length, byte_reader_, string_section, string_section_length, line_string_section, diff --git a/src/common/mac/dump_syms.cc b/src/common/mac/dump_syms.cc index e30d8ea9..3592e4bb 100644 --- a/src/common/mac/dump_syms.cc +++ b/src/common/mac/dump_syms.cc @@ -351,15 +351,18 @@ class DumpSymbols::DumperLineToModule: compilation_dir_ = compilation_dir; } - void ReadProgram(const uint8_t* program, uint64_t length, + void ReadProgram(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) { - DwarfLineToModule handler(module, compilation_dir_, lines); - LineInfo parser(program, length, byte_reader_, - nullptr, 0, nullptr, 0, &handler); + Module* module, + vector* lines, + std::map* files) { + DwarfLineToModule handler(module, compilation_dir_, lines, files); + LineInfo parser(program, length, byte_reader_, nullptr, 0, + nullptr, 0, &handler); parser.Start(); } private: diff --git a/src/common/module.cc b/src/common/module.cc index 0ecf6ca6..eccd01f0 100644 --- a/src/common/module.cc +++ b/src/common/module.cc @@ -38,14 +38,16 @@ #include #include +#include #include +#include #include namespace google_breakpad { using std::dec; using std::hex; - +using std::unique_ptr; Module::Module(const string& name, const string& os, const string& architecture, const string& id, @@ -214,6 +216,13 @@ void Module::AssignSourceIds() { line_it != func->lines.end(); ++line_it) line_it->file->source_id = 0; } + // Also mark all files cited by inline functions by setting each one's source + // id to zero. + for (InlineOrigin* origin : inline_origins_) + // There are some artificial inline functions which don't belong to + // any file. Those will have file id -1. + if (origin->file) + origin->file->source_id = 0; // Finally, assign source ids to those files that have been marked. // We could have just assigned source id numbers while traversing @@ -227,6 +236,33 @@ void Module::AssignSourceIds() { } } +static void InlineDFS( + vector>& inlines, + std::function&)> const& forEach) { + for (unique_ptr& in : inlines) { + forEach(in); + InlineDFS(in->child_inlines, forEach); + } +} + +void Module::CreateInlineOrigins() { + // Only add origins that have file and deduplicate origins with same name and + // file id by doing a DFS. + auto addInlineOrigins = [&](unique_ptr &in) { + auto it = inline_origins_.find(in->origin); + if (it == inline_origins_.end()) + inline_origins_.insert(in->origin); + else + in->origin = *it; + }; + for (Function* func : functions_) + InlineDFS(func->inlines, addInlineOrigins); + int next_id = 0; + for (InlineOrigin* origin: inline_origins_) { + origin->id = next_id++; + } +} + bool Module::ReportError() { fprintf(stderr, "error writing symbol file: %s\n", strerror(errno)); @@ -267,6 +303,8 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) { } if (symbol_data & SYMBOLS_AND_FILES) { + if (symbol_data & INLINES) + CreateInlineOrigins(); AssignSourceIds(); // Write out files. @@ -279,8 +317,17 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) { return ReportError(); } } + // Write out inline origins. + if (symbol_data & INLINES) { + for (InlineOrigin* origin : inline_origins_) { + stream << "INLINE_ORIGIN " << origin->id << " " << origin->getFileID() + << " " << origin->name << "\n"; + if (!stream.good()) + return ReportError(); + } + } - // Write out functions and their lines. + // Write out functions and their inlines and lines. for (FunctionSet::const_iterator func_it = functions_.begin(); func_it != functions_.end(); ++func_it) { Function* func = *func_it; @@ -296,6 +343,21 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) { if (!stream.good()) return ReportError(); + // Write out inlines. + if (symbol_data & INLINES) { + auto write_inline = [&](unique_ptr& in) { + stream << "INLINE "; + stream << in->inline_nest_level << " " << in->call_site_line << " " + << in->origin->id << hex; + for (const Range& r : in->ranges) + stream << " " << (r.address - load_address_) << " " << r.size; + stream << dec << "\n"; + }; + InlineDFS(func->inlines, write_inline); + if (!stream.good()) + return ReportError(); + } + while ((line_it != func->lines.end()) && (line_it->address >= range_it->address) && (line_it->address < (range_it->address + range_it->size))) { diff --git a/src/common/module.h b/src/common/module.h index f2fff490..e8678914 100644 --- a/src/common/module.h +++ b/src/common/module.h @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,8 @@ class Module { static constexpr uint64_t kMaxAddress = std::numeric_limits
::max(); struct File; struct Function; + struct InlineOrigin; + struct Inline; struct Line; struct Extern; @@ -120,6 +123,50 @@ class Module { // Source lines belonging to this function, sorted by increasing // address. vector lines; + + // Inlined call sites belonging to this functions. + vector> inlines; + }; + + struct InlineOrigin { + InlineOrigin(const string& name): id(-1), name(name), file(NULL) {} + + // A unique id for each InlineOrigin object. INLINE records use the id to + // refer to its INLINE_ORIGIN record. + int id; + + // The inlined function's name. + string name; + + File* file; + + int getFileID() const { return file ? file->source_id : -1; } + }; + + // A inlined call site. + struct Inline { + Inline(InlineOrigin* origin, + const vector& ranges, + int call_site_line, + int inline_nest_level, + vector> child_inlines) + : origin(origin), + ranges(ranges), + call_site_line(call_site_line), + inline_nest_level(inline_nest_level), + child_inlines(std::move(child_inlines)) {} + + InlineOrigin* origin; + + // The list of addresses and sizes. + vector ranges; + + int call_site_line; + + int inline_nest_level; + + // A list of inlines which are children of this inline. + vector> child_inlines; }; // A source line. @@ -179,6 +226,14 @@ class Module { } }; + struct InlineOriginCompare { + bool operator() (const InlineOrigin* lhs, const InlineOrigin* rhs) const { + if (lhs->getFileID() == rhs->getFileID()) + return lhs->name < rhs->name; + return lhs->getFileID() < rhs->getFileID(); + } + }; + struct ExternCompare { bool operator() (const Extern* lhs, const Extern* rhs) const { return lhs->address < rhs->address; @@ -275,6 +330,10 @@ class Module { // symbol file, at which point we omit any unused files. void AssignSourceIds(); + // This function should be called before AssignSourceIds() to get the set of + // valid InlineOrigins*. + void CreateInlineOrigins(); + // Call AssignSourceIds, and write this module to STREAM in the // breakpad symbol format. Return true if all goes well, or false if // an error occurs. This method writes out: @@ -334,6 +393,9 @@ class Module { // A set containing Function structures, sorted by address. typedef set FunctionSet; + // A set containing Function structures, sorted by address. + typedef set InlineOriginSet; + // A set containing Extern structures, sorted by address. typedef set ExternSet; @@ -342,6 +404,7 @@ class Module { // point to. FileByNameMap files_; // This module's source files. FunctionSet functions_; // This module's functions. + InlineOriginSet inline_origins_; // This module's inline origins. // The module owns all the call frame info entries that have been // added to it. diff --git a/src/tools/linux/dump_syms/dump_syms.cc b/src/tools/linux/dump_syms/dump_syms.cc index a562bffb..b0f56e95 100644 --- a/src/tools/linux/dump_syms/dump_syms.cc +++ b/src/tools/linux/dump_syms/dump_syms.cc @@ -50,6 +50,7 @@ int usage(const char* self) { fprintf(stderr, "Options:\n"); fprintf(stderr, " -i: Output module header information only.\n"); fprintf(stderr, " -c Do not generate CFI section\n"); + fprintf(stderr, " -d Generate INLINE/INLINE_ORIGIN records\n"); fprintf(stderr, " -r Do not handle inter-compilation " "unit references\n"); fprintf(stderr, " -v Print all warnings to stderr\n"); @@ -64,6 +65,7 @@ int main(int argc, char** argv) { return usage(argv[0]); bool header_only = false; bool cfi = true; + bool handle_inlines = false; bool handle_inter_cu_refs = true; bool log_to_stderr = false; std::string obj_name; @@ -75,6 +77,8 @@ int main(int argc, char** argv) { header_only = true; } else if (strcmp("-c", argv[arg_index]) == 0) { cfi = false; + } else if (strcmp("-d", argv[arg_index]) == 0) { + handle_inlines = true; } else if (strcmp("-r", argv[arg_index]) == 0) { handle_inter_cu_refs = false; } else if (strcmp("-v", argv[arg_index]) == 0) { @@ -127,8 +131,8 @@ int main(int argc, char** argv) { return 1; } } else { - SymbolData symbol_data = - INLINES | (cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES; + SymbolData symbol_data = (handle_inlines ? INLINES : NO_DATA) | + (cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES; google_breakpad::DumpOptions options(symbol_data, handle_inter_cu_refs); if (!WriteSymbolFile(binary, obj_name, obj_os, debug_dirs, options, std::cout)) {