Add INLINE and INLINE_ORIGIN records to symbol file.

The size of symbol file for chrome binary increased from 577 MB to
1205 MB. There are 7,453,748 INLINE records and 1,268,493 INLINE_ORIGIN
records.

Bug: 1190878
Change-Id: I802ec1b4574c14f74ff80d0f69daf3c81085778a
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2915828
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Zequan Wu 2021-08-03 14:26:38 -07:00 committed by Joshua Peraza
parent 0d9416d3bf
commit 4f5b814790
11 changed files with 511 additions and 86 deletions

View file

@ -44,6 +44,7 @@
#include <stdio.h> #include <stdio.h>
#include <algorithm> #include <algorithm>
#include <memory>
#include <numeric> #include <numeric>
#include <utility> #include <utility>
@ -58,6 +59,7 @@ using std::map;
using std::pair; using std::pair;
using std::sort; using std::sort;
using std::vector; using std::vector;
using std::unique_ptr;
// Data provided by a DWARF specification DIE. // Data provided by a DWARF specification DIE.
// //
@ -98,6 +100,71 @@ struct AbstractOrigin {
typedef map<uint64_t, AbstractOrigin> AbstractOriginByOffset; typedef map<uint64_t, AbstractOrigin> AbstractOriginByOffset;
using InlineOriginByOffset = map<uint64_t, Module::InlineOrigin*>;
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 == "<name omitted>") {
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<uint64_t>& 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<uint64_t, uint64_t> references_;
};
// Data global to the DWARF-bearing file that is private to the // Data global to the DWARF-bearing file that is private to the
// DWARF-to-Module process. // DWARF-to-Module process.
struct DwarfCUToModule::FilePrivate { struct DwarfCUToModule::FilePrivate {
@ -130,6 +197,8 @@ struct DwarfCUToModule::FilePrivate {
// Keep a list of forward references from DW_AT_abstract_origin and // Keep a list of forward references from DW_AT_abstract_origin and
// DW_AT_specification attributes so names can be fixed up. // DW_AT_specification attributes so names can be fixed up.
std::map<uint64_t, Module::Function*> forward_ref_die_to_func; std::map<uint64_t, Module::Function*> forward_ref_die_to_func;
InlineOriginMap inline_origin_map;
}; };
DwarfCUToModule::FileContext::FileContext(const string& filename, 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. // A map of function pointers to the its forward specification DIE's offset.
map<Module::Function*, uint64_t> spec_function_offsets; map<Module::Function*, uint64_t> spec_function_offsets;
// From file index to vector of subprogram's offset in this CU.
map<uint64_t, vector<uint64_t>> inline_origins;
}; };
// Information about the context of a particular DIE. This is for // Information about the context of a particular DIE. This is for
@ -304,7 +376,8 @@ class DwarfCUToModule::GenericDIEHandler: public DIEHandler {
offset_(offset), offset_(offset),
declaration_(false), declaration_(false),
specification_(NULL), 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 // Derived classes' ProcessAttributeUnsigned can defer to this to
// handle DW_AT_declaration, or simply not override it. // handle DW_AT_declaration, or simply not override it.
@ -356,11 +429,19 @@ class DwarfCUToModule::GenericDIEHandler: public DIEHandler {
// Otherwise, this is NULL. // Otherwise, this is NULL.
Specification* specification_; 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 // 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 // forward reference, no Specification will be available. Track the reference
// to be fixed up when the DIE is parsed. // to be fixed up when the DIE is parsed.
uint64_t forward_ref_die_offset_; 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 // The value of the DW_AT_name attribute, or the empty string if the
// DIE has no such attribute. // DIE has no such attribute.
string name_attribute_; string name_attribute_;
@ -412,6 +493,21 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
} else { } else {
cu_context_->reporter->UnknownSpecification(offset_, data); 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; break;
} }
default: break; default: break;
@ -519,6 +615,163 @@ string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
return return_value; return return_value;
} }
static bool IsEmptyRange(const vector<Module::Range>& 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<unique_ptr<Module::Inline>>& 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<unique_ptr<Module::Inline>>& inlines_;
// A vector of child inlines.
vector<unique_ptr<Module::Inline>> 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_ = "<name omitted>";
}
return true;
}
void DwarfCUToModule::InlineHandler::Finish() {
vector<Module::Range> 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<Module::Inline> in = std::make_unique<Module::Inline>(
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. // A handler class for DW_TAG_subprogram DIEs.
class DwarfCUToModule::FuncHandler: public GenericDIEHandler { class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
public: public:
@ -527,7 +780,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
: GenericDIEHandler(cu_context, parent_context, offset), : GenericDIEHandler(cu_context, parent_context, offset),
low_pc_(0), high_pc_(0), high_pc_form_(DW_FORM_addr), low_pc_(0), high_pc_(0), high_pc_form_(DW_FORM_addr),
ranges_form_(DW_FORM_sec_offset), ranges_data_(0), 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, void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form, enum DwarfForm form,
@ -535,10 +788,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
void ProcessAttributeSigned(enum DwarfAttribute attr, void ProcessAttributeSigned(enum DwarfAttribute attr,
enum DwarfForm form, enum DwarfForm form,
int64_t data); int64_t data);
void ProcessAttributeReference(enum DwarfAttribute attr, DIEHandler* FindChildHandler(uint64_t offset, enum DwarfTag tag);
enum DwarfForm form,
uint64_t data);
bool EndAttributes(); bool EndAttributes();
void Finish(); 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 high_pc_form_; // DW_AT_high_pc can be length or address.
DwarfForm ranges_form_; // DW_FORM_sec_offset or DW_FORM_rnglistx DwarfForm ranges_form_; // DW_FORM_sec_offset or DW_FORM_rnglistx
uint64_t ranges_data_; // DW_AT_ranges 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_; bool inline_;
vector<unique_ptr<Module::Inline>> child_inlines_;
}; };
void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned(
@ -573,7 +825,9 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned(
ranges_data_ = data; ranges_data_ = data;
ranges_form_ = form; ranges_form_ = form;
break; break;
case DW_AT_decl_file:
decl_file_data_ = data;
break;
default: default:
GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data);
break; break;
@ -595,27 +849,15 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeSigned(
} }
} }
void DwarfCUToModule::FuncHandler::ProcessAttributeReference( DIEHandler* DwarfCUToModule::FuncHandler::FindChildHandler(
enum DwarfAttribute attr, uint64_t offset,
enum DwarfForm form, enum DwarfTag tag) {
uint64_t data) { switch (tag) {
switch (attr) { case DW_TAG_inlined_subroutine:
case DW_AT_abstract_origin: { return new InlineHandler(cu_context_, new DIEContext(), offset, 0,
const AbstractOriginByOffset& origins = child_inlines_);
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;
}
default: default:
GenericDIEHandler::ProcessAttributeReference(attr, form, data); return NULL;
break;
} }
} }
@ -628,16 +870,6 @@ bool DwarfCUToModule::FuncHandler::EndAttributes() {
return true; return true;
} }
static bool IsEmptyRange(const vector<Module::Range>& 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() { void DwarfCUToModule::FuncHandler::Finish() {
vector<Module::Range> ranges; vector<Module::Range> 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 // Did we collect the information we need? Not all DWARF function
// entries are non-empty (for example, inlined functions that were never // 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 // used), but all the ones we're interested in cover a non-empty range of
// bytes. // bytes.
if (!IsEmptyRange(ranges)) { if (!empty_range) {
low_pc_ = ranges.front().address; low_pc_ = ranges.front().address;
// Malformed DWARF may omit the name, but all Module::Functions must // 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()] = cu_context_->spec_function_offsets[cu_context_->functions.back()] =
forward_ref_die_offset_; forward_ref_die_offset_;
} }
cu_context_->functions.back()->inlines.swap(child_inlines_);
} }
} else if (inline_) { } else if (inline_) {
AbstractOrigin origin(name_); AbstractOrigin origin(name_);
cu_context_->file_context->file_private_->origins[offset_] = origin; 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 omitted>" : 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 // 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, line_section_start, line_section_length,
string_section_start, string_section_length, string_section_start, string_section_length,
line_string_section_start, line_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 { 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() { void DwarfCUToModule::Finish() {
// Assembly language files have no function data, and that gives us // Assembly language files have no function data, and that gives us
// no place to store our line numbers (even though the GNU toolchain // 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. // Dole out lines to the appropriate functions.
AssignLinesToFunctions(); AssignLinesToFunctions();
AssignFilesToInlines();
// Add our functions, which now have source lines assigned to them, // Add our functions, which now have source lines assigned to them,
// to module_, and remove duplicate functions. // to module_, and remove duplicate functions.
for (Module::Function* func : *functions) for (Module::Function* func : *functions)

View file

@ -156,7 +156,8 @@ class DwarfCUToModule: public RootDIEHandler {
uint64_t string_section_length, uint64_t string_section_length,
const uint8_t* line_string_section, const uint8_t* line_string_section,
uint64_t line_string_length, uint64_t line_string_length,
Module* module, vector<Module::Line>* lines) = 0; Module* module, vector<Module::Line>* lines,
map<uint32_t, Module::File*>* files) = 0;
}; };
// The interface DwarfCUToModule uses to report warnings. The member // The interface DwarfCUToModule uses to report warnings. The member
@ -289,6 +290,7 @@ class DwarfCUToModule: public RootDIEHandler {
struct Specification; struct Specification;
class GenericDIEHandler; class GenericDIEHandler;
class FuncHandler; class FuncHandler;
class InlineHandler;
class NamedScopeHandler; class NamedScopeHandler;
// A map from section offsets to specifications. // A map from section offsets to specifications.
@ -309,6 +311,8 @@ class DwarfCUToModule: public RootDIEHandler {
// lines belong to which functions, beyond their addresses.) // lines belong to which functions, beyond their addresses.)
void AssignLinesToFunctions(); void AssignLinesToFunctions();
void AssignFilesToInlines();
// The only reason cu_context_ and child_context_ are pointers is // The only reason cu_context_ and child_context_ are pointers is
// that we want to keep their definitions private to // that we want to keep their definitions private to
// dwarf_cu_to_module.cc, instead of listing them all here. They are // 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 // during parsing. Then, in Finish, we call AssignLinesToFunctions
// to dole them out to the appropriate functions. // to dole them out to the appropriate functions.
vector<Module::Line> lines_; vector<Module::Line> lines_;
// The map from file index to File* in this CU.
std::map<uint32_t, Module::File*> files_;
}; };
} // namespace google_breakpad } // namespace google_breakpad

