aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthestig@chromium.org <thestig@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2013-04-24 21:18:44 +0000
committerthestig@chromium.org <thestig@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2013-04-24 21:18:44 +0000
commitf7566bd447f628d77152dc28fb70ab232c342b86 (patch)
treea092b168c9b64f59a72f62ba9680bc4a0d0dcb8a
parentFix Clang warning regarding null pointer argument. (diff)
downloadbreakpad-f7566bd447f628d77152dc28fb70ab232c342b86.tar.xz
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
-rw-r--r--src/common/dwarf_cu_to_module.cc98
-rw-r--r--src/common/dwarf_cu_to_module.h64
-rw-r--r--src/common/dwarf_cu_to_module_unittest.cc152
-rw-r--r--src/common/linux/dump_symbols.cc53
-rw-r--r--src/common/linux/dump_symbols.h14
-rw-r--r--src/common/linux/dump_symbols_unittest.cc18
-rw-r--r--src/tools/linux/dump_syms/dump_syms.cc30
7 files changed, 300 insertions, 129 deletions
diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc
index a8b0a29b..9f892290 100644
--- a/src/common/dwarf_cu_to_module.cc
+++ b/src/common/dwarf_cu_to_module.cc
@@ -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,
diff --git a/src/common/dwarf_cu_to_module.h b/src/common/dwarf_cu_to_module.h
index 85453316..4f466ace 100644
--- a/src/common/dwarf_cu_to_module.h
+++ b/src/common/dwarf_cu_to_module.h
@@ -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__
diff --git a/src/common/dwarf_cu_to_module_unittest.cc b/src/common/dwarf_cu_to_module_unittest.cc
index 81e629b0..5f61a58e 100644
--- a/src/common/dwarf_cu_to_module_unittest.cc
+++ b/src/common/dwarf_cu_to_module_unittest.cc
@@ -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
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
index eec5373c..517edee3 100644
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -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
diff --git a/src/common/linux/dump_symbols.h b/src/common/linux/dump_symbols.h
index f3c92cd5..636bb72f 100644
--- a/src/common/linux/dump_symbols.h
+++ b/src/common/linux/dump_symbols.h
@@ -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
diff --git a/src/common/linux/dump_symbols_unittest.cc b/src/common/linux/dump_symbols_unittest.cc
index 6f5aef2e..3f86dbe6 100644
--- a/src/common/linux/dump_symbols_unittest.cc
+++ b/src/common/linux/dump_symbols_unittest.cc
@@ -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
diff --git a/src/tools/linux/dump_syms/dump_syms.cc b/src/tools/linux/dump_syms/dump_syms.cc
index f58952ed..c8ade33a 100644
--- a/src/tools/linux/dump_syms/dump_syms.cc
+++ b/src/tools/linux/dump_syms/dump_syms.cc
@@ -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;
}