diff --git a/tools/import-trace/CMakeLists.txt b/tools/import-trace/CMakeLists.txt index 56afe3704aa795b08259ae8f2c7e6ce016007775..d0e5f6595a529a8b22a7bbbb21d30f1e0e5ccb1c 100644 --- a/tools/import-trace/CMakeLists.txt +++ b/tools/import-trace/CMakeLists.txt @@ -9,6 +9,7 @@ if (BUILD_LLVM_DISASSEMBLER) RegisterImporter.cc RandomJumpImporter.cc AdvancedMemoryImporter.cc + ElfImporter.cc ) include(FindLLVM) @@ -16,6 +17,10 @@ if (BUILD_LLVM_DISASSEMBLER) # llvm-config does add -fno-exception to the command line. But this # breaks some boost libraries. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_CXX_FLAGS} -fexceptions") + + find_package(Boost 1.42 COMPONENTS regex REQUIRED) + include_directories(${Boost_INCLUDE_DIRS}) + link_directories(${Boost_LIBRARY_DIRS}) endif(BUILD_LLVM_DISASSEMBLER) find_package(MySQL REQUIRED) @@ -31,7 +36,7 @@ target_link_libraries(import-trace fail-sal) if (BUILD_LLVM_DISASSEMBLER) - target_link_libraries(import-trace fail-llvmdisassembler fail-sal ${LLVM_LIBS} ${LLVM_LDFLAGS}) + target_link_libraries(import-trace fail-llvmdisassembler fail-sal ${LLVM_LIBS} ${LLVM_LDFLAGS} ${Boost_LIBRARIES}) endif (BUILD_LLVM_DISASSEMBLER) install(TARGETS import-trace RUNTIME DESTINATION bin) diff --git a/tools/import-trace/ElfImporter.cc b/tools/import-trace/ElfImporter.cc new file mode 100644 index 0000000000000000000000000000000000000000..aaa2828680b8031b9f277d62e27da81a6bcf3c73 --- /dev/null +++ b/tools/import-trace/ElfImporter.cc @@ -0,0 +1,209 @@ +#include <sstream> +#include <iostream> +#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"); + return true; +} + +bool ElfImporter::create_database() { + std::stringstream create_statement; + 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 "; + return db->query(create_statement.str().c_str()); +} + +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 + return import_with_objdump(std::string(cmd[OBJDUMP].first()->arg)); + } else { + LOG << "importing an objdump with internal llvm dissassembler is not yet implemented" << std::endl; + 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::clear_database() { + std::stringstream ss; + ss << "DELETE FROM objdump WHERE variant_id = " << m_variant_id; + + bool ret = db->query(ss.str().c_str()) == 0 ? false : true; + LOG << "deleted " << db->affected_rows() << " rows from objdump table" << std::endl; + return ret; +} + diff --git a/tools/import-trace/ElfImporter.hpp b/tools/import-trace/ElfImporter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..20f4a656c754a4a14d6bdcc6f0b677dbdc23fd09 --- /dev/null +++ b/tools/import-trace/ElfImporter.hpp @@ -0,0 +1,53 @@ +#ifndef __OBJDUMP_IMPORTER_H__ +#define __OBJDUMP_IMPORTER_H__ + +#include "Importer.hpp" +#include "util/llvmdisassembler/LLVMDisassembler.hpp" +#include "util/CommandLine.hpp" + +/** + The ElfImporter is not a real trace importer, but we locate it + into the import-trace utility, since here the infrastructure is + already in place to import things related to an elf binary into + the database. + + The ElfImporter calls objdump and dissassembles an elf binary + and imports the results into the database +*/ +class ElfImporter : public Importer { + llvm::OwningPtr<llvm::object::Binary> binary; + llvm::OwningPtr<fail::LLVMDisassembler> disas; + + fail::CommandLine::option_handle OBJDUMP; + + bool import_with_objdump(const std::string &objdump_binary); + bool evaluate_objdump_line(const std::string &line); + + /* Imports a single instruction into the objdump table */ + bool import_instruction(fail::address_t addr, char opcode[16], int opcode_length, + const std::string &instruction, const std::string &comment); + +protected: + virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev) { return true; } + virtual bool handle_mem_event(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev) { return true; } + + virtual void open_unused_ec_intervals() { + /* empty, Memory Map has a no meaning in this importer */ + } + +public: + ElfImporter() : Importer() {}; + + /** + * Callback function that can be used to add command line options + * to the cmd interface + */ + virtual bool cb_commandline_init(); + virtual bool create_database(); + virtual bool copy_to_database(fail::ProtoIStream &ps); + virtual bool clear_database(); +}; + +#endif diff --git a/tools/import-trace/main.cc b/tools/import-trace/main.cc index 7290636eadf5b3c0f3e477ffd2cdb4c842d4fd58..2f727d394785b0d6791af233266025fbe2491e42 100644 --- a/tools/import-trace/main.cc +++ b/tools/import-trace/main.cc @@ -13,6 +13,7 @@ #include "RegisterImporter.hpp" #include "RandomJumpImporter.hpp" #include "AdvancedMemoryImporter.hpp" +#include "ElfImporter.hpp" #endif @@ -140,6 +141,8 @@ int main(int argc, char *argv[]) { importer = new RandomJumpImporter(); } else if (imp == "AdvancedMemoryImporter") { importer = new AdvancedMemoryImporter(); + } else if (imp == "ObjdumpImporter" || imp == "objdump" || imp == "ElfImporter") { + importer = new ElfImporter(); #endif } else { LOG << "Unkown import method: " << imp << endl;