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:
thestig@chromium.org 2013-04-24 21:18:44 +00:00
parent ae3947e123
commit f7566bd447
7 changed files with 300 additions and 129 deletions

View file

@ -91,7 +91,7 @@ struct DwarfCUToModule::Specification {
// An abstract origin -- base definition of an inline function. // An abstract origin -- base definition of an inline function.
struct AbstractOrigin { struct AbstractOrigin {
AbstractOrigin() : name() {} AbstractOrigin() : name() {}
AbstractOrigin(const string& name) : name(name) {} explicit AbstractOrigin(const string& name) : name(name) {}
string name; string name;
}; };
@ -128,14 +128,43 @@ struct DwarfCUToModule::FilePrivate {
AbstractOriginByOffset origins; AbstractOriginByOffset origins;
}; };
DwarfCUToModule::FileContext::FileContext(const string &filename_arg, DwarfCUToModule::FileContext::FileContext(const string &filename,
Module *module_arg) Module *module,
: filename(filename_arg), module(module_arg) { bool handle_inter_cu_refs)
file_private = new FilePrivate(); : filename_(filename),
module_(module),
handle_inter_cu_refs_(handle_inter_cu_refs) {
file_private_ = new FilePrivate();
} }
DwarfCUToModule::FileContext::~FileContext() { 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 // Information global to the particular compilation unit we're
@ -146,10 +175,12 @@ struct DwarfCUToModule::CUContext {
: file_context(file_context_arg), : file_context(file_context_arg),
reporter(reporter_arg), reporter(reporter_arg),
language(Language::CPlusPlus) {} language(Language::CPlusPlus) {}
~CUContext() { ~CUContext() {
for (vector<Module::Function *>::iterator it = functions.begin(); for (vector<Module::Function *>::iterator it = functions.begin();
it != functions.end(); it++) it != functions.end(); ++it) {
delete *it; delete *it;
}
}; };
// The DWARF-bearing file into which this CU was incorporated. // The DWARF-bearing file into which this CU was incorporated.
@ -276,14 +307,19 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
uint64 data) { uint64 data) {
switch (attr) { switch (attr) {
case dwarf2reader::DW_AT_specification: { 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 // Find the Specification to which this attribute refers, and
// set specification_ appropriately. We could do more processing // set specification_ appropriately. We could do more processing
// here, but it's better to leave the real work to our // here, but it's better to leave the real work to our
// EndAttribute member function, at which point we know we have // EndAttribute member function, at which point we know we have
// seen all the DIE's attributes. // seen all the DIE's attributes.
FileContext *file_context = cu_context_->file_context; SpecificationByOffset *specifications =
SpecificationByOffset *specifications &file_context->file_private_->specifications;
= &file_context->file_private->specifications;
SpecificationByOffset::iterator spec = specifications->find(data); SpecificationByOffset::iterator spec = specifications->find(data);
if (spec != specifications->end()) { if (spec != specifications->end()) {
specification_ = &spec->second; specification_ = &spec->second;
@ -303,7 +339,7 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) { string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) {
pair<set<string>::iterator, bool> result = 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; return *result.first;
} }
@ -365,15 +401,14 @@ string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
// If this DIE was marked as a declaration, record its names in the // If this DIE was marked as a declaration, record its names in the
// specification table. // specification table.
if (declaration_) { if (declaration_) {
FileContext *file_context = cu_context_->file_context;
Specification spec; Specification spec;
if (qualified_name) if (qualified_name) {
spec.qualified_name = *qualified_name; spec.qualified_name = *qualified_name;
else { } else {
spec.enclosing_name = *enclosing_name; spec.enclosing_name = *enclosing_name;
spec.unqualified_name = *unqualified_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) if (qualified_name)
@ -460,7 +495,7 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeReference(
switch (attr) { switch (attr) {
case dwarf2reader::DW_AT_abstract_origin: { case dwarf2reader::DW_AT_abstract_origin: {
const AbstractOriginByOffset& origins = const AbstractOriginByOffset& origins =
cu_context_->file_context->file_private->origins; cu_context_->file_context->file_private_->origins;
AbstractOriginByOffset::const_iterator origin = origins.find(data); AbstractOriginByOffset::const_iterator origin = origins.find(data);
if (origin != origins.end()) { if (origin != origins.end()) {
abstract_origin_ = &(origin->second); abstract_origin_ = &(origin->second);
@ -516,7 +551,7 @@ void DwarfCUToModule::FuncHandler::Finish() {
} }
} 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;
} }
} }
@ -628,6 +663,15 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
filename_.c_str(), 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, DwarfCUToModule::DwarfCUToModule(FileContext *file_context,
LineToModuleHandler *line_reader, LineToModuleHandler *line_reader,
WarningReporter *reporter) WarningReporter *reporter)
@ -741,7 +785,7 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
void DwarfCUToModule::ReadSourceLines(uint64 offset) { void DwarfCUToModule::ReadSourceLines(uint64 offset) {
const dwarf2reader::SectionMap &section_map const dwarf2reader::SectionMap &section_map
= cu_context_->file_context->section_map; = cu_context_->file_context->section_map();
dwarf2reader::SectionMap::const_iterator map_entry dwarf2reader::SectionMap::const_iterator map_entry
= section_map.find(".debug_line"); = section_map.find(".debug_line");
// Mac OS X puts DWARF data in sections whose names begin with "__" // Mac OS X puts DWARF data in sections whose names begin with "__"
@ -759,7 +803,7 @@ void DwarfCUToModule::ReadSourceLines(uint64 offset) {
return; return;
} }
line_reader_->ReadProgram(section_start + offset, section_length - offset, line_reader_->ReadProgram(section_start + offset, section_length - offset,
cu_context_->file_context->module, &lines_); cu_context_->file_context->module_, &lines_);
} }
namespace { namespace {
@ -984,12 +1028,14 @@ void DwarfCUToModule::Finish() {
// Add our functions, which now have source lines assigned to them, // Add our functions, which now have source lines assigned to them,
// to module_. // to module_.
cu_context_->file_context->module->AddFunctions(functions->begin(), cu_context_->file_context->module_->AddFunctions(functions->begin(),
functions->end()); functions->end());
// Ownership of the function objects has shifted from cu_context to // Ownership of the function objects has shifted from cu_context to
// the Module. // the Module.
functions->clear(); functions->clear();
cu_context_->file_context->ClearSpecifications();
} }
bool DwarfCUToModule::StartCompilationUnit(uint64 offset, bool DwarfCUToModule::StartCompilationUnit(uint64 offset,

View file

@ -65,7 +65,6 @@ using dwarf2reader::DwarfTag;
class DwarfCUToModule: public dwarf2reader::RootDIEHandler { class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
struct FilePrivate; struct FilePrivate;
public: public:
// Information global to the DWARF-bearing file we are processing, // Information global to the DWARF-bearing file we are processing,
// for use by DwarfCUToModule. Each DwarfCUToModule instance deals // for use by DwarfCUToModule. Each DwarfCUToModule instance deals
// with a single compilation unit within the file, but information // 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' // for filling it in appropriately (except for the 'file_private'
// field, which the constructor and destructor take care of), and // field, which the constructor and destructor take care of), and
// then providing it to the DwarfCUToModule instance for each // then providing it to the DwarfCUToModule instance for each
// compilation unit we process in that file. // compilation unit we process in that file. Set HANDLE_INTER_CU_REFS
struct FileContext { // to true to handle debugging symbols with DW_FORM_ref_addr entries.
FileContext(const string &filename_arg, Module *module_arg); class FileContext {
public:
FileContext(const string &filename,
Module *module,
bool handle_inter_cu_refs);
~FileContext(); ~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. // 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 // A map of this file's sections, used for finding other DWARF
// sections that the .debug_info section may refer to. // 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. // 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. // 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 // An abstract base class for handlers that handle DWARF line data
@ -170,9 +198,17 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// link. // link.
virtual void UnnamedFunction(uint64 offset); 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: protected:
string filename_; const string filename_;
uint64 cu_offset_; const uint64 cu_offset_;
string cu_name_; string cu_name_;
bool printed_cu_header_; bool printed_cu_header_;
bool printed_unpaired_header_; bool printed_unpaired_header_;
@ -218,13 +254,11 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
bool StartRootDIE(uint64 offset, enum DwarfTag tag); bool StartRootDIE(uint64 offset, enum DwarfTag tag);
private: private:
// Used internally by the handler. Full definitions are in // Used internally by the handler. Full definitions are in
// dwarf_cu_to_module.cc. // dwarf_cu_to_module.cc.
struct FilePrivate;
struct Specification;
struct CUContext; struct CUContext;
struct DIEContext; struct DIEContext;
struct Specification;
class GenericDIEHandler; class GenericDIEHandler;
class FuncHandler; class FuncHandler;
class NamedScopeHandler; class NamedScopeHandler;

View file

@ -81,6 +81,7 @@ class MockWarningReporter: public DwarfCUToModule::WarningReporter {
MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function)); MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function));
MOCK_METHOD1(UncoveredLine, void(const Module::Line &line)); MOCK_METHOD1(UncoveredLine, void(const Module::Line &line));
MOCK_METHOD1(UnnamedFunction, void(uint64 offset)); 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 // 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. // for doing common kinds of setup and tests.
class CUFixtureBase { class CUFixtureBase {
public: public:
// If we have: // If we have:
// //
// vector<Module::Line> lines; // vector<Module::Line> lines;
@ -108,7 +108,8 @@ class CUFixtureBase {
// in which case calling l2m with some line vector will append lines. // in which case calling l2m with some line vector will append lines.
class AppendLinesFunctor { class AppendLinesFunctor {
public: 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, void operator()(const char *program, uint64 length,
Module *module, vector<Module::Line> *lines) { Module *module, vector<Module::Line> *lines) {
lines->insert(lines->end(), lines_->begin(), lines_->end()); lines->insert(lines->end(), lines_->begin(), lines_->end());
@ -119,7 +120,7 @@ class CUFixtureBase {
CUFixtureBase() CUFixtureBase()
: module_("module-name", "module-os", "module-arch", "module-id"), : 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_(dwarf2reader::DW_LANG_none),
language_signed_(false), language_signed_(false),
appender_(&lines_), appender_(&lines_),
@ -137,6 +138,7 @@ class CUFixtureBase {
EXPECT_CALL(reporter_, UncoveredFunction(_)).Times(0); EXPECT_CALL(reporter_, UncoveredFunction(_)).Times(0);
EXPECT_CALL(reporter_, UncoveredLine(_)).Times(0); EXPECT_CALL(reporter_, UncoveredLine(_)).Times(0);
EXPECT_CALL(reporter_, UnnamedFunction(_)).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 // By default, expect the line program reader not to be invoked. We
// may override this in StartCU. // may override this in StartCU.
@ -145,7 +147,8 @@ class CUFixtureBase {
// 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.
file_context_.section_map[".debug_line"] = make_pair(dummy_line_program_, file_context_.AddSectionToSectionMap(".debug_line",
dummy_line_program_,
dummy_line_size_); dummy_line_size_);
} }
@ -708,7 +711,7 @@ TEST_F(SimpleCU, IrrelevantNamedScopeChildren) {
// Verify that FileContexts can safely be deleted unused. // Verify that FileContexts can safely be deleted unused.
TEST_F(SimpleCU, UnusedFileContext) { TEST_F(SimpleCU, UnusedFileContext) {
Module m("module-name", "module-os", "module-arch", "module-id"); 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. // Kludge: satisfy reporter_'s expectation.
reporter_.SetCUName("compilation-unit-name"); reporter_.SetCUName("compilation-unit-name");
@ -891,7 +894,6 @@ TEST_P(FuncLinePairing, Pairing) {
} }
TEST_F(FuncLinePairing, EmptyCU) { TEST_F(FuncLinePairing, EmptyCU) {
StartCU(); StartCU();
root_handler_.Finish(); root_handler_.Finish();
@ -1127,8 +1129,8 @@ const LanguageAndQualifiedName LanguageAndQualifiedNameCases[] = {
{ dwarf2reader::DW_LANG_Mips_Assembler, NULL } { dwarf2reader::DW_LANG_Mips_Assembler, NULL }
}; };
class QualifiedForLanguage: class QualifiedForLanguage
public CUFixtureBase, : public CUFixtureBase,
public TestWithParam<LanguageAndQualifiedName> { }; public TestWithParam<LanguageAndQualifiedName> { };
INSTANTIATE_TEST_CASE_P(LanguageAndQualifiedName, QualifiedForLanguage, INSTANTIATE_TEST_CASE_P(LanguageAndQualifiedName, QualifiedForLanguage,
@ -1429,7 +1431,7 @@ TEST_F(Specifications, LongChain) {
TEST_F(Specifications, InterCU) { TEST_F(Specifications, InterCU) {
Module m("module-name", "module-os", "module-arch", "module-id"); 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()); EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
MockLineToModuleHandler lr; MockLineToModuleHandler lr;
EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0); EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0);
@ -1486,6 +1488,63 @@ TEST_F(Specifications, InterCU) {
EXPECT_STREQ("class_A::member_func_B", functions[0]->name.c_str()); 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) { TEST_F(Specifications, BadOffset) {
PushLine(0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL, "line-file", 56636272); PushLine(0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL, "line-file", 56636272);
EXPECT_CALL(reporter_, UnknownSpecification(_, 0x2be953efa6f9a996ULL)) EXPECT_CALL(reporter_, UnknownSpecification(_, 0x2be953efa6f9a996ULL))
@ -1554,8 +1613,9 @@ TEST_F(Specifications, PreferSpecificationParents) {
StartCU(); StartCU();
{ {
dwarf2reader::DIEHandler *declaration_class_handler dwarf2reader::DIEHandler *declaration_class_handler =
= StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "declaration-class"); StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
"declaration-class");
DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL, DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL,
dwarf2reader::DW_TAG_subprogram, "function-declaration", dwarf2reader::DW_TAG_subprogram, "function-declaration",
""); "");
@ -1603,7 +1663,7 @@ TEST_F(CUErrors, NoLineSection) {
EXPECT_CALL(reporter_, MissingSection(".debug_line")).Times(1); EXPECT_CALL(reporter_, MissingSection(".debug_line")).Times(1);
PushLine(0x88507fb678052611ULL, 0x42c8e9de6bbaa0faULL, "line-file", 64472290); PushLine(0x88507fb678052611ULL, 0x42c8e9de6bbaa0faULL, "line-file", 64472290);
// Delete the entry for .debug_line added by the fixture class's constructor. // Delete the entry for .debug_line added by the fixture class's constructor.
file_context_.section_map.clear(); file_context_.ClearSectionMapForTest();
StartCU(); StartCU();
root_handler_.Finish(); root_handler_.Finish();

View file

@ -72,6 +72,7 @@
// This namespace contains helper functions. // This namespace contains helper functions.
namespace { namespace {
using google_breakpad::DumpOptions;
using google_breakpad::DwarfCFIToModule; using google_breakpad::DwarfCFIToModule;
using google_breakpad::DwarfCUToModule; using google_breakpad::DwarfCUToModule;
using google_breakpad::DwarfLineToModule; using google_breakpad::DwarfLineToModule;
@ -216,6 +217,7 @@ template<typename ElfClass>
bool LoadDwarf(const string& dwarf_filename, bool LoadDwarf(const string& dwarf_filename,
const typename ElfClass::Ehdr* elf_header, const typename ElfClass::Ehdr* elf_header,
const bool big_endian, const bool big_endian,
bool handle_inter_cu_refs,
Module* module) { Module* module) {
typedef typename ElfClass::Shdr Shdr; typedef typename ElfClass::Shdr Shdr;
@ -224,7 +226,9 @@ bool LoadDwarf(const string& dwarf_filename,
dwarf2reader::ByteReader byte_reader(endianness); dwarf2reader::ByteReader byte_reader(endianness);
// Construct a context for this file. // 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. // Build a map of the ELF file's sections.
const Shdr* sections = const Shdr* sections =
@ -238,14 +242,16 @@ bool LoadDwarf(const string& dwarf_filename,
section->sh_name; section->sh_name;
const char* contents = GetOffset<ElfClass, char>(elf_header, const char* contents = GetOffset<ElfClass, char>(elf_header,
section->sh_offset); section->sh_offset);
uint64 length = section->sh_size; file_context.AddSectionToSectionMap(name, contents, section->sh_size);
file_context.section_map[name] = std::make_pair(contents, length);
} }
// Parse all the compilation units in the .debug_info section. // Parse all the compilation units in the .debug_info section.
DumperLineToModule line_to_module(&byte_reader); DumperLineToModule line_to_module(&byte_reader);
std::pair<const char *, uint64> debug_info_section dwarf2reader::SectionMap::const_iterator debug_info_entry =
= file_context.section_map[".debug_info"]; 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 // This should never have been called if the file doesn't have a
// .debug_info section. // .debug_info section.
assert(debug_info_section.first); assert(debug_info_section.first);
@ -258,7 +264,7 @@ bool LoadDwarf(const string& dwarf_filename,
// Make a Dwarf2Handler that drives the DIEHandler. // Make a Dwarf2Handler that drives the DIEHandler.
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
// Make a DWARF parser for the compilation unit at OFFSET. // 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, offset,
&byte_reader, &byte_reader,
&die_dispatcher); &die_dispatcher);
@ -521,7 +527,7 @@ bool LoadSymbols(const string& obj_file,
const typename ElfClass::Ehdr* elf_header, const typename ElfClass::Ehdr* elf_header,
const bool read_gnu_debug_link, const bool read_gnu_debug_link,
LoadSymbolsInfo<ElfClass>* info, LoadSymbolsInfo<ElfClass>* info,
SymbolData symbol_data, const DumpOptions& options,
Module* module) { Module* module) {
typedef typename ElfClass::Addr Addr; typedef typename ElfClass::Addr Addr;
typedef typename ElfClass::Phdr Phdr; typedef typename ElfClass::Phdr Phdr;
@ -542,7 +548,7 @@ bool LoadSymbols(const string& obj_file,
bool found_debug_info_section = false; bool found_debug_info_section = false;
bool found_usable_info = false; bool found_usable_info = false;
if (symbol_data != ONLY_CFI) { if (options.symbol_data != ONLY_CFI) {
#ifndef NO_STABS_SUPPORT #ifndef NO_STABS_SUPPORT
// Look for STABS debugging information, and load it if present. // Look for STABS debugging information, and load it if present.
const Shdr* stab_section = const Shdr* stab_section =
@ -573,13 +579,15 @@ bool LoadSymbols(const string& obj_file,
found_debug_info_section = true; found_debug_info_section = true;
found_usable_info = true; found_usable_info = true;
info->LoadedSection(".debug_info"); 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 " fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
"DWARF debugging information\n", obj_file.c_str()); "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 // Dwarf Call Frame Information (CFI) is actually independent from
// the other DWARF debugging information, and can be used alone. // the other DWARF debugging information, and can be used alone.
const Shdr* dwarf_cfi_section = const Shdr* dwarf_cfi_section =
@ -655,7 +663,7 @@ bool LoadSymbols(const string& obj_file,
obj_file.c_str()); obj_file.c_str());
} }
} else { } else {
if (symbol_data != ONLY_CFI) { if (options.symbol_data != ONLY_CFI) {
// The caller doesn't want to consult .gnu_debuglink. // The caller doesn't want to consult .gnu_debuglink.
// See if there are export symbols available. // See if there are export symbols available.
const Shdr* dynsym_section = const Shdr* dynsym_section =
@ -753,7 +761,7 @@ template<typename ElfClass>
bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
const string& obj_filename, const string& obj_filename,
const std::vector<string>& debug_dirs, const std::vector<string>& debug_dirs,
SymbolData symbol_data, const DumpOptions& options,
Module** out_module) { Module** out_module) {
typedef typename ElfClass::Ehdr Ehdr; typedef typename ElfClass::Ehdr Ehdr;
typedef typename ElfClass::Shdr Shdr; 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)); scoped_ptr<Module> module(new Module(name, os, architecture, id));
if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header, if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header,
!debug_dirs.empty(), &info, !debug_dirs.empty(), &info,
symbol_data, module.get())) { options, module.get())) {
const string debuglink_file = info.debuglink_file(); const string debuglink_file = info.debuglink_file();
if (debuglink_file.empty()) if (debuglink_file.empty())
return false; return false;
@ -827,7 +835,7 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian, if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian,
debug_elf_header, false, &info, debug_elf_header, false, &info,
symbol_data, module.get())) { options, module.get())) {
return false; return false;
} }
} }
@ -844,9 +852,8 @@ namespace google_breakpad {
bool ReadSymbolDataInternal(const uint8_t* obj_file, bool ReadSymbolDataInternal(const uint8_t* obj_file,
const string& obj_filename, const string& obj_filename,
const std::vector<string>& debug_dirs, const std::vector<string>& debug_dirs,
SymbolData symbol_data, const DumpOptions& options,
Module** module) { Module** module) {
if (!IsValidElf(obj_file)) { if (!IsValidElf(obj_file)) {
fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str()); fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str());
return false; return false;
@ -856,12 +863,12 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file,
if (elfclass == ELFCLASS32) { if (elfclass == ELFCLASS32) {
return ReadSymbolDataElfClass<ElfClass32>( return ReadSymbolDataElfClass<ElfClass32>(
reinterpret_cast<const Elf32_Ehdr*>(obj_file), obj_filename, debug_dirs, reinterpret_cast<const Elf32_Ehdr*>(obj_file), obj_filename, debug_dirs,
symbol_data, module); options, module);
} }
if (elfclass == ELFCLASS64) { if (elfclass == ELFCLASS64) {
return ReadSymbolDataElfClass<ElfClass64>( return ReadSymbolDataElfClass<ElfClass64>(
reinterpret_cast<const Elf64_Ehdr*>(obj_file), obj_filename, debug_dirs, reinterpret_cast<const Elf64_Ehdr*>(obj_file), obj_filename, debug_dirs,
symbol_data, module); options, module);
} }
return false; return false;
@ -869,20 +876,20 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file,
bool WriteSymbolFile(const string &obj_file, bool WriteSymbolFile(const string &obj_file,
const std::vector<string>& debug_dirs, const std::vector<string>& debug_dirs,
SymbolData symbol_data, const DumpOptions& options,
std::ostream &sym_stream) { std::ostream &sym_stream) {
Module* module; Module* module;
if (!ReadSymbolData(obj_file, debug_dirs, symbol_data, &module)) if (!ReadSymbolData(obj_file, debug_dirs, options, &module))
return false; return false;
bool result = module->Write(sym_stream, symbol_data); bool result = module->Write(sym_stream, options.symbol_data);
delete module; delete module;
return result; return result;
} }
bool ReadSymbolData(const string& obj_file, bool ReadSymbolData(const string& obj_file,
const std::vector<string>& debug_dirs, const std::vector<string>& debug_dirs,
SymbolData symbol_data, const DumpOptions& options,
Module** module) { Module** module) {
MmapWrapper map_wrapper; MmapWrapper map_wrapper;
void* elf_header = NULL; void* elf_header = NULL;
@ -890,7 +897,7 @@ bool ReadSymbolData(const string& obj_file,
return false; return false;
return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header), return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header),
obj_file, debug_dirs, symbol_data, module); obj_file, debug_dirs, options, module);
} }
} // namespace google_breakpad } // namespace google_breakpad

View file

@ -46,6 +46,16 @@ namespace google_breakpad {
class Module; 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 // Find all the debugging information in OBJ_FILE, an ELF executable
// or shared library, and write it to SYM_STREAM in the Breakpad symbol // or shared library, and write it to SYM_STREAM in the Breakpad symbol
// file format. // file format.
@ -54,7 +64,7 @@ class Module;
// SYMBOL_DATA allows limiting the type of symbol data written. // SYMBOL_DATA allows limiting the type of symbol data written.
bool WriteSymbolFile(const string &obj_file, bool WriteSymbolFile(const string &obj_file,
const std::vector<string>& debug_dirs, const std::vector<string>& debug_dirs,
SymbolData symbol_data, const DumpOptions& options,
std::ostream &sym_stream); std::ostream &sym_stream);
// As above, but simply return the debugging information in MODULE // 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. // Module object and must delete it when finished.
bool ReadSymbolData(const string& obj_file, bool ReadSymbolData(const string& obj_file,
const std::vector<string>& debug_dirs, const std::vector<string>& debug_dirs,
SymbolData symbol_data, const DumpOptions& options,
Module** module); Module** module);
} // namespace google_breakpad } // namespace google_breakpad

View file

@ -40,25 +40,24 @@
#include <vector> #include <vector>
#include "breakpad_googletest_includes.h" #include "breakpad_googletest_includes.h"
#include "common/linux/dump_symbols.h"
#include "common/linux/synth_elf.h" #include "common/linux/synth_elf.h"
#include "common/module.h" #include "common/module.h"
#include "common/using_std_string.h" #include "common/using_std_string.h"
namespace google_breakpad { namespace google_breakpad {
bool ReadSymbolDataInternal(const uint8_t* obj_file, bool ReadSymbolDataInternal(const uint8_t* obj_file,
const string& obj_filename, const string& obj_filename,
const std::vector<string>& debug_dir, const std::vector<string>& debug_dir,
SymbolData symbol_data, const DumpOptions& options,
Module** module); Module** module);
}
using google_breakpad::synth_elf::ELF; using google_breakpad::synth_elf::ELF;
using google_breakpad::synth_elf::StringTable; using google_breakpad::synth_elf::StringTable;
using google_breakpad::synth_elf::SymbolTable; using google_breakpad::synth_elf::SymbolTable;
using google_breakpad::test_assembler::kLittleEndian; using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section; using google_breakpad::test_assembler::Section;
using google_breakpad::Module;
using google_breakpad::ReadSymbolDataInternal;
using std::stringstream; using std::stringstream;
using std::vector; using std::vector;
using ::testing::Test; using ::testing::Test;
@ -83,10 +82,11 @@ TEST_F(DumpSymbols, Invalid) {
Elf32_Ehdr header; Elf32_Ehdr header;
memset(&header, 0, sizeof(header)); memset(&header, 0, sizeof(header));
Module* module; Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true);
EXPECT_FALSE(ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(&header), EXPECT_FALSE(ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(&header),
"foo", "foo",
vector<string>(), vector<string>(),
ALL_SYMBOL_DATA, options,
&module)); &module));
} }
@ -115,10 +115,11 @@ TEST_F(DumpSymbols, SimplePublic32) {
GetElfContents(elf); GetElfContents(elf);
Module* module; Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true);
EXPECT_TRUE(ReadSymbolDataInternal(elfdata, EXPECT_TRUE(ReadSymbolDataInternal(elfdata,
"foo", "foo",
vector<string>(), vector<string>(),
ALL_SYMBOL_DATA, options,
&module)); &module));
stringstream s; stringstream s;
@ -154,10 +155,11 @@ TEST_F(DumpSymbols, SimplePublic64) {
GetElfContents(elf); GetElfContents(elf);
Module* module; Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true);
EXPECT_TRUE(ReadSymbolDataInternal(elfdata, EXPECT_TRUE(ReadSymbolDataInternal(elfdata,
"foo", "foo",
vector<string>(), vector<string>(),
ALL_SYMBOL_DATA, options,
&module)); &module));
stringstream s; stringstream s;
@ -166,3 +168,5 @@ TEST_F(DumpSymbols, SimplePublic64) {
"PUBLIC 1000 0 superfunc\n", "PUBLIC 1000 0 superfunc\n",
s.str()); s.str());
} }
} // namespace google_breakpad

View file

@ -43,33 +43,43 @@ int usage(const char* self) {
"[directories-for-debug-file]\n\n", self); "[directories-for-debug-file]\n\n", self);
fprintf(stderr, "Options:\n"); fprintf(stderr, "Options:\n");
fprintf(stderr, " -c Do not generate CFI section\n"); fprintf(stderr, " -c Do not generate CFI section\n");
fprintf(stderr, " -r Do not handle inter-compilation unit references\n");
return 1; return 1;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc < 2 || argc > 4) if (argc < 2)
return usage(argv[0]); return usage(argv[0]);
bool cfi = true; bool cfi = true;
int binary_index = 1; bool handle_inter_cu_refs = true;
if (strcmp("-c", argv[1]) == 0) { 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; cfi = false;
++binary_index; } else if (strcmp("-r", argv[arg_index]) == 0) {
handle_inter_cu_refs = false;
} else {
return usage(argv[0]);
} }
if (!cfi && argc == 2) ++arg_index;
}
if (arg_index == argc)
return usage(argv[0]); return usage(argv[0]);
const char* binary; const char* binary;
std::vector<string> debug_dirs; std::vector<string> debug_dirs;
binary = argv[binary_index]; binary = argv[arg_index];
for (int debug_dir_index = binary_index + 1; for (int debug_dir_index = arg_index + 1;
debug_dir_index < argc; debug_dir_index < argc;
++debug_dir_index) { ++debug_dir_index) {
debug_dirs.push_back(argv[debug_dir_index]); debug_dirs.push_back(argv[debug_dir_index]);
} }
SymbolData symbol_data = cfi ? ALL_SYMBOL_DATA : NO_CFI; 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"); fprintf(stderr, "Failed to write symbol file.\n");
return 1; return 1;
} }