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