another directory rename: failstar -> fail

"failstar" sounds like a name for a cruise liner from the 80s.  As "*" isn't a
desirable part of directory names, just name the whole thing "fail/", the core
parts being stored in "fail/core/".

Additionally fixing two build system dependency issues:
 - missing jobserver -> protomessages dependency
 - broken bochs -> fail dependency (add_custom_target DEPENDS only allows plain
   file dependencies ... cmake for the win)


git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@956 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
hsc
2012-03-08 19:43:02 +00:00
commit b70b6fb43a
921 changed files with 473161 additions and 0 deletions

View File

@ -0,0 +1,31 @@
set(EXPERIMENT_NAME coolchecksum)
set(EXPERIMENT_TYPE CoolChecksumExperiment)
configure_file(../instantiate-experiment.ah.in
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
)
## Setup desired protobuf descriptions HERE ##
set(MY_PROTOS
coolchecksum.proto
)
set(MY_CAMPAIGN_SRCS
experiment.hpp
experiment.cc
campaign.hpp
campaign.cc
)
#### PROTOBUFS ####
find_package(Protobuf REQUIRED)
include_directories(${PROTOBUF_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
## Build library
add_library(${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
## This is the example's campaign server distributing experiment parameters
add_executable(${EXPERIMENT_NAME}-server main.cc)
target_link_libraries(${EXPERIMENT_NAME}-server ${EXPERIMENT_NAME} fail ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY})

View File

@ -0,0 +1,273 @@
#include <iostream>
#include "campaign.hpp"
#include "experimentInfo.hpp"
#include "controller/CampaignManager.hpp"
#include "util/Logger.hpp"
#include "SAL/SALConfig.hpp"
#if COOL_FAULTSPACE_PRUNING
#include "plugins/tracing/TracingPlugin.hpp"
char const * const trace_filename = "trace.pb";
#endif
using namespace fi;
using std::endl;
char const * const results_csv = "coolcampaign.csv";
// equivalence class type: addr, [i1, i2]
// addr: byte to inject a bit-flip into
// [i1, i2]: interval of instruction numbers, counted from experiment
// begin
struct equivalence_class {
unsigned byte_offset;
int instr1, instr2;
sal::address_t instr2_absolute; // FIXME we could record them all here
};
bool CoolChecksumCampaign::run()
{
Logger log("CoolChecksumCampaign");
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;
}
log << "startup" << endl;
#if !COOL_FAULTSPACE_PRUNING
int count = 0;
for (int bit_offset = 0; bit_offset < COOL_ECC_OBJUNDERTEST_SIZE*8; ++bit_offset) {
for (int instr_offset = 0; instr_offset < COOL_ECC_NUMINSTR; ++instr_offset) {
CoolChecksumExperimentData *d = new CoolChecksumExperimentData;
d->msg.set_instr_offset(instr_offset);
d->msg.set_bit_offset(bit_offset);
fi::campaignmanager.addParam(d);
++count;
}
}
fi::campaignmanager.noMoreParameters();
log << "done enqueueing parameter sets (" << count << ")." << endl;
// collect results
CoolChecksumExperimentData *res;
int rescount = 0;
results << "injection_ip\tinstr_offset\tinjection_bit\tresulttype\tresultdata\terror_corrected\tdetails" << endl;
while ((res = static_cast<CoolChecksumExperimentData *>(fi::campaignmanager.getDone()))) {
rescount++;
results
<< res->msg.injection_ip() << "\t"
<< res->msg.instr_offset() << "\t"
<< res->msg.bit_offset() << "\t"
<< res->msg.resulttype() << "\t"
<< res->msg.resultdata() << "\t"
<< res->msg.error_corrected() << "\t"
<< res->msg.details() << "\n";
delete res;
}
#else
// load trace
ifstream tracef(trace_filename);
if (tracef.fail()) {
log << "couldn't open " << trace_filename << endl;
return false;
}
Trace trace;
trace.ParseFromIstream(&tracef);
tracef.close();
// set of equivalence classes that need one (rather: eight, one for
// each bit in that byte) experiment to determine them all
std::vector<equivalence_class> ecs_need_experiment;
// set of equivalence classes that need no experiment, because we know
// they'd be identical to the golden run
std::vector<equivalence_class> ecs_no_effect;
Trace_Event end_event; // pseudo event
equivalence_class current_ec;
// for every injection address ...
// XXX in more complex cases we'll need to iterate over a MemoryMap here
for (unsigned byte_offset = 0; byte_offset < COOL_ECC_OBJUNDERTEST_SIZE; ++byte_offset) {
current_ec.instr1 = 0;
// for every section in the trace between subsequent memory
// accesses to that address ...
// XXX reorganizing the trace for efficient seeks could speed this up
int instr = 0;
sal::address_t instr_absolute = 0; // FIXME this one probably should also be recorded ...
Trace_Event const *ev;
for (int eventnr = 0; eventnr < trace.event_size(); ++eventnr) {
ev = &trace.event(eventnr);
// only count instruction events
if (!ev->has_memaddr()) {
// new instruction
instr++;
instr_absolute = ev->ip();
continue;
// skip accesses to other data
} else if (ev->memaddr() != byte_offset + COOL_ECC_OBJUNDERTEST) {
continue;
// skip zero-sized intervals: these can
// occur when an instruction accesses a
// memory location more than once
// (e.g., INC, CMPXCHG)
} else if (current_ec.instr1 > instr) {
continue;
}
// we now have an interval-terminating R/W
// event to the memaddr we're currently looking
// at:
// complete the equivalence interval
current_ec.instr2 = instr;
current_ec.instr2_absolute = instr_absolute;
current_ec.byte_offset = byte_offset;
if (ev->accesstype() == ev->READ) {
// a sequence ending with READ: we need
// to do one experiment to cover it
// completely
ecs_need_experiment.push_back(current_ec);
} else if (ev->accesstype() == ev->WRITE) {
// a sequence ending with WRITE: an
// injection anywhere here would have
// no effect.
ecs_no_effect.push_back(current_ec);
} else {
log << "WAT" << endl;
}
// next interval must start at next
// instruction; the aforementioned
// skipping mechanism wouldn't work
// otherwise
current_ec.instr1 = instr + 1;
}
// close the last interval:
// 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.
current_ec.instr2 = instr - 1;
current_ec.instr2_absolute = 0; // won't be used
current_ec.byte_offset = byte_offset;
// zero-sized? skip.
if (current_ec.instr1 > current_ec.instr2) {
continue;
}
// as the experiment ends, this byte is a "don't care":
ecs_no_effect.push_back(current_ec);
}
log << "equivalence classes generated:"
<< " need_experiment = " << ecs_need_experiment.size()
<< " no_effect = " << ecs_no_effect.size() << endl;
// statistics
int num_dumb_experiments = 0;
for (std::vector<equivalence_class>::const_iterator it = ecs_need_experiment.begin();
it != ecs_need_experiment.end(); ++it) {
num_dumb_experiments += (*it).instr2 - (*it).instr1 + 1;
}
for (std::vector<equivalence_class>::const_iterator it = ecs_no_effect.begin();
it != ecs_no_effect.end(); ++it) {
num_dumb_experiments += (*it).instr2 - (*it).instr1 + 1;
}
log << "pruning: reduced " << num_dumb_experiments * 8 <<
" experiments to " << ecs_need_experiment.size() * 8 << endl;
// map for efficient access when results come in
std::map<CoolChecksumExperimentData *, equivalence_class *> experiment_ecs;
int count = 0;
for (std::vector<equivalence_class>::iterator it = ecs_need_experiment.begin();
it != ecs_need_experiment.end(); ++it) {
for (int bitnr = 0; bitnr < 8; ++bitnr) {
CoolChecksumExperimentData *d = new CoolChecksumExperimentData;
// we pick the rightmost instruction in that interval
d->msg.set_instr_offset((*it).instr2);
d->msg.set_instr_address((*it).instr2_absolute);
d->msg.set_bit_offset((*it).byte_offset * 8 + bitnr);
experiment_ecs[d] = &(*it);
fi::campaignmanager.addParam(d);
++count;
}
}
fi::campaignmanager.noMoreParameters();
log << "done enqueueing parameter sets (" << count << ")." << endl;
// CSV header
results << "injection_ip\tinstr_offset\tinjection_bit\tresulttype\tresultdata\terror_corrected\tdetails" << endl;
// store no-effect "experiment" results
// (for comparison reasons; we'll store that more compactly later)
for (std::vector<equivalence_class>::const_iterator it = ecs_no_effect.begin();
it != ecs_no_effect.end(); ++it) {
for (int bitnr = 0; bitnr < 8; ++bitnr) {
for (int instr = (*it).instr1; instr <= (*it).instr2; ++instr) {
results
<< (*it).instr2_absolute << "\t" // incorrect in all but one case!
<< instr << "\t"
<< ((*it).byte_offset * 8 + bitnr) << "\t"
<< "1" << "\t"
<< "45" << "\t"
<< "0" << "\t"
<< "" << "\n";
}
}
}
// collect results
CoolChecksumExperimentData *res;
int rescount = 0;
while ((res = static_cast<CoolChecksumExperimentData *>(fi::campaignmanager.getDone()))) {
rescount++;
equivalence_class *ec = experiment_ecs[res];
// sanity check
if (ec->instr2 != res->msg.instr_offset()) {
results << "WTF" << endl;
log << "WTF" << endl;
delete res;
continue;
}
// explode equivalence class to single "experiments"
// (for comparison reasons; we'll store that more compactly later)
for (int instr = ec->instr1; instr <= ec->instr2; ++instr) {
results
<< res->msg.injection_ip() << "\t" // incorrect in all but one case!
<< instr << "\t"
<< res->msg.bit_offset() << "\t"
<< res->msg.resulttype() << "\t"
<< res->msg.resultdata() << "\t"
<< res->msg.error_corrected() << "\t"
<< res->msg.details() << "\n";
}
delete res;
}
#endif
log << "done. sent " << count << " received " << rescount << endl;
results.close();
return true;
}

View File

@ -0,0 +1,20 @@
#ifndef __COOLCAMPAIGN_HPP__
#define __COOLCAMPAIGN_HPP__
#include "controller/Campaign.hpp"
#include "controller/ExperimentData.hpp"
#include "coolchecksum.pb.h"
class CoolChecksumExperimentData : public fi::ExperimentData {
public:
CoolChecksumProtoMsg msg;
CoolChecksumExperimentData() : fi::ExperimentData(&msg) {}
};
class CoolChecksumCampaign : public fi::Campaign {
public:
virtual bool run();
};
#endif

View File

@ -0,0 +1,29 @@
message CoolChecksumProtoMsg {
// parameters
required int32 instr_offset = 1;
optional int32 instr_address = 8; // for sanity checks
required int32 bit_offset = 2;
// results
// make these optional to reduce overhead for server->client communication
enum ResultType {
CALCDONE = 1;
TIMEOUT = 2;
TRAP = 3;
UNKNOWN = 4;
}
// instruction pointer where injection was done
optional uint32 injection_ip = 3;
// result type, see above
optional ResultType resulttype = 4;
// result data, depending on resulttype:
// CALCDONE: resultdata = calculated value
// TIMEOUT: resultdata = latest EIP
// TRAP: resultdata = latest EIP
// UNKNOWN: resultdata = latest EIP
optional uint32 resultdata = 5;
// did ECC correct the fault?
optional int32 error_corrected = 6;
// optional textual description of what happened
optional string details = 7;
}

View File

@ -0,0 +1,197 @@
#include <iostream>
#include "util/Logger.hpp"
#include "experiment.hpp"
#include "experimentInfo.hpp"
#include "campaign.hpp"
#include "SAL/SALConfig.hpp"
#include "SAL/SALInst.hpp"
#include "SAL/Memory.hpp"
#include "SAL/bochs/BochsRegister.hpp"
#include "controller/Event.hpp"
#if COOL_FAULTSPACE_PRUNING
#include "plugins/tracing/TracingPlugin.hpp"
#endif
#include "coolchecksum.pb.h"
using std::endl;
bool CoolChecksumExperiment::run()
{
#if BX_SUPPORT_X86_64
int targetreg = sal::RID_RDX;
#else
int targetreg = sal::RID_EDX;
#endif
Logger log("CoolChecksum", false);
fi::BPEvent bp;
log << "startup" << endl;
#if 0
// STEP 1: run until interesting function starts, and save state
bp.setWatchInstructionPointer(COOL_ECC_FUNC_ENTRY);
sal::simulator.addEventAndWait(&bp);
log << "test function entry reached, saving state" << endl;
log << "EIP = " << std::hex << bp.getTriggerInstructionPointer() << " or " << sal::simulator.getRegisterManager().getInstructionPointer() << endl;
log << "error_corrected = " << std::dec << ((int)sal::simulator.getMemoryManager().getByte(COOL_ECC_ERROR_CORRECTED)) << endl;
sal::simulator.save("coolecc.state");
#elif 0
// STEP 2: determine # instructions from start to end
log << "restoring state" << endl;
sal::simulator.restore("coolecc.state");
log << "EIP = " << std::hex << sal::simulator.getRegisterManager().getInstructionPointer() << endl;
#if COOL_FAULTSPACE_PRUNING
// STEP 2.5: Additionally do a golden run with memory access tracing
// for fault-space pruning. (optional!)
log << "enabling tracing" << endl;
TracingPlugin tp;
// restrict memory access logging to injection target
MemoryMap mm;
mm.add(COOL_ECC_OBJUNDERTEST, COOL_ECC_OBJUNDERTEST_SIZE);
tp.restrictMemoryAddresses(&mm);
// record trace
Trace trace;
tp.setTraceMessage(&trace);
// this must be done *after* configuring the plugin:
sal::simulator.addFlow(&tp);
#endif
// make sure the timer interrupt doesn't disturb us
sal::simulator.addSuppressedInterrupt(32);
int count;
bp.setWatchInstructionPointer(fi::ANY_ADDR);
for (count = 0; bp.getTriggerInstructionPointer() != COOL_ECC_CALCDONE; ++count) {
sal::simulator.addEventAndWait(&bp);
// log << "EIP = " << std::hex << sal::simulator.getRegisterManager().getInstructionPointer() << endl;
}
log << "test function calculation position reached after " << std::dec << count << " instructions" << endl;
log << std::dec << "EDX = " << sal::simulator.getRegisterManager().getRegister(targetreg)->getData() << endl;
#if COOL_FAULTSPACE_PRUNING
sal::simulator.removeFlow(&tp);
// serialize trace to file
std::ofstream of("trace.pb");
if (of.fail()) {
log << "failed to write trace.pb" << endl;
return false;
}
trace.SerializeToOstream(&of);
of.close();
#endif
#elif 1
// FIXME consider moving experiment repetition into Fail* or even the
// SAL -- whether and how this is possible with the chosen backend is
// backend specific
for (int i = 0; i < 2000; ++i) {
// STEP 3: The actual experiment.
log << "restoring state" << endl;
sal::simulator.restore("coolecc.state");
log << "asking job server for experiment parameters" << endl;
CoolChecksumExperimentData param;
if (!m_jc.getParam(param)) {
log << "Dying." << endl;
// communicate that we were told to die
sal::simulator.terminate(1); // "return (false);" ?
}
int id = param.getWorkloadID();
int instr_offset = param.msg.instr_offset();
int bit_offset = param.msg.bit_offset();
log << "job " << id << " instr " << instr_offset << " bit " << bit_offset << endl;
// FIXME could be improved (especially for backends supporting
// breakpoints natively) by utilizing a previously recorded instruction
// trace
bp.setWatchInstructionPointer(fi::ANY_ADDR);
for (int count = 0; count < instr_offset; ++count) {
sal::simulator.addEventAndWait(&bp);
}
// inject
sal::guest_address_t inject_addr = COOL_ECC_OBJUNDERTEST + bit_offset / 8;
sal::MemoryManager& mm = sal::simulator.getMemoryManager();
sal::byte_t data = mm.getByte(inject_addr);
sal::byte_t newdata = data ^ (1 << (bit_offset % 8));
mm.setByte(inject_addr, newdata);
// note at what IP we did it
int32_t injection_ip = sal::simulator.getRegisterManager().getInstructionPointer();
param.msg.set_injection_ip(injection_ip);
log << "inject @ ip " << injection_ip
<< " (offset " << std::dec << instr_offset << ")"
<< " bit " << bit_offset << ": 0x"
<< std::hex << ((int)data) << " -> 0x" << ((int)newdata) << endl;
// sanity check (only works if we're working with an instruction trace)
if (param.msg.has_instr_address() &&
injection_ip != param.msg.instr_address()) {
std::stringstream ss;
ss << "SANITY CHECK FAILED: " << injection_ip
<< " != " << param.msg.instr_address() << endl;
log << ss.str();
param.msg.set_resulttype(param.msg.UNKNOWN);
param.msg.set_resultdata(injection_ip);
param.msg.set_details(ss.str());
sal::simulator.clearEvents();
m_jc.sendResult(param);
continue;
}
// aftermath
fi::BPEvent ev_done(COOL_ECC_CALCDONE);
sal::simulator.addEvent(&ev_done);
fi::BPEvent ev_timeout(fi::ANY_ADDR);
ev_timeout.setCounter(COOL_ECC_NUMINSTR + 3000);
sal::simulator.addEvent(&ev_timeout);
fi::TrapEvent ev_trap(fi::ANY_TRAP);
sal::simulator.addEvent(&ev_trap);
fi::BaseEvent* ev = sal::simulator.waitAny();
if (ev == &ev_done) {
int32_t data = sal::simulator.getRegisterManager().getRegister(targetreg)->getData();
log << std::dec << "Result EDX = " << data << endl;
param.msg.set_resulttype(param.msg.CALCDONE);
param.msg.set_resultdata(data);
} else if (ev == &ev_timeout) {
log << std::dec << "Result TIMEOUT" << endl;
param.msg.set_resulttype(param.msg.TIMEOUT);
param.msg.set_resultdata(sal::simulator.getRegisterManager().getInstructionPointer());
} else if (ev == &ev_trap) {
log << std::dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl;
param.msg.set_resulttype(param.msg.TRAP);
param.msg.set_resultdata(sal::simulator.getRegisterManager().getInstructionPointer());
} else {
log << std::dec << "Result WTF?" << endl;
param.msg.set_resulttype(param.msg.UNKNOWN);
param.msg.set_resultdata(sal::simulator.getRegisterManager().getInstructionPointer());
std::stringstream ss;
ss << "eventid " << ev << " EIP " << sal::simulator.getRegisterManager().getInstructionPointer();
param.msg.set_details(ss.str());
}
sal::simulator.clearEvents();
int32_t error_corrected = sal::simulator.getMemoryManager().getByte(COOL_ECC_ERROR_CORRECTED);
param.msg.set_error_corrected(error_corrected);
m_jc.sendResult(param);
}
// we do not want the simulator to continue running, especially for
// headless and distributed experiments
sal::simulator.terminate();
#endif
// simulator continues to run
return true;
}

View File

@ -0,0 +1,14 @@
#ifndef __COOLEXPERIMENT_HPP__
#define __COOLEXPERIMENT_HPP__
#include "controller/ExperimentFlow.hpp"
#include "jobserver/JobClient.hpp"
class CoolChecksumExperiment : public fi::ExperimentFlow {
fi::JobClient m_jc;
public:
CoolChecksumExperiment() : m_jc("ios.cs.tu-dortmund.de") {}
bool run();
};
#endif

View File

@ -0,0 +1,40 @@
#ifndef __EXPERIMENT_INFO_HPP__
#define __EXPERIMENT_INFO_HPP__
#define COOL_FAULTSPACE_PRUNING 0
// FIXME autogenerate this
#if 1 // with ECC
// the task function's entry address:
// nm -C ecc.elf|fgrep Alpha::functionTaskTask0
#define COOL_ECC_FUNC_ENTRY 0x00200b32
// one of the last instructions before the task calls printf:
// (objdump -Cd ecc.elf|less)
#define COOL_ECC_CALCDONE 0x00200bdf
// number of instructions the target executes under non-error conditions from ENTRY to CALCDONE:
// (result of experiment's step #2)
#define COOL_ECC_NUMINSTR 1995
// the ECC protected object's address:
// nm -C ecc.elf|fgrep objectUnderTest
#define COOL_ECC_OBJUNDERTEST 0x002127a4
// the ECC protected object's payload size:
// (we know that from the object's definition and usual memory layout)
#define COOL_ECC_OBJUNDERTEST_SIZE 10
// the variable that's increased if ECC corrects an error:
// nm -C ecc.elf|fgrep error_corrected
#define COOL_ECC_ERROR_CORRECTED 0x002127b0
#else // without ECC
#define COOL_ECC_FUNC_ENTRY 0x00200a90
#define COOL_ECC_CALCDONE 0x00200ab7
#define COOL_ECC_NUMINSTR 97
#define COOL_ECC_OBJUNDERTEST 0x0021263c
#define COOL_ECC_OBJUNDERTEST_SIZE 10
#define COOL_ECC_ERROR_CORRECTED 0x002127b0 // dummy
#endif
#endif

View File

@ -0,0 +1,15 @@
#include <iostream>
#include <cstdlib>
#include "controller/CampaignManager.hpp"
#include "experiments/coolchecksum/campaign.hpp"
int main(int argc, char **argv)
{
CoolChecksumCampaign c;
if (fi::campaignmanager.runCampaign(&c)) {
return 0;
} else {
return 1;
}
}