From 6789a313a9cf3ee1e16f85cc32994d7c0d4a889d Mon Sep 17 00:00:00 2001 From: Christian Dietrich Date: Wed, 10 Apr 2013 17:37:23 +0200 Subject: [PATCH] DCiAOKernelImporter: different injection semantic. Is now very similar to normal importer, and may be deleted in the future, but at the moment, this should be merged, since it is the importer used in the sobres-2013 paper. This changes the MySQL Schema. instr1_absolute was introduced. Change-Id: I1bc2919bd14c335beca6d586b7cc0f80767ad7d5 --- src/core/cpn/DatabaseCampaign.cc | 4 +- src/core/util/DatabaseProtobufAdapter.cc | 1 + .../dciao-kernelstructs/CMakeLists.txt | 2 +- tools/import-trace/BasicImporter.cc | 39 ++++++++++------- tools/import-trace/BasicImporter.hpp | 8 +--- tools/import-trace/DCiAOKernelImporter.cc | 36 ++++++++++++---- tools/import-trace/DCiAOKernelImporter.hpp | 5 ++- tools/import-trace/Importer.cc | 42 +++++++++---------- tools/import-trace/Importer.hpp | 8 ++-- tools/prune-trace/BasicPruner.cc | 2 +- 10 files changed, 85 insertions(+), 62 deletions(-) diff --git a/src/core/cpn/DatabaseCampaign.cc b/src/core/cpn/DatabaseCampaign.cc index 1303722e..9570c243 100644 --- a/src/core/cpn/DatabaseCampaign.cc +++ b/src/core/cpn/DatabaseCampaign.cc @@ -82,7 +82,7 @@ bool DatabaseCampaign::run() { /* Gather all unfinished jobs */ int experiment_count; - std::string sql_select = "SELECT pilot_id, g.fspmethod_id, g.variant_id, g.injection_instr, g.injection_instr_absolute, g.data_address"; + std::string sql_select = "SELECT pilot_id, g.fspmethod_id, g.variant_id, p.injection_instr, p.injection_instr_absolute, g.data_address"; std::stringstream ss; ss << " FROM fspgroup g" << " INNER JOIN fsppilot p ON p.id = g.pilot_id " @@ -90,7 +90,7 @@ bool DatabaseCampaign::run() { << " AND g.fspmethod_id = " << fspmethod_id << " AND g.variant_id = " << variant_id << " AND (SELECT COUNT(*) FROM " + db_connect.result_table() + " as r WHERE r.pilot_id = g.pilot_id) = 0" - << " ORDER BY g.injection_instr"; + << " ORDER BY p.injection_instr"; std::string sql_body = ss.str(); /* Get the number of unfinished experiments */ diff --git a/src/core/util/DatabaseProtobufAdapter.cc b/src/core/util/DatabaseProtobufAdapter.cc index 21c8dcb5..8d35e10e 100644 --- a/src/core/util/DatabaseProtobufAdapter.cc +++ b/src/core/util/DatabaseProtobufAdapter.cc @@ -369,6 +369,7 @@ void DatabaseProtobufAdapter::create_table(const Descriptor *toplevel_desc) { create_table_stmt << "CREATE TABLE IF NOT EXISTS " << result_table_name << "("; create_table_stmt << top_level_msg.sql_create_stmt() << ", PRIMARY KEY(" << primary_join.join(", ") << "))"; + create_table_stmt << " ENGINE=MyISAM"; insert_stmt << "INSERT INTO " << result_table_name << "(" << insert_join.join(","); insert_stmt << ") VALUES (" << question_marks.join(",") << ")"; diff --git a/src/experiments/dciao-kernelstructs/CMakeLists.txt b/src/experiments/dciao-kernelstructs/CMakeLists.txt index d45e26e8..a1834697 100644 --- a/src/experiments/dciao-kernelstructs/CMakeLists.txt +++ b/src/experiments/dciao-kernelstructs/CMakeLists.txt @@ -30,6 +30,6 @@ target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY}) ## This is the example's campaign server distributing experiment parameters add_executable(${EXPERIMENT_NAME}-server main.cc) -target_link_libraries(${EXPERIMENT_NAME}-server fail-${EXPERIMENT_NAME} fail ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY} -lmysqlclient) +target_link_libraries(${EXPERIMENT_NAME}-server -Wl,--start-group fail-${EXPERIMENT_NAME} fail-sal fail-util fail-cpn fail-comm ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY} -lmysqlclient -Wl,--end-group) install(TARGETS ${EXPERIMENT_NAME}-server RUNTIME DESTINATION bin) diff --git a/tools/import-trace/BasicImporter.cc b/tools/import-trace/BasicImporter.cc index 04e0d0b0..22aac15c 100644 --- a/tools/import-trace/BasicImporter.cc +++ b/tools/import-trace/BasicImporter.cc @@ -7,6 +7,7 @@ bool BasicImporter::create_database() { std::string create_statement = "CREATE TABLE IF NOT EXISTS trace (" " variant_id int(11) NOT NULL," " instr1 int(10) unsigned NOT NULL," + " instr1_absolute int(10) unsigned DEFAULT NULL," " instr2 int(10) unsigned NOT NULL," " instr2_absolute int(10) unsigned DEFAULT NULL," " time1 bigint(10) unsigned NOT NULL," @@ -19,15 +20,13 @@ bool BasicImporter::create_database() { return db->query(create_statement.c_str()); } -bool BasicImporter::add_trace_event(instruction_count_t begin, instruction_count_t end, - fail::simtime_t time_begin, fail::simtime_t time_end, - const Trace_Event &event, bool is_fake) { - +bool BasicImporter::add_trace_event(margin_info_t &begin, margin_info_t &end, + const Trace_Event &event, bool is_fake) { static MYSQL_STMT *stmt = 0; if (!stmt) { - std::string sql("INSERT INTO trace (variant_id, instr1, instr2, instr2_absolute, time1, time2, data_address, width," + std::string sql("INSERT INTO trace (variant_id, instr1, instr1_absolute, instr2, instr2_absolute, time1, time2, data_address, width," " accesstype)" - "VALUES (?,?,?,?,?,?,?,?,?)"); + "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; @@ -35,13 +34,18 @@ bool BasicImporter::add_trace_event(instruction_count_t begin, instruction_count } } - MYSQL_BIND bind[9]; + MYSQL_BIND bind[10]; my_bool is_null = is_fake; + my_bool null = true; + unsigned long accesstype_len = 1; - unsigned ip = event.ip(); + unsigned data_address = event.memaddr(); unsigned width = event.width(); + char accesstype = event.accesstype() == event.READ ? 'R' : 'W'; + // LOG << m_variant_id << "-" << ":" << begin.dyninstr << ":" << end.dyninstr << "-" << data_address << " " << accesstype << std::endl; + memset(bind, 0, sizeof(bind)); for (unsigned i = 0; i < sizeof(bind)/sizeof(*bind); ++i) { @@ -49,19 +53,22 @@ bool BasicImporter::add_trace_event(instruction_count_t begin, instruction_count bind[i].is_unsigned = 1; switch (i) { case 0: bind[i].buffer = &m_variant_id; break; - case 1: bind[i].buffer = &begin; break; - case 2: bind[i].buffer = &end; break; - case 3: bind[i].buffer = &ip; + case 1: bind[i].buffer = &begin.dyninstr; break; + case 2: bind[i].buffer = &begin.ip; + bind[i].is_null = begin.ip == 0 ? &null : &is_null; + break; + case 3: bind[i].buffer = &end.dyninstr; break; + case 4: bind[i].buffer = &end.ip; bind[i].is_null = &is_null; break; - case 4: bind[i].buffer = &time_begin; + case 5: bind[i].buffer = &begin.time; bind[i].buffer_type = MYSQL_TYPE_LONGLONG; break; - case 5: bind[i].buffer = &time_end; + case 6: bind[i].buffer = &end.time; bind[i].buffer_type = MYSQL_TYPE_LONGLONG; break; - case 6: bind[i].buffer = &data_address; break; - case 7: bind[i].buffer = &width; break; - case 8: bind[i].buffer = &accesstype; + case 7: bind[i].buffer = &data_address; break; + case 8: bind[i].buffer = &width; break; + case 9: bind[i].buffer = &accesstype; bind[i].buffer_type = MYSQL_TYPE_STRING; bind[i].buffer_length = accesstype_len; bind[i].length = &accesstype_len; diff --git a/tools/import-trace/BasicImporter.hpp b/tools/import-trace/BasicImporter.hpp index 936f8e25..a37a4014 100644 --- a/tools/import-trace/BasicImporter.hpp +++ b/tools/import-trace/BasicImporter.hpp @@ -7,12 +7,8 @@ class BasicImporter : public Importer { public: virtual bool create_database(); - bool add_trace_event(instruction_count_t begin, - instruction_count_t end, - fail::simtime_t time_begin, - fail::simtime_t time_end, - const Trace_Event &event, - bool is_fake = false); + virtual bool add_trace_event(margin_info_t &begin, margin_info_t &end, + const Trace_Event &event, bool is_fake = false); }; #endif diff --git a/tools/import-trace/DCiAOKernelImporter.cc b/tools/import-trace/DCiAOKernelImporter.cc index 4451c76a..ed00acf3 100644 --- a/tools/import-trace/DCiAOKernelImporter.cc +++ b/tools/import-trace/DCiAOKernelImporter.cc @@ -11,9 +11,9 @@ using namespace fail; bool DCiAOKernelImporter::inDynamicKernelMemory(fail::address_t addr) { const std::string &name = m_elf->getSymbol(addr).getDemangledName(); bool dynamic = name.find("os::data::dynamic", 0) != std::string::npos; - // bool stack = name.find("_stack") != std::string::npos; - // return dynamic && !stack; - return dynamic; + bool stack = name.find("_stack") != std::string::npos; + return dynamic && !stack; + // return dynamic; } bool DCiAOKernelImporter::copy_to_database(fail::ProtoIStream &ps) { @@ -31,8 +31,12 @@ bool DCiAOKernelImporter::copy_to_database(fail::ProtoIStream &ps) { // instruction counter within trace unsigned instr = 0; + unsigned instr_last_kernel_inject = 0; unsigned instr_last_kernel_leave = 0; + unsigned instr_last_kernel_enter = 0; + + address_t inject_kernel_addr = getInjectKernelAddress(); address_t enter_kernel_addr = getEnterKernelAddress(); address_t leave_kernel_addr = getLeaveKernelAddress(); @@ -50,14 +54,18 @@ bool DCiAOKernelImporter::copy_to_database(fail::ProtoIStream &ps) { // new instruction instr++; + if (ev.ip() == inject_kernel_addr) { + instr_last_kernel_inject = instr; + already_written_addresses.clear(); + } if (ev.ip() == enter_kernel_addr) { in_kernel_space = true; + instr_last_kernel_enter = instr; } if (ev.ip() == leave_kernel_addr) { instr_last_kernel_leave = instr; in_kernel_space = false; - already_written_addresses.clear(); } continue; } @@ -77,13 +85,18 @@ bool DCiAOKernelImporter::copy_to_database(fail::ProtoIStream &ps) { address_t from = ev.memaddr(), to = ev.memaddr() + ev.width(); // Iterate over all accessed bytes for (address_t data_address = from; data_address < to; ++data_address) { - int instr1 = instr_last_kernel_leave; - int instr2 = instr; // the current instruction + margin_info_t left_margin, right_margin; + left_margin.dyninstr = instr_last_kernel_inject + 1; + left_margin.ip = inject_kernel_addr; + left_margin.time = 0; + right_margin.dyninstr = instr; // the current instruction + right_margin.ip = ev.ip(); + right_margin.time = 0; /* Was the byte already written in this kernel space */ if (already_written_addresses.find(data_address) - == already_written_addresses.end()) + != already_written_addresses.end()) continue; ev.set_memaddr(data_address); @@ -91,8 +104,9 @@ bool DCiAOKernelImporter::copy_to_database(fail::ProtoIStream &ps) { // we now have an interval-terminating R/W event to the memaddr // we're currently looking at; the EC is defined by - // data_address [last_kernel_leave, read_instr] (instr_absolute) - if (!add_trace_event(instr1, instr2, 0, 0, ev)) { // FIXME use timing data + // data_address [last_kernel_leave, read_instr] + // (instr_absolute) + if (!add_trace_event(left_margin, right_margin, ev)) { LOG << "add_trace_event failed" << std::endl; return false; } @@ -100,6 +114,10 @@ bool DCiAOKernelImporter::copy_to_database(fail::ProtoIStream &ps) { if (row_count % 1000 == 0) { LOG << "Imported " << row_count << " traces into the database" << std::endl; } + + /* This was the first read, we ignore all further + reads in this kernel transition */ + already_written_addresses.insert(data_address); } } } diff --git a/tools/import-trace/DCiAOKernelImporter.hpp b/tools/import-trace/DCiAOKernelImporter.hpp index bfd3db17..e96a4a28 100644 --- a/tools/import-trace/DCiAOKernelImporter.hpp +++ b/tools/import-trace/DCiAOKernelImporter.hpp @@ -20,8 +20,9 @@ */ class DCiAOKernelImporter : public BasicImporter { protected: - fail::address_t getEnterKernelAddress() { return m_elf->getSymbol("os::dep::KernelStructs::correct").getAddress(); } - fail::address_t getLeaveKernelAddress() { return m_elf->getSymbol("os::dep::KernelStructs::calculate").getAddress(); } + fail::address_t getInjectKernelAddress() { return m_elf->getSymbol("os::dep::KernelStructs::correct").getAddress(); } + fail::address_t getEnterKernelAddress() { return m_elf->getSymbol("os::dep::kernel_protected_open").getAddress(); } + fail::address_t getLeaveKernelAddress() { return m_elf->getSymbol("os::dep::kernel_protected_close").getAddress(); } bool inDynamicKernelMemory(fail::address_t addr); public: diff --git a/tools/import-trace/Importer.cc b/tools/import-trace/Importer.cc index 8c7aafb8..d872eb58 100644 --- a/tools/import-trace/Importer.cc +++ b/tools/import-trace/Importer.cc @@ -82,21 +82,21 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { if (m_mm && !m_mm->isMatching(data_address)) { continue; } - instruction_count_t instr1 = - open_ecs[data_address].dyninstr; // defaults to 0 if nonexistent - instruction_count_t instr2 = instr; // the current instruction - simtime_t time1 = open_ecs[data_address].time; + margin_info_t right_margin; + margin_info_t left_margin = open_ecs[data_address]; // defaulting to 0 is not such a good idea, memory reads at the // beginning of the trace would get an unnaturally high weight: - if (time1 == 0) { - time1 = time_trace_start; + if (left_margin.time == 0) { + left_margin.time = time_trace_start; } - simtime_t time2 = curtime; + right_margin.time = curtime; + right_margin.dyninstr = instr; // !< The current instruction + right_margin.ip = ev.ip(); // skip zero-sized intervals: these can occur when an instruction // accesses a memory location more than once (e.g., INC, CMPXCHG) // FIXME: look at timing instead? - if (instr1 > instr2) { + if (left_margin.dyninstr > right_margin.dyninstr) { continue; } @@ -107,7 +107,7 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { // we're currently looking at; the EC is defined by // data_address, dynamic instruction start/end, the absolute PC at // the end, and time start/end - if (!add_trace_event(instr1, instr2, time1, time2, ev)) { + if (!add_trace_event(left_margin, right_margin, ev)) { LOG << "add_trace_event failed" << std::endl; return false; } @@ -118,9 +118,10 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { // next interval must start at next instruction; the aforementioned // skipping mechanism wouldn't work otherwise - //lastuse_it->second = instr2 + 1; - open_ecs[data_address].dyninstr = instr2 + 1; - open_ecs[data_address].time = time2 + 1; + open_ecs[data_address].dyninstr = instr + 1; + open_ecs[data_address].time = curtime + 1; + // FIXME: This should be the next IP, not the current, or? + open_ecs[data_address].ip = ev.ip(); } } @@ -138,31 +139,30 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) { fake_ev.set_width(1); fake_ev.set_accesstype(m_faultspace_rightmargin == 'R' ? fake_ev.READ : fake_ev.WRITE); - instruction_count_t instr1 = lastuse_it->second.dyninstr; - simtime_t time1 = lastuse_it->second.time; + margin_info_t left_margin, right_margin; + left_margin = lastuse_it->second; // Why -1? In most cases it does not make sense to inject before the // very last instruction, as we won't execute it anymore. This *only* // makes sense if we also inject into parts of the result vector. This // is not the case in this experiment, and with -1 we'll get a result // comparable to the non-pruned campaign. - instruction_count_t instr2 = instr - 1; - - simtime_t time2 = curtime; // -1? - + right_margin.dyninstr = instr - 1; + right_margin.time = curtime; // -1? + right_margin.ip = ev.ip(); // The last event in the log. // #else // // EcosKernelTestCampaign only variant: fault space ends with the last FI experiment // FIXME probably implement this with cmdline parameter FAULTSPACE_CUTOFF -// int instr2 = instr_rightmost; +// right_margin.dyninstr = instr_rightmost; // #endif // zero-sized? skip. // FIXME: look at timing instead? - if (instr1 > instr2) { + if (left_margin.dyninstr > right_margin.dyninstr) { continue; } - if (!add_trace_event(instr1, instr2, time1, time2, fake_ev, true)) { + if (!add_trace_event(left_margin, right_margin, fake_ev, true)) { LOG << "add_trace_event failed" << std::endl; return false; } diff --git a/tools/import-trace/Importer.hpp b/tools/import-trace/Importer.hpp index 4ac49b5b..60145aca 100644 --- a/tools/import-trace/Importer.hpp +++ b/tools/import-trace/Importer.hpp @@ -21,6 +21,7 @@ protected: public: typedef unsigned instruction_count_t; //!< not big enough for some benchmarks + struct margin_info_t { instruction_count_t dyninstr; fail::guest_address_t ip; fail::simtime_t time; }; Importer() : m_sanitychecks(false) {} bool init(const std::string &variant, const std::string &benchmark, fail::Database *db); @@ -28,8 +29,7 @@ public: virtual bool create_database() = 0; virtual bool copy_to_database(fail::ProtoIStream &ps); virtual bool clear_database(); - virtual bool add_trace_event(instruction_count_t begin, instruction_count_t end, - fail::simtime_t time_begin, fail::simtime_t time_end, + virtual bool add_trace_event(margin_info_t &begin, margin_info_t &end, const Trace_Event &event, bool is_fake = false) = 0; void set_elf_file(fail::ElfReader *elf) { m_elf = elf; } @@ -38,8 +38,8 @@ public: void set_sanitychecks(bool enabled) { m_sanitychecks = enabled; } protected: private: - struct leftmargin_info_t { instruction_count_t dyninstr; fail::simtime_t time; }; - typedef std::map AddrLastaccessMap; + + typedef std::map AddrLastaccessMap; }; #endif diff --git a/tools/prune-trace/BasicPruner.cc b/tools/prune-trace/BasicPruner.cc index 9b5b5f86..4d2402e1 100644 --- a/tools/prune-trace/BasicPruner.cc +++ b/tools/prune-trace/BasicPruner.cc @@ -13,7 +13,7 @@ bool BasicPruner::prune_all() { // FIXME "basic-left mode" doesn't make any sense; injections at instr1 and // at instr2 are completely equivalent. std::string injection_instr = this->use_instr1 ? "instr1" : "instr2"; - std::string injection_instr_absolute = this->use_instr1 ? "NULL" : "instr2_absolute"; + std::string injection_instr_absolute = this->use_instr1 ? "instr1_absolute" : "instr2_absolute"; ss << "INSERT INTO fsppilot (known_outcome, variant_id, instr2, injection_instr, injection_instr_absolute, data_address, fspmethod_id) " "SELECT 0, variant_id, instr2, " << injection_instr << ", " << injection_instr_absolute << ", "