diff --git a/src/core/util/ElfReader.hpp b/src/core/util/ElfReader.hpp index 1722e0e0..2d12ec10 100644 --- a/src/core/util/ElfReader.hpp +++ b/src/core/util/ElfReader.hpp @@ -80,6 +80,8 @@ namespace fail { typedef ElfSymbol entry_t; typedef std::vector container_t; typedef container_t::const_iterator symbol_iterator; + typedef container_t::const_iterator section_iterator; + /** * Constructor. @@ -144,6 +146,14 @@ namespace fail { container_t::const_iterator sym_begin() { return m_symboltable.begin(); } container_t::const_iterator sym_end() { return m_symboltable.end(); } + /** + * Get section iterator. Derefences to a ElfSymbol + * @return iterator + */ + container_t::const_iterator sec_begin() { return m_sectiontable.begin(); } + container_t::const_iterator sec_end() { return m_sectiontable.end(); } + + private: Logger m_log; diff --git a/src/experiments/kesorefs/CMakeLists.txt b/src/experiments/kesorefs/CMakeLists.txt index 6aaaae5e..d3dfb253 100644 --- a/src/experiments/kesorefs/CMakeLists.txt +++ b/src/experiments/kesorefs/CMakeLists.txt @@ -30,5 +30,5 @@ 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}) +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/src/experiments/kesorefs/campaign.cc b/src/experiments/kesorefs/campaign.cc index a656d2cd..9d5ea419 100644 --- a/src/experiments/kesorefs/campaign.cc +++ b/src/experiments/kesorefs/campaign.cc @@ -5,80 +5,16 @@ #include "experimentInfo.hpp" #include "cpn/CampaignManager.hpp" #include "util/Logger.hpp" -#include "util/ElfReader.hpp" #include "util/ProtoStream.hpp" #include "sal/SALConfig.hpp" -//#if COOL_FAULTSPACE_PRUNING -//#include "../plugins/tracing/TracingPlugin.hpp" -//char const * const trace_filename = "trace.pb"; -//#endif using namespace std; using namespace fail; +using namespace google::protobuf; -char const * const results_csv = "kesorefs.csv"; - -bool KesoRefCampaign::run() -{ - Logger log("KesoRefCampaign"); - ElfReader elf; - - ifstream test(results_csv); - if (test.is_open()) { - log << results_csv << " already exists" << endl; - return false; - } - ofstream results(results_csv); - if (!results.is_open()) { - log << "failed to open " << results_csv << endl; - return false; - } - - address_t injip = elf.getSymbol("c23_PersistentDetectorScopeEntry_m5_run").getAddress(); - - address_t rambase = elf.getSymbol("__CIAO_APPDATA_cdx_det__heap").getAddress(); - // address_t ramend = rambase + 0x80000; - address_t ramend = rambase + 4; - cout << "ramend: " << hex << ramend << endl; - - log << "startup, injecting ram @ " << hex << rambase << endl; - - int count = 0; - for (address_t ram_address = rambase; ram_address < ramend ; ram_address += 4) { - for (int bit_offset = 23; bit_offset < 24; ++bit_offset) { - KesoRefExperimentData *d = new KesoRefExperimentData; - - d->msg.set_pc_address(injip); - d->msg.set_ram_address(ram_address); - d->msg.set_bit_offset(bit_offset); - - campaignmanager.addParam(d); - ++count; - } - } - campaignmanager.noMoreParameters(); - log << "done enqueueing parameter sets (" << dec << count << ")." << endl; - - // collect results - KesoRefExperimentData *res; - int rescount = 0; - results << "injection_ip\tram_address\tbit_offset\tresulttype\toriginal_value\tdetails" << endl; - while ((res = static_cast(campaignmanager.getDone()))) { - rescount++; - - results - << "0x" << hex << res->msg.pc_address() << "\t" - << "0x" << hex << res->msg.ram_address() << "\t" - << dec << res->msg.bit_offset() << "\t" - << res->msg.resulttype() << "\t" - << res->msg.original_value() << "\t" - << res->msg.details() << "\n"; - delete res; - } - - log << "done. sent " << count << " received " << rescount << endl; - results.close(); - - return true; +void KesoRefCampaign::cb_send_pilot(DatabaseCampaignMessage pilot) { + KesoRefExperimentData *data = new KesoRefExperimentData; + data->msg.mutable_fsppilot()->CopyFrom(pilot); + campaignmanager.addParam(data); } diff --git a/src/experiments/kesorefs/campaign.hpp b/src/experiments/kesorefs/campaign.hpp index d9c2859f..994631bc 100644 --- a/src/experiments/kesorefs/campaign.hpp +++ b/src/experiments/kesorefs/campaign.hpp @@ -1,19 +1,24 @@ #ifndef __KESOREFCAMPAIGN_HPP__ #define __KESOREFCAMPAIGN_HPP__ -#include "cpn/Campaign.hpp" +#include "cpn/DatabaseCampaign.hpp" #include "comm/ExperimentData.hpp" +#include #include "kesoref.pb.h" + class KesoRefExperimentData : public fail::ExperimentData { public: KesoRefProtoMsg msg; KesoRefExperimentData() : fail::ExperimentData(&msg) {} }; -class KesoRefCampaign : public fail::Campaign { -public: - virtual bool run(); + +class KesoRefCampaign : public fail::DatabaseCampaign { + virtual const google::protobuf::Descriptor * cb_result_message() + { return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("KesoRefProtoMsg"); } + + virtual void cb_send_pilot(DatabaseCampaignMessage pilot); }; #endif // __KESOREFCAMPAIGN_HPP__ diff --git a/src/experiments/kesorefs/experiment.cc b/src/experiments/kesorefs/experiment.cc index 1d39c8f2..2decaf79 100644 --- a/src/experiments/kesorefs/experiment.cc +++ b/src/experiments/kesorefs/experiment.cc @@ -28,9 +28,8 @@ using namespace fail; #define SAFESTATE (1) // Check if configuration dependencies are satisfied: -#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \ - !defined(CONFIG_SR_SAVE) -#error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration. +#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) + #error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration. #endif // //void KESOrefs::printEIP() { @@ -39,27 +38,28 @@ using namespace fail; unsigned KESOrefs::injectBitFlip(address_t data_address, unsigned bitpos){ - MemoryManager& mm = simulator.getMemoryManager(); - unsigned value, injectedval; + MemoryManager& mm = simulator.getMemoryManager(); + unsigned int value, injectedval; - mm.getBytes(data_address, 4, (void*)&value); - injectedval = value ^ (1< 0x" << setw(8) << setfill('0') << injectedval << endl; + m_log << "INJECTION at: 0x" << hex << setw(2) << setfill('0') << data_address + << " value: 0x" << setw(2) << setfill('0') << value << " -> 0x" << setw(2) << setfill('0') << injectedval << endl; - return value; + return value; } -void handleEvent(KesoRefExperimentData& param, KesoRefProtoMsg_ResultType restype, const std::string &msg){ - cout << msg << endl; - param.msg.set_resulttype(restype); - param.msg.set_details(msg); +void handleEvent(KesoRefProtoMsg_Result& result, KesoRefProtoMsg_Result_ResultType restype, const std::string &msg) { + cout << msg << endl; + result.set_resulttype(restype); + result.set_details(msg); } -void handleMemoryAccessEvent(KesoRefExperimentData& param, const fail::MemAccessListener& l_mem){ + +void handleMemoryAccessEvent(KesoRefProtoMsg_Result& result, const fail::MemAccessListener& l_mem){ stringstream sstr; sstr << "mem access ("; switch (l_mem.getTriggerAccessType()) { @@ -75,128 +75,187 @@ void handleMemoryAccessEvent(KesoRefExperimentData& param, const fail::MemAccess sstr << " ip @ 0x" << hex << l_mem.getTriggerInstructionPointer(); - handleEvent(param, param.msg.MEMACCESS, sstr.str()); + handleEvent(result, result.MEMACCESS, sstr.str()); } bool KESOrefs::run() { - m_dis.init(); -//******* Boot, and store state *******// - m_log << "STARTING EXPERIMENT" << endl; -#if SAFESTATE // define SS (SafeState) when building: make -DSS -#warning "Building safe state variant" - m_log << "Booting, and saving state at main" << std::endl; - // m_elf.printSections(); - // m_elf.printDemangled(); + address_t minimal_ip = INT_MAX; // 1 Mbyte + address_t maximal_ip = 0; + address_t minimal_data = 0x100000; // 1 Mbyte + address_t maximal_data = 0; - simulator.terminate(); - BPSingleListener bp; - // STEP 1: run until interesting function starts, and save state - bp.setWatchInstructionPointer(m_elf.getSymbol("main").getAddress()); - if(simulator.addListenerAndResume(&bp) == &bp){ - m_log << "main function entry reached, saving state" << endl; - } + for (ElfReader::section_iterator it = m_elf.sec_begin(); + it != m_elf.sec_end(); ++it) { + const ElfSymbol &symbol = *it; + std::string prefix(".text"); + if (symbol.getName().compare(0, prefix.size(), prefix) == 0) { + minimal_ip = std::min(minimal_ip, symbol.getStart()); + maximal_ip = std::max(maximal_ip, symbol.getEnd()); + } else { + minimal_data = std::min(minimal_data, symbol.getStart()); + maximal_data = std::max(maximal_data, symbol.getEnd()); + } + } - simulator.save("keso.state"); - simulator.terminate(); -#else + std::cout << "Code section from " << hex << minimal_ip << " to " << maximal_ip << std::endl; + std::cout << "Whole programm section from " << hex << minimal_data << " to " << maximal_data << std::endl; -//******* Fault injection *******// -#warning "Building restore state variant" - for (int experiment_count = 0; experiment_count < 200 || (m_jc.getNumberOfUndoneJobs() != 0) ; ) { // only do 200 sequential experiments, to prevent swapping - m_log << "asking jobserver for parameters" << endl; - KesoRefExperimentData param; - if(!m_jc.getParam(param)){ - m_log << "Dying." << endl; // We were told to die. - simulator.terminate(1); - } + // m_dis.init(); + //******* Boot, and store state *******// + m_log << "STARTING EXPERIMENT" << endl; - // Get input data from Jobserver - address_t injectionPC = param.msg.pc_address(); - address_t data_address = param.msg.ram_address(); - unsigned bitpos = param.msg.bit_offset(); + unsigned executed_jobs = 0; - simulator.restore("keso.state"); - // Goto injection point - BPSingleListener injBP; - m_log << "Trying to inject @ " << hex << m_elf.getSymbol(injectionPC).getAddress() << endl; + // Setup exit points + const ElfSymbol &s_error = m_elf.getSymbol("keso_throw_error"); + BPSingleListener l_error(s_error.getAddress()); + const ElfSymbol &s_nullp = m_elf.getSymbol("keso_throw_nullpointer"); + BPSingleListener l_nullp(s_nullp.getAddress()); + const ElfSymbol &s_parity = m_elf.getSymbol("keso_throw_parity"); + BPSingleListener l_parity(s_parity.getAddress()); + const ElfSymbol &s_oobounds = m_elf.getSymbol("keso_throw_index_out_of_bounds"); + BPSingleListener l_oobounds(s_oobounds.getAddress()); + BPSingleListener l_dump(m_elf.getSymbol("c17_Main_m4_dumpResults_console").getAddress()); - injBP.setWatchInstructionPointer(injectionPC); + MemAccessListener l_mem_text(minimal_ip, MemAccessEvent::MEM_WRITE); + l_mem_text.setWatchWidth(maximal_ip - minimal_ip); - simulator.addListenerAndResume(&injBP); - /// INJECT BITFLIP: - param.msg.set_original_value(injectBitFlip(data_address, bitpos)); + MemAccessListener l_mem_outerspace( maximal_data); + l_mem_outerspace.setWatchWidth(0xfffffff0); + TrapListener l_trap(ANY_TRAP); - // Setup exit points - BPSingleListener l_error(m_elf.getSymbol("keso_throw_error").getAddress()); - BPSingleListener l_nullp(m_elf.getSymbol("keso_throw_nullpointer").getAddress()); - BPSingleListener l_parity(m_elf.getSymbol("keso_throw_parity").getAddress()); - BPSingleListener l_oobounds(m_elf.getSymbol("keso_throw_index_out_of_bounds").getAddress()); - BPSingleListener l_dump(m_elf.getSymbol("c17_Main_m4_dumpResults_console").getAddress()); + TimerListener l_timeout(1000 * 1000); // 1 second in microseconds - ElfSymbol sym = m_elf.getSection(".text"); - MemAccessListener l_mem_text(sym.getStart(), , AccessEvent::MEM_WRITE); l_mem_text.setWatchWidth(sym.getSize()); + while (executed_jobs < 25 || m_jc.getNumberOfUndoneJobs() > 0) { + m_log << "asking jobserver for parameters" << endl; + KesoRefExperimentData param; + if(!m_jc.getParam(param)){ + m_log << "Dying." << endl; // We were told to die. + simulator.terminate(1); + } - sym = m_elf.getSection(".text.cdx_det"); - MemAccessListener l_mem_textcdx_det(sym.getStart(), MemAccessEvent::MEM_WRITE ); l_mem_textcdx_det.setWatchWidth(sym.getSize()); + // Get input data from Jobserver + unsigned injection_instr = param.msg.fsppilot().injection_instr(); + address_t data_address = param.msg.fsppilot().data_address(); - sym = m_elf.getSection(".copy_sec"); - MemAccessListener l_mem_outerspace( sym.getStart() ); l_mem_outerspace.setWatchWidth(0xfffffff0); - TrapListener l_trap(ANY_TRAP); + for (int bit_offset = 0; bit_offset < 8; ++bit_offset) { + // 8 results in one job + KesoRefProtoMsg_Result *result = param.msg.add_result(); + result->set_bitoffset(bit_offset); -cout << " outerspace : " << l_mem_outerspace.getWatchWidth() << " --- @ :" << l_mem_outerspace.getWatchAddress() << endl; - simulator.addListener(&l_trap); - simulator.addListener(&l_error); - simulator.addListener(&l_nullp); - simulator.addListener(&l_oobounds); - simulator.addListener(&l_dump); - simulator.addListener(&l_parity); - simulator.addListener(&l_mem_text); - simulator.addListener(&l_mem_outerspace); - simulator.addListener(&l_mem_textcdx_det); - // resume and wait for results - fail::BaseListener* l = simulator.resume(); + m_log << "restoring state" << endl; + // Restore to the image, which starts at address(main) + simulator.restore("state"); + executed_jobs ++; - // Evaluate result - if(l == &l_error) { - handleEvent(param, param.msg.EXC_ERROR, "exc error"); + m_log << "Trying to inject @ instr #" << dec << injection_instr << endl; - } else if ( l == &l_nullp ) { - handleEvent(param, param.msg.EXC_NULLPOINTER, "exc nullpointer"); - } else if ( l == &l_oobounds ) { - handleEvent(param, param.msg.EXC_OOBOUNDS, "exc out of bounds"); + if (injection_instr > 0) { + simulator.clearListeners(); + // XXX could be improved with intermediate states (reducing runtime until injection) + simulator.addListener(&l_dump); - } else if (l == &l_dump) { - handleEvent(param, param.msg.CALCDONE, "calculation done"); + BPSingleListener bp; + bp.setWatchInstructionPointer(ANY_ADDR); + bp.setCounter(injection_instr + 1); + simulator.addListener(&bp); - } else if (l == &l_parity) { - handleEvent(param, param.msg.EXC_PARITY, "exc parity"); + bool inject = true; + while (1) { + fail::BaseListener * listener = simulator.resume(); + // finish() before FI? + if (listener == &l_dump) { + m_log << "experiment reached finish() before FI" << endl; + handleEvent(*result, result->NOINJECTION, "time_marker reached before instr2"); + inject = false; + break; + } else if (listener == &bp) { + break; + } else { + inject = false; + handleEvent(*result, result->NOINJECTION, "WTF"); + break; + } + } - } else if (l == &l_trap) { - stringstream sstr; - sstr << "trap #" << l_trap.getTriggerNumber(); - handleEvent(param, param.msg.TRAP, sstr.str()); + // Next experiment + if (!inject) + continue; + } + address_t injection_instr_absolute = param.msg.fsppilot().injection_instr_absolute(); + if (simulator.getCPU(0).getInstructionPointer() != injection_instr_absolute) { + m_log << "Invalid Injection address EIP=0x" + << std::hex << simulator.getCPU(0).getInstructionPointer() + << " != 0x" << injection_instr_absolute << std::endl; + simulator.terminate(1); + } - } else if (l == &l_mem_text){ - handleMemoryAccessEvent(param, l_mem_text); + /// INJECT BITFLIP: + result->set_original_value(injectBitFlip(data_address, bit_offset)); - } else if (l == &l_mem_textcdx_det){ - handleMemoryAccessEvent(param, l_mem_textcdx_det); + cout << " outerspace : " << l_mem_outerspace.getWatchWidth() << " --- @ :" << l_mem_outerspace.getWatchAddress() << endl; + simulator.clearListeners(); + simulator.addListener(&l_trap); + if (s_error.isValid()) + simulator.addListener(&l_error); + if (s_nullp.isValid()) + simulator.addListener(&l_nullp); + if (s_oobounds.isValid()) + simulator.addListener(&l_oobounds); + simulator.addListener(&l_dump); + if (s_parity.isValid()) + simulator.addListener(&l_parity); + simulator.addListener(&l_mem_text); + simulator.addListener(&l_mem_outerspace); + simulator.addListener(&l_timeout); + m_log << "Resuming till the crash" << std::endl; + // resume and wait for results + fail::BaseListener* l = simulator.resume(); + m_log << "CDX has ended" << std::endl; - } else if (l == &l_mem_outerspace){ - handleMemoryAccessEvent(param, l_mem_outerspace); + // Evaluate result + if(l == &l_error) { + handleEvent(*result, result->EXC_ERROR, "exc error"); + } else if ( l == &l_nullp ) { + handleEvent(*result, result->EXC_NULLPOINTER, "exc nullpointer"); - } else { - handleEvent(param, param.msg.UNKNOWN, "UNKNOWN event"); - } - simulator.clearListeners(); - m_jc.sendResult(param); -} // end while (1) -// Explicitly terminate, or the simulator will continue to run. -#endif -simulator.terminate(); + } else if ( l == &l_oobounds ) { + handleEvent(*result, result->EXC_OOBOUNDS, "exc out of bounds"); + + } else if (l == &l_dump) { + handleEvent(*result, result->CALCDONE, "calculation done"); + + } else if (l == &l_parity) { + handleEvent(*result, result->EXC_PARITY, "exc parity"); + + } else if (l == &l_timeout) { + handleEvent(*result, result->TIMEOUT, "1s"); + + + } else if (l == &l_trap) { + stringstream sstr; + sstr << "trap #" << l_trap.getTriggerNumber(); + handleEvent(*result, result->TRAP, sstr.str()); + + } else if (l == &l_mem_text){ + handleMemoryAccessEvent(*result, l_mem_text); + + } else if (l == &l_mem_outerspace){ + handleMemoryAccessEvent(*result, l_mem_outerspace); + + } else { + handleEvent(*result, result->UNKNOWN, "UNKNOWN event"); + } + simulator.clearListeners(); + } + + m_jc.sendResult(param); + } + // Explicitly terminate, or the simulator will continue to run. + simulator.terminate(); } diff --git a/src/experiments/kesorefs/kesoref.proto b/src/experiments/kesorefs/kesoref.proto index 799ff0e1..d5209fe6 100644 --- a/src/experiments/kesorefs/kesoref.proto +++ b/src/experiments/kesorefs/kesoref.proto @@ -1,27 +1,26 @@ -message KesoRefProtoMsg { - // parameters - required int32 pc_address = 1; - required int32 ram_address = 2; - required int32 bit_offset = 3; +import "DatabaseCampaignMessage.proto"; - // results - // make these optional to reduce overhead for server->client communication - enum ResultType { - CALCDONE = 1; - TIMEOUT = 2; - TRAP = 3; - EXC_ERROR = 4; - EXC_PARITY = 5; - EXC_NULLPOINTER = 6; - EXC_OOBOUNDS = 7; - MEMACCESS = 8; - UNKNOWN = 9; +message KesoRefProtoMsg { + required DatabaseCampaignMessage fsppilot = 1; + + repeated group Result = 2 { + // make these optional to reduce overhead for server->client communication + enum ResultType { + CALCDONE = 1; + TIMEOUT = 2; + TRAP = 3; + EXC_ERROR = 4; + EXC_PARITY = 5; + EXC_NULLPOINTER = 6; + EXC_OOBOUNDS = 7; + MEMACCESS = 8; + NOINJECTION = 9; + UNKNOWN = 10; + } + // result type, see above + required ResultType resulttype = 4; + required uint32 original_value = 5; + required uint32 bitoffset = 6 [(sql_primary_key) = true]; + optional string details = 7; } - // result type, see above - optional ResultType resulttype = 4; - optional uint32 original_value = 5; - // did ECC correct the fault? - //optional int32 error_corrected = 6; - // optional textual description of what happened - optional string details = 7; } diff --git a/src/experiments/kesorefs/main.cc b/src/experiments/kesorefs/main.cc index 4af91718..93105c47 100644 --- a/src/experiments/kesorefs/main.cc +++ b/src/experiments/kesorefs/main.cc @@ -2,10 +2,15 @@ #include #include "cpn/CampaignManager.hpp" +#include "util/CommandLine.hpp" #include "campaign.hpp" int main(int argc, char **argv) { + fail::CommandLine &cmd = fail::CommandLine::Inst(); + for (int i = 1; i < argc; ++i) + cmd.add_args(argv[i]); + KesoRefCampaign c; if (fail::campaignmanager.runCampaign(&c)) { return 0;