View file

@ -67,12 +67,13 @@ using ::testing::ValuesIn;
class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler { class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler {
public: public:
MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir)); 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, const uint8_t* string_section,
uint64_t string_section_length, uint64_t string_section_length,
const uint8_t* line_string_section, const uint8_t* line_string_section,
uint64_t line_string_section_length, uint64_t line_string_section_length,
Module* module, vector<Module::Line>* lines)); Module* module, vector<Module::Line>* lines,
std::map<uint32_t, Module::File*>* files));
}; };
class MockWarningReporter: public DwarfCUToModule::WarningReporter { class MockWarningReporter: public DwarfCUToModule::WarningReporter {
@ -122,7 +123,8 @@ class CUFixtureBase {
uint64_t string_section_length, uint64_t string_section_length,
const uint8_t* line_string_section, const uint8_t* line_string_section,
uint64_t line_string_section_length, uint64_t line_string_section_length,
Module *module, vector<Module::Line>* lines) { Module *module, vector<Module::Line>* lines,
std::map<uint32_t, Module::File*>* files) {
lines->insert(lines->end(), lines_->begin(), lines_->end()); lines->insert(lines->end(), lines_->begin(), lines_->end());
} }
private: private:
@ -155,7 +157,7 @@ class CUFixtureBase {
// By default, expect the line program reader not to be invoked. We // By default, expect the line program reader not to be invoked. We
// may override this in StartCU. // may override this in StartCU.
EXPECT_CALL(line_reader_, StartCompilationUnit(_)).Times(0); 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 // The handler will consult this section map to decide what to
// pass to our line reader. // pass to our line reader.
@ -341,7 +343,7 @@ void CUFixtureBase::StartCU() {
EXPECT_CALL(line_reader_, EXPECT_CALL(line_reader_,
ReadProgram(&dummy_line_program_[0], dummy_line_size_, ReadProgram(&dummy_line_program_[0], dummy_line_size_,
_,_,_,_, _,_,_,_,
&module_, _)) &module_, _,_))
.Times(AtMost(1)) .Times(AtMost(1))
.WillOnce(DoAll(Invoke(appender_), Return())); .WillOnce(DoAll(Invoke(appender_), Return()));
ASSERT_TRUE(root_handler_ ASSERT_TRUE(root_handler_
@ -1517,7 +1519,7 @@ TEST_F(Specifications, InterCU) {
DwarfCUToModule::FileContext fc("dwarf-filename", &m, true); DwarfCUToModule::FileContext fc("dwarf-filename", &m, true);
EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
MockLineToModuleHandler lr; MockLineToModuleHandler lr;
EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_)).Times(0); EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_,_)).Times(0);
// Kludge: satisfy reporter_'s expectation. // Kludge: satisfy reporter_'s expectation.
reporter_.SetCUName("compilation-unit-name"); reporter_.SetCUName("compilation-unit-name");
@ -1576,7 +1578,7 @@ TEST_F(Specifications, UnhandledInterCU) {
DwarfCUToModule::FileContext fc("dwarf-filename", &m, false); DwarfCUToModule::FileContext fc("dwarf-filename", &m, false);
EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
MockLineToModuleHandler lr; MockLineToModuleHandler lr;
EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_)).Times(0); EXPECT_CALL(lr, ReadProgram(_,_,_,_,_,_,_,_,_)).Times(0);
// Kludge: satisfy reporter_'s expectation. // Kludge: satisfy reporter_'s expectation.
reporter_.SetCUName("compilation-unit-name"); reporter_.SetCUName("compilation-unit-name");

View file

@ -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 // Find a Module::File object of the given name, and add it to the
// file table. // 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, 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. // Find the source file being referred to.
Module::File *file = files_[file_num]; Module::File *file = (*files_)[file_num];
if (!file) { if (!file) {
if (!warned_bad_file_number_) { if (!warned_bad_file_number_) {
fprintf(stderr, "warning: DWARF line number data refers to " fprintf(stderr, "warning: DWARF line number data refers to "

View file

@ -120,11 +120,14 @@ class DwarfLineToModule: public LineInfoHandler {
// end of the address space, we clip it. It's up to our client to // 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 // sort out which lines belong to which functions; we don't add them
// to any particular function in MODULE ourselves. // to any particular function in MODULE ourselves.
DwarfLineToModule(Module *module, const string& compilation_dir, DwarfLineToModule(Module* module,
vector<Module::Line>* lines) const string& compilation_dir,
vector<Module::Line>* lines,
std::map<uint32_t, Module::File*>* files)
: module_(module), : module_(module),
compilation_dir_(compilation_dir), compilation_dir_(compilation_dir),
lines_(lines), lines_(lines),
files_(files),
highest_file_number_(-1), highest_file_number_(-1),
omitted_line_end_(0), omitted_line_end_(0),
warned_bad_file_number_(false), warned_bad_file_number_(false),
@ -167,7 +170,7 @@ class DwarfLineToModule: public LineInfoHandler {
DirectoryTable directories_; DirectoryTable directories_;
// A table mapping file numbers to Module::File pointers. // 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 // The highest file number we've seen so far, or -1 if we've seen
// none. Used for dynamically defined file numbers. // none. Used for dynamically defined file numbers.

View file

@ -45,7 +45,8 @@ using google_breakpad::Module;
TEST(SimpleModule, One) { TEST(SimpleModule, One) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineFile("file1", 0x30bf0f27, 0, 0, 0); h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
@ -66,7 +67,8 @@ TEST(SimpleModule, One) {
TEST(SimpleModule, Many) { TEST(SimpleModule, Many) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineDir("directory1", 0x838299ab); h.DefineDir("directory1", 0x838299ab);
h.DefineDir("directory2", 0xf85de023); h.DefineDir("directory2", 0xf85de023);
@ -126,7 +128,8 @@ TEST(SimpleModule, Many) {
TEST(Filenames, Absolute) { TEST(Filenames, Absolute) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineDir("directory1", 1); h.DefineDir("directory1", 1);
h.DefineFile("/absolute", 1, 1, 0, 0); h.DefineFile("/absolute", 1, 1, 0, 0);
@ -144,7 +147,8 @@ TEST(Filenames, Absolute) {
TEST(Filenames, Relative) { TEST(Filenames, Relative) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineDir("directory1", 1); h.DefineDir("directory1", 1);
h.DefineFile("relative", 1, 1, 0, 0); h.DefineFile("relative", 1, 1, 0, 0);
@ -162,7 +166,8 @@ TEST(Filenames, Relative) {
TEST(Filenames, StrangeFile) { TEST(Filenames, StrangeFile) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineDir("directory1", 1); h.DefineDir("directory1", 1);
h.DefineFile("", 1, 1, 0, 0); h.DefineFile("", 1, 1, 0, 0);
@ -175,7 +180,8 @@ TEST(Filenames, StrangeFile) {
TEST(Filenames, StrangeDirectory) { TEST(Filenames, StrangeDirectory) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineDir("", 1); h.DefineDir("", 1);
h.DefineFile("file1", 1, 1, 0, 0); h.DefineFile("file1", 1, 1, 0, 0);
@ -188,7 +194,8 @@ TEST(Filenames, StrangeDirectory) {
TEST(Filenames, StrangeDirectoryAndFile) { TEST(Filenames, StrangeDirectoryAndFile) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineDir("", 1); h.DefineDir("", 1);
h.DefineFile("", 1, 1, 0, 0); h.DefineFile("", 1, 1, 0, 0);
@ -203,7 +210,8 @@ TEST(Filenames, StrangeDirectoryAndFile) {
TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) { TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "src/build", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "src/build", &lines, &cu_files);
h.DefineDir("Dir", 1); h.DefineDir("Dir", 1);
h.DefineFile("File", 1, 0, 0, 0); h.DefineFile("File", 1, 0, 0, 0);
@ -219,7 +227,8 @@ TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) {
TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) { TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "src/build", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "src/build", &lines, &cu_files);
h.DefineDir("Dir", 1); h.DefineDir("Dir", 1);
h.DefineFile("File", 1, 1, 0, 0); h.DefineFile("File", 1, 1, 0, 0);
@ -235,7 +244,8 @@ TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) {
TEST(Filenames, IncludeDirectoryAbsolute) { TEST(Filenames, IncludeDirectoryAbsolute) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "src/build", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "src/build", &lines, &cu_files);
h.DefineDir("/Dir", 1); h.DefineDir("/Dir", 1);
h.DefineFile("File", 1, 1, 0, 0); h.DefineFile("File", 1, 1, 0, 0);
@ -251,7 +261,8 @@ TEST(Filenames, IncludeDirectoryAbsolute) {
TEST(ModuleErrors, DirectoryZero) { TEST(ModuleErrors, DirectoryZero) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineDir("directory0", 0); // should be ignored h.DefineDir("directory0", 0); // should be ignored
h.DefineFile("relative", 1, 0, 0, 0); h.DefineFile("relative", 1, 0, 0, 0);
@ -267,7 +278,8 @@ TEST(ModuleErrors, DirectoryZero) {
TEST(ModuleErrors, BadFileNumber) { TEST(ModuleErrors, BadFileNumber) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineFile("relative", 1, 0, 0, 0); h.DefineFile("relative", 1, 0, 0, 0);
h.AddLine(1, 1, 2, 0, 0); // bad file number h.AddLine(1, 1, 2, 0, 0); // bad file number
@ -281,7 +293,8 @@ TEST(ModuleErrors, BadFileNumber) {
TEST(ModuleErrors, BadDirectoryNumber) { TEST(ModuleErrors, BadDirectoryNumber) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineDir("directory1", 1); h.DefineDir("directory1", 1);
h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number
@ -296,7 +309,8 @@ TEST(ModuleErrors, BadDirectoryNumber) {
TEST(ModuleErrors, EmptyLine) { TEST(ModuleErrors, EmptyLine) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineFile("filename1", 1, 0, 0, 0); h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(1, 0, 1, 0, 0); h.AddLine(1, 0, 1, 0, 0);
@ -309,7 +323,8 @@ TEST(ModuleErrors, EmptyLine) {
TEST(ModuleErrors, BigLine) { TEST(ModuleErrors, BigLine) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineFile("filename1", 1, 0, 0, 0); h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0); h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0);
@ -326,7 +341,8 @@ TEST(ModuleErrors, BigLine) {
TEST(Omitted, DroppedThenGood) { TEST(Omitted, DroppedThenGood) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineFile("filename1", 1, 0, 0, 0); h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0, 10, 1, 83816211, 0); // should be omitted h.AddLine(0, 10, 1, 83816211, 0); // should be omitted
@ -339,7 +355,8 @@ TEST(Omitted, DroppedThenGood) {
TEST(Omitted, GoodThenDropped) { TEST(Omitted, GoodThenDropped) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineFile("filename1", 1, 0, 0, 0); h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded
@ -352,7 +369,8 @@ TEST(Omitted, GoodThenDropped) {
TEST(Omitted, Mix1) { TEST(Omitted, Mix1) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineFile("filename1", 1, 0, 0, 0); h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded
@ -373,7 +391,8 @@ TEST(Omitted, Mix1) {
TEST(Omitted, Mix2) { TEST(Omitted, Mix2) {
Module m("name", "os", "architecture", "id"); Module m("name", "os", "architecture", "id");
vector<Module::Line> lines; vector<Module::Line> lines;
DwarfLineToModule h(&m, "/", &lines); std::map<uint32_t, Module::File*> cu_files;
DwarfLineToModule h(&m, "/", &lines, &cu_files);
h.DefineFile("filename1", 1, 0, 0, 0); h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted

View file

@ -260,13 +260,16 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler {
void StartCompilationUnit(const string& compilation_dir) { void StartCompilationUnit(const string& compilation_dir) {
compilation_dir_ = 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, const uint8_t* string_section,
uint64_t string_section_length, uint64_t string_section_length,
const uint8_t* line_string_section, const uint8_t* line_string_section,
uint64_t line_string_section_length, uint64_t line_string_section_length,
Module* module, std::vector<Module::Line>* lines) { Module* module,
DwarfLineToModule handler(module, compilation_dir_, lines); std::vector<Module::Line>* lines,
std::map<uint32_t, Module::File*>* files) {
DwarfLineToModule handler(module, compilation_dir_, lines, files);
google_breakpad::LineInfo parser(program, length, byte_reader_, google_breakpad::LineInfo parser(program, length, byte_reader_,
string_section, string_section_length, string_section, string_section_length,
line_string_section, line_string_section,

View file

@ -351,15 +351,18 @@ class DumpSymbols::DumperLineToModule:
compilation_dir_ = 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, const uint8_t* string_section,
uint64_t string_section_length, uint64_t string_section_length,
const uint8_t* line_string_section, const uint8_t* line_string_section,
uint64_t line_string_section_length, uint64_t line_string_section_length,
Module* module, vector<Module::Line>* lines) { Module* module,
DwarfLineToModule handler(module, compilation_dir_, lines); vector<Module::Line>* lines,
LineInfo parser(program, length, byte_reader_, std::map<uint32_t, Module::File*>* files) {
nullptr, 0, nullptr, 0, &handler); DwarfLineToModule handler(module, compilation_dir_, lines, files);
LineInfo parser(program, length, byte_reader_, nullptr, 0,
nullptr, 0, &handler);
parser.Start(); parser.Start();
} }
private: private:

View file

@ -38,14 +38,16 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <functional>
#include <iostream> #include <iostream>
#include <memory>
#include <utility> #include <utility>
namespace google_breakpad { namespace google_breakpad {
using std::dec; using std::dec;
using std::hex; using std::hex;
using std::unique_ptr;
Module::Module(const string& name, const string& os, Module::Module(const string& name, const string& os,
const string& architecture, const string& id, const string& architecture, const string& id,
@ -214,6 +216,13 @@ void Module::AssignSourceIds() {
line_it != func->lines.end(); ++line_it) line_it != func->lines.end(); ++line_it)
line_it->file->source_id = 0; 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. // Finally, assign source ids to those files that have been marked.
// We could have just assigned source id numbers while traversing // We could have just assigned source id numbers while traversing
@ -227,6 +236,33 @@ void Module::AssignSourceIds() {
} }
} }
static void InlineDFS(
vector<unique_ptr<Module::Inline>>& inlines,
std::function<void(unique_ptr<Module::Inline>&)> const& forEach) {
for (unique_ptr<Module::Inline>& 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<Inline> &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() { bool Module::ReportError() {
fprintf(stderr, "error writing symbol file: %s\n", fprintf(stderr, "error writing symbol file: %s\n",
strerror(errno)); strerror(errno));
@ -267,6 +303,8 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
} }
if (symbol_data & SYMBOLS_AND_FILES) { if (symbol_data & SYMBOLS_AND_FILES) {
if (symbol_data & INLINES)
CreateInlineOrigins();
AssignSourceIds(); AssignSourceIds();
// Write out files. // Write out files.
@ -279,8 +317,17 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
return ReportError(); 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(); for (FunctionSet::const_iterator func_it = functions_.begin();
func_it != functions_.end(); ++func_it) { func_it != functions_.end(); ++func_it) {
Function* func = *func_it; Function* func = *func_it;
@ -296,6 +343,21 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
if (!stream.good()) if (!stream.good())
return ReportError(); return ReportError();
// Write out inlines.
if (symbol_data & INLINES) {
auto write_inline = [&](unique_ptr<Inline>& 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()) && while ((line_it != func->lines.end()) &&
(line_it->address >= range_it->address) && (line_it->address >= range_it->address) &&
(line_it->address < (range_it->address + range_it->size))) { (line_it->address < (range_it->address + range_it->size))) {

View file

@ -41,6 +41,7 @@
#include <iostream> #include <iostream>
#include <limits> #include <limits>
#include <map> #include <map>
#include <memory>
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
@ -66,6 +67,8 @@ class Module {
static constexpr uint64_t kMaxAddress = std::numeric_limits<Address>::max(); static constexpr uint64_t kMaxAddress = std::numeric_limits<Address>::max();
struct File; struct File;
struct Function; struct Function;
struct InlineOrigin;
struct Inline;
struct Line; struct Line;
struct Extern; struct Extern;
@ -120,6 +123,50 @@ class Module {
// Source lines belonging to this function, sorted by increasing // Source lines belonging to this function, sorted by increasing
// address. // address.
vector<Line> lines; vector<Line> lines;
// Inlined call sites belonging to this functions.
vector<std::unique_ptr<Inline>> 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<Range>& ranges,
int call_site_line,
int inline_nest_level,
vector<std::unique_ptr<Inline>> 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<Range> ranges;
int call_site_line;
int inline_nest_level;
// A list of inlines which are children of this inline.
vector<std::unique_ptr<Inline>> child_inlines;
}; };
// A source line. // 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 { struct ExternCompare {
bool operator() (const Extern* lhs, const Extern* rhs) const { bool operator() (const Extern* lhs, const Extern* rhs) const {
return lhs->address < rhs->address; return lhs->address < rhs->address;
@ -275,6 +330,10 @@ class Module {
// symbol file, at which point we omit any unused files. // symbol file, at which point we omit any unused files.
void AssignSourceIds(); 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 // Call AssignSourceIds, and write this module to STREAM in the
// breakpad symbol format. Return true if all goes well, or false if // breakpad symbol format. Return true if all goes well, or false if
// an error occurs. This method writes out: // an error occurs. This method writes out:
@ -334,6 +393,9 @@ class Module {
// A set containing Function structures, sorted by address. // A set containing Function structures, sorted by address.
typedef set<Function*, FunctionCompare> FunctionSet; typedef set<Function*, FunctionCompare> FunctionSet;
// A set containing Function structures, sorted by address.
typedef set<InlineOrigin*, InlineOriginCompare> InlineOriginSet;
// A set containing Extern structures, sorted by address. // A set containing Extern structures, sorted by address.
typedef set<Extern*, ExternCompare> ExternSet; typedef set<Extern*, ExternCompare> ExternSet;
@ -342,6 +404,7 @@ class Module {
// point to. // point to.
FileByNameMap files_; // This module's source files. FileByNameMap files_; // This module's source files.
FunctionSet functions_; // This module's functions. 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 // The module owns all the call frame info entries that have been
// added to it. // added to it.

View file

@ -50,6 +50,7 @@ int usage(const char* self) {
fprintf(stderr, "Options:\n"); fprintf(stderr, "Options:\n");
fprintf(stderr, " -i: Output module header information only.\n"); fprintf(stderr, " -i: Output module header information only.\n");
fprintf(stderr, " -c Do not generate CFI section\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 " fprintf(stderr, " -r Do not handle inter-compilation "
"unit references\n"); "unit references\n");
fprintf(stderr, " -v Print all warnings to stderr\n"); fprintf(stderr, " -v Print all warnings to stderr\n");
@ -64,6 +65,7 @@ int main(int argc, char** argv) {
return usage(argv[0]); return usage(argv[0]);
bool header_only = false; bool header_only = false;
bool cfi = true; bool cfi = true;
bool handle_inlines = false;
bool handle_inter_cu_refs = true; bool handle_inter_cu_refs = true;
bool log_to_stderr = false; bool log_to_stderr = false;
std::string obj_name; std::string obj_name;
@ -75,6 +77,8 @@ int main(int argc, char** argv) {
header_only = true; header_only = true;
} else if (strcmp("-c", argv[arg_index]) == 0) { } else if (strcmp("-c", argv[arg_index]) == 0) {
cfi = false; cfi = false;
} else if (strcmp("-d", argv[arg_index]) == 0) {
handle_inlines = true;
} else if (strcmp("-r", argv[arg_index]) == 0) { } else if (strcmp("-r", argv[arg_index]) == 0) {
handle_inter_cu_refs = false; handle_inter_cu_refs = false;
} else if (strcmp("-v", argv[arg_index]) == 0) { } else if (strcmp("-v", argv[arg_index]) == 0) {
@ -127,8 +131,8 @@ int main(int argc, char** argv) {
return 1; return 1;
} }
} else { } else {
SymbolData symbol_data = SymbolData symbol_data = (handle_inlines ? INLINES : NO_DATA) |
INLINES | (cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES; (cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES;
google_breakpad::DumpOptions options(symbol_data, handle_inter_cu_refs); google_breakpad::DumpOptions options(symbol_data, handle_inter_cu_refs);
if (!WriteSymbolFile(binary, obj_name, obj_os, debug_dirs, options, if (!WriteSymbolFile(binary, obj_name, obj_os, debug_dirs, options,
std::cout)) { std::cout)) {