mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-12-23 15:05:34 +00:00
Include the compilation directory for FILE entries, making them absolute instead of relative
A=Ryan Sleevi <rsleevi@chromium.org> R=mark,ted at https://breakpad.appspot.com/385001/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1106 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
be81ededf8
commit
1f87c4a732
|
@ -623,7 +623,7 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DwarfCUToModule::DwarfCUToModule(FileContext *file_context,
|
DwarfCUToModule::DwarfCUToModule(FileContext *file_context,
|
||||||
LineToModuleFunctor *line_reader,
|
LineToModuleHandler *line_reader,
|
||||||
WarningReporter *reporter)
|
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);
|
cu_context_ = new CUContext(file_context, reporter);
|
||||||
|
@ -666,8 +666,16 @@ void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr,
|
||||||
void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr,
|
void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr,
|
||||||
enum DwarfForm form,
|
enum DwarfForm form,
|
||||||
const string &data) {
|
const string &data) {
|
||||||
if (attr == dwarf2reader::DW_AT_name)
|
switch (attr) {
|
||||||
|
case dwarf2reader::DW_AT_name:
|
||||||
cu_context_->reporter->SetCUName(data);
|
cu_context_->reporter->SetCUName(data);
|
||||||
|
break;
|
||||||
|
case dwarf2reader::DW_AT_comp_dir:
|
||||||
|
line_reader_->StartCompilationUnit(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DwarfCUToModule::EndAttributes() {
|
bool DwarfCUToModule::EndAttributes() {
|
||||||
|
@ -744,7 +752,7 @@ void DwarfCUToModule::ReadSourceLines(uint64 offset) {
|
||||||
cu_context_->reporter->BadLineInfoOffset(offset);
|
cu_context_->reporter->BadLineInfoOffset(offset);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(*line_reader_)(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_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,20 +92,26 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||||
FilePrivate *file_private;
|
FilePrivate *file_private;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An abstract base class for functors that handle DWARF line data
|
// An abstract base class for handlers that handle DWARF line data
|
||||||
// for DwarfCUToModule. DwarfCUToModule could certainly just use
|
// for DwarfCUToModule. DwarfCUToModule could certainly just use
|
||||||
// dwarf2reader::LineInfo itself directly, but decoupling things
|
// dwarf2reader::LineInfo itself directly, but decoupling things
|
||||||
// this way makes unit testing a little easier.
|
// this way makes unit testing a little easier.
|
||||||
class LineToModuleFunctor {
|
class LineToModuleHandler {
|
||||||
public:
|
public:
|
||||||
LineToModuleFunctor() { }
|
LineToModuleHandler() { }
|
||||||
virtual ~LineToModuleFunctor() { }
|
virtual ~LineToModuleHandler() { }
|
||||||
|
|
||||||
|
// Called at the beginning of a new compilation unit, prior to calling
|
||||||
|
// ReadProgram(). compilation_dir will indicate the path that the
|
||||||
|
// current compilation unit was compiled in, consistent with the
|
||||||
|
// DW_AT_comp_dir DIE.
|
||||||
|
virtual void StartCompilationUnit(const string& compilation_dir) = 0;
|
||||||
|
|
||||||
// Populate MODULE and LINES with source file names and code/line
|
// Populate MODULE and LINES with source file names and code/line
|
||||||
// mappings, given a pointer to some DWARF line number data
|
// mappings, given a pointer to some DWARF line number data
|
||||||
// PROGRAM, and an overestimate of its size. Add no zero-length
|
// PROGRAM, and an overestimate of its size. Add no zero-length
|
||||||
// lines to LINES.
|
// lines to LINES.
|
||||||
virtual void operator()(const char *program, uint64 length,
|
virtual void ReadProgram(const char *program, uint64 length,
|
||||||
Module *module, vector<Module::Line> *lines) = 0;
|
Module *module, vector<Module::Line> *lines) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -186,7 +192,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||||
// unit's line number data. Use REPORTER to report problems with the
|
// unit's line number data. Use REPORTER to report problems with the
|
||||||
// data we find.
|
// data we find.
|
||||||
DwarfCUToModule(FileContext *file_context,
|
DwarfCUToModule(FileContext *file_context,
|
||||||
LineToModuleFunctor *line_reader,
|
LineToModuleHandler *line_reader,
|
||||||
WarningReporter *reporter);
|
WarningReporter *reporter);
|
||||||
~DwarfCUToModule();
|
~DwarfCUToModule();
|
||||||
|
|
||||||
|
@ -247,8 +253,8 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||||
// owned by this DwarfCUToModule: the constructor sets them, and the
|
// owned by this DwarfCUToModule: the constructor sets them, and the
|
||||||
// destructor deletes them.
|
// destructor deletes them.
|
||||||
|
|
||||||
// The functor to use to handle line number data.
|
// The handler to use to handle line number data.
|
||||||
LineToModuleFunctor *line_reader_;
|
LineToModuleHandler *line_reader_;
|
||||||
|
|
||||||
// This compilation unit's context.
|
// This compilation unit's context.
|
||||||
CUContext *cu_context_;
|
CUContext *cu_context_;
|
||||||
|
|
|
@ -62,14 +62,11 @@ using ::testing::ValuesIn;
|
||||||
|
|
||||||
// Mock classes.
|
// Mock classes.
|
||||||
|
|
||||||
class MockLineToModuleFunctor: public DwarfCUToModule::LineToModuleFunctor {
|
class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD4(mock_apply, void(const char *program, uint64 length,
|
MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir));
|
||||||
|
MOCK_METHOD4(ReadProgram, void(const char* program, uint64 length,
|
||||||
Module *module, vector<Module::Line> *lines));
|
Module *module, vector<Module::Line> *lines));
|
||||||
void operator()(const char *program, uint64 length,
|
|
||||||
Module *module, vector<Module::Line> *lines) {
|
|
||||||
mock_apply(program, length, module, lines);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockWarningReporter: public DwarfCUToModule::WarningReporter {
|
class MockWarningReporter: public DwarfCUToModule::WarningReporter {
|
||||||
|
@ -102,10 +99,10 @@ class CUFixtureBase {
|
||||||
// appender(line_program, length, module, line_vector);
|
// appender(line_program, length, module, line_vector);
|
||||||
//
|
//
|
||||||
// will append lines to the end of line_vector. We can use this with
|
// will append lines to the end of line_vector. We can use this with
|
||||||
// MockLineToModuleFunctor like this:
|
// MockLineToModuleHandler like this:
|
||||||
//
|
//
|
||||||
// MockLineToModuleFunctor l2m;
|
// MockLineToModuleHandler l2m;
|
||||||
// EXPECT_CALL(l2m, mock_apply(_,_,_,_))
|
// EXPECT_CALL(l2m, ReadProgram(_,_,_,_))
|
||||||
// .WillOnce(DoAll(Invoke(appender), Return()));
|
// .WillOnce(DoAll(Invoke(appender), Return()));
|
||||||
//
|
//
|
||||||
// in which case calling l2m with some line vector will append lines.
|
// in which case calling l2m with some line vector will append lines.
|
||||||
|
@ -143,7 +140,8 @@ class CUFixtureBase {
|
||||||
|
|
||||||
// By default, expect the line program reader not to be invoked. We
|
// By default, expect the line program reader not to be invoked. We
|
||||||
// may override this in StartCU.
|
// may override this in StartCU.
|
||||||
EXPECT_CALL(line_reader_, mock_apply(_,_,_,_)).Times(0);
|
EXPECT_CALL(line_reader_, StartCompilationUnit(_)).Times(0);
|
||||||
|
EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_)).Times(0);
|
||||||
|
|
||||||
// The handler will consult this section map to decide what to
|
// The handler will consult this section map to decide what to
|
||||||
// pass to our line reader.
|
// pass to our line reader.
|
||||||
|
@ -153,7 +151,7 @@ class CUFixtureBase {
|
||||||
|
|
||||||
// Add a line with the given address, size, filename, and line
|
// Add a line with the given address, size, filename, and line
|
||||||
// number to the end of the statement list the handler will receive
|
// number to the end of the statement list the handler will receive
|
||||||
// when it invokes its LineToModuleFunctor. Call this before calling
|
// when it invokes its LineToModuleHandler. Call this before calling
|
||||||
// StartCU.
|
// StartCU.
|
||||||
void PushLine(Module::Address address, Module::Address size,
|
void PushLine(Module::Address address, Module::Address size,
|
||||||
const string &filename, int line_number);
|
const string &filename, int line_number);
|
||||||
|
@ -271,13 +269,17 @@ class CUFixtureBase {
|
||||||
// report it as an unsigned value.
|
// report it as an unsigned value.
|
||||||
bool language_signed_;
|
bool language_signed_;
|
||||||
|
|
||||||
|
// If this is not empty, we'll give the CU a DW_AT_comp_dir attribute that
|
||||||
|
// indicates the path that this compilation unit was compiled in.
|
||||||
|
string compilation_dir_;
|
||||||
|
|
||||||
// If this is not empty, we'll give the CU a DW_AT_stmt_list
|
// If this is not empty, we'll give the CU a DW_AT_stmt_list
|
||||||
// attribute that, when passed to line_reader_, adds these lines to the
|
// attribute that, when passed to line_reader_, adds these lines to the
|
||||||
// provided lines array.
|
// provided lines array.
|
||||||
vector<Module::Line> lines_;
|
vector<Module::Line> lines_;
|
||||||
|
|
||||||
// Mock line program reader.
|
// Mock line program reader.
|
||||||
MockLineToModuleFunctor line_reader_;
|
MockLineToModuleHandler line_reader_;
|
||||||
AppendLinesFunctor appender_;
|
AppendLinesFunctor appender_;
|
||||||
static const char dummy_line_program_[];
|
static const char dummy_line_program_[];
|
||||||
static const size_t dummy_line_size_;
|
static const size_t dummy_line_size_;
|
||||||
|
@ -311,6 +313,10 @@ void CUFixtureBase::PushLine(Module::Address address, Module::Address size,
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUFixtureBase::StartCU() {
|
void CUFixtureBase::StartCU() {
|
||||||
|
if (!compilation_dir_.empty())
|
||||||
|
EXPECT_CALL(line_reader_,
|
||||||
|
StartCompilationUnit(compilation_dir_)).Times(1);
|
||||||
|
|
||||||
// If we have lines, make the line reader expect to be invoked at
|
// If we have lines, make the line reader expect to be invoked at
|
||||||
// most once. (Hey, if the handler can pass its tests without
|
// most once. (Hey, if the handler can pass its tests without
|
||||||
// bothering to read the line number data, that's great.)
|
// bothering to read the line number data, that's great.)
|
||||||
|
@ -318,7 +324,7 @@ void CUFixtureBase::StartCU() {
|
||||||
// initial expectation (no calls) in force.
|
// initial expectation (no calls) in force.
|
||||||
if (!lines_.empty())
|
if (!lines_.empty())
|
||||||
EXPECT_CALL(line_reader_,
|
EXPECT_CALL(line_reader_,
|
||||||
mock_apply(&dummy_line_program_[0], dummy_line_size_,
|
ReadProgram(&dummy_line_program_[0], dummy_line_size_,
|
||||||
&module_, _))
|
&module_, _))
|
||||||
.Times(AtMost(1))
|
.Times(AtMost(1))
|
||||||
.WillOnce(DoAll(Invoke(appender_), Return()));
|
.WillOnce(DoAll(Invoke(appender_), Return()));
|
||||||
|
@ -333,6 +339,10 @@ void CUFixtureBase::StartCU() {
|
||||||
root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name,
|
root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name,
|
||||||
dwarf2reader::DW_FORM_strp,
|
dwarf2reader::DW_FORM_strp,
|
||||||
"compilation-unit-name");
|
"compilation-unit-name");
|
||||||
|
if (!compilation_dir_.empty())
|
||||||
|
root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_comp_dir,
|
||||||
|
dwarf2reader::DW_FORM_strp,
|
||||||
|
compilation_dir_);
|
||||||
if (!lines_.empty())
|
if (!lines_.empty())
|
||||||
root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list,
|
root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list,
|
||||||
dwarf2reader::DW_FORM_ref4,
|
dwarf2reader::DW_FORM_ref4,
|
||||||
|
@ -626,6 +636,13 @@ void CUFixtureBase::TestLine(int i, int j,
|
||||||
class SimpleCU: public CUFixtureBase, public Test {
|
class SimpleCU: public CUFixtureBase, public Test {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST_F(SimpleCU, CompilationDir) {
|
||||||
|
compilation_dir_ = "/src/build/";
|
||||||
|
|
||||||
|
StartCU();
|
||||||
|
root_handler_.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SimpleCU, OneFunc) {
|
TEST_F(SimpleCU, OneFunc) {
|
||||||
PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772);
|
PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772);
|
||||||
|
|
||||||
|
@ -1414,8 +1431,8 @@ 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);
|
||||||
EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
|
EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
|
||||||
MockLineToModuleFunctor lr;
|
MockLineToModuleHandler lr;
|
||||||
EXPECT_CALL(lr, mock_apply(_,_,_,_)).Times(0);
|
EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0);
|
||||||
|
|
||||||
// Kludge: satisfy reporter_'s expectation.
|
// Kludge: satisfy reporter_'s expectation.
|
||||||
reporter_.SetCUName("compilation-unit-name");
|
reporter_.SetCUName("compilation-unit-name");
|
||||||
|
|
|
@ -48,13 +48,17 @@ static bool PathIsAbsolute(const string &path) {
|
||||||
return (path.size() >= 1 && path[0] == '/');
|
return (path.size() >= 1 && path[0] == '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool HasTrailingSlash(const string &path) {
|
||||||
|
return (path.size() >= 1 && path[path.size() - 1] == '/');
|
||||||
|
}
|
||||||
|
|
||||||
// If PATH is an absolute path, return PATH. If PATH is a relative path,
|
// If PATH is an absolute path, return PATH. If PATH is a relative path,
|
||||||
// treat it as relative to BASE and return the combined path.
|
// treat it as relative to BASE and return the combined path.
|
||||||
static string ExpandPath(const string &path,
|
static string ExpandPath(const string &path,
|
||||||
const string &base) {
|
const string &base) {
|
||||||
if (PathIsAbsolute(path))
|
if (PathIsAbsolute(path) || base.empty())
|
||||||
return path;
|
return path;
|
||||||
return base + "/" + path;
|
return base + (HasTrailingSlash(base) ? "" : "/") + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
@ -63,7 +67,7 @@ void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) {
|
||||||
// Directory number zero is reserved to mean the compilation
|
// Directory number zero is reserved to mean the compilation
|
||||||
// directory. Silently ignore attempts to redefine it.
|
// directory. Silently ignore attempts to redefine it.
|
||||||
if (dir_num != 0)
|
if (dir_num != 0)
|
||||||
directories_[dir_num] = name;
|
directories_[dir_num] = ExpandPath(name, compilation_dir_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
|
void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
|
||||||
|
@ -74,25 +78,26 @@ void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
|
||||||
else if (file_num > highest_file_number_)
|
else if (file_num > highest_file_number_)
|
||||||
highest_file_number_ = file_num;
|
highest_file_number_ = file_num;
|
||||||
|
|
||||||
string full_name;
|
string dir_name;
|
||||||
if (dir_num != 0) {
|
if (dir_num == 0) {
|
||||||
|
// Directory number zero is the compilation directory, and is stored as
|
||||||
|
// an attribute on the compilation unit, rather than in the program table.
|
||||||
|
dir_name = compilation_dir_;
|
||||||
|
} else {
|
||||||
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
|
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
|
||||||
if (directory_it != directories_.end()) {
|
if (directory_it != directories_.end()) {
|
||||||
full_name = ExpandPath(name, directory_it->second);
|
dir_name = directory_it->second;
|
||||||
} else {
|
} else {
|
||||||
if (!warned_bad_directory_number_) {
|
if (!warned_bad_directory_number_) {
|
||||||
fprintf(stderr, "warning: DWARF line number data refers to undefined"
|
fprintf(stderr, "warning: DWARF line number data refers to undefined"
|
||||||
" directory numbers\n");
|
" directory numbers\n");
|
||||||
warned_bad_directory_number_ = true;
|
warned_bad_directory_number_ = true;
|
||||||
}
|
}
|
||||||
full_name = name; // just treat name as relative
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Directory number zero is the compilation directory; we just report
|
|
||||||
// relative paths in that case.
|
|
||||||
full_name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string full_name = ExpandPath(name, dir_name);
|
||||||
|
|
||||||
// Find a Module::File object of the given name, and add it to the
|
// Find a Module::File object of the given name, and add it to the
|
||||||
// file table.
|
// file table.
|
||||||
files_[file_num] = module_->FindFile(full_name);
|
files_[file_num] = module_->FindFile(full_name);
|
||||||
|
|
|
@ -120,8 +120,10 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
|
||||||
// end of the address space, we clip it. It's up to our client to
|
// end of the address space, we clip it. It's up to our client to
|
||||||
// sort out which lines belong to which functions; we don't add them
|
// sort out which lines belong to which functions; we don't add them
|
||||||
// to any particular function in MODULE ourselves.
|
// to any particular function in MODULE ourselves.
|
||||||
DwarfLineToModule(Module *module, vector<Module::Line> *lines)
|
DwarfLineToModule(Module *module, const string& compilation_dir,
|
||||||
|
vector<Module::Line> *lines)
|
||||||
: module_(module),
|
: module_(module),
|
||||||
|
compilation_dir_(compilation_dir),
|
||||||
lines_(lines),
|
lines_(lines),
|
||||||
highest_file_number_(-1),
|
highest_file_number_(-1),
|
||||||
omitted_line_end_(0),
|
omitted_line_end_(0),
|
||||||
|
@ -146,6 +148,10 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
|
||||||
// client.
|
// client.
|
||||||
Module *module_;
|
Module *module_;
|
||||||
|
|
||||||
|
// The compilation directory for the current compilation unit whose
|
||||||
|
// lines are being accumulated.
|
||||||
|
string compilation_dir_;
|
||||||
|
|
||||||
// The vector of lines we're accumulating. Owned by our client.
|
// The vector of lines we're accumulating. Owned by our client.
|
||||||
//
|
//
|
||||||
// In a Module, as in a breakpad symbol file, lines belong to
|
// In a Module, as in a breakpad symbol file, lines belong to
|
||||||
|
|
|
@ -45,7 +45,7 @@ using google_breakpad::Module;
|
||||||
TEST(SimpleModule, One) {
|
TEST(SimpleModule, One) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
|
h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
|
||||||
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
|
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
|
||||||
|
@ -54,7 +54,7 @@ TEST(SimpleModule, One) {
|
||||||
vector<Module::File *> files;
|
vector<Module::File *> files;
|
||||||
m.GetFiles(&files);
|
m.GetFiles(&files);
|
||||||
EXPECT_EQ(1U, files.size());
|
EXPECT_EQ(1U, files.size());
|
||||||
EXPECT_STREQ("file1", files[0]->name.c_str());
|
EXPECT_STREQ("/file1", files[0]->name.c_str());
|
||||||
|
|
||||||
EXPECT_EQ(1U, lines.size());
|
EXPECT_EQ(1U, lines.size());
|
||||||
EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address);
|
EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address);
|
||||||
|
@ -66,7 +66,7 @@ TEST(SimpleModule, One) {
|
||||||
TEST(SimpleModule, Many) {
|
TEST(SimpleModule, Many) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineDir("directory1", 0x838299ab);
|
h.DefineDir("directory1", 0x838299ab);
|
||||||
h.DefineDir("directory2", 0xf85de023);
|
h.DefineDir("directory2", 0xf85de023);
|
||||||
|
@ -89,11 +89,11 @@ TEST(SimpleModule, Many) {
|
||||||
vector<Module::File *> files;
|
vector<Module::File *> files;
|
||||||
m.GetFiles(&files);
|
m.GetFiles(&files);
|
||||||
ASSERT_EQ(5U, files.size());
|
ASSERT_EQ(5U, files.size());
|
||||||
EXPECT_STREQ("directory1/file1", files[0]->name.c_str());
|
EXPECT_STREQ("/directory1/file1", files[0]->name.c_str());
|
||||||
EXPECT_STREQ("directory1/file2", files[1]->name.c_str());
|
EXPECT_STREQ("/directory1/file2", files[1]->name.c_str());
|
||||||
EXPECT_STREQ("directory2/file1", files[2]->name.c_str());
|
EXPECT_STREQ("/directory2/file1", files[2]->name.c_str());
|
||||||
EXPECT_STREQ("directory2/file2", files[3]->name.c_str());
|
EXPECT_STREQ("/directory2/file2", files[3]->name.c_str());
|
||||||
EXPECT_STREQ("file3", files[4]->name.c_str());
|
EXPECT_STREQ("/file3", files[4]->name.c_str());
|
||||||
|
|
||||||
ASSERT_EQ(5U, lines.size());
|
ASSERT_EQ(5U, lines.size());
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ TEST(SimpleModule, Many) {
|
||||||
TEST(Filenames, Absolute) {
|
TEST(Filenames, Absolute) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineDir("directory1", 1);
|
h.DefineDir("directory1", 1);
|
||||||
h.DefineFile("/absolute", 1, 1, 0, 0);
|
h.DefineFile("/absolute", 1, 1, 0, 0);
|
||||||
|
@ -144,7 +144,7 @@ TEST(Filenames, Absolute) {
|
||||||
TEST(Filenames, Relative) {
|
TEST(Filenames, Relative) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineDir("directory1", 1);
|
h.DefineDir("directory1", 1);
|
||||||
h.DefineFile("relative", 1, 1, 0, 0);
|
h.DefineFile("relative", 1, 1, 0, 0);
|
||||||
|
@ -154,7 +154,7 @@ TEST(Filenames, Relative) {
|
||||||
vector<Module::File *> files;
|
vector<Module::File *> files;
|
||||||
m.GetFiles(&files);
|
m.GetFiles(&files);
|
||||||
ASSERT_EQ(1U, files.size());
|
ASSERT_EQ(1U, files.size());
|
||||||
EXPECT_STREQ("directory1/relative", files[0]->name.c_str());
|
EXPECT_STREQ("/directory1/relative", files[0]->name.c_str());
|
||||||
ASSERT_EQ(1U, lines.size());
|
ASSERT_EQ(1U, lines.size());
|
||||||
EXPECT_TRUE(lines[0].file == files[0]);
|
EXPECT_TRUE(lines[0].file == files[0]);
|
||||||
}
|
}
|
||||||
|
@ -162,20 +162,20 @@ TEST(Filenames, Relative) {
|
||||||
TEST(Filenames, StrangeFile) {
|
TEST(Filenames, StrangeFile) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineDir("directory1", 1);
|
h.DefineDir("directory1", 1);
|
||||||
h.DefineFile("", 1, 1, 0, 0);
|
h.DefineFile("", 1, 1, 0, 0);
|
||||||
h.AddLine(1, 1, 1, 0, 0);
|
h.AddLine(1, 1, 1, 0, 0);
|
||||||
|
|
||||||
ASSERT_EQ(1U, lines.size());
|
ASSERT_EQ(1U, lines.size());
|
||||||
EXPECT_STREQ("directory1/", lines[0].file->name.c_str());
|
EXPECT_STREQ("/directory1/", lines[0].file->name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Filenames, StrangeDirectory) {
|
TEST(Filenames, StrangeDirectory) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineDir("", 1);
|
h.DefineDir("", 1);
|
||||||
h.DefineFile("file1", 1, 1, 0, 0);
|
h.DefineFile("file1", 1, 1, 0, 0);
|
||||||
|
@ -188,7 +188,7 @@ TEST(Filenames, StrangeDirectory) {
|
||||||
TEST(Filenames, StrangeDirectoryAndFile) {
|
TEST(Filenames, StrangeDirectoryAndFile) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineDir("", 1);
|
h.DefineDir("", 1);
|
||||||
h.DefineFile("", 1, 1, 0, 0);
|
h.DefineFile("", 1, 1, 0, 0);
|
||||||
|
@ -198,12 +198,60 @@ TEST(Filenames, StrangeDirectoryAndFile) {
|
||||||
EXPECT_STREQ("/", lines[0].file->name.c_str());
|
EXPECT_STREQ("/", lines[0].file->name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should use the compilation directory when encountering a file for
|
||||||
|
// directory number zero.
|
||||||
|
TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) {
|
||||||
|
Module m("name", "os", "architecture", "id");
|
||||||
|
vector<Module::Line> lines;
|
||||||
|
DwarfLineToModule h(&m, "src/build", &lines);
|
||||||
|
|
||||||
|
h.DefineDir("Dir", 1);
|
||||||
|
h.DefineFile("File", 1, 0, 0, 0);
|
||||||
|
|
||||||
|
h.AddLine(1, 1, 1, 0, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(1U, lines.size());
|
||||||
|
EXPECT_STREQ("src/build/File", lines[0].file->name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should treat non-absolute directories as relative to the compilation
|
||||||
|
// directory.
|
||||||
|
TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) {
|
||||||
|
Module m("name", "os", "architecture", "id");
|
||||||
|
vector<Module::Line> lines;
|
||||||
|
DwarfLineToModule h(&m, "src/build", &lines);
|
||||||
|
|
||||||
|
h.DefineDir("Dir", 1);
|
||||||
|
h.DefineFile("File", 1, 1, 0, 0);
|
||||||
|
|
||||||
|
h.AddLine(1, 1, 1, 0, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(1U, lines.size());
|
||||||
|
EXPECT_STREQ("src/build/Dir/File", lines[0].file->name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should treat absolute directories as absolute, and not relative to
|
||||||
|
// the compilation dir.
|
||||||
|
TEST(Filenames, IncludeDirectoryAbsolute) {
|
||||||
|
Module m("name", "os", "architecture", "id");
|
||||||
|
vector<Module::Line> lines;
|
||||||
|
DwarfLineToModule h(&m, "src/build", &lines);
|
||||||
|
|
||||||
|
h.DefineDir("/Dir", 1);
|
||||||
|
h.DefineFile("File", 1, 1, 0, 0);
|
||||||
|
|
||||||
|
h.AddLine(1, 1, 1, 0, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(1U, lines.size());
|
||||||
|
EXPECT_STREQ("/Dir/File", lines[0].file->name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
// We should silently ignore attempts to define directory number zero,
|
// We should silently ignore attempts to define directory number zero,
|
||||||
// since that is always the compilation directory.
|
// since that is always the compilation directory.
|
||||||
TEST(ModuleErrors, DirectoryZero) {
|
TEST(ModuleErrors, DirectoryZero) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineDir("directory0", 0); // should be ignored
|
h.DefineDir("directory0", 0); // should be ignored
|
||||||
h.DefineFile("relative", 1, 0, 0, 0);
|
h.DefineFile("relative", 1, 0, 0, 0);
|
||||||
|
@ -211,7 +259,7 @@ TEST(ModuleErrors, DirectoryZero) {
|
||||||
h.AddLine(1, 1, 1, 0, 0);
|
h.AddLine(1, 1, 1, 0, 0);
|
||||||
|
|
||||||
ASSERT_EQ(1U, lines.size());
|
ASSERT_EQ(1U, lines.size());
|
||||||
EXPECT_STREQ("relative", lines[0].file->name.c_str());
|
EXPECT_STREQ("/relative", lines[0].file->name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should refuse to add lines with bogus file numbers. We should
|
// We should refuse to add lines with bogus file numbers. We should
|
||||||
|
@ -219,7 +267,7 @@ TEST(ModuleErrors, DirectoryZero) {
|
||||||
TEST(ModuleErrors, BadFileNumber) {
|
TEST(ModuleErrors, BadFileNumber) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineFile("relative", 1, 0, 0, 0);
|
h.DefineFile("relative", 1, 0, 0, 0);
|
||||||
h.AddLine(1, 1, 2, 0, 0); // bad file number
|
h.AddLine(1, 1, 2, 0, 0); // bad file number
|
||||||
|
@ -233,7 +281,7 @@ TEST(ModuleErrors, BadFileNumber) {
|
||||||
TEST(ModuleErrors, BadDirectoryNumber) {
|
TEST(ModuleErrors, BadDirectoryNumber) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineDir("directory1", 1);
|
h.DefineDir("directory1", 1);
|
||||||
h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number
|
h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number
|
||||||
|
@ -248,7 +296,7 @@ TEST(ModuleErrors, BadDirectoryNumber) {
|
||||||
TEST(ModuleErrors, EmptyLine) {
|
TEST(ModuleErrors, EmptyLine) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||||
h.AddLine(1, 0, 1, 0, 0);
|
h.AddLine(1, 0, 1, 0, 0);
|
||||||
|
@ -261,7 +309,7 @@ TEST(ModuleErrors, EmptyLine) {
|
||||||
TEST(ModuleErrors, BigLine) {
|
TEST(ModuleErrors, BigLine) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||||
h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0);
|
h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0);
|
||||||
|
@ -278,7 +326,7 @@ TEST(ModuleErrors, BigLine) {
|
||||||
TEST(Omitted, DroppedThenGood) {
|
TEST(Omitted, DroppedThenGood) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||||
h.AddLine(0, 10, 1, 83816211, 0); // should be omitted
|
h.AddLine(0, 10, 1, 83816211, 0); // should be omitted
|
||||||
|
@ -291,7 +339,7 @@ TEST(Omitted, DroppedThenGood) {
|
||||||
TEST(Omitted, GoodThenDropped) {
|
TEST(Omitted, GoodThenDropped) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||||
h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded
|
h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded
|
||||||
|
@ -304,7 +352,7 @@ TEST(Omitted, GoodThenDropped) {
|
||||||
TEST(Omitted, Mix1) {
|
TEST(Omitted, Mix1) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||||
h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded
|
h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded
|
||||||
|
@ -325,7 +373,7 @@ TEST(Omitted, Mix1) {
|
||||||
TEST(Omitted, Mix2) {
|
TEST(Omitted, Mix2) {
|
||||||
Module m("name", "os", "architecture", "id");
|
Module m("name", "os", "architecture", "id");
|
||||||
vector<Module::Line> lines;
|
vector<Module::Line> lines;
|
||||||
DwarfLineToModule h(&m, &lines);
|
DwarfLineToModule h(&m, "/", &lines);
|
||||||
|
|
||||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||||
h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted
|
h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted
|
||||||
|
|
|
@ -186,18 +186,22 @@ bool LoadStabs(const typename ElfClass::Ehdr* elf_header,
|
||||||
// A line-to-module loader that accepts line number info parsed by
|
// A line-to-module loader that accepts line number info parsed by
|
||||||
// dwarf2reader::LineInfo and populates a Module and a line vector
|
// dwarf2reader::LineInfo and populates a Module and a line vector
|
||||||
// with the results.
|
// with the results.
|
||||||
class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
|
class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler {
|
||||||
public:
|
public:
|
||||||
// Create a line-to-module converter using BYTE_READER.
|
// Create a line-to-module converter using BYTE_READER.
|
||||||
explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
||||||
: byte_reader_(byte_reader) { }
|
: byte_reader_(byte_reader) { }
|
||||||
void operator()(const char *program, uint64 length,
|
void StartCompilationUnit(const string& compilation_dir) {
|
||||||
|
compilation_dir_ = compilation_dir;
|
||||||
|
}
|
||||||
|
void ReadProgram(const char *program, uint64 length,
|
||||||
Module *module, std::vector<Module::Line> *lines) {
|
Module *module, std::vector<Module::Line> *lines) {
|
||||||
DwarfLineToModule handler(module, lines);
|
DwarfLineToModule handler(module, compilation_dir_, lines);
|
||||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
||||||
parser.Start();
|
parser.Start();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
string compilation_dir_;
|
||||||
dwarf2reader::ByteReader *byte_reader_;
|
dwarf2reader::ByteReader *byte_reader_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -227,18 +227,24 @@ string DumpSymbols::Identifier() {
|
||||||
// dwarf2reader::LineInfo and populates a Module and a line vector
|
// dwarf2reader::LineInfo and populates a Module and a line vector
|
||||||
// with the results.
|
// with the results.
|
||||||
class DumpSymbols::DumperLineToModule:
|
class DumpSymbols::DumperLineToModule:
|
||||||
public DwarfCUToModule::LineToModuleFunctor {
|
public DwarfCUToModule::LineToModuleHandler {
|
||||||
public:
|
public:
|
||||||
// Create a line-to-module converter using BYTE_READER.
|
// Create a line-to-module converter using BYTE_READER.
|
||||||
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
||||||
: byte_reader_(byte_reader) { }
|
: byte_reader_(byte_reader) { }
|
||||||
void operator()(const char *program, uint64 length,
|
|
||||||
|
void StartCompilationUnit(const string& compilation_dir) {
|
||||||
|
compilation_dir_ = compilation_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadProgram(const char *program, uint64 length,
|
||||||
Module *module, vector<Module::Line> *lines) {
|
Module *module, vector<Module::Line> *lines) {
|
||||||
DwarfLineToModule handler(module, lines);
|
DwarfLineToModule handler(module, compilation_dir_, lines);
|
||||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
||||||
parser.Start();
|
parser.Start();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
string compilation_dir_;
|
||||||
dwarf2reader::ByteReader *byte_reader_; // WEAK
|
dwarf2reader::ByteReader *byte_reader_; // WEAK
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
204
src/tools/python/filter_syms.py
Normal file
204
src/tools/python/filter_syms.py
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Copyright (c) 2012 Google Inc.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
"""Normalizes and de-duplicates paths within Breakpad symbol files.
|
||||||
|
|
||||||
|
When using DWARF for storing debug symbols, some file information will be
|
||||||
|
stored relative to the current working directory of the current compilation
|
||||||
|
unit, and may be further relativized based upon how the file was #included.
|
||||||
|
|
||||||
|
This helper can be used to parse the Breakpad symbol file generated from such
|
||||||
|
DWARF files and normalize and de-duplicate the FILE records found within,
|
||||||
|
updating any references to the FILE records in the other record types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import macpath
|
||||||
|
import ntpath
|
||||||
|
import optparse
|
||||||
|
import os
|
||||||
|
import posixpath
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class BreakpadParseError(Exception):
|
||||||
|
"""Unsupported Breakpad symbol record exception class."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SymbolFileParser(object):
|
||||||
|
"""Parser for Breakpad symbol files.
|
||||||
|
|
||||||
|
The format of these files is documented at
|
||||||
|
https://code.google.com/p/google-breakpad/wiki/SymbolFiles
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, input_stream, output_stream, ignored_prefixes=None,
|
||||||
|
path_handler=os.path):
|
||||||
|
"""Inits a SymbolFileParser to read symbol records from |input_stream| and
|
||||||
|
write the processed output to |output_stream|.
|
||||||
|
|
||||||
|
|ignored_prefixes| contains a list of optional path prefixes that
|
||||||
|
should be stripped from the final, normalized path outputs.
|
||||||
|
|
||||||
|
For example, if the Breakpad symbol file had all paths starting with a
|
||||||
|
common prefix, such as:
|
||||||
|
FILE 1 /b/build/src/foo.cc
|
||||||
|
FILE 2 /b/build/src/bar.cc
|
||||||
|
Then adding "/b/build/src" as an ignored prefix would result in an output
|
||||||
|
file that contained:
|
||||||
|
FILE 1 foo.cc
|
||||||
|
FILE 2 bar.cc
|
||||||
|
|
||||||
|
Note that |ignored_prefixes| does not necessarily contain file system
|
||||||
|
paths, as the contents of the DWARF DW_AT_comp_dir attribute is dependent
|
||||||
|
upon the host system and compiler, and may contain additional information
|
||||||
|
such as hostname or compiler version.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.unique_files = {}
|
||||||
|
self.duplicate_files = {}
|
||||||
|
self.input_stream = input_stream
|
||||||
|
self.output_stream = output_stream
|
||||||
|
self.ignored_prefixes = ignored_prefixes or []
|
||||||
|
self.path_handler = path_handler
|
||||||
|
|
||||||
|
def Process(self):
|
||||||
|
"""Processes the Breakpad symbol file."""
|
||||||
|
for line in self.input_stream:
|
||||||
|
parsed = self._ParseRecord(line.rstrip())
|
||||||
|
if parsed:
|
||||||
|
self.output_stream.write(parsed + '\n')
|
||||||
|
|
||||||
|
def _ParseRecord(self, record):
|
||||||
|
"""Parses a single Breakpad symbol record - a single line from the symbol
|
||||||
|
file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The modified string to write to the output file, or None if no line
|
||||||
|
should be written.
|
||||||
|
"""
|
||||||
|
record_type = record.partition(' ')[0]
|
||||||
|
if record_type == 'FILE':
|
||||||
|
return self._ParseFileRecord(record)
|
||||||
|
elif self._IsLineRecord(record_type):
|
||||||
|
return self._ParseLineRecord(record)
|
||||||
|
else:
|
||||||
|
# Simply pass the record through unaltered.
|
||||||
|
return record
|
||||||
|
|
||||||
|
def _NormalizePath(self, path):
|
||||||
|
"""Normalizes a file path to its canonical form.
|
||||||
|
|
||||||
|
As this may not execute on the machine or file system originally
|
||||||
|
responsible for compilation, it may be necessary to further correct paths
|
||||||
|
for symlinks, junctions, or other such file system indirections.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A unique, canonical representation for the the file path.
|
||||||
|
"""
|
||||||
|
return self.path_handler.normpath(path)
|
||||||
|
|
||||||
|
def _AdjustPath(self, path):
|
||||||
|
"""Adjusts the supplied path after performing path de-duplication.
|
||||||
|
|
||||||
|
This may be used to perform secondary adjustments, such as removing a
|
||||||
|
common prefix, such as "/D/build", or replacing the file system path with
|
||||||
|
information from the version control system.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The actual path to use when writing the FILE record.
|
||||||
|
"""
|
||||||
|
return path[len(filter(path.startswith,
|
||||||
|
self.ignored_prefixes + [''])[0]):]
|
||||||
|
|
||||||
|
def _ParseFileRecord(self, file_record):
|
||||||
|
"""Parses and corrects a FILE record."""
|
||||||
|
file_info = file_record[5:].split(' ', 3)
|
||||||
|
if len(file_info) > 2:
|
||||||
|
raise BreakpadParseError('Unsupported FILE record: ' + file_record)
|
||||||
|
file_index = int(file_info[0])
|
||||||
|
file_name = self._NormalizePath(file_info[1])
|
||||||
|
existing_file_index = self.unique_files.get(file_name)
|
||||||
|
if existing_file_index is None:
|
||||||
|
self.unique_files[file_name] = file_index
|
||||||
|
file_info[1] = self._AdjustPath(file_name)
|
||||||
|
return 'FILE ' + ' '.join(file_info)
|
||||||
|
else:
|
||||||
|
self.duplicate_files[file_index] = existing_file_index
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _IsLineRecord(self, record_type):
|
||||||
|
"""Determines if the current record type is a Line record"""
|
||||||
|
try:
|
||||||
|
line = int(record_type, 16)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _ParseLineRecord(self, line_record):
|
||||||
|
"""Parses and corrects a Line record."""
|
||||||
|
line_info = line_record.split(' ', 5)
|
||||||
|
if len(line_info) > 4:
|
||||||
|
raise BreakpadParseError('Unsupported Line record: ' + line_record)
|
||||||
|
file_index = int(line_info[3])
|
||||||
|
line_info[3] = str(self.duplicate_files.get(file_index, file_index))
|
||||||
|
return ' '.join(line_info)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
option_parser = optparse.OptionParser()
|
||||||
|
option_parser.add_option("-p", "--prefix",
|
||||||
|
action="append", dest="prefixes", type="string",
|
||||||
|
default=[],
|
||||||
|
help="A path prefix that should be removed from "
|
||||||
|
"all FILE lines. May be repeated to specify "
|
||||||
|
"multiple prefixes.")
|
||||||
|
option_parser.add_option("-t", "--path_type",
|
||||||
|
action="store", type="choice", dest="path_handler",
|
||||||
|
choices=['win32', 'posix'],
|
||||||
|
help="Indicates how file paths should be "
|
||||||
|
"interpreted. The default is to treat paths "
|
||||||
|
"the same as the OS running Python (eg: "
|
||||||
|
"os.path)")
|
||||||
|
options, args = option_parser.parse_args()
|
||||||
|
if args:
|
||||||
|
option_parser.error('Unknown argument: %s' % args)
|
||||||
|
|
||||||
|
path_handler = { 'win32': ntpath,
|
||||||
|
'posix': posixpath }.get(options.path_handler, os.path)
|
||||||
|
try:
|
||||||
|
symbol_parser = SymbolFileParser(sys.stdin, sys.stdout, options.prefixes,
|
||||||
|
path_handler)
|
||||||
|
symbol_parser.Process()
|
||||||
|
except BreakpadParseError, e:
|
||||||
|
print >> sys.stderr, 'Got an error while processing symbol file'
|
||||||
|
print >> sys.stderr, str(e)
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
138
src/tools/python/tests/filter_syms_unittest.py
Normal file
138
src/tools/python/tests/filter_syms_unittest.py
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Copyright (c) 2012 Google Inc.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
"""Unit tests for filter_syms.py"""
|
||||||
|
|
||||||
|
import cStringIO
|
||||||
|
import ntpath
|
||||||
|
import os
|
||||||
|
import StringIO
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
sys.path.insert(0, os.path.join(ROOT_DIR, '..'))
|
||||||
|
|
||||||
|
# In root
|
||||||
|
import filter_syms
|
||||||
|
|
||||||
|
class FilterSysmsTest(unittest.TestCase):
|
||||||
|
def assertParsed(self, input_data, ignored_prefixes, expected):
|
||||||
|
input_io = cStringIO.StringIO(input_data)
|
||||||
|
output_io = cStringIO.StringIO()
|
||||||
|
parser = filter_syms.SymbolFileParser(input_io, output_io,
|
||||||
|
ignored_prefixes, ntpath)
|
||||||
|
parser.Process()
|
||||||
|
self.assertEqual(output_io.getvalue(), expected)
|
||||||
|
|
||||||
|
def testDuplicateFiles(self):
|
||||||
|
"""Tests that duplicate files in FILE records are correctly removed and
|
||||||
|
that Line records are updated."""
|
||||||
|
|
||||||
|
INPUT = \
|
||||||
|
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
|
||||||
|
INFO CODE_ID FFFFFFFF module1.exe
|
||||||
|
FILE 1 foo/../file1_1.cc
|
||||||
|
FILE 2 bar/../file1_1.cc
|
||||||
|
FILE 3 baz/../file1_1.cc
|
||||||
|
FUNC 1000 c 0 Function1_1
|
||||||
|
1000 8 45 2
|
||||||
|
1008 4 46 3
|
||||||
|
100c 4 44 1
|
||||||
|
"""
|
||||||
|
EXPECTED_OUTPUT = \
|
||||||
|
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
|
||||||
|
INFO CODE_ID FFFFFFFF module1.exe
|
||||||
|
FILE 1 file1_1.cc
|
||||||
|
FUNC 1000 c 0 Function1_1
|
||||||
|
1000 8 45 1
|
||||||
|
1008 4 46 1
|
||||||
|
100c 4 44 1
|
||||||
|
"""
|
||||||
|
self.assertParsed(INPUT, [], EXPECTED_OUTPUT)
|
||||||
|
|
||||||
|
def testIgnoredPrefix(self):
|
||||||
|
"""Tests that prefixes in FILE records are correctly removed."""
|
||||||
|
|
||||||
|
INPUT = \
|
||||||
|
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
|
||||||
|
INFO CODE_ID FFFFFFFF module1.exe
|
||||||
|
FILE 1 /src/build/foo/../file1_1.cc
|
||||||
|
FILE 2 /src/build/bar/../file1_2.cc
|
||||||
|
FILE 3 /src/build/baz/../file1_2.cc
|
||||||
|
FUNC 1000 c 0 Function1_1
|
||||||
|
1000 8 45 2
|
||||||
|
1008 4 46 3
|
||||||
|
100c 4 44 1
|
||||||
|
"""
|
||||||
|
EXPECTED_OUTPUT = \
|
||||||
|
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
|
||||||
|
INFO CODE_ID FFFFFFFF module1.exe
|
||||||
|
FILE 1 file1_1.cc
|
||||||
|
FILE 2 file1_2.cc
|
||||||
|
FUNC 1000 c 0 Function1_1
|
||||||
|
1000 8 45 2
|
||||||
|
1008 4 46 2
|
||||||
|
100c 4 44 1
|
||||||
|
"""
|
||||||
|
IGNORED_PREFIXES = ['\\src\\build\\']
|
||||||
|
self.assertParsed(INPUT, IGNORED_PREFIXES, EXPECTED_OUTPUT)
|
||||||
|
|
||||||
|
def testIgnoredPrefixesDuplicateFiles(self):
|
||||||
|
"""Tests that de-duplication of FILE records happens BEFORE prefixes
|
||||||
|
are removed."""
|
||||||
|
|
||||||
|
INPUT = \
|
||||||
|
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
|
||||||
|
INFO CODE_ID FFFFFFFF module1.exe
|
||||||
|
FILE 1 /src/build/foo/../file1_1.cc
|
||||||
|
FILE 2 /src/build/bar/../file1_2.cc
|
||||||
|
FILE 3 D:/src/build2/baz/../file1_2.cc
|
||||||
|
FUNC 1000 c 0 Function1_1
|
||||||
|
1000 8 45 2
|
||||||
|
1008 4 46 3
|
||||||
|
100c 4 44 1
|
||||||
|
"""
|
||||||
|
EXPECTED_OUTPUT = \
|
||||||
|
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
|
||||||
|
INFO CODE_ID FFFFFFFF module1.exe
|
||||||
|
FILE 1 file1_1.cc
|
||||||
|
FILE 2 file1_2.cc
|
||||||
|
FILE 3 file1_2.cc
|
||||||
|
FUNC 1000 c 0 Function1_1
|
||||||
|
1000 8 45 2
|
||||||
|
1008 4 46 3
|
||||||
|
100c 4 44 1
|
||||||
|
"""
|
||||||
|
IGNORED_PREFIXES = ['\\src\\build\\', 'D:\\src\\build2\\']
|
||||||
|
self.assertParsed(INPUT, IGNORED_PREFIXES, EXPECTED_OUTPUT)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in a new issue