Files
fail/core/experiments/weathermonitor/campaign.cc
hsc e6e04bdab9 wmoo: campaign appends to output CSV
Usually you'll want to remove weathermonitor.csv before running the
campaign.  If you forgot that but desperately need the old or the new
data, you can easily recover these manually (as they're separated by CSV
headers).

git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1115 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
2012-04-19 07:31:44 +00:00

197 lines
6.1 KiB
C++

#include <iostream>
#include <vector>
#include <map>
#include "campaign.hpp"
#include "experimentInfo.hpp"
#include "controller/CampaignManager.hpp"
#include "util/Logger.hpp"
#include "util/MemoryMap.hpp"
#include "vptr_map.hpp"
#include "plugins/tracing/TracingPlugin.hpp"
using namespace fi;
using std::endl;
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 {
sal::address_t data_address;
int instr1, instr2;
sal::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
log << "loading trace ..." << endl;
ifstream tracef(trace_filename);
if (tracef.fail()) {
log << "couldn't open " << trace_filename << endl;
return false;
}
Trace trace;
trace.ParseFromIstream(&tracef);
tracef.close();
log << "... done." << endl;
// 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
std::vector<equivalence_class> ecs_need_experiment;
// set of equivalence classes that need no experiment, because we know
// they'd be identical to the golden run
std::vector<equivalence_class> ecs_no_effect;
equivalence_class current_ec;
// map for efficient access when results come in
std::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) {
std::cerr << ".";
sal::address_t data_address = *it;
current_ec.instr1 = 0;
int instr = 0;
sal::address_t instr_absolute = 0; // FIXME this one probably should also be recorded ...
Trace_Event const *ev;
// for every section in the trace between subsequent memory
// accesses to that address ...
for (int eventnr = 0; eventnr < trace.event_size(); ++eventnr) {
ev = &trace.event(eventnr);
// 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;
fi::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; // 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);
}
fi::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 (std::vector<equivalence_class>::const_iterator it = ecs_need_experiment.begin();
it != ecs_need_experiment.end(); ++it) {
num_dumb_experiments += (*it).instr2 - (*it).instr1 + 1;
}
for (std::vector<equivalence_class>::const_iterator it = ecs_no_effect.begin();
it != ecs_no_effect.end(); ++it) {
num_dumb_experiments += (*it).instr2 - (*it).instr1 + 1;
}
log << "pruning: reduced " << num_dumb_experiments * 8 <<
" experiments to " << ecs_need_experiment.size() * 8 << endl;
return true;
}