Files
fail/src/experiments/ecos_kernel_test/campaign.cc
2012-11-20 15:01:37 +00:00

526 lines
16 KiB
C++

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include "campaign.hpp"
#include "experimentInfo.hpp"
#include "cpn/CampaignManager.hpp"
#include "util/ProtoStream.hpp"
#include "util/MemoryMap.hpp"
#include "../plugins/tracing/TracingPlugin.hpp"
//#define PRUNING_DEBUG_OUTPUT
using namespace std;
using namespace fail;
const std::string EcosKernelTestCampaign::dir_images("images");
const std::string EcosKernelTestCampaign::dir_prerequisites("prerequisites");
const std::string EcosKernelTestCampaign::dir_results("results");
bool EcosKernelTestCampaign::readMemoryMap(fail::MemoryMap &mm, char const * const filename) {
ifstream file(filename);
if (!file.is_open()) {
cout << "failed to open " << filename << endl;
return false;
}
string buf;
unsigned guest_addr, guest_len;
unsigned count = 0;
while (getline(file, buf)) {
stringstream ss(buf, ios::in);
ss >> guest_addr >> guest_len;
mm.add(guest_addr, guest_len);
count++;
}
file.close();
assert(count > 0);
return (count > 0);
}
bool EcosKernelTestCampaign::writeTraceInfo(unsigned instr_counter, unsigned timeout, unsigned lowest_addr, unsigned highest_addr) {
ofstream ti(filename_traceinfo().c_str(), ios::out);
if (!ti.is_open()) {
cout << "failed to open " << filename_traceinfo() << endl;
return false;
}
ti << instr_counter << endl << timeout << endl << lowest_addr << endl << highest_addr << endl;
ti.flush();
ti.close();
return true;
}
bool EcosKernelTestCampaign::readTraceInfo(unsigned &instr_counter, unsigned &timeout, unsigned &lowest_addr, unsigned &highest_addr,
const std::string& variant, const std::string& benchmark) {
ifstream file(filename_traceinfo(variant, benchmark).c_str());
if (!file.is_open()) {
cout << "failed to open " << filename_traceinfo(variant, benchmark) << endl;
return false;
}
string buf;
unsigned count = 0;
while (getline(file, buf)) {
stringstream ss(buf, ios::in);
switch(count) {
case 0:
ss >> instr_counter;
break;
case 1:
ss >> timeout;
break;
case 2:
ss >> lowest_addr;
break;
case 3:
ss >> highest_addr;
break;
}
count++;
}
file.close();
assert(count == 4);
return (count == 4);
}
std::string EcosKernelTestCampaign::filename_memorymap(const std::string& variant, const std::string& benchmark)
{
if (variant.size() && benchmark.size()) {
return dir_prerequisites + "/" + variant + "-" + benchmark + "-" + "memorymap.txt";
}
return "memorymap.txt";
}
std::string EcosKernelTestCampaign::filename_state(const std::string& variant, const std::string& benchmark)
{
if (variant.size() && benchmark.size()) {
return dir_prerequisites + "/" + variant + "-" + benchmark + "-" + "state";
}
return "state";
}
std::string EcosKernelTestCampaign::filename_trace(const std::string& variant, const std::string& benchmark)
{
if (variant.size() && benchmark.size()) {
return dir_prerequisites + "/" + variant + "-" + benchmark + "-" + "trace.tc";
}
return "trace.tc";
}
std::string EcosKernelTestCampaign::filename_traceinfo(const std::string& variant, const std::string& benchmark)
{
if (variant.size() && benchmark.size()) {
return dir_prerequisites + "/" + variant + "-" + benchmark + "-" + "traceinfo.txt";
}
return "traceinfo.txt";
}
std::string EcosKernelTestCampaign::filename_results(const std::string& variant, const std::string& benchmark)
{
if (variant.size() && benchmark.size()) {
return dir_results + "/" + variant + "-" + benchmark + "-" + "results.csv";
}
return "results.csv";
}
std::string EcosKernelTestCampaign::filename_elf(const std::string& variant, const std::string& benchmark)
{
if (variant.size() && benchmark.size()) {
return dir_images + "/" + variant + "/" + benchmark + ".elf";
}
return "kernel.elf";
}
// equivalence class type: addr, [i1, i2]
// addr: byte to inject a bit-flip into
// [i1, i2]: interval of instruction numbers, counted from experiment
// begin
typedef std::map<address_t, int> AddrLastaccessMap;
char const *variants[] = {
"bitmap_vanilla",
"bitmap_SUM+DMR",
"bitmap_CRC",
"bitmap_CRC+DMR",
"bitmap_TMR",
// "bitmap_Hamming"
0
};
char const *benchmarks[] = {
"bin_sem0", "bin_sem1", "bin_sem2", "bin_sem3", "clock1", "clockcnv",
"clocktruth", "cnt_sem1", "except1", "flag1", "kill", "mqueue1", "mutex1",
"mutex2", "mutex3", "release", "sched1", "sync2", "sync3", "thread0",
"thread1", "thread2",
0
};
bool EcosKernelTestCampaign::run()
{
m_log << "startup" << endl;
if (!init_results()) {
return false;
}
for (int variant_nr = 0; variants[variant_nr]; ++variant_nr) {
char const *variant = variants[variant_nr];
for (int benchmark_nr = 0; benchmarks[benchmark_nr]; ++benchmark_nr) {
char const *benchmark = benchmarks[benchmark_nr];
// local copies of experiment/job count (to calculate differences)
int local_count_exp = count_exp, local_count_exp_jobs = count_exp_jobs,
local_count_known = count_known, local_count_known_jobs = count_known_jobs;
// load trace
ifstream tracef(filename_trace(variant, benchmark).c_str());
if (tracef.fail()) {
m_log << "couldn't open " << filename_trace(variant, benchmark) << endl;
return false;
}
ProtoIStream ps(&tracef);
// read trace info
unsigned instr_counter, estimated_timeout, lowest_addr, highest_addr;
EcosKernelTestCampaign::readTraceInfo(instr_counter,
estimated_timeout, lowest_addr, highest_addr, variant, benchmark);
// a map of addresses of ECC protected objects
MemoryMap mm;
EcosKernelTestCampaign::readMemoryMap(mm,
filename_memorymap(variant, benchmark).c_str());
// map for keeping one "open" EC for every address
// (maps injection data address => equivalence class)
AddrLastaccessMap open_ecs;
// instruction counter within trace
unsigned instr = 0;
// "rightmost" instr where we did a FI experiment
unsigned instr_rightmost = 0;
// fill open_ecs with one EC for every address
for (MemoryMap::iterator it = mm.begin(); it != mm.end(); ++it) {
open_ecs[*it] = instr;
}
// absolute address of current trace instruction
address_t instr_absolute = 0;
Trace_Event ev;
// for every event in the trace ...
while (ps.getNext(&ev) && instr < instr_counter) {
// instruction events just get counted
if (!ev.has_memaddr()) {
// new instruction
instr++;
instr_absolute = ev.ip();
continue;
}
// for each single byte in this memory access ...
for (address_t data_address = ev.memaddr();
data_address < ev.memaddr() + ev.width();
++data_address) {
// skip accesses to data outside our map of interesting addresses
AddrLastaccessMap::iterator lastuse_it;
if ((lastuse_it = open_ecs.find(data_address)) == open_ecs.end()) {
continue;
}
int instr1 = lastuse_it->second;
int instr2 = instr;
// skip zero-sized intervals: these can occur when an instruction
// accesses a memory location more than once (e.g., INC, CMPXCHG)
if (instr1 > instr2) {
continue;
}
// we now have an interval-terminating R/W event to the memaddr
// we're currently looking at; the EC is defined by
// data_address [instr1, instr2] (instr_absolute)
if (ev.accesstype() == ev.READ) {
// a sequence ending with READ: we need to do one experiment to
// cover it completely
add_experiment_ec(variant, benchmark, data_address, instr1, instr2, instr_absolute);
instr_rightmost = instr2;
} else if (ev.accesstype() == ev.WRITE) {
// a sequence ending with WRITE: an injection anywhere here
// would have no effect.
add_known_ec(variant, benchmark, data_address, instr1, instr2, instr_absolute);
} else {
m_log << "WAT" << endl;
}
// next interval must start at next instruction; the aforementioned
// skipping mechanism wouldn't work otherwise
lastuse_it->second = instr2 + 1;
}
}
// close all open intervals (right end of the fault-space)
for (AddrLastaccessMap::iterator lastuse_it = open_ecs.begin();
lastuse_it != open_ecs.end(); ++lastuse_it) {
address_t data_address = lastuse_it->first;
int instr1 = lastuse_it->second;
#if 0
// 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.
int instr2 = instr - 1;
#else
// EcosKernelTestCampaign only variant: fault space ends with the last FI experiment
int instr2 = instr_rightmost;
#endif
int instr_absolute = 0; // unknown
// zero-sized? skip.
if (instr1 > instr2) {
continue;
}
#if 0
// 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_need_experiment.push_back(current_ec);
add_experiment_ec(variant, benchmark, data_address, instr1, instr2, instr_absolute);
#else
// as the experiment ends, this byte is a "don't care":
add_known_ec(variant, benchmark, data_address, instr1, instr2, instr_absolute);
#endif
}
// conserve some memory
open_ecs.clear();
// progress report
m_log << variant << "/" << benchmark
<< " exp " << (count_exp - local_count_exp) << " (" << (count_exp_jobs - local_count_exp_jobs) << " jobs)"
<< " known " << (count_known - local_count_known) << " (" << (count_known_jobs - local_count_known_jobs) << " jobs)"
<< " faultspace cutoff @ " << instr_rightmost << " out of " << instr
<< endl;
}
}
available_results.clear();
campaignmanager.noMoreParameters();
m_log << "total"
<< " exp " << count_exp << " (" << count_exp_jobs << " jobs)"
<< " known " << count_known << " (" << count_known_jobs << " jobs)"
<< endl;
// collect results
EcosKernelTestExperimentData *res;
while ((res = static_cast<EcosKernelTestExperimentData *>(campaignmanager.getDone()))) {
// sanity check
if (res->msg.result_size() != 8) {
m_log << "wtf, result_size = " << res->msg.result_size() << endl;
continue;
}
EcosKernelTestProtoMsg_Result const *prev_singleres = 0;
int first_bit = 0, bit_width = 0;
// one job contains 8 experiments
for (int idx = 0; idx < res->msg.result_size(); ++idx) {
EcosKernelTestProtoMsg_Result const *cur_singleres = &res->msg.result(idx);
if (!prev_singleres) {
prev_singleres = cur_singleres;
first_bit = cur_singleres->bit_offset();
bit_width = 1;
continue;
}
// compatible? merge.
if (cur_singleres->bit_offset() == first_bit + bit_width // neighbor?
&& prev_singleres->resulttype() == cur_singleres->resulttype()
&& prev_singleres->latest_ip() == cur_singleres->latest_ip()
&& prev_singleres->ecos_test_result() == cur_singleres->ecos_test_result()
&& prev_singleres->error_corrected() == cur_singleres->error_corrected()
&& prev_singleres->details() == cur_singleres->details()) {
bit_width++;
continue;
}
add_result(res->msg.variant(), res->msg.benchmark(), res->msg.instr1_offset(),
res->msg.instr2_offset(), res->msg.instr2_address(), res->msg.mem_addr(),
first_bit, bit_width, prev_singleres->resulttype(), prev_singleres->ecos_test_result(),
prev_singleres->latest_ip(), prev_singleres->error_corrected(), prev_singleres->details(),
res->msg.runtime() * bit_width / 8.0);
prev_singleres = cur_singleres;
first_bit = cur_singleres->bit_offset();
bit_width = 1;
}
add_result(res->msg.variant(), res->msg.benchmark(), res->msg.instr1_offset(),
res->msg.instr2_offset(), res->msg.instr2_address(), res->msg.mem_addr(),
first_bit, bit_width, prev_singleres->resulttype(), prev_singleres->ecos_test_result(),
prev_singleres->latest_ip(), prev_singleres->error_corrected(), prev_singleres->details(),
res->msg.runtime() * bit_width / 8.0);
delete res;
}
finalize_results();
m_log << "done." << endl;
return true;
}
bool EcosKernelTestCampaign::add_experiment_ec(const std::string& variant, const std::string& benchmark,
address_t data_address, int instr1, int instr2, address_t instr_absolute)
{
if (check_available(variant, benchmark, data_address, instr2)) {
return false;
}
count_exp_jobs++;
count_exp += 8;
// enqueue job
#if 1
EcosKernelTestExperimentData *d = new EcosKernelTestExperimentData;
d->msg.set_variant(variant);
d->msg.set_benchmark(benchmark);
d->msg.set_instr1_offset(instr1);
d->msg.set_instr2_offset(instr2);
d->msg.set_instr2_address(instr_absolute);
d->msg.set_mem_addr(data_address);
campaignmanager.addParam(d);
#endif
return true;
}
bool EcosKernelTestCampaign::add_known_ec(const std::string& variant, const std::string& benchmark,
address_t data_address, int instr1, int instr2, address_t instr_absolute)
{
if (check_available(variant, benchmark, data_address, instr2)) {
return false;
}
count_known_jobs++;
count_known += 8;
#if 1
add_result(variant, benchmark, instr1, instr2, instr_absolute, data_address,
0, 8, // bitnr, bit_width
1, // resulttype
1, // ecos_test_result
99, // latest_ip
0, // error_corrected
"", // details
0 // runtime
);
#endif
return true;
}
bool EcosKernelTestCampaign::init_results()
{
// read already existing results
bool file_exists = false;
ifstream oldresults(filename_results().c_str(), ios::in);
if (oldresults.is_open()) {
file_exists = true;
char buf[16*1024];
std::string variant, benchmark;
unsigned ignore;
int instr2;
address_t data_address;
int bit_width;
int rowcount = 0;
int expcount = 0;
m_log << "scanning existing results ..." << endl;
while (oldresults.getline(buf, sizeof(buf)).good()) {
stringstream ss;
ss << buf;
ss >> hex >> variant >> benchmark >> ignore >> instr2 >> ignore
>> data_address >> ignore >> bit_width;
if (ss.fail()) {
continue;
}
++rowcount;
expcount += bit_width;
// TODO: sanity check (duplicates?)
available_results
[AvailableResultMap::key_type(variant, benchmark)]
[data_address].insert(instr2);
}
m_log << "found " << dec << expcount << " existing experiment results ("
<< rowcount << " CSV rows)" << endl;
oldresults.close();
}
// non-destructive: due to the CSV header we can always manually recover
// from an accident (append mode)
resultstream.open(filename_results().c_str(), ios::out | ios::app);
if (!resultstream.is_open()) {
m_log << "failed to open " << filename_results() << endl;
return false;
}
// only write CSV header if file didn't exist before
if (!file_exists) {
resultstream << "variant\tbenchmark\tec_instr1\tec_instr2\t"
"ec_instr2_absolute\tec_data_address\tbitnr\tbit_width\t"
"resulttype\tecos_test_result\tlatest_ip\t"
"error_corrected\tdetails\truntime" << endl;
}
return true;
}
bool EcosKernelTestCampaign::check_available(const std::string& variant, const std::string& benchmark,
address_t data_address, int instr2)
{
AvailableResultMap::const_iterator it_variant =
available_results.find(AvailableResultMap::key_type(variant, benchmark));
if (it_variant == available_results.end()) {
return false;
}
AvailableResultMap::mapped_type::const_iterator it_address =
it_variant->second.find(data_address);
if (it_address == it_variant->second.end()) {
return false;
}
AvailableResultMap::mapped_type::mapped_type::const_iterator it_instr =
it_address->second.find(instr2);
if (it_instr == it_address->second.end()) {
return false;
}
return true;
}
void EcosKernelTestCampaign::add_result(const std::string& variant, const std::string& benchmark,
int instr1, int instr2, address_t instr2_absolute, address_t ec_data_address,
int bitnr, int bit_width, int resulttype, int ecos_test_result, address_t latest_ip,
int error_corrected, const std::string& details, float runtime)
{
resultstream << hex
<< variant << "\t"
<< benchmark << "\t"
<< instr1 << "\t"
<< instr2 << "\t"
<< instr2_absolute << "\t"
<< ec_data_address << "\t"
<< bitnr << "\t"
<< bit_width << "\t"
<< resulttype << "\t"
<< ecos_test_result << "\t"
<< latest_ip << "\t"
<< error_corrected << "\t"
<< details << "\t"
<< runtime << "\n";
//resultstream.flush(); // for debugging purposes
}
void EcosKernelTestCampaign::finalize_results()
{
resultstream.close();
}