import-trace: ElfImporter cleanups

-  added several insert_multiple() flushes (which completely broke
    the import before)
 -  import_source_code() refactored
 -  parameter naming improved
 -  better error handling
 -  whitespace/coding-style cleanups
 -  documentation added + cleanups

Change-Id: I70ac95391b9678e0dcce8adfa7df69a4f91ca30d
This commit is contained in:
Horst Schirmeier
2014-10-08 13:06:12 +02:00
parent 31166f990e
commit 8fcbc7eeae
2 changed files with 125 additions and 113 deletions

View File

@ -21,17 +21,19 @@ static Logger LOG("ElfImporter");
* Callback function that can be used to add command line options * Callback function that can be used to add command line options
* to the campaign * to the campaign
*/ */
bool ElfImporter::cb_commandline_init() { bool ElfImporter::cb_commandline_init()
{
CommandLine &cmd = CommandLine::Inst(); CommandLine &cmd = CommandLine::Inst();
OBJDUMP = cmd.addOption("", "objdump", Arg::Required, OBJDUMP = cmd.addOption("", "objdump", Arg::Required,
"--objdump \tObjdump: location of objdump binary, otherwise LLVM Disassembler is used"); "--objdump \tObjdump: location of objdump binary, otherwise LLVM Disassembler is used");
SOURCECODE = cmd.addOption("", "sources", Arg::None, SOURCECODE = cmd.addOption("", "sources", Arg::None,
"--sources \timport all source files and the mapping of code line<->static instruction into the database"); "--sources \timport all source files and the mapping of code line<->static instruction into the database");
return true; return true;
} }
bool ElfImporter::create_database() { bool ElfImporter::create_database()
{
CommandLine &cmd = CommandLine::Inst(); CommandLine &cmd = CommandLine::Inst();
std::stringstream create_statement; std::stringstream create_statement;
@ -88,7 +90,8 @@ bool ElfImporter::create_database() {
return true; return true;
} }
bool ElfImporter::copy_to_database(ProtoIStream &ps) { bool ElfImporter::copy_to_database(ProtoIStream &ps)
{
if (!m_elf) { if (!m_elf) {
LOG << "please give an elf binary as parameter (-e/--elf)" << std::endl; LOG << "please give an elf binary as parameter (-e/--elf)" << std::endl;
return false; return false;
@ -101,7 +104,7 @@ bool ElfImporter::copy_to_database(ProtoIStream &ps) {
} }
if (cmd[OBJDUMP]) { // using objdump if (cmd[OBJDUMP]) { // using objdump
if(!import_with_objdump(std::string(cmd[OBJDUMP].first()->arg))) { if (!import_with_objdump(std::string(cmd[OBJDUMP].first()->arg))) {
return false; return false;
} }
} else { } else {
@ -111,19 +114,19 @@ bool ElfImporter::copy_to_database(ProtoIStream &ps) {
if (cmd[SOURCECODE]) { // import sources if (cmd[SOURCECODE]) { // import sources
std::list<std::string> sourceFiles; std::list<std::string> sourceFiles;
if (!import_source_files(m_elf->getFilename(),sourceFiles)) { if (!import_source_files(m_elf->getFilename(), sourceFiles)) {
cerr << "unable to read dwarf2 debug info" << endl; cerr << "unable to read dwarf2 debug info" << endl;
return false; return false;
} }
for (std::list<std::string>::iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) { for (std::list<std::string>::iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) {
if(!import_source_code(*it)) { if (!import_source_code(*it)) {
return false; return false;
} }
} }
// import debug information // import debug information
if(!import_mapping(m_elf->getFilename())) { if (!import_mapping(m_elf->getFilename())) {
return false; return false;
} }
} }
@ -131,7 +134,8 @@ bool ElfImporter::copy_to_database(ProtoIStream &ps) {
return true; return true;
} }
bool ElfImporter::import_with_objdump(const std::string &binary) { bool ElfImporter::import_with_objdump(const std::string &binary)
{
#ifndef __puma #ifndef __puma
LOG << "importing with " << binary << std::endl; LOG << "importing with " << binary << std::endl;
@ -162,7 +166,8 @@ bool ElfImporter::import_with_objdump(const std::string &binary) {
return true; return true;
} }
bool ElfImporter::evaluate_objdump_line(const std::string& line){ bool ElfImporter::evaluate_objdump_line(const std::string& line)
{
#ifndef __puma #ifndef __puma
// Only read in real code lines: // Only read in real code lines:
// Code lines start with a leading whitespace! (hopefully in each objdump implementation!) // Code lines start with a leading whitespace! (hopefully in each objdump implementation!)
@ -198,7 +203,7 @@ bool ElfImporter::evaluate_objdump_line(const std::string& line){
/* import instruction into database */ /* import instruction into database */
if (!import_instruction(addr, opcode_read, opcode_length, if (!import_instruction(addr, opcode_read, opcode_length,
instruction, comment)) instruction, comment))
return false; return false;
} }
} }
@ -209,7 +214,8 @@ bool ElfImporter::evaluate_objdump_line(const std::string& line){
bool ElfImporter::import_instruction(fail::address_t addr, char *opcode, int opcode_length, bool ElfImporter::import_instruction(fail::address_t addr, char *opcode, int opcode_length,
const std::string &instruction, const std::string &comment) { const std::string &instruction, const std::string &comment)
{
/* Prepare a mysql statement if it was not done before */ /* Prepare a mysql statement if it was not done before */
static MYSQL_STMT *stmt = 0; static MYSQL_STMT *stmt = 0;
if (!stmt) { if (!stmt) {
@ -263,163 +269,172 @@ bool ElfImporter::import_instruction(fail::address_t addr, char *opcode, int opc
return true; return true;
} }
bool ElfImporter::import_source_code(std::string fileName) { static inline std::string rtrim(std::string str)
{
std::string whitespaces(" \t\f\v\n\r");
std::size_t found = str.find_last_not_of(whitespaces);
if (found != std::string::npos) {
str.erase(found+1);
} else {
str.clear();
}
return str;
}
bool ElfImporter::import_source_code(std::string fileName)
{
LOG << "Importing Sourcefile: " << fileName << endl; LOG << "Importing Sourcefile: " << fileName << endl;
ifstream in(fileName.c_str()); ifstream in(fileName.c_str());
if (!in.is_open()) { if (!in.is_open()) {
LOG << "Sourcefile not found: " << fileName << endl; LOG << "Sourcefile not found: " << fileName << endl;
} else { return true; // we can live with this
unsigned lineNo=0;
while (!in.eof()) {
++lineNo;
// Read and strip the current line
string currentLine;
getline(in,currentLine);
if ((!currentLine.length())&&(in.eof())) break;
while (true) {
unsigned l=currentLine.length();
if (!l) break;
char c=currentLine[l-1];
if ((c!='\n')&&(c!='\r')&&(c!=' ')&&(c!='\t')) break;
currentLine=currentLine.substr(0,l-1);
}
//Sonderzeichen
currentLine = db->escape_string(currentLine);
std::stringstream ss;
ss << "SELECT file_id FROM dbg_filename "
<< "WHERE path = '" << fileName.c_str() << "' "
<< "AND variant_id = " << m_variant_id;
MYSQL_RES *res = db->query(ss.str().c_str(), true);
MYSQL_ROW row;
row = mysql_fetch_row(res);
// INSERT group entry
std::stringstream sql;
sql << "(" << m_variant_id << "," << lineNo << ",";
if (row != NULL) {
sql << row[0];
} else {
sql << -1;
}
sql << "," << "\"" << currentLine << "\"" << ")";
if (!db->insert_multiple("INSERT INTO dbg_source (variant_id, linenumber, file_id, line) VALUES ", sql.str().c_str())){
LOG << "Can not import sourcelines!" << endl;;
}
}
db->insert_multiple();
} }
return true;
}
bool ElfImporter::import_source_files(const std::string& fileName,std::list<std::string>& lines) { // retrieve file_id
std::stringstream ss;
//std::list<addrToLine> lines; ss << "SELECT file_id FROM dbg_filename "
if (!dwReader.read_source_files(fileName, lines)) { << "WHERE path = '" << fileName.c_str() << "' "
<< "AND variant_id = " << m_variant_id;
MYSQL_RES *res = db->query(ss.str().c_str(), true);
if (!res) {
return false; return false;
} }
for (std::list<std::string>::iterator it = lines.begin(); it != lines.end(); ++it) { MYSQL_ROW row;
// INSERT group entry if (!(row = mysql_fetch_row(res))) {
std::stringstream sql; // this should not happen
sql << "(" << m_variant_id << "," << "\"" << (*it).c_str() << "\"" << ")"; LOG << "error: no entry for '" << fileName.c_str() << "' in dbg_filename, aborting" << std::endl;
return false;
}
std::string file_id = row[0];
if(!db->insert_multiple("INSERT INTO dbg_filename (variant_id, path) VALUES ", sql.str().c_str())){ // import lines from this file
LOG << "Can not import filename!"<< endl; for (unsigned lineNo = 1; !in.eof(); ++lineNo) {
// read and strip a line
string currentLine;
getline(in, currentLine);
if (!currentLine.length() && in.eof()) {
break;
}
currentLine = rtrim(currentLine);
// escape special characters for use in SQL
currentLine = db->escape_string(currentLine);
// insert line into dbg_source
std::stringstream sql;
sql << "(" << m_variant_id << "," << lineNo
<< "," << file_id << ",'" << currentLine << "')";
if (!db->insert_multiple("INSERT INTO dbg_source (variant_id, linenumber, file_id, line) VALUES ",
sql.str().c_str())) {
return false;
} }
} }
db->insert_multiple();
return true; return true;
} }
bool ElfImporter::import_mapping(std::string fileName) { bool ElfImporter::import_source_files(const std::string& elf_filename, std::list<std::string>& filenames)
{
if (!dwReader.read_source_files(elf_filename, filenames)) {
return false;
}
for (std::list<std::string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
// INSERT group entry
std::stringstream sql;
sql << "(" << m_variant_id << ",'" << it->c_str() << "')";
if (!db->insert_multiple("INSERT INTO dbg_filename (variant_id, path) VALUES ",
sql.str().c_str())) {
return false;
}
}
db->insert_multiple();
return true;
}
bool ElfImporter::import_mapping(std::string fileName)
{
std::list<addrToLine> mapping; std::list<addrToLine> mapping;
if (!dwReader.read_mapping(fileName, mapping)) { if (!dwReader.read_mapping(fileName, mapping)) {
return false; return false;
} }
while (!mapping.empty()) while (!mapping.empty()) {
{
struct addrToLine temp_addrToLine; struct addrToLine temp_addrToLine;
temp_addrToLine = mapping.front(); temp_addrToLine = mapping.front();
// FIXME reuse file_id from previous iteration if file name stays constant
std::stringstream ss; std::stringstream ss;
ss << "SELECT file_id FROM dbg_filename " ss << "SELECT file_id FROM dbg_filename "
<< "WHERE path = '" << temp_addrToLine.lineSource << "' " << "WHERE path = '" << temp_addrToLine.lineSource << "' "
<< "AND variant_id = " << m_variant_id; << "AND variant_id = " << m_variant_id;
MYSQL_RES *res = db->query(ss.str().c_str(), true); MYSQL_RES *res = db->query(ss.str().c_str(), true);
if (!res) {
return false;
}
MYSQL_ROW row; MYSQL_ROW row;
row = mysql_fetch_row(res); row = mysql_fetch_row(res);
// INSERT group entry // INSERT group entry
std::stringstream sql; std::stringstream sql;
sql << "(" << m_variant_id << "," << temp_addrToLine.absoluteAddr << "," << temp_addrToLine.lineNumber << ","; sql << "(" << m_variant_id << "," << temp_addrToLine.absoluteAddr << "," << temp_addrToLine.lineNumber << ",";
if(row != NULL) { if (row != NULL) {
sql << row[0] << ")"; sql << row[0] << ")";
} else { } else {
sql << -1 << ")"; // this should not happen
LOG << "error: no entry for '" << temp_addrToLine.lineSource << "' in dbg_filename, aborting" << std::endl;
return false;
} }
//ToDo: Skip duplicated entrys with ON DUPLICATE KEY UPDATE // TODO: Skip duplicated entries with ON DUPLICATE KEY UPDATE (?)
if(!db->insert_multiple("INSERT IGNORE INTO dbg_mapping (variant_id, instr_absolute, linenumber, file_id) VALUES ", sql.str().c_str())){ if (!db->insert_multiple("INSERT IGNORE INTO dbg_mapping (variant_id, instr_absolute, linenumber, file_id) VALUES ",
LOG << "Can not import line number information!" << endl;; sql.str().c_str())) {
return false;
} }
mapping.pop_front(); mapping.pop_front();
} }
db->insert_multiple();
return true; return true;
} }
bool ElfImporter::clear_database() { bool ElfImporter::clear_database()
{
CommandLine &cmd = CommandLine::Inst(); CommandLine &cmd = CommandLine::Inst();
std::stringstream ss; std::stringstream ss;
bool ret = true; bool ret = true, result;
ss.str(""); ss.str("");
ss << "DELETE FROM objdump WHERE variant_id = " << m_variant_id; ss << "DELETE FROM objdump WHERE variant_id = " << m_variant_id;
result = db->query(ss.str().c_str());
if (ret) { ret = ret && result;
ret = db->query(ss.str().c_str()) == 0 ? false : true;
}
LOG << "deleted " << db->affected_rows() << " rows from objdump table" << std::endl; LOG << "deleted " << db->affected_rows() << " rows from objdump table" << std::endl;
//ToDo: Reset auto increment value to 1
if (cmd[SOURCECODE]) { if (cmd[SOURCECODE]) {
ss.str(""); ss.str("");
ss << "DELETE FROM dbg_source WHERE variant_id = " << m_variant_id; ss << "DELETE FROM dbg_source WHERE variant_id = " << m_variant_id;
result = db->query(ss.str().c_str());
if (ret) { ret = ret && result;
ret = db->query(ss.str().c_str()) == 0 ? false : true;
}
LOG << "deleted " << db->affected_rows() << " rows from dbg_source table" << std::endl; LOG << "deleted " << db->affected_rows() << " rows from dbg_source table" << std::endl;
ss.str(""); ss.str("");
ss << "DELETE FROM dbg_filename WHERE variant_id = " << m_variant_id; ss << "DELETE FROM dbg_filename WHERE variant_id = " << m_variant_id;
result = db->query(ss.str().c_str());
if (ret) { ret = ret && result;
ret = db->query(ss.str().c_str()) == 0 ? false : true; LOG << "deleted " << db->affected_rows() << " rows from dbg_filename table" << std::endl;
}
LOG << "deleted " << db->affected_rows() << " rows from dbg_source table" << std::endl;
//ToDo: Reset auto increment value to 1 //ToDo: Reset auto increment value to 1
ss.str(""); ss.str("");
ss << "DELETE FROM dbg_mapping WHERE variant_id = " << m_variant_id; ss << "DELETE FROM dbg_mapping WHERE variant_id = " << m_variant_id;
result = db->query(ss.str().c_str());
if (ret) { ret = ret && result;
ret = db->query(ss.str().c_str()) == 0 ? false : true;
}
LOG << "deleted " << db->affected_rows() << " rows from source table" << std::endl; LOG << "deleted " << db->affected_rows() << " rows from source table" << std::endl;
} }
return ret; return ret;
} }

View File

@ -14,21 +14,18 @@
/** /**
The ElfImporter is not a real trace importer, but we locate it The ElfImporter is not a real trace importer, but we locate it
into the import-trace utility, since here the infrastructure is into the import-trace utility, since here the infrastructure is
already in place to import things related to an elf binary into already in place to import things related to an ELF binary into
the database. the database.
The ElfImporter calls objdump and dissassembles an elf binary The ElfImporter calls objdump and dissassembles an ELF binary
and imports the results into the database and imports the results into the database
In addition, debugging information can be imported: In addition, debugging information can be imported:
If the --sources option is set, the source files will be imported If the --sources option is set, the source files will be imported
into the database. Only the files that were actually used in the into the database. Only the files that were actually used in the
elf binary will be imported. ELF binary will be imported. Additionally, the line number table of the
ELF binary will be imported into the database.
If the --debug option is set, the line number table of the elf binary will
be imported into the database. The information will be stored in the
"mapping" table.
*/ */
class ElfImporter : public Importer { class ElfImporter : public Importer {
llvm::OwningPtr<llvm::object::Binary> binary; llvm::OwningPtr<llvm::object::Binary> binary;
@ -44,24 +41,24 @@ class ElfImporter : public Importer {
/* Imports a single instruction into the objdump table */ /* Imports a single instruction into the objdump table */
bool import_instruction(fail::address_t addr, char opcode[16], int opcode_length, bool import_instruction(fail::address_t addr, char opcode[16], int opcode_length,
const std::string &instruction, const std::string &comment); const std::string &instruction, const std::string &comment);
bool import_source_files(const std::string& fileName,std::list<std::string>& lines); bool import_source_files(const std::string& elf_filename, std::list<std::string>& filenames);
bool import_source_code(std::string fileName); bool import_source_code(std::string fileName);
bool import_mapping(std::string fileName); bool import_mapping(std::string fileName);
protected: protected:
virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr,
Trace_Event &ev) { return true; } Trace_Event &ev) { return true; }
virtual bool handle_mem_event(fail::simtime_t curtime, instruction_count_t instr, virtual bool handle_mem_event(fail::simtime_t curtime, instruction_count_t instr,
Trace_Event &ev) { return true; } Trace_Event &ev) { return true; }
virtual void open_unused_ec_intervals() { virtual void open_unused_ec_intervals() {
/* empty, Memory Map has a no meaning in this importer */ /* empty, Memory Map has a no meaning in this importer */
} }
public: public:
ElfImporter() : Importer() {}; ElfImporter() : Importer() {}
/** /**
* Callback function that can be used to add command line options * Callback function that can be used to add command line options