Files
fail/tools/import-trace/ElfImporter.cc
Richard Hellwig 13175c259b import-trace: import debug info
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
"dbg_mapping" table.

If the --sources option is set, the source files will be imported
into the database. Only the files that were actually used in the
elf binary will be imported.

Change-Id: I0e9de6b456bc42b329c1700c25e5839d9552cdbb
2014-01-28 11:07:34 +01:00

429 lines
11 KiB
C++

#include <sstream>
#include <iostream>
#include <fstream>
#include "util/Logger.hpp"
#include "ElfImporter.hpp"
#include "util/pstream.h"
#ifndef __puma
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
#endif
using namespace llvm;
using namespace llvm::object;
using namespace fail;
using namespace std;
static Logger LOG("ElfImporter");
/**
* Callback function that can be used to add command line options
* to the campaign
*/
bool ElfImporter::cb_commandline_init() {
CommandLine &cmd = CommandLine::Inst();
OBJDUMP = cmd.addOption("", "objdump", Arg::Required,
"--objdump \tObjdump: location of objdump binary, otherwise LLVM Disassembler is used");
SOURCECODE = cmd.addOption("", "sources", Arg::None,
"--sources \timport all source files into the database");
DEBUGINFO = cmd.addOption("", "debug", Arg::None,
"--debug \timport some debug informations into the database");
return true;
}
bool ElfImporter::create_database() {
CommandLine &cmd = CommandLine::Inst();
std::stringstream create_statement;
create_statement.str("");
create_statement << "CREATE TABLE IF NOT EXISTS objdump ("
" variant_id int(11) NOT NULL,"
" instr_address int(11) NOT NULL,"
" opcode varchar(32) NOT NULL,"
" disassemble VARCHAR(64),"
" comment VARCHAR(128),"
" PRIMARY KEY (variant_id,instr_address)"
") engine=MyISAM ";
if (!db->query(create_statement.str().c_str())) {
return false;
}
if (cmd[SOURCECODE]) {
create_statement.str("");
create_statement << "CREATE TABLE IF NOT EXISTS dbg_filename ("
" file_id int(11) NOT NULL AUTO_INCREMENT,"
" variant_id int(11) NOT NULL,"
" path VARCHAR(1024),"
" PRIMARY KEY (file_id)"
") engine=MyISAM ";
if (!db->query(create_statement.str().c_str())) {
return false;
}
create_statement.str("");
create_statement << "CREATE TABLE IF NOT EXISTS dbg_source ("
" variant_id int(11) NOT NULL,"
" linenumber int(11) NOT NULL,"
" file_id int(11) NOT NULL,"
" line VARCHAR(1024),"
" PRIMARY KEY (variant_id, linenumber, file_id)"
") engine=MyISAM ";
if (!db->query(create_statement.str().c_str())) {
return false;
}
}
if (cmd[DEBUGINFO]) {
create_statement.str("");
create_statement << "CREATE TABLE IF NOT EXISTS dbg_mapping ("
" variant_id int(11) NOT NULL,"
" instr_absolute int(11) NOT NULL,"
" linenumber int(11) NOT NULL,"
" file_id int(11) NOT NULL,"
" PRIMARY KEY (variant_id, instr_absolute ,linenumber)"
") engine=MyISAM ";
if (!db->query(create_statement.str().c_str())) {
return false;
}
}
return true;
}
bool ElfImporter::copy_to_database(ProtoIStream &ps) {
if (!m_elf) {
LOG << "please give an elf binary as parameter (-e/--elf)" << std::endl;
return false;
}
CommandLine &cmd = CommandLine::Inst();
if (!cmd.parse()) {
std::cerr << "Error parsing arguments." << std::endl;
return false;
}
if (cmd[OBJDUMP]) { // using objdump
if(!import_with_objdump(std::string(cmd[OBJDUMP].first()->arg))) {
return false;
}
} else {
LOG << "importing an objdump with internal llvm dissassembler is not yet implemented" << std::endl;
}
if (cmd[SOURCECODE]) { // import sources
std::list<std::string> sourceFiles;
if (!import_source_files(m_elf->getFilename(),sourceFiles)) {
cerr << "unable to read dwarf2 debug info" << endl;
return false;
}
for (std::list<std::string>::iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) {
if(!import_source_code(*it)) {
return false;
}
}
}
if (cmd[DEBUGINFO]) { // import debug informations
if(!import_mapping(m_elf->getFilename())) {
return false;
}
}
return true;
}
bool ElfImporter::import_with_objdump(const std::string &binary) {
#ifndef __puma
LOG << "importing with " << binary << std::endl;
// -d: disassemble
// -C: demangle C++
std::string command = binary + " -C -d " + m_elf->getFilename();
LOG << "Executing: " << command << std::endl;
redi::ipstream objdump( command );
std::string str;
while (std::getline(objdump, str)) {
if (!evaluate_objdump_line(str)) {
objdump.close();
return false;
}
}
objdump.close();
if (objdump.rdbuf()->exited()) {
int ex = objdump.rdbuf()->status();
if (ex != 0) {
clear_database();
LOG << "Could not disassemble!" << std::endl;
return false;
}
}
#endif
return true;
}
bool ElfImporter::evaluate_objdump_line(const std::string& line){
#ifndef __puma
// Only read in real code lines:
// Code lines start with a leading whitespace! (hopefully in each objdump implementation!)
if (line.size() > 0 && isspace(line[0])) {
// a line looks like: 800156c:\tdd14 \tble.n 8001598 <_ZN2hw3hal7T32Term8PutBlockEPci+0x30>
static boost::regex expr("\\s+([A-Fa-f0-9]+):((?:\\s+[A-Fa-f0-9]{2})+)\\s+(.+?)(;.*)?$");
boost::smatch res;
if (boost::regex_search(line, res, expr)) {
std::string address = res[1];
std::stringstream ss;
ss << std::hex << address;
address_t addr = 0;
ss >> addr;
std::string opcode = res[2];
boost::trim(opcode);
std::string instruction = res[3];
boost::trim(instruction);
std::string comment = res[4];
boost::trim(comment);
// transform hex opcode to char array
char opcode_read[33];
std::stringstream opcode_stream(opcode);
unsigned long opcode_length = 0;
char c;
while (opcode_length < 16 && opcode_stream) {
opcode_stream << std::hex;
opcode_stream >> c;
if (!opcode_stream) break;
opcode_read[opcode_length++] = c;
}
opcode_read[opcode_length] = 0;
/* import instruction into database */
if (!import_instruction(addr, opcode_read, opcode_length,
instruction, comment))
return false;
}
}
#endif
return true;
}
bool ElfImporter::import_instruction(fail::address_t addr, char *opcode, int opcode_length,
const std::string &instruction, const std::string &comment) {
/* Prepare a mysql statement if it was not done before */
static MYSQL_STMT *stmt = 0;
if (!stmt) {
std::string sql("INSERT INTO objdump (variant_id, instr_address, opcode, disassemble, comment) VALUES (?,?,?,?,?)");
stmt = mysql_stmt_init(db->getHandle());
if (mysql_stmt_prepare(stmt, sql.c_str(), sql.length())) {
LOG << "query '" << sql << "' failed: " << mysql_error(db->getHandle()) << std::endl;
return false;
}
}
MYSQL_BIND bind[5];
memset(bind, 0, sizeof(bind));
for (unsigned i = 0; i < 5; ++i) {
bind[i].buffer_type = MYSQL_TYPE_STRING;
bind[i].is_unsigned = 1;
switch (i) {
case 0:
bind[i].buffer = &m_variant_id;
bind[i].buffer_type = MYSQL_TYPE_LONG;
break;
case 1:
bind[i].buffer = &addr;
bind[i].buffer_type = MYSQL_TYPE_LONG;
break;
case 2:
bind[i].buffer_type = MYSQL_TYPE_BLOB;
bind[i].buffer = opcode;
bind[i].buffer_length = opcode_length;
break;
case 3:
bind[i].buffer = const_cast<char *>(instruction.c_str());
bind[i].buffer_length = instruction.length();
break;
case 4:
bind[i].buffer = const_cast<char *>(comment.c_str());
bind[i].buffer_length = comment.length();
break;
}
}
if (mysql_stmt_bind_param(stmt, bind)) {
LOG << "mysql_stmt_bind_param() failed: " << mysql_stmt_error(stmt) << std::endl;
return false;
}
if (mysql_stmt_execute(stmt)) {
LOG << "mysql_stmt_execute() failed: " << mysql_stmt_error(stmt) << std::endl;
return false;
}
return true;
}
bool ElfImporter::import_source_code(std::string fileName) {
LOG << "Importing Sourcefile: " << fileName << endl;
ifstream in(fileName.c_str());
if (!in.is_open()) {
LOG << "Sourcefile not found: " << fileName << endl;
} else {
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() << "\"";
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) {
//std::list<addrToLine> lines;
if (!dwReader.read_source_files(fileName, lines)) {
return false;
}
for (std::list<std::string>::iterator it = lines.begin(); it != lines.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())){
LOG << "Can not import filename!"<< endl;
}
}
return true;
}
bool ElfImporter::import_mapping(std::string fileName) {
std::list<addrToLine> mapping;
if (!dwReader.read_mapping(fileName, mapping)) {
return false;
}
while (!mapping.empty())
{
struct addrToLine temp_addrToLine;
temp_addrToLine = mapping.front();
std::stringstream ss;
ss << "SELECT file_id FROM dbg_filename WHERE path = " << "\"" << temp_addrToLine.lineSource << "\"";
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 << "," << temp_addrToLine.absoluteAddr << "," << temp_addrToLine.lineNumber << ",";
if(row != NULL) {
sql << row[0] << ")";
} else {
sql << -1 << ")";
}
//ToDo: Skip duplicated entrys 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())){
LOG << "Can not import line number information!" << endl;;
}
mapping.pop_front();
}
return true;
}
bool ElfImporter::clear_database() {
CommandLine &cmd = CommandLine::Inst();
std::stringstream ss;
bool ret = true;
ss.str("");
ss << "DELETE FROM objdump WHERE variant_id = " << m_variant_id;
if (ret) {
ret = db->query(ss.str().c_str()) == 0 ? false : true;
}
LOG << "deleted " << db->affected_rows() << " rows from objdump table" << std::endl;
//ToDo: Reset auto increment value to 1
if (cmd[SOURCECODE]) {
ss.str("");
ss << "DELETE FROM dbg_source WHERE variant_id = " << m_variant_id;
if (ret) {
ret = db->query(ss.str().c_str()) == 0 ? false : true;
}
LOG << "deleted " << db->affected_rows() << " rows from dbg_source table" << std::endl;
ss.str("");
ss << "DELETE FROM dbg_filename WHERE variant_id = " << m_variant_id;
if (ret) {
ret = db->query(ss.str().c_str()) == 0 ? false : true;
}
LOG << "deleted " << db->affected_rows() << " rows from dbg_source table" << std::endl;
}
//ToDo: Reset auto increment value to 1
if (cmd[DEBUGINFO]) {
ss.str("");
ss << "DELETE FROM dbg_mapping WHERE variant_id = " << m_variant_id;
if (ret) {
ret = db->query(ss.str().c_str()) == 0 ? false : true;
}
LOG << "deleted " << db->affected_rows() << " rows from source table" << std::endl;
}
return ret;
}