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.
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 &section_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,

View file

@ -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__

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}