Fail* directories reorganized, Code-cleanup (-> coding-style), Typos+comments fixed.
git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1321 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
6
src/experiments/CMakeLists.txt
Normal file
6
src/experiments/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# Note that we're allowing *multiple* experiments to be enabled at once.
|
||||
set(EXPERIMENTS_ACTIVATED "" CACHE STRING "Activated experiments (a semicolon-separated list of fail/src/experiments/ subdirectories)")
|
||||
|
||||
foreach(experiment_name ${EXPERIMENTS_ACTIVATED})
|
||||
add_subdirectory(${experiment_name})
|
||||
endforeach(experiment_name)
|
||||
32
src/experiments/checksum-oostubs/CMakeLists.txt
Normal file
32
src/experiments/checksum-oostubs/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
set(EXPERIMENT_NAME checksum-oostubs)
|
||||
set(EXPERIMENT_TYPE ChecksumOOStuBSExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
checksum-oostubs.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})
|
||||
add_dependencies(${EXPERIMENT_NAME} tracing)
|
||||
|
||||
## 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})
|
||||
258
src/experiments/checksum-oostubs/campaign.cc
Normal file
258
src/experiments/checksum-oostubs/campaign.cc
Normal file
@ -0,0 +1,258 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/ProtoStream.hpp"
|
||||
#include "util/MemoryMap.hpp"
|
||||
|
||||
#include "ecc_region.hpp"
|
||||
|
||||
#include "../plugins/tracing/TracingPlugin.hpp"
|
||||
char const * const trace_filename = "trace.pb";
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
char const * const results_csv = "chksumoostubs.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 {
|
||||
address_t data_address;
|
||||
int instr1, instr2;
|
||||
address_t instr2_absolute; // FIXME we could record them all here
|
||||
};
|
||||
|
||||
bool ChecksumOOStuBSCampaign::run()
|
||||
{
|
||||
Logger log("ChecksumOOStuBS Campaign");
|
||||
|
||||
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;
|
||||
|
||||
// load trace
|
||||
ifstream tracef(trace_filename);
|
||||
if (tracef.fail()) {
|
||||
log << "couldn't open " << trace_filename << endl;
|
||||
return false;
|
||||
}
|
||||
ProtoIStream ps(&tracef);
|
||||
|
||||
// a map of addresses of ECC protected objects
|
||||
MemoryMap mm;
|
||||
for (unsigned i = 0; i < sizeof(memoryMap)/sizeof(*memoryMap); ++i) {
|
||||
mm.add(memoryMap[i][0], memoryMap[i][1]);
|
||||
}
|
||||
|
||||
// set of equivalence classes that need one (rather: eight, one for
|
||||
// each bit in that byte) experiment to determine them all
|
||||
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
|
||||
vector<equivalence_class> ecs_no_effect;
|
||||
|
||||
equivalence_class current_ec;
|
||||
|
||||
// map for efficient access when results come in
|
||||
map<ChecksumOOStuBSExperimentData *, unsigned> experiment_ecs;
|
||||
// experiment count
|
||||
int count = 0;
|
||||
|
||||
// XXX do it the other way around: iterate over trace, search addresses
|
||||
// for every injection address ...
|
||||
for (MemoryMap::iterator it = mm.begin(); it != mm.end(); ++it) {
|
||||
cerr << ".";
|
||||
address_t data_address = *it;
|
||||
current_ec.instr1 = 0;
|
||||
int instr = 0;
|
||||
address_t instr_absolute = 0; // FIXME this one probably should also be recorded ...
|
||||
Trace_Event ev;
|
||||
ps.reset();
|
||||
|
||||
// for every section in the trace between subsequent memory
|
||||
// accesses to that address ...
|
||||
// XXX reorganizing the trace for efficient seeks could speed this up
|
||||
while(ps.getNext(&ev)) {
|
||||
// instruction events just get counted
|
||||
if (!ev.has_memaddr()) {
|
||||
// new instruction
|
||||
instr++;
|
||||
instr_absolute = ev.ip();
|
||||
continue;
|
||||
|
||||
// skip accesses to other data
|
||||
} else if (ev.memaddr() != data_address) {
|
||||
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.data_address = data_address;
|
||||
|
||||
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);
|
||||
|
||||
// instantly enqueue jobs: that way the job clients can already
|
||||
// start working in parallel
|
||||
for (int bitnr = 0; bitnr < 8; ++bitnr) {
|
||||
ChecksumOOStuBSExperimentData *d = new ChecksumOOStuBSExperimentData;
|
||||
// we pick the rightmost instruction in that interval
|
||||
d->msg.set_instr_offset(current_ec.instr2);
|
||||
d->msg.set_instr_address(current_ec.instr2_absolute);
|
||||
d->msg.set_mem_addr(current_ec.data_address);
|
||||
d->msg.set_bit_offset(bitnr);
|
||||
|
||||
// store index into ecs_need_experiment
|
||||
experiment_ecs[d] = ecs_need_experiment.size() - 1;
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
} 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.
|
||||
// XXX still true for checksum-oostubs?
|
||||
current_ec.instr2 = instr - 1;
|
||||
current_ec.instr2_absolute = 0; // won't be used
|
||||
current_ec.data_address = data_address;
|
||||
// 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);
|
||||
}
|
||||
|
||||
campaignmanager.noMoreParameters();
|
||||
log << "done enqueueing parameter sets (" << count << ")." << endl;
|
||||
|
||||
log << "equivalence classes generated:"
|
||||
<< " need_experiment = " << ecs_need_experiment.size()
|
||||
<< " no_effect = " << ecs_no_effect.size() << endl;
|
||||
|
||||
// statistics
|
||||
unsigned long num_dumb_experiments = 0;
|
||||
for (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 (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;
|
||||
|
||||
// CSV header
|
||||
results << "ec_instr1\tec_instr2\tec_instr2_absolute\tec_data_address\tbitnr\tresulttype\tresult0\tresult1\tresult2\tfinish_reached\tlatest_ip\terror_corrected\tdetails" << endl;
|
||||
|
||||
// store no-effect "experiment" results
|
||||
for (vector<equivalence_class>::const_iterator it = ecs_no_effect.begin();
|
||||
it != ecs_no_effect.end(); ++it) {
|
||||
results
|
||||
<< (*it).instr1 << "\t"
|
||||
<< (*it).instr2 << "\t"
|
||||
<< (*it).instr2_absolute << "\t" // incorrect in all but one case!
|
||||
<< (*it).data_address << "\t"
|
||||
<< "99\t" // dummy value: we didn't do any real experiments
|
||||
<< "1\t"
|
||||
<< "99\t99\t99\t"
|
||||
<< "1\t"
|
||||
<< "99\t"
|
||||
<< "0\t\n";
|
||||
}
|
||||
|
||||
// collect results
|
||||
ChecksumOOStuBSExperimentData *res;
|
||||
int rescount = 0;
|
||||
while ((res = static_cast<ChecksumOOStuBSExperimentData *>(campaignmanager.getDone()))) {
|
||||
rescount++;
|
||||
|
||||
map<ChecksumOOStuBSExperimentData *, unsigned>::iterator it =
|
||||
experiment_ecs.find(res);
|
||||
if (it == experiment_ecs.end()) {
|
||||
results << "WTF, didn't find res!" << endl;
|
||||
log << "WTF, didn't find res!" << endl;
|
||||
continue;
|
||||
}
|
||||
equivalence_class &ec = ecs_need_experiment[it->second];
|
||||
|
||||
// sanity check
|
||||
if (ec.instr2 != res->msg.instr_offset()) {
|
||||
results << "WTF" << endl;
|
||||
log << "WTF" << endl;
|
||||
//delete res; // currently racy if jobs are reassigned
|
||||
}
|
||||
|
||||
results
|
||||
<< ec.instr1 << "\t"
|
||||
<< ec.instr2 << "\t"
|
||||
<< ec.instr2_absolute << "\t" // incorrect in all but one case!
|
||||
<< ec.data_address << "\t"
|
||||
<< res->msg.bit_offset() << "\t"
|
||||
<< res->msg.resulttype() << "\t"
|
||||
<< res->msg.resultdata(0) << "\t"
|
||||
<< res->msg.resultdata(1) << "\t"
|
||||
<< res->msg.resultdata(2) << "\t"
|
||||
<< res->msg.finish_reached() << "\t"
|
||||
<< res->msg.latest_ip() << "\t"
|
||||
<< res->msg.error_corrected() << "\t"
|
||||
<< res->msg.details() << "\n";
|
||||
//delete res; // currently racy if jobs are reassigned
|
||||
}
|
||||
log << "done. sent " << count << " received " << rescount << endl;
|
||||
results.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
19
src/experiments/checksum-oostubs/campaign.hpp
Normal file
19
src/experiments/checksum-oostubs/campaign.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __CHECKSUM_OOSTUBS_CAMPAIGN_HPP__
|
||||
#define __CHECKSUM_OOSTUBS_CAMPAIGN_HPP__
|
||||
|
||||
#include "cpn/Campaign.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "checksum-oostubs.pb.h"
|
||||
|
||||
class ChecksumOOStuBSExperimentData : public fail::ExperimentData {
|
||||
public:
|
||||
OOStuBSProtoMsg msg;
|
||||
ChecksumOOStuBSExperimentData() : fail::ExperimentData(&msg) {}
|
||||
};
|
||||
|
||||
class ChecksumOOStuBSCampaign : public fail::Campaign {
|
||||
public:
|
||||
virtual bool run();
|
||||
};
|
||||
|
||||
#endif // __CHECKSUM_OOSTUBS_CAMPAIGN_HPP__
|
||||
42
src/experiments/checksum-oostubs/checksum-oostubs.proto
Normal file
42
src/experiments/checksum-oostubs/checksum-oostubs.proto
Normal file
@ -0,0 +1,42 @@
|
||||
message OOStuBSProtoMsg {
|
||||
// Input: experiment parameters
|
||||
required int32 instr_offset = 1;
|
||||
optional int32 instr_address = 2; // for sanity checks
|
||||
required int32 mem_addr = 3;
|
||||
required int32 bit_offset = 4;
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
// Output: experiment results
|
||||
// (make these optional to reduce overhead for server->client communication)
|
||||
|
||||
// instruction pointer where injection was done
|
||||
optional uint32 injection_ip = 5;
|
||||
|
||||
// result type:
|
||||
// FINISHED = planned number of instructions were executed
|
||||
// TRAP = premature guest "crash"
|
||||
enum ResultType {
|
||||
FINISHED = 1;
|
||||
TRAP = 2;
|
||||
HALT = 3;
|
||||
UNKNOWN = 4;
|
||||
}
|
||||
optional ResultType resulttype = 6;
|
||||
|
||||
// result details:
|
||||
// resultdata = result[0-2]
|
||||
repeated uint32 resultdata = 7 [packed=true];
|
||||
|
||||
// was finish() ever reached?
|
||||
optional bool finish_reached = 8;
|
||||
|
||||
// especially interesting for TRAP/ UNKNOWN: latest IP
|
||||
optional uint32 latest_ip = 9;
|
||||
|
||||
// did ECC correct the fault?
|
||||
optional int32 error_corrected = 10;
|
||||
|
||||
// optional textual description of what happened
|
||||
optional string details = 11;
|
||||
}
|
||||
74
src/experiments/checksum-oostubs/ecc_region.hpp
Normal file
74
src/experiments/checksum-oostubs/ecc_region.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
// generated from STEP 0 output with region2array.sh
|
||||
static const unsigned memoryMap[][2] = {
|
||||
{0x10b338, 89},
|
||||
{0x10b394, 89},
|
||||
{0x10b3f0, 89},
|
||||
{0x10b44c, 13},
|
||||
{0x10b45c, 13},
|
||||
{0x10c408, 4},
|
||||
{0x10c410, 4},
|
||||
{0x10c448, 4},
|
||||
{0x10c4e4, 256},
|
||||
{0x10c5f8, 4},
|
||||
{0x10c604, 4},
|
||||
{0x10c608, 4},
|
||||
{0x10c61c, 1},
|
||||
{0x10c63c, 1},
|
||||
{0x10c648, 1},
|
||||
{0x10c649, 1},
|
||||
{0x10c64a, 1},
|
||||
{0x10c64b, 1},
|
||||
{0x10c64c, 1},
|
||||
{0x10c651, 1},
|
||||
{0x10c654, 4},
|
||||
{0x10c65c, 4},
|
||||
{0x10c668, 1},
|
||||
{0x10c668, 1},
|
||||
{0x10c668, 1},
|
||||
{0x10c669, 1},
|
||||
{0x10c669, 1},
|
||||
{0x10c669, 1},
|
||||
{0x10c66a, 1},
|
||||
{0x10c66a, 1},
|
||||
{0x10c66a, 1},
|
||||
{0x10c66f, 1},
|
||||
{0x10c670, 1},
|
||||
{0x10c671, 1},
|
||||
{0x10c67c, 4},
|
||||
{0x10c680, 4},
|
||||
{0x10c688, 4},
|
||||
{0x10c698, 4},
|
||||
{0x10c6a4, 4},
|
||||
{0x10c6a8, 4},
|
||||
{0x10c6b0, 1},
|
||||
{0x10c6b8, 4},
|
||||
{0x10c6bc, 4},
|
||||
{0x10d6c4, 4},
|
||||
{0x10d6cc, 4},
|
||||
{0x10d6d4, 4},
|
||||
{0x10d6dc, 4},
|
||||
{0x10d6f4, 4},
|
||||
{0x10d6fc, 4},
|
||||
{0x10d70c, 1},
|
||||
{0x10d720, 4},
|
||||
{0x10d724, 4},
|
||||
{0x10d734, 4},
|
||||
{0x10d739, 1},
|
||||
{0x10d740, 4},
|
||||
{0x10d744, 4},
|
||||
{0x10d754, 4},
|
||||
{0x10d759, 1},
|
||||
{0x10d760, 4},
|
||||
{0x10d764, 4},
|
||||
{0x10d774, 4},
|
||||
{0x10d779, 1},
|
||||
{0x10d780, 4},
|
||||
{0x10d784, 4},
|
||||
{0x10d78c, 4},
|
||||
{0x110618, 4},
|
||||
{0x110620, 4},
|
||||
{0x110644, 4},
|
||||
{0x11064c, 4},
|
||||
{0x110670, 4},
|
||||
{0x110678, 4},
|
||||
};
|
||||
282
src/experiments/checksum-oostubs/experiment.cc
Normal file
282
src/experiments/checksum-oostubs/experiment.cc
Normal file
@ -0,0 +1,282 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
// getpid
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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 "sal/Event.hpp"
|
||||
|
||||
// You need to have the tracing plugin enabled for this
|
||||
#include "../plugins/tracing/TracingPlugin.hpp"
|
||||
|
||||
#include "ecc_region.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \
|
||||
!defined(CONFIG_SR_SAVE) || !defined(CONFIG_EVENT_TRAP)
|
||||
#error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
bool ChecksumOOStuBSExperiment::run()
|
||||
{
|
||||
// char const *statename = "checksum-oostubs.state"; // FIXME: Variable is unused!
|
||||
Logger log("Checksum-OOStuBS", false);
|
||||
BPSingleEvent bp;
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
#if 1
|
||||
// STEP 0: record memory map with addresses of "interesting" objects
|
||||
GuestEvent g;
|
||||
while (true) {
|
||||
simulator.addEventAndWait(&g);
|
||||
cout << g.getData() << flush;
|
||||
}
|
||||
#elif 0
|
||||
// STEP 1: run until interesting function starts, and save state
|
||||
bp.setWatchInstructionPointer(OOSTUBS_FUNC_ENTRY);
|
||||
simulator.addEventAndWait(&bp);
|
||||
log << "test function entry reached, saving state" << endl;
|
||||
log << "EIP = " << hex << bp.getTriggerInstructionPointer() << endl;
|
||||
log << "error_corrected = " << dec << ((int)simulator.getMemoryManager().getByte(OOSTUBS_ERROR_CORRECTED)) << endl;
|
||||
simulator.save(statename);
|
||||
assert(bp.getTriggerInstructionPointer() == OOSTUBS_FUNC_ENTRY);
|
||||
assert(simulator.getRegisterManager().getInstructionPointer() == OOSTUBS_FUNC_ENTRY);
|
||||
#elif 1
|
||||
// STEP 2: record trace for fault-space pruning
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(statename);
|
||||
log << "EIP = " << hex << simulator.getRegisterManager().getInstructionPointer() << endl;
|
||||
assert(simulator.getRegisterManager().getInstructionPointer() == OOSTUBS_FUNC_ENTRY);
|
||||
|
||||
log << "enabling tracing" << endl;
|
||||
TracingPlugin tp;
|
||||
|
||||
// restrict memory access logging to injection target
|
||||
MemoryMap mm;
|
||||
for (unsigned i = 0; i < sizeof(memoryMap)/sizeof(*memoryMap); ++i) {
|
||||
mm.add(memoryMap[i][0], memoryMap[i][1]);
|
||||
}
|
||||
tp.restrictMemoryAddresses(&mm);
|
||||
|
||||
// record trace
|
||||
char const *tracefile = "trace.pb";
|
||||
ofstream of(tracefile);
|
||||
tp.setTraceFile(&of);
|
||||
|
||||
// this must be done *after* configuring the plugin:
|
||||
simulator.addFlow(&tp);
|
||||
|
||||
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||
bp.setCounter(OOSTUBS_NUMINSTR);
|
||||
simulator.addEvent(&bp);
|
||||
BPSingleEvent func_finish(OOSTUBS_FUNC_FINISH);
|
||||
simulator.addEvent(&func_finish);
|
||||
|
||||
if (simulator.waitAny() == &func_finish) {
|
||||
log << "experiment reached finish()" << endl;
|
||||
// FIXME add instruction counter to SimulatorController
|
||||
simulator.waitAny();
|
||||
}
|
||||
log << "experiment finished after " << dec << OOSTUBS_NUMINSTR << " instructions" << endl;
|
||||
|
||||
uint32_t results[OOSTUBS_RESULTS_BYTES / sizeof(uint32_t)];
|
||||
simulator.getMemoryManager().getBytes(OOSTUBS_RESULTS_ADDR, sizeof(results), results);
|
||||
for (unsigned i = 0; i < sizeof(results) / sizeof(*results); ++i) {
|
||||
log << "results[" << i << "]: " << dec << results[i] << endl;
|
||||
}
|
||||
|
||||
simulator.removeFlow(&tp);
|
||||
|
||||
// serialize trace to file
|
||||
if (of.fail()) {
|
||||
log << "failed to write " << tracefile << endl;
|
||||
simulator.clearEvents(this);
|
||||
return false;
|
||||
}
|
||||
of.close();
|
||||
log << "trace written to " << tracefile << endl;
|
||||
|
||||
#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
|
||||
while (true) {
|
||||
|
||||
// STEP 3: The actual experiment.
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(statename);
|
||||
|
||||
// get an experiment parameter set
|
||||
log << "asking job server for experiment parameters" << endl;
|
||||
ChecksumOOStuBSExperimentData param;
|
||||
if (!m_jc.getParam(param)) {
|
||||
log << "Dying." << endl;
|
||||
// communicate that we were told to die
|
||||
simulator.terminate(1);
|
||||
}
|
||||
/*
|
||||
// XXX debug
|
||||
param.msg.set_instr_offset(2576034);
|
||||
param.msg.set_instr_address(1066640);
|
||||
param.msg.set_mem_addr(1099428);
|
||||
param.msg.set_bit_offset(4);
|
||||
*/
|
||||
|
||||
int id = param.getWorkloadID();
|
||||
int instr_offset = param.msg.instr_offset();
|
||||
int mem_addr = param.msg.mem_addr();
|
||||
int bit_offset = param.msg.bit_offset();
|
||||
log << "job " << id << " instr " << instr_offset << " mem " << mem_addr << "+" << bit_offset << endl;
|
||||
|
||||
// XXX debug
|
||||
stringstream fname;
|
||||
fname << "job." << ::getpid();
|
||||
ofstream job(fname.str().c_str());
|
||||
job << "job " << id << " instr " << instr_offset << " (" << param.msg.instr_address() << ") mem " << mem_addr << "+" << bit_offset << endl;
|
||||
job.close();
|
||||
|
||||
// reaching finish() could happen before OR after FI
|
||||
BPSingleEvent func_finish(OOSTUBS_FUNC_FINISH);
|
||||
simulator.addEvent(&func_finish);
|
||||
bool finish_reached = false;
|
||||
|
||||
// no need to wait if offset is 0
|
||||
if (instr_offset > 0) {
|
||||
// XXX test this with coolchecksum first (or reassure with sanity checks)
|
||||
// XXX could be improved with intermediate states (reducing runtime until injection)
|
||||
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||
bp.setCounter(instr_offset);
|
||||
simulator.addEvent(&bp);
|
||||
|
||||
// finish() before FI?
|
||||
if (simulator.waitAny() == &func_finish) {
|
||||
finish_reached = true;
|
||||
log << "experiment reached finish() before FI" << endl;
|
||||
|
||||
// wait for bp
|
||||
simulator.waitAny();
|
||||
}
|
||||
}
|
||||
|
||||
// --- fault injection ---
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
byte_t data = mm.getByte(mem_addr);
|
||||
byte_t newdata = data ^ (1 << bit_offset);
|
||||
mm.setByte(mem_addr, newdata);
|
||||
// note at what IP we did it
|
||||
int32_t injection_ip = simulator.getRegisterManager().getInstructionPointer();
|
||||
param.msg.set_injection_ip(injection_ip);
|
||||
log << "fault injected @ ip " << injection_ip
|
||||
<< " 0x" << hex << ((int)data) << " -> 0x" << ((int)newdata) << endl;
|
||||
// sanity check
|
||||
if (param.msg.has_instr_address() &&
|
||||
injection_ip != param.msg.instr_address()) {
|
||||
stringstream ss;
|
||||
ss << "SANITY CHECK FAILED: " << injection_ip
|
||||
<< " != " << param.msg.instr_address();
|
||||
log << ss.str() << endl;
|
||||
param.msg.set_resulttype(param.msg.UNKNOWN);
|
||||
param.msg.set_latest_ip(injection_ip);
|
||||
param.msg.set_details(ss.str());
|
||||
|
||||
simulator.clearEvents();
|
||||
m_jc.sendResult(param);
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- aftermath ---
|
||||
// four possible outcomes:
|
||||
// - guest causes a trap, "crashes"
|
||||
// - guest reaches a "weird" state, stops with CLI+HLT ("panic")
|
||||
// - guest runs OOSTUBS_NUMINSTR+OOSTUBS_RECOVERYINSTR instructions but
|
||||
// never reaches finish()
|
||||
// - guest reaches finish() within OOSTUBS_NUMINSTR+OOSTUBS_RECOVERYINSTR
|
||||
// instructions with
|
||||
// * a wrong result[0-2]
|
||||
// * a correct result[0-2]
|
||||
|
||||
// catch traps as "extraordinary" ending
|
||||
TrapEvent ev_trap(ANY_TRAP);
|
||||
simulator.addEvent(&ev_trap);
|
||||
// OOStuBS' way to terminally halt (CLI+HLT)
|
||||
BPSingleEvent ev_halt(OOSTUBS_FUNC_CPU_HALT);
|
||||
simulator.addEvent(&ev_halt);
|
||||
// remaining instructions until "normal" ending
|
||||
BPSingleEvent ev_done(ANY_ADDR);
|
||||
ev_done.setCounter(OOSTUBS_NUMINSTR + OOSTUBS_RECOVERYINSTR - instr_offset);
|
||||
simulator.addEvent(&ev_done);
|
||||
|
||||
/*
|
||||
// XXX debug
|
||||
log << "enabling tracing" << endl;
|
||||
TracingPlugin tp;
|
||||
tp.setLogIPOnly(true);
|
||||
tp.setOstream(&cout);
|
||||
// this must be done *after* configuring the plugin:
|
||||
simulator.addFlow(&tp);
|
||||
*/
|
||||
|
||||
BaseEvent* ev = simulator.waitAny();
|
||||
|
||||
// Do we reach finish() while waiting for ev_trap/ev_done?
|
||||
if (ev == &func_finish) {
|
||||
finish_reached = true;
|
||||
log << "experiment reached finish()" << endl;
|
||||
|
||||
// wait for ev_trap/ev_done
|
||||
ev = simulator.waitAny();
|
||||
}
|
||||
|
||||
// record resultdata, finish_reached and error_corrected regardless of result
|
||||
uint32_t results[OOSTUBS_RESULTS_BYTES / sizeof(uint32_t)];
|
||||
simulator.getMemoryManager().getBytes(OOSTUBS_RESULTS_ADDR, sizeof(results), results);
|
||||
for (unsigned i = 0; i < sizeof(results) / sizeof(*results); ++i) {
|
||||
log << "results[" << i << "]: " << dec << results[i] << endl;
|
||||
param.msg.add_resultdata(results[i]);
|
||||
}
|
||||
param.msg.set_finish_reached(finish_reached);
|
||||
int32_t error_corrected = simulator.getMemoryManager().getByte(OOSTUBS_ERROR_CORRECTED);
|
||||
param.msg.set_error_corrected(error_corrected);
|
||||
param.msg.set_latest_ip(simulator.getRegisterManager().getInstructionPointer());
|
||||
|
||||
if (ev == &ev_done) {
|
||||
log << dec << "Result FINISHED" << endl;
|
||||
param.msg.set_resulttype(param.msg.FINISHED);
|
||||
} else if (ev == &ev_halt) {
|
||||
log << dec << "Result HALT" << endl;
|
||||
param.msg.set_resulttype(param.msg.HALT);
|
||||
} else if (ev == &ev_trap) {
|
||||
log << dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl;
|
||||
param.msg.set_resulttype(param.msg.TRAP);
|
||||
|
||||
stringstream ss;
|
||||
ss << ev_trap.getTriggerNumber();
|
||||
param.msg.set_details(ss.str());
|
||||
} else {
|
||||
log << dec << "Result WTF?" << endl;
|
||||
param.msg.set_resulttype(param.msg.UNKNOWN);
|
||||
|
||||
stringstream ss;
|
||||
ss << "eventid " << ev->getId() << " EIP " << simulator.getRegisterManager().getInstructionPointer();
|
||||
param.msg.set_details(ss.str());
|
||||
}
|
||||
m_jc.sendResult(param);
|
||||
|
||||
}
|
||||
#endif
|
||||
// Explicitly terminate, or the simulator will continue to run.
|
||||
simulator.terminate();
|
||||
}
|
||||
14
src/experiments/checksum-oostubs/experiment.hpp
Normal file
14
src/experiments/checksum-oostubs/experiment.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __CHECKSUM_OOSTUBS_EXPERIMENT_HPP__
|
||||
#define __CHECKSUM_OOSTUBS_EXPERIMENT_HPP__
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
|
||||
class ChecksumOOStuBSExperiment : public fail::ExperimentFlow {
|
||||
fail::JobClient m_jc;
|
||||
public:
|
||||
ChecksumOOStuBSExperiment() : m_jc("ios.cs.tu-dortmund.de") {}
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __CHECKSUM_OOSTUBS_EXPERIMENT_HPP__
|
||||
51
src/experiments/checksum-oostubs/experimentInfo.hpp
Normal file
51
src/experiments/checksum-oostubs/experimentInfo.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef __EXPERIMENT_INFO_HPP__
|
||||
#define __EXPERIMENT_INFO_HPP__
|
||||
|
||||
// FIXME autogenerate this
|
||||
|
||||
#if 1 // with ECC
|
||||
|
||||
// the task function's entry address:
|
||||
// nm -C ecc.elf|fgrep main
|
||||
#define OOSTUBS_FUNC_ENTRY 0x00103f2c
|
||||
// empty function that is called explicitly when the experiment finished
|
||||
// nm -C ecc.elf|fgrep "finished()"
|
||||
#define OOSTUBS_FUNC_FINISH 0x001093f0
|
||||
// function executing HLT with no chance for further progress (after panic())
|
||||
// nm -C ecc.elf|fgrep cpu_halt
|
||||
#define OOSTUBS_FUNC_CPU_HALT 0x00100987
|
||||
// number of instructions the target executes under non-error conditions from ENTRY to DONE:
|
||||
// (result of experiment's step #2)
|
||||
#define OOSTUBS_NUMINSTR 0x4a3401
|
||||
// number of instructions that are executed additionally for error corrections
|
||||
// (this is a rough guess ... TODO)
|
||||
#define OOSTUBS_RECOVERYINSTR 0x2000
|
||||
// the ECC protected object's address:
|
||||
// nm -C ecc.elf|fgrep objectUnderTest
|
||||
#define COOL_ECC_OBJUNDERTEST 0x002127a4 //FIXME
|
||||
// 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 //FIXME
|
||||
// the variable that's increased if ECC corrects an error:
|
||||
// nm -C ecc.elf|fgrep errors_corrected
|
||||
#define OOSTUBS_ERROR_CORRECTED 0x0010e3a4
|
||||
//
|
||||
// nm -C ecc.elf|fgrep results
|
||||
#define OOSTUBS_RESULTS_ADDR 0x0010d794
|
||||
#define OOSTUBS_RESULTS_BYTES 12
|
||||
#define OOSTUBS_RESULT0 0xab3566a9
|
||||
#define OOSTUBS_RESULT1 0x44889112
|
||||
#define OOSTUBS_RESULT2 0x10420844
|
||||
|
||||
#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 // __EXPERIMENT_INFO_HPP__
|
||||
15
src/experiments/checksum-oostubs/main.cc
Normal file
15
src/experiments/checksum-oostubs/main.cc
Normal file
@ -0,0 +1,15 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ChecksumOOStuBSCampaign c;
|
||||
if (fail::campaignmanager.runCampaign(&c)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
31
src/experiments/cool-checksum/CMakeLists.txt
Normal file
31
src/experiments/cool-checksum/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
set(EXPERIMENT_NAME cool-checksum)
|
||||
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})
|
||||
272
src/experiments/cool-checksum/campaign.cc
Normal file
272
src/experiments/cool-checksum/campaign.cc
Normal file
@ -0,0 +1,272 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/Logger.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;
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
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 *>(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;
|
||||
}
|
||||
ProtoIStream ps(&tracef);
|
||||
|
||||
// set of equivalence classes that need one (rather: eight, one for
|
||||
// each bit in that byte) experiment to determine them all
|
||||
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
|
||||
vector<equivalence_class> ecs_no_effect;
|
||||
|
||||
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;
|
||||
address_t instr_absolute = 0; // FIXME this one probably should also be recorded ...
|
||||
Trace_Event ev;
|
||||
ps.reset();
|
||||
|
||||
while(ps.getNext(&ev)) {
|
||||
// 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 (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 (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
|
||||
map<CoolChecksumExperimentData *, equivalence_class *> experiment_ecs;
|
||||
int count = 0;
|
||||
for (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);
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
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 (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 *>(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;
|
||||
}
|
||||
19
src/experiments/cool-checksum/campaign.hpp
Normal file
19
src/experiments/cool-checksum/campaign.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __COOLCAMPAIGN_HPP__
|
||||
#define __COOLCAMPAIGN_HPP__
|
||||
|
||||
#include "cpn/Campaign.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "coolchecksum.pb.h"
|
||||
|
||||
class CoolChecksumExperimentData : public fail::ExperimentData {
|
||||
public:
|
||||
CoolChecksumProtoMsg msg;
|
||||
CoolChecksumExperimentData() : fail::ExperimentData(&msg) {}
|
||||
};
|
||||
|
||||
class CoolChecksumCampaign : public fail::Campaign {
|
||||
public:
|
||||
virtual bool run();
|
||||
};
|
||||
|
||||
#endif // __COOLCAMPAIGN_HPP__
|
||||
29
src/experiments/cool-checksum/coolchecksum.proto
Normal file
29
src/experiments/cool-checksum/coolchecksum.proto
Normal 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;
|
||||
}
|
||||
201
src/experiments/cool-checksum/experiment.cc
Normal file
201
src/experiments/cool-checksum/experiment.cc
Normal file
@ -0,0 +1,201 @@
|
||||
#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 "sal/Event.hpp"
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#if COOL_FAULTSPACE_PRUNING
|
||||
#include "../plugins/tracing/TracingPlugin.hpp"
|
||||
#endif
|
||||
|
||||
#include "coolchecksum.pb.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \
|
||||
!defined(CONFIG_SR_SAVE) || !defined(CONFIG_SUPPRESS_INTERRUPTS) || \
|
||||
!defined(CONFIG_EVENT_TRAP)
|
||||
#error This experiment needs: breakpoints, suppressed-interrupts, traps, save, and restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
bool CoolChecksumExperiment::run()
|
||||
{
|
||||
Logger log("CoolChecksum", false);
|
||||
BPSingleEvent bp;
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
#if 1
|
||||
// STEP 1: run until interesting function starts, and save state
|
||||
bp.setWatchInstructionPointer(COOL_ECC_FUNC_ENTRY);
|
||||
simulator.addEventAndWait(&bp);
|
||||
log << "test function entry reached, saving state" << endl;
|
||||
log << "EIP = " << hex << bp.getTriggerInstructionPointer() << " or " << simulator.getRegisterManager().getInstructionPointer() << endl;
|
||||
log << "error_corrected = " << dec << ((int)simulator.getMemoryManager().getByte(COOL_ECC_ERROR_CORRECTED)) << endl;
|
||||
simulator.save("coolecc.state");
|
||||
#elif 0
|
||||
|
||||
// STEP 2: determine # instructions from start to end
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore("coolecc.state");
|
||||
log << "EIP = " << hex << 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
|
||||
ofstream of("trace.pb");
|
||||
tp.setTraceFile(&of);
|
||||
|
||||
// this must be done *after* configuring the plugin:
|
||||
simulator.addFlow(&tp);
|
||||
#endif
|
||||
|
||||
// make sure the timer interrupt doesn't disturb us
|
||||
simulator.addSuppressedInterrupt(0);
|
||||
|
||||
int count;
|
||||
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||
for (count = 0; bp.getTriggerInstructionPointer() != COOL_ECC_CALCDONE; ++count) {
|
||||
simulator.addEventAndWait(&bp);
|
||||
// log << "EIP = " << hex << simulator.getRegisterManager().getInstructionPointer() << endl;
|
||||
}
|
||||
log << "test function calculation position reached after " << dec << count << " instructions" << endl;
|
||||
Register* reg = simulator.getRegisterManager().getRegister(RID_CDX);
|
||||
log << dec << reg->getName() << " = " << reg->getData() << endl;
|
||||
|
||||
#if COOL_FAULTSPACE_PRUNING
|
||||
simulator.removeFlow(&tp);
|
||||
|
||||
// serialize trace to file
|
||||
if (of.fail()) {
|
||||
log << "failed to write trace.pb" << endl;
|
||||
simulator.clearEvents(this);
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
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
|
||||
simulator.terminate(1);
|
||||
}
|
||||
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(ANY_ADDR);
|
||||
for (int count = 0; count < instr_offset; ++count) {
|
||||
simulator.addEventAndWait(&bp);
|
||||
}
|
||||
|
||||
// inject
|
||||
guest_address_t inject_addr = COOL_ECC_OBJUNDERTEST + bit_offset / 8;
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
byte_t data = mm.getByte(inject_addr);
|
||||
byte_t newdata = data ^ (1 << (bit_offset % 8));
|
||||
mm.setByte(inject_addr, newdata);
|
||||
// note at what IP we did it
|
||||
int32_t injection_ip = simulator.getRegisterManager().getInstructionPointer();
|
||||
param.msg.set_injection_ip(injection_ip);
|
||||
log << "inject @ ip " << injection_ip
|
||||
<< " (offset " << dec << instr_offset << ")"
|
||||
<< " bit " << bit_offset << ": 0x"
|
||||
<< 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()) {
|
||||
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());
|
||||
|
||||
simulator.clearEvents();
|
||||
m_jc.sendResult(param);
|
||||
continue;
|
||||
}
|
||||
|
||||
// aftermath
|
||||
BPSingleEvent ev_done(COOL_ECC_CALCDONE);
|
||||
simulator.addEvent(&ev_done);
|
||||
BPSingleEvent ev_timeout(ANY_ADDR);
|
||||
ev_timeout.setCounter(COOL_ECC_NUMINSTR + 3000);
|
||||
simulator.addEvent(&ev_timeout);
|
||||
TrapEvent ev_trap(ANY_TRAP);
|
||||
simulator.addEvent(&ev_trap);
|
||||
|
||||
BaseEvent* ev = simulator.waitAny();
|
||||
if (ev == &ev_done) {
|
||||
Register* pRegRes = simulator.getRegisterManager().getRegister(RID_CDX);
|
||||
int32_t data = pRegRes->getData();
|
||||
log << dec << "Result " << pRegRes->getName() << " = " << data << endl;
|
||||
param.msg.set_resulttype(param.msg.CALCDONE);
|
||||
param.msg.set_resultdata(data);
|
||||
} else if (ev == &ev_timeout) {
|
||||
log << dec << "Result TIMEOUT" << endl;
|
||||
param.msg.set_resulttype(param.msg.TIMEOUT);
|
||||
param.msg.set_resultdata(simulator.getRegisterManager().getInstructionPointer());
|
||||
} else if (ev == &ev_trap) {
|
||||
log << dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl;
|
||||
param.msg.set_resulttype(param.msg.TRAP);
|
||||
param.msg.set_resultdata(simulator.getRegisterManager().getInstructionPointer());
|
||||
} else {
|
||||
log << dec << "Result WTF?" << endl;
|
||||
param.msg.set_resulttype(param.msg.UNKNOWN);
|
||||
param.msg.set_resultdata(simulator.getRegisterManager().getInstructionPointer());
|
||||
|
||||
stringstream ss;
|
||||
ss << "eventid " << ev << " EIP " << simulator.getRegisterManager().getInstructionPointer();
|
||||
param.msg.set_details(ss.str());
|
||||
}
|
||||
simulator.clearEvents();
|
||||
int32_t error_corrected = 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
|
||||
simulator.terminate();
|
||||
#endif
|
||||
// simulator continues to run
|
||||
simulator.clearEvents(this);
|
||||
return true;
|
||||
}
|
||||
14
src/experiments/cool-checksum/experiment.hpp
Normal file
14
src/experiments/cool-checksum/experiment.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __COOLEXPERIMENT_HPP__
|
||||
#define __COOLEXPERIMENT_HPP__
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
|
||||
class CoolChecksumExperiment : public fail::ExperimentFlow {
|
||||
fail::JobClient m_jc;
|
||||
public:
|
||||
CoolChecksumExperiment() : m_jc("ios.cs.tu-dortmund.de") {}
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __COOLEXPERIMENT_HPP__
|
||||
40
src/experiments/cool-checksum/experimentInfo.hpp
Normal file
40
src/experiments/cool-checksum/experimentInfo.hpp
Normal 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 // __EXPERIMENT_INFO_HPP__
|
||||
15
src/experiments/cool-checksum/main.cc
Normal file
15
src/experiments/cool-checksum/main.cc
Normal file
@ -0,0 +1,15 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CoolChecksumCampaign c;
|
||||
if (fail::campaignmanager.runCampaign(&c)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
18
src/experiments/fault-coverage/CMakeLists.txt
Normal file
18
src/experiments/fault-coverage/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
#FaultCoverage experiment
|
||||
set(EXPERIMENT_NAME fault-coverage)
|
||||
set(EXPERIMENT_TYPE FaultCoverageExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
#experiment sources
|
||||
set(MY_EXPERIMENT_SRCS
|
||||
experiment.cc
|
||||
experiment.hpp
|
||||
)
|
||||
|
||||
#### include directories ####
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
## build library
|
||||
add_library(${EXPERIMENT_NAME} ${MY_EXPERIMENT_SRCS})
|
||||
135
src/experiments/fault-coverage/experiment.cc
Normal file
135
src/experiments/fault-coverage/experiment.cc
Normal file
@ -0,0 +1,135 @@
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <time.h>
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "sal/bochs/BochsRegister.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
bool FaultCoverageExperiment::run()
|
||||
{
|
||||
// FIXME: This should be translated (-> English)!
|
||||
/*
|
||||
Experimentskizze:
|
||||
- starte Gastsystem
|
||||
- setze Breakpoint auf Beginn der betrachteten Funktion; warte darauf
|
||||
- sichere Zustand
|
||||
- iteriere über alle Register
|
||||
-- iteriere über alle 32 Bit in diesem Register
|
||||
--- iteriere über alle Instruktionsadressen innerhalb der betrachteten Funktion
|
||||
---- setze Breakpoint auf diese Adresse; warte darauf
|
||||
---- flippe Bit x in Register y
|
||||
---- setze Breakpoint auf Verlassen der Funktion; warte darauf
|
||||
---- bei Erreichen des Breakpoint: sichere Funktionsergebnis (irgendein bestimmtes Register)
|
||||
---- lege Ergebnisdaten ab:
|
||||
a) Ergebnis korrekt (im Vergleich zum bekannt korrekten Ergebnis für die Eingabe)
|
||||
b) Ergebnis falsch
|
||||
c) Breakpoint wird nicht erreicht, Timeout (z.B. gefangen in Endlosschleife)
|
||||
d) Trap wurde ausgelöst
|
||||
---- stelle zuvor gesicherten Zustand wieder her
|
||||
*/
|
||||
|
||||
// set breakpoint at start address of the function to be analyzed ("observed");
|
||||
// wait until instruction pointer reaches that address
|
||||
cout << "[FaultCoverageExperiment] Setting up experiment. Allowing to start now." << endl;
|
||||
BPSingleEvent ev_func_start(INST_ADDR_FUNC_START);
|
||||
simulator.addEvent(&ev_func_start);
|
||||
|
||||
cout << "[FaultCoverageExperiment] Waiting for function start address..." << endl;
|
||||
while (simulator.waitAny() != &ev_func_start)
|
||||
;
|
||||
|
||||
// store current state
|
||||
cout << "[FaultCoverageExperiment] Saving state in ./bochs_save_point ..."; cout.flush();
|
||||
simulator.save("./bochs_save_point");
|
||||
cout << "done!" << endl;
|
||||
|
||||
// log the results on std::cout
|
||||
Logger res;
|
||||
cout << "[FaultCoverageExperiment] Logging results on std::cout." << endl;
|
||||
|
||||
RegisterManager& regMan = simulator.getRegisterManager();
|
||||
// iterate over all registers
|
||||
for (RegisterManager::iterator it = regMan.begin(); it != regMan.end(); it++) {
|
||||
Register* pReg = *it; // get a ptr to the current register-object
|
||||
// loop over the 32 bits within this register
|
||||
for (regwidth_t bitnr = 0; bitnr < pReg->getWidth(); ++bitnr) {
|
||||
// loop over all instruction addresses of observed function
|
||||
for (int instr = 0; ; ++instr) {
|
||||
// clear event queues
|
||||
simulator.clearEvents();
|
||||
|
||||
// restore previously saved simulator state
|
||||
cout << "[FaultCoverageExperiment] Restoring previous simulator state..."; cout.flush();
|
||||
simulator.restore("./bochs_save_point");
|
||||
cout << "done!" << endl;
|
||||
|
||||
// breakpoint at function exit
|
||||
BPSingleEvent ev_func_end(INST_ADDR_FUNC_END);
|
||||
simulator.addEvent(&ev_func_end);
|
||||
|
||||
// no need to continue simulation if we want to
|
||||
// inject *now*
|
||||
if (instr > 0) {
|
||||
// breakpoint $instr instructions in the future
|
||||
BPSingleEvent ev_instr_reached(ANY_ADDR);
|
||||
ev_instr_reached.setCounter(instr);
|
||||
simulator.addEvent(&ev_instr_reached);
|
||||
|
||||
// if we reach the exit first, this round is done
|
||||
if (simulator.waitAny() == &ev_func_end)
|
||||
break;
|
||||
}
|
||||
|
||||
// inject bit-flip at bit $bitnr in register $reg
|
||||
regdata_t data = pReg->getData();
|
||||
data ^= 1 << bitnr;
|
||||
pReg->setData(data); // write back data to register
|
||||
|
||||
// catch traps and timeout
|
||||
TrapEvent ev_trap; // any traps
|
||||
simulator.addEvent(&ev_trap);
|
||||
BPSingleEvent ev_timeout(ANY_ADDR);
|
||||
ev_timeout.setCounter(1000);
|
||||
simulator.addEvent(&ev_timeout);
|
||||
|
||||
// wait for function exit, trap or timeout
|
||||
BaseEvent* ev = simulator.waitAny();
|
||||
if (ev == &ev_func_end) {
|
||||
// log result
|
||||
#if BX_SUPPORT_X86_64
|
||||
const size_t expected_size = sizeof(uint32_t)*8;
|
||||
#else
|
||||
const size_t expected_size = sizeof(uint64_t)*8;
|
||||
#endif
|
||||
Register* pCAX = simulator.getRegisterManager().getSetOfType(RT_GP)->getRegister(RID_CAX);
|
||||
assert(expected_size == pCAX->getWidth()); // we assume to get 32(64) bits...
|
||||
regdata_t result = pCAX->getData();
|
||||
res << "[FaultCoverageExperiment] Reg: " << pCAX->getName()
|
||||
<< ", #Bit: " << bitnr << ", Instr-Idx: " << instr
|
||||
<< ", Data: " << result;
|
||||
}
|
||||
else if (ev == &ev_trap)
|
||||
res << "[FaultCoverageExperiment] Reg: " << pReg->getName()
|
||||
<< ", #Bit: " << bitnr << ", Instr-Idx: " << instr
|
||||
<< ", Trap#: " << ev_trap.getTriggerNumber() << " (Trap)";
|
||||
else if (ev == &ev_timeout)
|
||||
res << "[FaultCoverageExperiment] Reg: " << pReg->getName()
|
||||
<< ", #Bit: " << bitnr << ", Instr-Idx: " << instr
|
||||
<< " (Timeout)";
|
||||
else
|
||||
cout << "We've received an unkown event! "
|
||||
<< "What the hell is going on?" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
simulator.clearEvents(this);
|
||||
return true;
|
||||
}
|
||||
25
src/experiments/fault-coverage/experiment.hpp
Normal file
25
src/experiments/fault-coverage/experiment.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef __FAULT_COVERAGE_EXPERIMENT_HPP__
|
||||
#define __FAULT_COVERAGE_EXPERIMENT_HPP__
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
|
||||
#define INST_ADDR_FUNC_START 0x4ae6
|
||||
#define INST_ADDR_FUNC_END 0x4be6
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_EVENT_TRAP) || \
|
||||
!defined(CONFIG_SR_RESTORE) || !defined(CONFIG_SR_SAVE)
|
||||
#error At least one of the following configuration dependencies are not satisfied: \
|
||||
breakpoints, traps, save/restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
class FaultCoverageExperiment : public fail::ExperimentFlow {
|
||||
public:
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __FAULT_COVERAGE_EXPERIMENT_HPP__
|
||||
17
src/experiments/fire-interrupt/CMakeLists.txt
Normal file
17
src/experiments/fire-interrupt/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
set(EXPERIMENT_NAME fire-interrupt)
|
||||
set(EXPERIMENT_TYPE FireInterruptExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
#experiment sources
|
||||
set(MY_EXPERIMENT_SRCS
|
||||
experiment.hpp
|
||||
experiment.cc
|
||||
)
|
||||
|
||||
#### include directories ####
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
## build library
|
||||
add_library(${EXPERIMENT_NAME} ${MY_EXPERIMENT_SRCS})
|
||||
BIN
src/experiments/fire-interrupt/bootdisk.img
Normal file
BIN
src/experiments/fire-interrupt/bootdisk.img
Normal file
Binary file not shown.
47
src/experiments/fire-interrupt/experiment.cc
Normal file
47
src/experiments/fire-interrupt/experiment.cc
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* \brief This experiment demonstrates the fireInterrupt feature.
|
||||
*
|
||||
* The keyboard-interrupts are disabled. So nothing happens if you press a button
|
||||
* on keyboard. Only the pressed button will be stored in keyboard-buffer. With
|
||||
* the fireInterrupt feature keyboard-interrupts are generated manually. The result
|
||||
* is that the keyboard interrupts will be compensated manually. bootdisk.img can
|
||||
* be used as example image (Turbo-Pacman :-) ).
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "sal/Event.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_DISABLE_KEYB_INTERRUPTS) || !defined(CONFIG_FIRE_INTERRUPTS)
|
||||
#error This experiment needs: breakpoints, disabled keyboard interrupts and fire interrupts. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
bool FireInterruptExperiment::run()
|
||||
{
|
||||
Logger log("FireInterrupt", false);
|
||||
log << "experiment start" << endl;
|
||||
|
||||
#if 1
|
||||
while (true) {
|
||||
int j = 0;
|
||||
for (j = 0; j <= 100; j++) {
|
||||
BPSingleEvent mainbp(0x1045f5);
|
||||
simulator.addEventAndWait(&mainbp);
|
||||
}
|
||||
simulator.fireInterrupt(1);
|
||||
}
|
||||
#elif 1
|
||||
simulator.dbgEnableInstrPtrOutput(500);
|
||||
#endif
|
||||
|
||||
simulator.clearEvents(this);
|
||||
return true;
|
||||
}
|
||||
12
src/experiments/fire-interrupt/experiment.hpp
Normal file
12
src/experiments/fire-interrupt/experiment.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __FIREINTERRUPT_EXPERIMENT_HPP__
|
||||
#define __FIREINTERRUPT_EXPERIMENT_HPP__
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
|
||||
class FireInterruptExperiment : public fail::ExperimentFlow {
|
||||
public:
|
||||
FireInterruptExperiment() { }
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __FIREINTERRUPT_EXPERIMENT_HPP__
|
||||
17
src/experiments/hsc-simple/CMakeLists.txt
Normal file
17
src/experiments/hsc-simple/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
set(EXPERIMENT_NAME hsc-simple)
|
||||
set(EXPERIMENT_TYPE HSCSimpleExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
#experiment sources
|
||||
set(MY_EXPERIMENT_SRCS
|
||||
experiment.hpp
|
||||
experiment.cc
|
||||
)
|
||||
|
||||
#### include directories ####
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
## build library
|
||||
add_library(${EXPERIMENT_NAME} ${MY_EXPERIMENT_SRCS})
|
||||
54
src/experiments/hsc-simple/experiment.cc
Normal file
54
src/experiments/hsc-simple/experiment.cc
Normal file
@ -0,0 +1,54 @@
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "sal/bochs/BochsRegister.hpp"
|
||||
#include "sal/Event.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || !defined(CONFIG_SR_SAVE)
|
||||
#error This experiment needs: breakpoints, save, and restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
bool HSCSimpleExperiment::run()
|
||||
{
|
||||
Logger log("HSC", false);
|
||||
log << "experiment start" << endl;
|
||||
|
||||
// do funny things here...
|
||||
#if 1
|
||||
// STEP 1
|
||||
BPSingleEvent mainbp(0x00003c34);
|
||||
simulator.addEventAndWait(&mainbp);
|
||||
log << "breakpoint reached, saving" << endl;
|
||||
simulator.save("hello.state");
|
||||
#elif 0
|
||||
// STEP 2
|
||||
log << "restoring ..." << endl;
|
||||
simulator.restore("hello.state");
|
||||
log << "restored!" << endl;
|
||||
|
||||
log << "waiting for last square() instruction" << endl;
|
||||
BPSingleEvent breakpoint(0x3c9e); // square(x) ret instruction
|
||||
simulator.addEventAndWait(&breakpoint);
|
||||
log << "injecting hellish fault" << endl;
|
||||
// RID_CAX is the RAX register in 64 bit mode and EAX in 32 bit mode:
|
||||
simulator.getRegisterManager().getRegister(RID_CAX)->setData(666);
|
||||
log << "waiting for last main() instruction" << endl;
|
||||
breakpoint.setWatchInstructionPointer(0x3c92);
|
||||
simulator.addEventAndWait(&breakpoint);
|
||||
|
||||
log << "reached" << endl;
|
||||
|
||||
simulator.addEventAndWait(&breakpoint);
|
||||
#endif
|
||||
|
||||
simulator.clearEvents(this);
|
||||
return true;
|
||||
}
|
||||
14
src/experiments/hsc-simple/experiment.hpp
Normal file
14
src/experiments/hsc-simple/experiment.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __HSC_SIMPLE_EXPERIMENT_HPP__
|
||||
#define __HSC_SIMPLE_EXPERIMENT_HPP__
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
|
||||
class HSCSimpleExperiment : public fail::ExperimentFlow
|
||||
{
|
||||
public:
|
||||
HSCSimpleExperiment() { }
|
||||
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __HSC_SIMPLE_EXPERIMENT_HPP__
|
||||
19
src/experiments/instantiate-experiment.ah.in
Normal file
19
src/experiments/instantiate-experiment.ah.in
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __INSTANTIATE_@EXPERIMENT_TYPE@_AH__
|
||||
#define __INSTANTIATE_@EXPERIMENT_TYPE@_AH__
|
||||
|
||||
// FIXME: cmake does not remove these .ah files when the user configures
|
||||
// another experiment (or even makes "clean"). Currently, this needs to be
|
||||
// worked around by manually removing $BUILDDIR/core/experiments/*/*.ah .
|
||||
|
||||
// Make sure your experiment declaration is in experiment.hpp:
|
||||
#include "../experiments/@EXPERIMENT_NAME@/experiment.hpp"
|
||||
#include "sal/SALInst.hpp"
|
||||
|
||||
aspect @EXPERIMENT_TYPE@ExperimentHook {
|
||||
@EXPERIMENT_TYPE@ experiment;
|
||||
advice execution ("void fail::SimulatorController::initExperiments()") : after () {
|
||||
fail::simulator.addFlow(&experiment);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __INSTANTIATE_@EXPERIMENT_TYPE@_AH__
|
||||
31
src/experiments/l4-sys/CMakeLists.txt
Normal file
31
src/experiments/l4-sys/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
set(EXPERIMENT_NAME l4-sys)
|
||||
set(EXPERIMENT_TYPE L4SysExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
l4sys.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})
|
||||
75
src/experiments/l4-sys/campaign.cc
Normal file
75
src/experiments/l4-sys/campaign.cc
Normal file
@ -0,0 +1,75 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "sal/SALConfig.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
char const * const results_csv = "l4sys.csv";
|
||||
|
||||
bool L4SysCampaign::run()
|
||||
{
|
||||
Logger log("L4SysCampaign");
|
||||
|
||||
#if 0
|
||||
ifstream test(results_csv);
|
||||
if (test.is_open()) {
|
||||
log << results_csv << " already exists" << endl;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
ofstream results(results_csv);
|
||||
if (!results.is_open()) {
|
||||
log << "failed to open " << results_csv << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
int count = 0;
|
||||
//iterate over one register
|
||||
for (int bit_offset = 0; bit_offset < 1; ++bit_offset) {
|
||||
for (int instr_offset = 0; instr_offset < L4SYS_NUMINSTR; ++instr_offset) {
|
||||
L4SysExperimentData *d = new L4SysExperimentData;
|
||||
d->msg.set_instr_offset(instr_offset);
|
||||
d->msg.set_bit_offset(bit_offset);
|
||||
d->msg.set_bit_offset(0);
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
campaignmanager.noMoreParameters();
|
||||
log << "done enqueueing parameter sets (" << count << ")." << endl;
|
||||
|
||||
// collect results
|
||||
L4SysExperimentData *res;
|
||||
int rescount = 0;
|
||||
results << "injection_ip,instr_offset,injection_bit,resulttype,resultdata,output,details" << endl;
|
||||
while ((res = static_cast<L4SysExperimentData *>(campaignmanager.getDone()))) {
|
||||
rescount++;
|
||||
|
||||
results << hex
|
||||
<< res->msg.injection_ip() << ","
|
||||
<< dec << res->msg.instr_offset() << ","
|
||||
<< res->msg.bit_offset() << ","
|
||||
<< res->msg.resulttype() << ","
|
||||
<< res->msg.resultdata();
|
||||
if(res->msg.has_output())
|
||||
results << "," << res->msg.output();
|
||||
if(res->msg.has_details())
|
||||
results << "," << res->msg.details();
|
||||
results << endl;
|
||||
delete res;
|
||||
}
|
||||
|
||||
log << "done. sent " << count << " received " << rescount << endl;
|
||||
results.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
19
src/experiments/l4-sys/campaign.hpp
Normal file
19
src/experiments/l4-sys/campaign.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __L4SYS_CAMPAIGN_HPP__
|
||||
#define __L4SYS_CAMPAIGN_HPP__
|
||||
|
||||
#include "cpn/Campaign.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "l4sys.pb.h"
|
||||
|
||||
class L4SysExperimentData : public fail::ExperimentData {
|
||||
public:
|
||||
L4SysProtoMsg msg;
|
||||
L4SysExperimentData() : fail::ExperimentData(&msg) {}
|
||||
};
|
||||
|
||||
class L4SysCampaign : public fail::Campaign {
|
||||
public:
|
||||
virtual bool run();
|
||||
};
|
||||
|
||||
#endif // __L4SYS_CAMPAIGN_HPP__
|
||||
319
src/experiments/l4-sys/experiment.cc
Normal file
319
src/experiments/l4-sys/experiment.cc
Normal file
@ -0,0 +1,319 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 "sal/Event.hpp"
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#include "l4sys.pb.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \
|
||||
!defined(CONFIG_SR_SAVE) || !defined(CONFIG_SUPPRESS_INTERRUPTS) || \
|
||||
!defined(CONFIG_EVENT_TRAP) || !defined(CONFIG_EVENT_IOPORT) || \
|
||||
!defined(CONFIG_EVENT_INTERRUPT)
|
||||
#error This experiment needs: breakpoints, suppressed-interrupts, traps, I/O port and interrupt events, \
|
||||
save, and restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
char const * const state_folder = "l4sys.state";
|
||||
char const * const instr_list_fn = "ip.list";
|
||||
char const * const golden_run_fn = "golden.out";
|
||||
address_t const aspace = 0x01e00000;
|
||||
string output;
|
||||
vector<address_t> instr_list;
|
||||
string golden_run;
|
||||
//the program needs to run 5 times without a fault
|
||||
const unsigned times_run = 5;
|
||||
|
||||
string L4SysExperiment::sanitised(string in_str)
|
||||
{
|
||||
string result;
|
||||
result.reserve(in_str.size());
|
||||
for (string::iterator it = in_str.begin(); it != in_str.end(); it++) {
|
||||
unsigned char_value = static_cast<unsigned>(*it);
|
||||
if (char_value < 0x20 || char_value > 0x7E) {
|
||||
char str_nr[5];
|
||||
sprintf(str_nr, "\\%03o", char_value);
|
||||
result += str_nr;
|
||||
} else {
|
||||
result += *it;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
BaseEvent* L4SysExperiment::waitIOOrOther(bool clear_output)
|
||||
{
|
||||
IOPortEvent ev_ioport(0x3F8, true);
|
||||
BaseEvent* ev = NULL;
|
||||
if (clear_output)
|
||||
output.clear();
|
||||
while (true) {
|
||||
simulator.addEvent(&ev_ioport);
|
||||
ev = simulator.waitAny();
|
||||
simulator.removeEvent(&ev_ioport);
|
||||
if (ev == &ev_ioport) {
|
||||
output += ev_ioport.getData();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ev;
|
||||
}
|
||||
|
||||
bool L4SysExperiment::run()
|
||||
{
|
||||
Logger log("L4Sys", false);
|
||||
BPSingleEvent bp(0, aspace);
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
#ifdef PREPARE_EXPERIMENT
|
||||
|
||||
struct stat teststruct;
|
||||
// STEP 1: run until interesting function starts, and save state
|
||||
if (stat(state_folder, &teststruct) == -1) {
|
||||
bp.setWatchInstructionPointer(L4SYS_FUNC_ENTRY);
|
||||
simulator.addEventAndWait(&bp);
|
||||
|
||||
log << "test function entry reached, saving state" << endl;
|
||||
log << "EIP = " << hex << bp.getTriggerInstructionPointer()
|
||||
<< " or "
|
||||
<< simulator.getRegisterManager().getInstructionPointer()
|
||||
<< endl;
|
||||
simulator.save(state_folder);
|
||||
}
|
||||
|
||||
// STEP 2: determine instructions executed
|
||||
if (stat(instr_list_fn, &teststruct) == -1) {
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(state_folder);
|
||||
log << "EIP = " << hex
|
||||
<< simulator.getRegisterManager().getInstructionPointer()
|
||||
<< endl;
|
||||
|
||||
// make sure the timer interrupt doesn't disturb us
|
||||
simulator.addSuppressedInterrupt(32);
|
||||
|
||||
ofstream instr_list_file(instr_list_fn);
|
||||
instr_list_file << hex;
|
||||
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||
while (bp.getTriggerInstructionPointer() != L4SYS_FUNC_EXIT) {
|
||||
simulator.addEventAndWait(&bp);
|
||||
//short sanity check
|
||||
address_t curr_instr = bp.getTriggerInstructionPointer();
|
||||
assert(
|
||||
curr_instr == simulator.getRegisterManager().getInstructionPointer());
|
||||
instr_list.push_back(curr_instr);
|
||||
instr_list_file << curr_instr << endl;
|
||||
}
|
||||
log << "saving instructions triggered during normal execution" << endl;
|
||||
instr_list_file.close();
|
||||
} else {
|
||||
ifstream instr_list_file(instr_list_fn);
|
||||
instr_list_file >> hex;
|
||||
while (!instr_list_file.eof()) {
|
||||
address_t curr_instr;
|
||||
instr_list_file >> curr_instr;
|
||||
instr_list.push_back(curr_instr);
|
||||
}
|
||||
instr_list_file.close();
|
||||
}
|
||||
|
||||
// STEP 3: determine the output of a "golden run"
|
||||
if (stat(golden_run_fn, &teststruct) == -1) {
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(state_folder);
|
||||
log << "EIP = " << hex
|
||||
<< simulator.getRegisterManager().getInstructionPointer()
|
||||
<< endl;
|
||||
|
||||
// make sure the timer interrupt doesn't disturb us
|
||||
simulator.addSuppressedInterrupt(32);
|
||||
|
||||
ofstream golden_run_file(golden_run_fn);
|
||||
bp.setWatchInstructionPointer(L4SYS_FUNC_EXIT);
|
||||
bp.setCounter(times_run);
|
||||
simulator.addEvent(&bp);
|
||||
BaseEvent* ev = waitIOOrOther(true);
|
||||
if (ev == &bp) {
|
||||
golden_run.assign(output.c_str());
|
||||
golden_run_file << output.c_str();
|
||||
log << "Output successfully logged!" << endl;
|
||||
} else {
|
||||
log
|
||||
<< "Obviously, there is some trouble with the events registered - aborting simulation!"
|
||||
<< endl;
|
||||
golden_run_file.close();
|
||||
simulator.terminate(10);
|
||||
}
|
||||
simulator.clearEvents();
|
||||
bp.setCounter(1);
|
||||
log << "saving output generated during normal execution" << endl;
|
||||
golden_run_file.close();
|
||||
} else {
|
||||
ifstream golden_run_file(golden_run_fn);
|
||||
|
||||
//shamelessly copied from http://stackoverflow.com/questions/2602013/:
|
||||
golden_run_file.seekg(0, ios::end);
|
||||
size_t flen = golden_run_file.tellg();
|
||||
golden_run.reserve(flen);
|
||||
golden_run_file.seekg(0, ios::beg);
|
||||
|
||||
golden_run.assign((istreambuf_iterator<char>(golden_run_file)),
|
||||
istreambuf_iterator<char>());
|
||||
|
||||
golden_run_file.close();
|
||||
|
||||
//the generated output probably has a similar length
|
||||
output.reserve(flen);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// STEP 4: The actual experiment.
|
||||
for (int i = 0; i < 1/*L4SYS_NUMINSTR*/; i++) {
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(state_folder);
|
||||
|
||||
log << "asking job server for experiment parameters" << endl;
|
||||
L4SysExperimentData param;
|
||||
if (!m_jc.getParam(param)) {
|
||||
log << "Dying." << endl;
|
||||
// communicate that we were told to die
|
||||
simulator.terminate(1);
|
||||
}
|
||||
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;
|
||||
|
||||
bp.setWatchInstructionPointer(instr_list[instr_offset]);
|
||||
simulator.addEvent(&bp);
|
||||
//and log the output
|
||||
waitIOOrOther(true);
|
||||
|
||||
// inject
|
||||
RegisterManager& rm = simulator.getRegisterManager();
|
||||
Register *ebx = rm.getRegister(RID_CBX);
|
||||
regdata_t data = ebx->getData();
|
||||
regdata_t newdata = data ^ (1 << bit_offset);
|
||||
ebx->setData(newdata);
|
||||
// note at what IP we did it
|
||||
address_t injection_ip =
|
||||
simulator.getRegisterManager().getInstructionPointer();
|
||||
param.msg.set_injection_ip(injection_ip);
|
||||
log << "inject @ ip " << injection_ip << " (offset " << dec
|
||||
<< instr_offset << ")" << " bit " << bit_offset << ": 0x"
|
||||
<< hex << ((int) data) << " -> 0x" << ((int) newdata)
|
||||
<< endl;
|
||||
|
||||
// sanity check (only works if we're working with an instruction trace)
|
||||
if (injection_ip != instr_list[instr_offset]) {
|
||||
stringstream ss;
|
||||
ss << "SANITY CHECK FAILED: " << injection_ip << " != "
|
||||
<< instr_list[instr_offset] << endl;
|
||||
log << ss.str();
|
||||
param.msg.set_resulttype(param.msg.UNKNOWN);
|
||||
param.msg.set_resultdata(injection_ip);
|
||||
param.msg.set_details(ss.str());
|
||||
|
||||
simulator.clearEvents();
|
||||
m_jc.sendResult(param);
|
||||
continue;
|
||||
}
|
||||
|
||||
// aftermath
|
||||
BPSingleEvent ev_done(L4SYS_FUNC_EXIT, aspace);
|
||||
ev_done.setCounter(times_run);
|
||||
simulator.addEvent(&ev_done);
|
||||
const unsigned instr_run = times_run * L4SYS_NUMINSTR;
|
||||
BPSingleEvent ev_timeout(ANY_ADDR, aspace);
|
||||
ev_timeout.setCounter(instr_run + 3000);
|
||||
simulator.addEvent(&ev_timeout);
|
||||
TrapEvent ev_trap(ANY_TRAP);
|
||||
simulator.addEvent(&ev_trap);
|
||||
InterruptEvent ev_intr(ANY_INTERRUPT);
|
||||
//ten times as many interrupts as instructions justify an exception
|
||||
ev_intr.setCounter(instr_run * 10);
|
||||
simulator.addEvent(&ev_intr);
|
||||
|
||||
//do not discard output recorded so far
|
||||
BaseEvent *ev = waitIOOrOther(false);
|
||||
|
||||
/* copying a string object that contains control sequences
|
||||
* unfortunately does not work with the library I am using,
|
||||
* which is why output is passed on as C string and
|
||||
* the string compare is done on C strings
|
||||
*/
|
||||
if (ev == &ev_done) {
|
||||
if (strcmp(output.c_str(), golden_run.c_str()) == 0) {
|
||||
log << dec << "Result DONE" << endl;
|
||||
param.msg.set_resulttype(param.msg.DONE);
|
||||
} else {
|
||||
log << dec << "Result WRONG" << endl;
|
||||
param.msg.set_resulttype(param.msg.WRONG);
|
||||
param.msg.set_output(sanitised(output.c_str()));
|
||||
}
|
||||
} else if (ev == &ev_timeout) {
|
||||
log << dec << "Result TIMEOUT" << endl;
|
||||
param.msg.set_resulttype(param.msg.TIMEOUT);
|
||||
param.msg.set_resultdata(
|
||||
simulator.getRegisterManager().getInstructionPointer());
|
||||
param.msg.set_output(sanitised(output.c_str()));
|
||||
} else if (ev == &ev_trap) {
|
||||
log << dec << "Result TRAP #" << ev_trap.getTriggerNumber()
|
||||
<< endl;
|
||||
param.msg.set_resulttype(param.msg.TRAP);
|
||||
param.msg.set_resultdata(
|
||||
simulator.getRegisterManager().getInstructionPointer());
|
||||
param.msg.set_output(sanitised(output.c_str()));
|
||||
} else if (ev == &ev_intr) {
|
||||
log << hex << "Result INT FLOOD; Last INT #:"
|
||||
<< ev_intr.getTriggerNumber() << endl;
|
||||
param.msg.set_resulttype(param.msg.INTR);
|
||||
param.msg.set_resultdata(
|
||||
simulator.getRegisterManager().getInstructionPointer());
|
||||
param.msg.set_output(sanitised(output.c_str()));
|
||||
} else {
|
||||
log << dec << "Result WTF?" << endl;
|
||||
param.msg.set_resulttype(param.msg.UNKNOWN);
|
||||
param.msg.set_resultdata(
|
||||
simulator.getRegisterManager().getInstructionPointer());
|
||||
param.msg.set_output(sanitised(output.c_str()));
|
||||
|
||||
stringstream ss;
|
||||
ss << "eventid " << ev << " EIP "
|
||||
<< simulator.getRegisterManager().getInstructionPointer()
|
||||
<< endl;
|
||||
param.msg.set_details(ss.str());
|
||||
}
|
||||
|
||||
simulator.clearEvents();
|
||||
m_jc.sendResult(param);
|
||||
}
|
||||
|
||||
#ifdef HEADLESS_EXPERIMENT
|
||||
simulator.terminate(0);
|
||||
#endif
|
||||
// experiment successfully conducted
|
||||
return true;
|
||||
}
|
||||
23
src/experiments/l4-sys/experiment.hpp
Normal file
23
src/experiments/l4-sys/experiment.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef __L4SYS_EXPERIMENT_HPP__
|
||||
#define __L4SYS_EXPERIMENT_HPP__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
|
||||
class L4SysExperiment : public fail::ExperimentFlow {
|
||||
fail::JobClient m_jc;
|
||||
public:
|
||||
L4SysExperiment() : m_jc("localhost") {}
|
||||
bool run();
|
||||
private:
|
||||
// NOTE: It's good practise to use "const std::string&" as parameter type.
|
||||
// Additionaly, if you don't need the return value to be copied,
|
||||
// return a (const) reference to a class member or a static string-
|
||||
// object.
|
||||
std::string sanitised(std::string in_str);
|
||||
fail::BaseEvent* waitIOOrOther(bool clear_output);
|
||||
};
|
||||
|
||||
#endif // __L4SYS_EXPERIMENT_HPP__
|
||||
12
src/experiments/l4-sys/experimentInfo.hpp
Normal file
12
src/experiments/l4-sys/experimentInfo.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __EXPERIMENT_INFO_HPP__
|
||||
#define __EXPERIMENT_INFO_HPP__
|
||||
|
||||
// FIXME autogenerate this
|
||||
|
||||
#define L4SYS_FUNC_ENTRY 0x1007cd0
|
||||
#define L4SYS_FUNC_EXIT 0x1007d3a
|
||||
#define L4SYS_NUMINSTR 3184
|
||||
//#define HEADLESS_EXPERIMENT
|
||||
#define PREPARE_EXPERIMENT
|
||||
|
||||
#endif // __EXPERIMENT_INFO_HPP__
|
||||
26
src/experiments/l4-sys/l4sys.proto
Normal file
26
src/experiments/l4-sys/l4sys.proto
Normal file
@ -0,0 +1,26 @@
|
||||
message L4SysProtoMsg {
|
||||
// parameters
|
||||
required int32 instr_offset = 1;
|
||||
required int32 bit_offset = 2;
|
||||
|
||||
// results
|
||||
// make these optional to reduce overhead for server->client communication
|
||||
enum ResultType {
|
||||
DONE = 1;
|
||||
TIMEOUT = 2;
|
||||
TRAP = 3;
|
||||
INTR = 4;
|
||||
WRONG = 5;
|
||||
UNKNOWN = 6;
|
||||
}
|
||||
// instruction pointer where injection was done
|
||||
optional uint32 injection_ip = 3;
|
||||
// result type, see above
|
||||
optional ResultType resulttype = 4;
|
||||
// result data, depending on resulttype (see source code)
|
||||
optional uint32 resultdata = 5;
|
||||
// generated output
|
||||
optional string output = 6;
|
||||
// optional textual description of what happened
|
||||
optional string details = 7;
|
||||
}
|
||||
15
src/experiments/l4-sys/main.cc
Normal file
15
src/experiments/l4-sys/main.cc
Normal file
@ -0,0 +1,15 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
L4SysCampaign c;
|
||||
if (fail::campaignmanager.runCampaign(&c)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
31
src/experiments/mh-test-campaign/CMakeLists.txt
Normal file
31
src/experiments/mh-test-campaign/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
set(EXPERIMENT_NAME mh-test-campaign)
|
||||
set(EXPERIMENT_TYPE MHTestExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
MHTest.proto
|
||||
)
|
||||
|
||||
set(MY_CAMPAIGN_SRCS
|
||||
MHTestCampaign.hpp
|
||||
MHTestCampaign.cc
|
||||
experiment.hpp
|
||||
experiment.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 mhcampaign.cc)
|
||||
target_link_libraries(${EXPERIMENT_NAME}-server fail ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY})
|
||||
5
src/experiments/mh-test-campaign/MHTest.proto
Normal file
5
src/experiments/mh-test-campaign/MHTest.proto
Normal file
@ -0,0 +1,5 @@
|
||||
message MHTestData {
|
||||
optional string foo = 1;
|
||||
optional int32 input = 2;
|
||||
optional int32 output = 3;
|
||||
}
|
||||
42
src/experiments/mh-test-campaign/MHTestCampaign.cc
Normal file
42
src/experiments/mh-test-campaign/MHTestCampaign.cc
Normal file
@ -0,0 +1,42 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "MHTestCampaign.hpp"
|
||||
#include <cpn/CampaignManager.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
bool MHTestCampaign::run()
|
||||
{
|
||||
MHExperimentData* datas[m_parameter_count];
|
||||
cout << "[MHTestCampaign] Adding " << m_parameter_count << " values." << endl;
|
||||
for (int i = 1; i <= m_parameter_count; i++) {
|
||||
datas[i] = new MHExperimentData;
|
||||
datas[i]->msg.set_input(i);
|
||||
|
||||
campaignmanager.addParam(datas[i]);
|
||||
usleep(100 * 1000); // 100 ms
|
||||
}
|
||||
campaignmanager.noMoreParameters();
|
||||
// test results.
|
||||
int f;
|
||||
int res = 0;
|
||||
int res2 = 0;
|
||||
MHExperimentData * exp;
|
||||
for (int i = 1; i <= m_parameter_count; i++) {
|
||||
exp = static_cast<MHExperimentData*>(campaignmanager.getDone());
|
||||
f = exp->msg.output();
|
||||
// cout << ">>>>>>>>>>>>>>> Output: " << i << "^2 = " << f << endl;
|
||||
res += f;
|
||||
res2 += (i*i);
|
||||
delete exp;
|
||||
}
|
||||
if (res == res2) {
|
||||
cout << "TEST SUCCESSFUL FINISHED! " << "[" << res << "==" << res2 << "]" << endl;
|
||||
} else {
|
||||
cout << "TEST FAILED!" << " [" << res << "!=" << res2 << "]" << endl;
|
||||
}
|
||||
cout << "thats all... " << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
22
src/experiments/mh-test-campaign/MHTestCampaign.hpp
Normal file
22
src/experiments/mh-test-campaign/MHTestCampaign.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef __MH_TEST_CAMPAIGN_HPP__
|
||||
#define __MH_TEST_CAMPAIGN_HPP__
|
||||
|
||||
#include <cpn/Campaign.hpp>
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "MHTest.pb.h"
|
||||
|
||||
class MHExperimentData : public fail::ExperimentData {
|
||||
public:
|
||||
MHTestData msg;
|
||||
MHExperimentData() : fail::ExperimentData(&msg) { }
|
||||
};
|
||||
|
||||
class MHTestCampaign : public fail::Campaign {
|
||||
private:
|
||||
int m_parameter_count;
|
||||
public:
|
||||
MHTestCampaign(int parametercount) : m_parameter_count(parametercount) { }
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __MH_TEST_CAMPAIGN_HPP__
|
||||
46
src/experiments/mh-test-campaign/experiment.cc
Normal file
46
src/experiments/mh-test-campaign/experiment.cc
Normal file
@ -0,0 +1,46 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "MHTestCampaign.hpp"
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "sal/Register.hpp"
|
||||
#include "sal/Event.hpp"
|
||||
|
||||
// FIXME: You should provide a dependency check here!
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
bool MHTestExperiment::run()
|
||||
{
|
||||
cout << "[MHTestExperiment] Let's go" << endl;
|
||||
#if 0
|
||||
BPSingleEvent mainbp(0x00003c34);
|
||||
simulator.addEventAndWait(&mainbp);
|
||||
cout << "[MHTestExperiment] breakpoint reached, saving" << endl;
|
||||
simulator.save("hello.main");
|
||||
#else
|
||||
MHExperimentData par;
|
||||
if (m_jc.getParam(par)) {
|
||||
int num = par.msg.input();
|
||||
cout << "[MHExperiment] stepping " << num << " instructions" << endl;
|
||||
if (num > 0) {
|
||||
BPSingleEvent nextbp(ANY_ADDR);
|
||||
nextbp.setCounter(num);
|
||||
simulator.addEventAndWait(&nextbp);
|
||||
}
|
||||
address_t instr = simulator.getRegisterManager().getInstructionPointer();
|
||||
cout << "[MHTestExperiment] Reached instruction: "
|
||||
<< hex << instr
|
||||
<< endl;
|
||||
par.msg.set_output(instr);
|
||||
m_jc.sendResult(par);
|
||||
} else {
|
||||
cout << "No data for me? :(" << endl;
|
||||
}
|
||||
#endif
|
||||
simulator.clearEvents(this);
|
||||
|
||||
simulator.terminate();
|
||||
return true;
|
||||
}
|
||||
17
src/experiments/mh-test-campaign/experiment.hpp
Normal file
17
src/experiments/mh-test-campaign/experiment.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef __MH_TEST_EXPERIMENT_HPP__
|
||||
#define __MH_TEST_EXPERIMENT_HPP__
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
|
||||
class MHTestExperiment : public fail::ExperimentFlow {
|
||||
private:
|
||||
fail::JobClient m_jc;
|
||||
public:
|
||||
MHTestExperiment() { }
|
||||
~MHTestExperiment() { }
|
||||
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __MH_TEST_EXPERIMENT_HPP__
|
||||
24
src/experiments/mh-test-campaign/mhcampaign.cc
Normal file
24
src/experiments/mh-test-campaign/mhcampaign.cc
Normal file
@ -0,0 +1,24 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "MHTestCampaign.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char**argv)
|
||||
{
|
||||
int paramcount = 0;
|
||||
if (argc == 2)
|
||||
paramcount = atoi(argv[1]);
|
||||
else
|
||||
paramcount = 10;
|
||||
cout << "Running MHTestCampaign [" << paramcount << " parameter sets]" << endl;
|
||||
|
||||
MHTestCampaign mhc(paramcount);
|
||||
fail::campaignmanager.runCampaign(&mhc);
|
||||
|
||||
cout << "Campaign complete." << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
17
src/experiments/tracing-test/CMakeLists.txt
Normal file
17
src/experiments/tracing-test/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
set(EXPERIMENT_NAME tracing-test)
|
||||
set(EXPERIMENT_TYPE TracingTest)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
set(MY_CAMPAIGN_SRCS
|
||||
experiment.hpp
|
||||
experiment.cc
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
## Build library
|
||||
add_library(${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
|
||||
add_dependencies(${EXPERIMENT_NAME} tracing)
|
||||
78
src/experiments/tracing-test/experiment.cc
Normal file
78
src/experiments/tracing-test/experiment.cc
Normal file
@ -0,0 +1,78 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "sal/Register.hpp"
|
||||
#include "experiment.hpp"
|
||||
#include "../plugins/tracing/TracingPlugin.hpp"
|
||||
|
||||
/*
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
#include <google/protobuf/io/gzip_stream.h>
|
||||
*/
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
bool TracingTest::run()
|
||||
{
|
||||
cout << "[TracingTest] Setting up experiment" << endl;
|
||||
|
||||
#if 0
|
||||
// STEP 1: run until interesting function starts, and save state
|
||||
BPSingleEvent breakpoint(0x00101658);
|
||||
simulator.addEventAndWait(&breakpoint);
|
||||
cout << "[TracingTest] main() reached, saving" << endl;
|
||||
|
||||
simulator.save("state");
|
||||
#else
|
||||
// STEP 2: test tracing plugin
|
||||
simulator.restore("state");
|
||||
|
||||
cout << "[TracingTest] enabling tracing" << endl;
|
||||
|
||||
TracingPlugin tp;
|
||||
ofstream of("trace.pb");
|
||||
tp.setTraceFile(&of);
|
||||
// this must be done *after* configuring the plugin:
|
||||
simulator.addFlow(&tp);
|
||||
|
||||
cout << "[TracingTest] tracing 1000000 instructions" << endl;
|
||||
BPSingleEvent timeout(ANY_ADDR);
|
||||
timeout.setCounter(1000000);
|
||||
simulator.addEvent(&timeout);
|
||||
|
||||
InterruptEvent ie(ANY_INTERRUPT);
|
||||
while (simulator.addEventAndWait(&ie) != &timeout) {
|
||||
cout << "INTERRUPT #" << ie.getTriggerNumber() << "\n";
|
||||
}
|
||||
|
||||
cout << "[TracingTest] tracing finished. (trace.pb)";
|
||||
simulator.removeFlow(&tp);
|
||||
of.close();
|
||||
|
||||
/*
|
||||
// serialize trace to file
|
||||
ofstream of("trace.pb");
|
||||
if (of.fail()) { return false; }
|
||||
trace.SerializeToOstream(&of);
|
||||
of.close();
|
||||
|
||||
// serialize trace to gzip-compressed file
|
||||
int fd = open("trace.pb.gz", O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (!fd) { return false; }
|
||||
google::protobuf::io::FileOutputStream fo(fd);
|
||||
google::protobuf::io::GzipOutputStream::Options options;
|
||||
options.compression_level = 9;
|
||||
google::protobuf::io::GzipOutputStream go(&fo, options);
|
||||
trace.SerializeToZeroCopyStream(&go);
|
||||
go.Close();
|
||||
fo.Close();
|
||||
*/
|
||||
#endif
|
||||
cout << "[TracingTest] Finished." << endl;
|
||||
simulator.clearEvents(this);
|
||||
simulator.terminate();
|
||||
|
||||
return true;
|
||||
}
|
||||
11
src/experiments/tracing-test/experiment.hpp
Normal file
11
src/experiments/tracing-test/experiment.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __TRACING_TEST_HPP__
|
||||
#define __TRACING_TEST_HPP__
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
|
||||
class TracingTest : public fail::ExperimentFlow {
|
||||
public:
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __TRACING_TEST_HPP__
|
||||
34
src/experiments/weather-monitor/CMakeLists.txt
Normal file
34
src/experiments/weather-monitor/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
set(EXPERIMENT_NAME weather-monitor)
|
||||
set(EXPERIMENT_TYPE WeatherMonitorExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
weathermonitor.proto
|
||||
)
|
||||
|
||||
set(MY_CAMPAIGN_SRCS
|
||||
experimentInfo.hpp
|
||||
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})
|
||||
|
||||
add_dependencies(${EXPERIMENT_NAME} tracing)
|
||||
|
||||
## 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})
|
||||
277
src/experiments/weather-monitor/campaign.cc
Normal file
277
src/experiments/weather-monitor/campaign.cc
Normal file
@ -0,0 +1,277 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/MemoryMap.hpp"
|
||||
#include "util/ProtoStream.hpp"
|
||||
|
||||
#include "vptr_map.hpp"
|
||||
|
||||
#include "../plugins/tracing/TracingPlugin.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
char const * const trace_filename = "trace.pb";
|
||||
char const * const results_filename = "weathermonitor.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 {
|
||||
address_t data_address;
|
||||
int instr1, instr2;
|
||||
address_t instr2_absolute;
|
||||
};
|
||||
|
||||
bool WeatherMonitorCampaign::run()
|
||||
{
|
||||
Logger log("Weathermonitor Campaign");
|
||||
|
||||
// non-destructive: due to the CSV header we can always manually recover
|
||||
// from an accident (append mode)
|
||||
ofstream results(results_filename, ios::out | ios::app);
|
||||
if (!results.is_open()) {
|
||||
log << "failed to open " << results_filename << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
// load trace
|
||||
ifstream tracef(trace_filename);
|
||||
if (tracef.fail()) {
|
||||
log << "couldn't open " << trace_filename << endl;
|
||||
return false;
|
||||
}
|
||||
ProtoIStream ps(&tracef);
|
||||
|
||||
// a map of FI data addresses
|
||||
MemoryMap mm;
|
||||
mm.add(WEATHER_DATA_START, WEATHER_DATA_END - WEATHER_DATA_START);
|
||||
|
||||
// set of equivalence classes that need one (rather: eight, one for
|
||||
// each bit in that byte) experiment to determine them all
|
||||
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
|
||||
vector<equivalence_class> ecs_no_effect;
|
||||
|
||||
equivalence_class current_ec;
|
||||
|
||||
// map for efficient access when results come in
|
||||
map<WeatherMonitorExperimentData *, unsigned> experiment_ecs;
|
||||
// experiment count
|
||||
int count = 0;
|
||||
|
||||
// XXX do it the other way around: iterate over trace, search addresses
|
||||
// -> one "open" EC for every address
|
||||
// for every injection address ...
|
||||
for (MemoryMap::iterator it = mm.begin(); it != mm.end(); ++it) {
|
||||
cerr << ".";
|
||||
address_t data_address = *it;
|
||||
current_ec.instr1 = 0;
|
||||
int instr = 0;
|
||||
address_t instr_absolute = 0; // FIXME this one probably should also be recorded ...
|
||||
Trace_Event ev;
|
||||
ps.reset();
|
||||
|
||||
// for every section in the trace between subsequent memory
|
||||
// accesses to that address ...
|
||||
while(ps.getNext(&ev)) {
|
||||
// instruction events just get counted
|
||||
if (!ev.has_memaddr()) {
|
||||
// new instruction
|
||||
instr++;
|
||||
instr_absolute = ev.ip();
|
||||
continue;
|
||||
|
||||
// skip accesses to other data
|
||||
// FIXME again, do it the other way around, and use mm.isMatching()!
|
||||
} else if (ev.memaddr() + ev.width() <= data_address
|
||||
|| ev.memaddr() > data_address) {
|
||||
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.data_address = data_address;
|
||||
|
||||
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);
|
||||
|
||||
// instantly enqueue job: that way the job clients can already
|
||||
// start working in parallel
|
||||
WeatherMonitorExperimentData *d = new WeatherMonitorExperimentData;
|
||||
// we pick the rightmost instruction in that interval
|
||||
d->msg.set_instr_offset(current_ec.instr2);
|
||||
d->msg.set_instr_address(current_ec.instr2_absolute);
|
||||
d->msg.set_mem_addr(current_ec.data_address);
|
||||
|
||||
// store index into ecs_need_experiment
|
||||
experiment_ecs[d] = ecs_need_experiment.size() - 1;
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
} 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.
|
||||
// XXX still true for weathermonitor?
|
||||
current_ec.instr2 = instr - 1;
|
||||
current_ec.instr2_absolute = 0; // unknown
|
||||
current_ec.data_address = data_address;
|
||||
// zero-sized? skip.
|
||||
if (current_ec.instr1 > current_ec.instr2) {
|
||||
continue;
|
||||
}
|
||||
// the run continues after the FI window, so do this experiment
|
||||
// XXX this creates at least one experiment for *every* bit!
|
||||
// fix: full trace, limited FI window
|
||||
//ecs_no_effect.push_back(current_ec);
|
||||
ecs_need_experiment.push_back(current_ec);
|
||||
|
||||
// FIXME copy/paste, encapsulate this:
|
||||
// instantly enqueue job: that way the job clients can already
|
||||
// start working in parallel
|
||||
WeatherMonitorExperimentData *d = new WeatherMonitorExperimentData;
|
||||
// we pick the rightmost instruction in that interval
|
||||
d->msg.set_instr_offset(current_ec.instr2);
|
||||
//d->msg.set_instr_address(current_ec.instr2_absolute); // unknown!
|
||||
d->msg.set_mem_addr(current_ec.data_address);
|
||||
|
||||
// store index into ecs_need_experiment
|
||||
experiment_ecs[d] = ecs_need_experiment.size() - 1;
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
|
||||
campaignmanager.noMoreParameters();
|
||||
log << "done enqueueing parameter sets (" << count << ")." << endl;
|
||||
|
||||
log << "equivalence classes generated:"
|
||||
<< " need_experiment = " << ecs_need_experiment.size()
|
||||
<< " no_effect = " << ecs_no_effect.size() << endl;
|
||||
|
||||
// statistics
|
||||
unsigned long num_dumb_experiments = 0;
|
||||
for (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 (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;
|
||||
|
||||
// CSV header
|
||||
results << "ec_instr1\tec_instr2\tec_instr2_absolute\tec_data_address\tbitnr\tbit_width\tresulttype\tlatest_ip\titer1\titer2\tdetails" << endl;
|
||||
|
||||
// store no-effect "experiment" results
|
||||
for (vector<equivalence_class>::const_iterator it = ecs_no_effect.begin();
|
||||
it != ecs_no_effect.end(); ++it) {
|
||||
results
|
||||
<< (*it).instr1 << "\t"
|
||||
<< (*it).instr2 << "\t"
|
||||
<< (*it).instr2_absolute << "\t" // incorrect in all but one case!
|
||||
<< (*it).data_address << "\t"
|
||||
<< "0\t" // this entry starts at bit 0 ...
|
||||
<< "8\t" // ... and is 8 bits wide
|
||||
<< "1\t"
|
||||
<< "99\t" // dummy value: we didn't do any real experiments
|
||||
<< "0\t"
|
||||
<< (WEATHER_NUMITER_TRACING + WEATHER_NUMITER_AFTER) << "\t\n";
|
||||
}
|
||||
|
||||
// collect results
|
||||
WeatherMonitorExperimentData *res;
|
||||
int rescount = 0;
|
||||
while ((res = static_cast<WeatherMonitorExperimentData *>(campaignmanager.getDone()))) {
|
||||
rescount++;
|
||||
|
||||
map<WeatherMonitorExperimentData *, unsigned>::iterator it =
|
||||
experiment_ecs.find(res);
|
||||
if (it == experiment_ecs.end()) {
|
||||
results << "WTF, didn't find res!" << endl;
|
||||
log << "WTF, didn't find res!" << endl;
|
||||
continue;
|
||||
}
|
||||
equivalence_class &ec = ecs_need_experiment[it->second];
|
||||
|
||||
// sanity check
|
||||
if (ec.instr2 != res->msg.instr_offset()) {
|
||||
results << "ec.instr2 != instr_offset" << endl;
|
||||
log << "ec.instr2 != instr_offset" << endl;
|
||||
}
|
||||
if (res->msg.result_size() != 8) {
|
||||
results << "result_size " << res->msg.result_size() << endl;
|
||||
log << "result_size " << res->msg.result_size() << endl;
|
||||
}
|
||||
|
||||
// one job contains 8 experiments
|
||||
for (int idx = 0; idx < res->msg.result_size(); ++idx) {
|
||||
//results << "ec_instr1\tec_instr2\tec_instr2_absolute\tec_data_address\tbitnr\tresulttype\tlatest_ip\titer1\titer2\tdetails" << endl;
|
||||
results
|
||||
// repeated for all single experiments:
|
||||
<< ec.instr1 << "\t"
|
||||
<< ec.instr2 << "\t"
|
||||
<< ec.instr2_absolute << "\t"
|
||||
<< ec.data_address << "\t"
|
||||
// individual results:
|
||||
<< res->msg.result(idx).bit_offset() << "\t"
|
||||
<< "1\t" // 1 bit wide
|
||||
<< res->msg.result(idx).resulttype() << "\t"
|
||||
<< res->msg.result(idx).latest_ip() << "\t"
|
||||
<< res->msg.result(idx).iter_before_fi() << "\t"
|
||||
<< res->msg.result(idx).iter_after_fi() << "\t"
|
||||
<< res->msg.result(idx).details() << "\n";
|
||||
}
|
||||
//delete res; // currently racy if jobs are reassigned
|
||||
}
|
||||
log << "done. sent " << count << " received " << rescount << endl;
|
||||
results.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
19
src/experiments/weather-monitor/campaign.hpp
Normal file
19
src/experiments/weather-monitor/campaign.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __WEATHERMONITOR_CAMPAIGN_HPP__
|
||||
#define __WEATHERMONITOR_CAMPAIGN_HPP__
|
||||
|
||||
#include "cpn/Campaign.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "weathermonitor.pb.h"
|
||||
|
||||
class WeatherMonitorExperimentData : public fail::ExperimentData {
|
||||
public:
|
||||
WeathermonitorProtoMsg msg;
|
||||
WeatherMonitorExperimentData() : fail::ExperimentData(&msg) {}
|
||||
};
|
||||
|
||||
class WeatherMonitorCampaign : public fail::Campaign {
|
||||
public:
|
||||
virtual bool run();
|
||||
};
|
||||
|
||||
#endif // __WEATHERMONITOR_CAMPAIGN_HPP__
|
||||
315
src/experiments/weather-monitor/experiment.cc
Normal file
315
src/experiments/weather-monitor/experiment.cc
Normal file
@ -0,0 +1,315 @@
|
||||
#include <iostream>
|
||||
|
||||
// getpid
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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 "sal/Event.hpp"
|
||||
|
||||
// you need to have the tracing plugin enabled for this
|
||||
#include "../plugins/tracing/TracingPlugin.hpp"
|
||||
|
||||
#include "vptr_map.hpp"
|
||||
|
||||
#define LOCAL 0
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \
|
||||
!defined(CONFIG_SR_SAVE) || !defined(CONFIG_EVENT_TRAP)
|
||||
#error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
bool WeatherMonitorExperiment::run()
|
||||
{
|
||||
// char const *statename = "bochs.state"; // FIXME: Variable is unused, accidental?
|
||||
Logger log("Weathermonitor", false);
|
||||
BPSingleEvent bp;
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
#if 1
|
||||
// STEP 0: record memory map with vptr addresses
|
||||
GuestEvent g;
|
||||
while (true) {
|
||||
simulator.addEventAndWait(&g);
|
||||
cout << g.getData() << flush;
|
||||
}
|
||||
#elif 0
|
||||
// STEP 1: run until interesting function starts, and save state
|
||||
bp.setWatchInstructionPointer(WEATHER_FUNC_MAIN);
|
||||
simulator.addEventAndWait(&bp);
|
||||
log << "test function entry reached, saving state" << endl;
|
||||
log << "EIP = " << hex << bp.getTriggerInstructionPointer() << endl;
|
||||
simulator.save(statename);
|
||||
assert(bp.getTriggerInstructionPointer() == WEATHER_FUNC_MAIN);
|
||||
assert(simulator.getRegisterManager().getInstructionPointer() == WEATHER_FUNC_MAIN);
|
||||
|
||||
// STEP 2: record trace for fault-space pruning
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(statename);
|
||||
log << "EIP = " << hex << simulator.getRegisterManager().getInstructionPointer() << endl;
|
||||
assert(simulator.getRegisterManager().getInstructionPointer() == WEATHER_FUNC_MAIN);
|
||||
|
||||
log << "enabling tracing" << endl;
|
||||
TracingPlugin tp;
|
||||
|
||||
// TODO: record max(ESP)
|
||||
|
||||
// restrict memory access logging to injection target
|
||||
MemoryMap mm;
|
||||
mm.add(WEATHER_DATA_START, WEATHER_DATA_END - WEATHER_DATA_START);
|
||||
tp.restrictMemoryAddresses(&mm);
|
||||
//tp.setLogIPOnly(true);
|
||||
|
||||
// record trace
|
||||
char const *tracefile = "trace.pb";
|
||||
ofstream of(tracefile);
|
||||
tp.setTraceFile(&of);
|
||||
|
||||
// this must be done *after* configuring the plugin:
|
||||
simulator.addFlow(&tp);
|
||||
|
||||
// trace WEATHER_NUMITER_TRACING measurement loop iterations
|
||||
bp.setWatchInstructionPointer(WEATHER_FUNC_WAIT_END);
|
||||
bp.setCounter(WEATHER_NUMITER_TRACING);
|
||||
simulator.addEvent(&bp);
|
||||
BPSingleEvent ev_count(ANY_ADDR);
|
||||
simulator.addEvent(&ev_count);
|
||||
|
||||
// count instructions
|
||||
// FIXME add SAL functionality for this?
|
||||
int instr_counter = 0;
|
||||
while (simulator.waitAny() == &ev_count) {
|
||||
++instr_counter;
|
||||
simulator.addEvent(&ev_count);
|
||||
}
|
||||
|
||||
log << dec << "tracing finished after " << instr_counter
|
||||
<< " instructions, seeing wait_end " << WEATHER_NUMITER_TRACING << " times" << endl;
|
||||
simulator.removeFlow(&tp);
|
||||
|
||||
// serialize trace to file
|
||||
if (of.fail()) {
|
||||
log << "failed to write " << tracefile << endl;
|
||||
simulator.clearEvents(this); // cleanup
|
||||
return false;
|
||||
}
|
||||
of.close();
|
||||
log << "trace written to " << tracefile << endl;
|
||||
|
||||
// wait another WEATHER_NUMITER_AFTER measurement loop iterations
|
||||
bp.setWatchInstructionPointer(WEATHER_FUNC_WAIT_END);
|
||||
bp.setCounter(WEATHER_NUMITER_AFTER);
|
||||
simulator.addEvent(&bp);
|
||||
|
||||
// count instructions
|
||||
// FIXME add SAL functionality for this?
|
||||
instr_counter = 0;
|
||||
while (simulator.waitAny() == &ev_count) {
|
||||
++instr_counter;
|
||||
simulator.addEvent(&ev_count);
|
||||
}
|
||||
|
||||
log << dec << "experiment finished after " << instr_counter
|
||||
<< " instructions, seeing wait_end " << WEATHER_NUMITER_AFTER << " times" << endl;
|
||||
|
||||
#elif 0
|
||||
// STEP 3: The actual experiment.
|
||||
#if !LOCAL
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
#endif
|
||||
|
||||
// get an experiment parameter set
|
||||
log << "asking job server for experiment parameters" << endl;
|
||||
WeatherMonitorExperimentData param;
|
||||
#if !LOCAL
|
||||
if (!m_jc.getParam(param)) {
|
||||
log << "Dying." << endl;
|
||||
// communicate that we were told to die
|
||||
simulator.terminate(1);
|
||||
}
|
||||
#else
|
||||
// XXX debug
|
||||
param.msg.set_instr_offset(1000);
|
||||
//param.msg.set_instr_address(12345);
|
||||
param.msg.set_mem_addr(0x00103bdc);
|
||||
#endif
|
||||
|
||||
int id = param.getWorkloadID();
|
||||
int instr_offset = param.msg.instr_offset();
|
||||
int mem_addr = param.msg.mem_addr();
|
||||
|
||||
// for each job we're actually doing *8* experiments (one for each bit)
|
||||
for (int bit_offset = 0; bit_offset < 8; ++bit_offset) {
|
||||
// 8 results in one job
|
||||
WeathermonitorProtoMsg_Result *result = param.msg.add_result();
|
||||
result->set_bit_offset(bit_offset);
|
||||
log << dec << "job " << id << " instr " << instr_offset
|
||||
<< " mem " << mem_addr << "+" << bit_offset << endl;
|
||||
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(statename);
|
||||
|
||||
// XXX debug
|
||||
/*
|
||||
stringstream fname;
|
||||
fname << "job." << ::getpid();
|
||||
ofstream job(fname.str().c_str());
|
||||
job << "job " << id << " instr " << instr_offset << " (" << param.msg.instr_address() << ") mem " << mem_addr << "+" << bit_offset << endl;
|
||||
job.close();
|
||||
*/
|
||||
|
||||
// this marks THE END
|
||||
BPSingleEvent ev_end(ANY_ADDR);
|
||||
ev_end.setCounter(WEATHER_NUMINSTR_TRACING + WEATHER_NUMINSTR_AFTER);
|
||||
simulator.addEvent(&ev_end);
|
||||
|
||||
// count loop iterations by counting wait_begin() calls
|
||||
// FIXME would be nice to have a callback API for this as this needs to
|
||||
// be done "in parallel"
|
||||
BPSingleEvent ev_wait_begin(WEATHER_FUNC_WAIT_BEGIN);
|
||||
simulator.addEvent(&ev_wait_begin);
|
||||
int count_loop_iter_before = 0;
|
||||
|
||||
// no need to wait if offset is 0
|
||||
if (instr_offset > 0) {
|
||||
// XXX could be improved with intermediate states (reducing runtime until injection)
|
||||
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||
bp.setCounter(instr_offset);
|
||||
simulator.addEvent(&bp);
|
||||
|
||||
// count loop iterations until FI
|
||||
while (simulator.waitAny() == &ev_wait_begin) {
|
||||
++count_loop_iter_before;
|
||||
simulator.addEvent(&ev_wait_begin);
|
||||
}
|
||||
}
|
||||
|
||||
// --- fault injection ---
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
byte_t data = mm.getByte(mem_addr);
|
||||
byte_t newdata = data ^ (1 << bit_offset);
|
||||
mm.setByte(mem_addr, newdata);
|
||||
// note at what IP we did it
|
||||
int32_t injection_ip = simulator.getRegisterManager().getInstructionPointer();
|
||||
param.msg.set_injection_ip(injection_ip);
|
||||
result->set_iter_before_fi(count_loop_iter_before);
|
||||
log << "fault injected @ ip " << injection_ip
|
||||
<< " 0x" << hex << ((int)data) << " -> 0x" << ((int)newdata) << endl;
|
||||
// sanity check
|
||||
if (param.msg.has_instr_address() &&
|
||||
injection_ip != param.msg.instr_address()) {
|
||||
stringstream ss;
|
||||
ss << "SANITY CHECK FAILED: " << injection_ip
|
||||
<< " != " << param.msg.instr_address();
|
||||
log << ss.str() << endl;
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
result->set_latest_ip(injection_ip);
|
||||
result->set_details(ss.str());
|
||||
result->set_iter_after_fi(0);
|
||||
|
||||
simulator.clearEvents();
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- aftermath ---
|
||||
// possible outcomes:
|
||||
// - trap, "crash"
|
||||
// - jump outside text segment
|
||||
// - (XXX unaligned jump inside text segment)
|
||||
// - (XXX weird instructions?)
|
||||
// - (XXX results displayed?)
|
||||
// - reaches THE END
|
||||
// - error detected, stop
|
||||
// additional info:
|
||||
// - #loop iterations before/after FI
|
||||
// - (XXX "sane" display?)
|
||||
|
||||
// catch traps as "extraordinary" ending
|
||||
TrapEvent ev_trap(ANY_TRAP);
|
||||
simulator.addEvent(&ev_trap);
|
||||
// jump outside text segment
|
||||
BPRangeEvent ev_below_text(ANY_ADDR, WEATHER_TEXT_START - 1);
|
||||
BPRangeEvent ev_beyond_text(WEATHER_TEXT_END + 1, ANY_ADDR);
|
||||
simulator.addEvent(&ev_below_text);
|
||||
simulator.addEvent(&ev_beyond_text);
|
||||
// error detected
|
||||
BPSingleEvent ev_detected(WEATHER_FUNC_VPTR_PANIC);
|
||||
simulator.addEvent(&ev_detected);
|
||||
|
||||
#if LOCAL && 0
|
||||
// XXX debug
|
||||
log << "enabling tracing" << endl;
|
||||
TracingPlugin tp;
|
||||
tp.setLogIPOnly(true);
|
||||
tp.setOstream(&cout);
|
||||
// this must be done *after* configuring the plugin:
|
||||
simulator.addFlow(&tp);
|
||||
#endif
|
||||
|
||||
BaseEvent* ev;
|
||||
|
||||
// count loop iterations
|
||||
int count_loop_iter_after = 0;
|
||||
while ((ev = simulator.waitAny()) == &ev_wait_begin) {
|
||||
++count_loop_iter_after;
|
||||
simulator.addEvent(&ev_wait_begin);
|
||||
}
|
||||
result->set_iter_after_fi(count_loop_iter_after);
|
||||
|
||||
// record latest IP regardless of result
|
||||
result->set_latest_ip(simulator.getRegisterManager().getInstructionPointer());
|
||||
|
||||
if (ev == &ev_end) {
|
||||
log << "Result FINISHED (" << dec
|
||||
<< count_loop_iter_before << "+" << count_loop_iter_after << ")" << endl;
|
||||
result->set_resulttype(result->FINISHED);
|
||||
} else if (ev == &ev_below_text || ev == &ev_beyond_text) {
|
||||
log << "Result OUTSIDE" << endl;
|
||||
result->set_resulttype(result->OUTSIDE);
|
||||
} else if (ev == &ev_trap) {
|
||||
log << dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl;
|
||||
result->set_resulttype(result->TRAP);
|
||||
|
||||
stringstream ss;
|
||||
ss << ev_trap.getTriggerNumber();
|
||||
result->set_details(ss.str());
|
||||
} else if (ev == &ev_detected) {
|
||||
log << dec << "Result DETECTED" << endl;
|
||||
result->set_resulttype(result->DETECTED);
|
||||
} else {
|
||||
log << "Result WTF?" << endl;
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
|
||||
stringstream ss;
|
||||
ss << "eventid " << ev->getId() << " EIP " << simulator.getRegisterManager().getInstructionPointer();
|
||||
result->set_details(ss.str());
|
||||
}
|
||||
}
|
||||
// sanity check: do we have exactly 8 results?
|
||||
if (param.msg.result_size() != 8) {
|
||||
log << "WTF? param.msg.result_size() != 8" << endl;
|
||||
}
|
||||
#if !LOCAL
|
||||
m_jc.sendResult(param);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
// Explicitly terminate, or the simulator will continue to run.
|
||||
simulator.terminate();
|
||||
}
|
||||
14
src/experiments/weather-monitor/experiment.hpp
Normal file
14
src/experiments/weather-monitor/experiment.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __WEATHERMONITOR_EXPERIMENT_HPP__
|
||||
#define __WEATHERMONITOR_EXPERIMENT_HPP__
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
|
||||
class WeatherMonitorExperiment : public fail::ExperimentFlow {
|
||||
fail::JobClient m_jc;
|
||||
public:
|
||||
WeatherMonitorExperiment() : m_jc("ios.cs.tu-dortmund.de") {}
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __WEATHERMONITOR_EXPERIMENT_HPP__
|
||||
121
src/experiments/weather-monitor/experimentInfo.hpp
Normal file
121
src/experiments/weather-monitor/experimentInfo.hpp
Normal file
@ -0,0 +1,121 @@
|
||||
#ifndef __WEATHERMONITOR_EXPERIMENT_INFO_HPP__
|
||||
#define __WEATHERMONITOR_EXPERIMENT_INFO_HPP__
|
||||
|
||||
// autogenerated, don't edit!
|
||||
|
||||
// 0 = vanilla, 1 = guarded, 2 = plausibility
|
||||
#define WEATHERMONITOR_VARIANT 0
|
||||
|
||||
#if WEATHERMONITOR_VARIANT == 0 // without vptr guards
|
||||
|
||||
// main() address:
|
||||
// nm -C vanilla.elf|fgrep main
|
||||
#define WEATHER_FUNC_MAIN 0x00100f70
|
||||
// wait_begin address
|
||||
#define WEATHER_FUNC_WAIT_BEGIN 0x00100f50
|
||||
// wait_end address
|
||||
#define WEATHER_FUNC_WAIT_END 0x00100f60
|
||||
// vptr_panic address (only exists in guarded variant)
|
||||
#define WEATHER_FUNC_VPTR_PANIC 0x99999999
|
||||
// number of main loop iterations to trace
|
||||
// (determines trace length and therefore fault-space width)
|
||||
#define WEATHER_NUMITER_TRACING 4
|
||||
// number of instructions needed for these iterations in golden run (taken from
|
||||
// experiment step #2)
|
||||
#define WEATHER_NUMINSTR_TRACING 20549
|
||||
// number of additional loop iterations for FI experiments (to see whether
|
||||
// everything continues working fine)
|
||||
#define WEATHER_NUMITER_AFTER 2
|
||||
// number of instructions needed for these iterations in golden run (taken from
|
||||
// experiment step #2)
|
||||
#define WEATHER_NUMINSTR_AFTER 10232
|
||||
// data/BSS begin:
|
||||
// nm -C vanilla.elf|fgrep ___DATA_START__
|
||||
#define WEATHER_DATA_START 0x00101814
|
||||
// data/BSS end:
|
||||
// nm -C vanilla.elf|fgrep ___BSS_END__
|
||||
#define WEATHER_DATA_END 0x00103108
|
||||
// text begin:
|
||||
// nm -C vanilla.elf|fgrep ___TEXT_START__
|
||||
#define WEATHER_TEXT_START 0x00100000
|
||||
// text end:
|
||||
// nm -C vanilla.elf|fgrep ___TEXT_END__
|
||||
#define WEATHER_TEXT_END 0x0010165b
|
||||
|
||||
#elif WEATHERMONITOR_VARIANT == 1 // with guards
|
||||
|
||||
// main() address:
|
||||
// nm -C guarded.elf|fgrep main
|
||||
#define WEATHER_FUNC_MAIN 0x00100fc0
|
||||
// wait_begin address
|
||||
#define WEATHER_FUNC_WAIT_BEGIN 0x00100fa0
|
||||
// wait_end address
|
||||
#define WEATHER_FUNC_WAIT_END 0x00100fb0
|
||||
// vptr_panic address (only exists in guarded variant)
|
||||
#define WEATHER_FUNC_VPTR_PANIC 0x00101460
|
||||
// number of main loop iterations to trace
|
||||
// (determines trace length and therefore fault-space width)
|
||||
#define WEATHER_NUMITER_TRACING 4
|
||||
// number of instructions needed for these iterations in golden run (taken from
|
||||
// experiment step #2)
|
||||
#define WEATHER_NUMINSTR_TRACING 20549
|
||||
// number of additional loop iterations for FI experiments (to see whether
|
||||
// everything continues working fine)
|
||||
#define WEATHER_NUMITER_AFTER 2
|
||||
// number of instructions needed for these iterations in golden run (taken from
|
||||
// experiment step #2)
|
||||
#define WEATHER_NUMINSTR_AFTER 10232
|
||||
// data/BSS begin:
|
||||
// nm -C guarded.elf|fgrep ___DATA_START__
|
||||
#define WEATHER_DATA_START 0x00101b94
|
||||
// data/BSS end:
|
||||
// nm -C guarded.elf|fgrep ___BSS_END__
|
||||
#define WEATHER_DATA_END 0x001034b8
|
||||
// text begin:
|
||||
// nm -C guarded.elf|fgrep ___TEXT_START__
|
||||
#define WEATHER_TEXT_START 0x00100000
|
||||
// text end:
|
||||
// nm -C guarded.elf|fgrep ___TEXT_END__
|
||||
#define WEATHER_TEXT_END 0x001019db
|
||||
|
||||
#elif WEATHERMONITOR_VARIANT == 2 // with guards + plausibility check
|
||||
|
||||
// main() address:
|
||||
// nm -C plausibility.elf|fgrep main
|
||||
#define WEATHER_FUNC_MAIN 0x00100fd0
|
||||
// wait_begin address
|
||||
#define WEATHER_FUNC_WAIT_BEGIN 0x00100fb0
|
||||
// wait_end address
|
||||
#define WEATHER_FUNC_WAIT_END 0x00100fc0
|
||||
// vptr_panic address (only exists in guarded variant)
|
||||
#define WEATHER_FUNC_VPTR_PANIC 0x00101500
|
||||
// number of main loop iterations to trace
|
||||
// (determines trace length and therefore fault-space width)
|
||||
#define WEATHER_NUMITER_TRACING 4
|
||||
// number of instructions needed for these iterations in golden run (taken from
|
||||
// experiment step #2)
|
||||
#define WEATHER_NUMINSTR_TRACING 20549
|
||||
// number of additional loop iterations for FI experiments (to see whether
|
||||
// everything continues working fine)
|
||||
#define WEATHER_NUMITER_AFTER 2
|
||||
// number of instructions needed for these iterations in golden run (taken from
|
||||
// experiment step #2)
|
||||
#define WEATHER_NUMINSTR_AFTER 10232
|
||||
// data/BSS begin:
|
||||
// nm -C plausibility.elf|fgrep ___DATA_START__
|
||||
#define WEATHER_DATA_START 0x00101c94
|
||||
// data/BSS end:
|
||||
// nm -C plausibility.elf|fgrep ___BSS_END__
|
||||
#define WEATHER_DATA_END 0x001035b8
|
||||
// text begin:
|
||||
// nm -C plausibility.elf|fgrep ___TEXT_START__
|
||||
#define WEATHER_TEXT_START 0x00100000
|
||||
// text end:
|
||||
// nm -C plausibility.elf|fgrep ___TEXT_END__
|
||||
#define WEATHER_TEXT_END 0x00101adb
|
||||
|
||||
#else
|
||||
#error Unknown WEATHERMONITOR_VARIANT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
80
src/experiments/weather-monitor/experimentInfo.hpp.sh
Executable file
80
src/experiments/weather-monitor/experimentInfo.hpp.sh
Executable file
@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
TARGET=experimentInfo.hpp
|
||||
|
||||
[ ! -e "$1" -o ! -e "$2" -o ! -e "$3" ] && echo "usage: $0 vanilla.elf guarded.elf plausibility.elf" && exit 1
|
||||
|
||||
function addrof() { nm -C $1 | (fgrep "$2" || echo 99999999) | awk '{print $1}'; }
|
||||
|
||||
cat >$TARGET <<EOF
|
||||
#ifndef __WEATHERMONITOR_EXPERIMENT_INFO_HPP__
|
||||
#define __WEATHERMONITOR_EXPERIMENT_INFO_HPP__
|
||||
|
||||
// autogenerated, don't edit!
|
||||
|
||||
// 0 = vanilla, 1 = guarded, 2 = plausibility
|
||||
#define WEATHERMONITOR_VARIANT 0
|
||||
|
||||
#if WEATHERMONITOR_VARIANT == 0 // without vptr guards
|
||||
|
||||
EOF
|
||||
|
||||
function alldefs() {
|
||||
cat <<EOF
|
||||
// main() address:
|
||||
// nm -C $(basename $1)|fgrep main
|
||||
#define WEATHER_FUNC_MAIN 0x`addrof $1 main`
|
||||
// wait_begin address
|
||||
#define WEATHER_FUNC_WAIT_BEGIN 0x`addrof $1 wait_begin`
|
||||
// wait_end address
|
||||
#define WEATHER_FUNC_WAIT_END 0x`addrof $1 wait_end`
|
||||
// vptr_panic address (only exists in guarded variant)
|
||||
#define WEATHER_FUNC_VPTR_PANIC 0x`addrof $1 vptr_panic`
|
||||
// number of main loop iterations to trace
|
||||
// (determines trace length and therefore fault-space width)
|
||||
#define WEATHER_NUMITER_TRACING 4
|
||||
// number of instructions needed for these iterations in golden run (taken from
|
||||
// experiment step #2)
|
||||
#define WEATHER_NUMINSTR_TRACING 20549
|
||||
// number of additional loop iterations for FI experiments (to see whether
|
||||
// everything continues working fine)
|
||||
#define WEATHER_NUMITER_AFTER 2
|
||||
// number of instructions needed for these iterations in golden run (taken from
|
||||
// experiment step #2)
|
||||
#define WEATHER_NUMINSTR_AFTER 10232
|
||||
// data/BSS begin:
|
||||
// nm -C $(basename $1)|fgrep ___DATA_START__
|
||||
#define WEATHER_DATA_START 0x`addrof $1 ___DATA_START__`
|
||||
// data/BSS end:
|
||||
// nm -C $(basename $1)|fgrep ___BSS_END__
|
||||
#define WEATHER_DATA_END 0x`addrof $1 ___BSS_END__`
|
||||
// text begin:
|
||||
// nm -C $(basename $1)|fgrep ___TEXT_START__
|
||||
#define WEATHER_TEXT_START 0x`addrof $1 ___TEXT_START__`
|
||||
// text end:
|
||||
// nm -C $(basename $1)|fgrep ___TEXT_END__
|
||||
#define WEATHER_TEXT_END 0x`addrof $1 ___TEXT_END__`
|
||||
EOF
|
||||
}
|
||||
|
||||
alldefs $1 >>$TARGET
|
||||
cat >>$TARGET <<EOF
|
||||
|
||||
#elif WEATHERMONITOR_VARIANT == 1 // with guards
|
||||
|
||||
EOF
|
||||
alldefs $2 >>$TARGET
|
||||
cat >>$TARGET <<EOF
|
||||
|
||||
#elif WEATHERMONITOR_VARIANT == 2 // with guards + plausibility check
|
||||
|
||||
EOF
|
||||
alldefs $3 >>$TARGET
|
||||
cat >>$TARGET <<EOF
|
||||
|
||||
#else
|
||||
#error Unknown WEATHERMONITOR_VARIANT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
EOF
|
||||
11
src/experiments/weather-monitor/main.cc
Normal file
11
src/experiments/weather-monitor/main.cc
Normal file
@ -0,0 +1,11 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
WeatherMonitorCampaign c;
|
||||
return !fail::campaignmanager.runCampaign(&c);
|
||||
}
|
||||
2
src/experiments/weather-monitor/vptr_map.hpp
Normal file
2
src/experiments/weather-monitor/vptr_map.hpp
Normal file
@ -0,0 +1,2 @@
|
||||
// will be generated from STEP 0 output with region2array.sh
|
||||
// XXX
|
||||
48
src/experiments/weather-monitor/weathermonitor.proto
Normal file
48
src/experiments/weather-monitor/weathermonitor.proto
Normal file
@ -0,0 +1,48 @@
|
||||
message WeathermonitorProtoMsg {
|
||||
// Input: experiment parameters
|
||||
// (client executes 8 experiments, one for each bit at mem_addr)
|
||||
|
||||
// FI at #instructions from experiment start
|
||||
required int32 instr_offset = 1;
|
||||
// the exact IP value at this point in time (from golden run)
|
||||
optional int32 instr_address = 2; // for sanity checks
|
||||
// address of the byte to inject bit-flips
|
||||
required int32 mem_addr = 3;
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
// Output: experiment results
|
||||
|
||||
// IP where we did the injection: for debugging purposes, must be identical
|
||||
// to instr_address
|
||||
optional int32 injection_ip = 4;
|
||||
|
||||
repeated group Result = 5 {
|
||||
// single experiment bit offset
|
||||
required int32 bit_offset = 1;
|
||||
|
||||
// result type:
|
||||
// FINISHED = planned number of instructions were executed
|
||||
// TRAP = premature guest "crash"
|
||||
// OUTSIDE = IP left text segment
|
||||
enum ResultType {
|
||||
FINISHED = 1;
|
||||
TRAP = 2;
|
||||
OUTSIDE = 3;
|
||||
DETECTED = 4;
|
||||
UNKNOWN = 5;
|
||||
}
|
||||
required ResultType resulttype = 2;
|
||||
|
||||
// especially interesting for TRAP/UNKNOWN: latest IP
|
||||
required uint32 latest_ip = 3;
|
||||
|
||||
// number of wmoo measuring/displaying iterations before FI
|
||||
required uint32 iter_before_fi = 4;
|
||||
// number of wmoo measuring/displaying iterations after FI
|
||||
required uint32 iter_after_fi = 5;
|
||||
|
||||
// optional textual description of what happened
|
||||
optional string details = 6;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user