Files
fail/src/experiments/dciao-kernelstructs/experiment.cc
Horst Schirmeier 193e5b757e adapt experiments to new restore() behavior
This change adapts several experiments, including the
DatabaseExperiment framework, to the restore() behavior update from
the previous change.  Existing traces should continue to be usable.

This is not tested yet, mainly because I don't have access to most of
the experiment targets / guest systems necessary for testing.  Please
test your own experiments if possible, or at least leave me a note
that you couldn't test it!

Especially the cored-voter/experiment.cc update may be broken, but
maybe the "FISHY" +2 in there was not OK in the first place.

Change-Id: I0c5daeabc8fe6ce0c3ce3e7e13d02195f41340ad
2015-03-18 18:22:21 +01:00

423 lines
14 KiB
C++

#include <iostream>
#include <fstream>
// getpid
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include "experiment.hpp"
#include "sal/SALConfig.hpp"
#include "sal/SALInst.hpp"
#include "sal/Memory.hpp"
#include "sal/Listener.hpp"
#include "sal/bochs/BochsListener.hpp"
#include <string>
#include <vector>
#include <set>
#include "campaign.hpp"
#include "dciao_kernel.pb.h"
using namespace std;
using namespace fail;
#define SAFESTATE (1)
// Check if configuration dependencies are satisfied:
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \
!defined(CONFIG_SR_SAVE)
#error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration.
#endif
unsigned DCIAOKernelStructs::injectBitFlip(address_t data_address, unsigned bitpos){
MemoryManager& mm = simulator.getMemoryManager();
unsigned int value, injectedval;
value = mm.getByte(data_address);
injectedval = value ^ (1 << bitpos);
mm.setByte(data_address, injectedval);
m_log << "INJECTION at: 0x" << hex << setw(2) << setfill('0') << data_address
<< " value: 0x" << setw(2) << setfill('0') << value << " -> 0x" << setw(2) << setfill('0') << injectedval << endl;
return value;
}
void handleEvent(DCIAOKernelProtoMsg_Result& result,
DCIAOKernelProtoMsg_Result_ResultType restype,
simtime_t time_of_fail,
const std::string &msg) {
cout << msg << endl;
result.set_fail_time(time_of_fail);
result.set_resulttype(restype);
result.set_details(msg);
}
// std::string handleMemoryAccessEvent(fail::MemAccessListener& l_mem) {
// stringstream sstr;
// sstr << "mem access (";
// switch (l_mem.getTriggerAccessType()) {
// case MemAccessEvent::MEM_READ:
// sstr << "r";
// break;
// case MemAccessEvent::MEM_WRITE:
// sstr << "w";
// break;
// default: break;
// }
// sstr << ") @ 0x" << hex << l_mem.getTriggerAddress();
// sstr << " ip @ 0x" << hex << l_mem.getTriggerInstructionPointer();
// return sstr.str();
// }
DCIAOKernelStructs::time_markers_t DCIAOKernelStructs::getTimeMarkerList() {
std::ifstream task_activation("task_activation");
if (!task_activation) {
m_log << "could not open `task_activation'" << std::endl;
simulator.terminate(-1);
}
DCIAOKernelStructs::time_markers_t ret;
while (task_activation) {
uint32_t time, at;
task_activation >> time >> at;
time_marker marker;
marker.time = time;
marker.at = at;
if (!task_activation)
break;
ret.push_back(marker);
}
return ret;
}
int DCIAOKernelStructs::time_markers_compare(const time_markers_t &a, const time_markers_t &b) {
int pos = -1;
unsigned max_index = std::min(a.size(), b.size());
for (unsigned i = 0; i < max_index; i++) {
if (a[i].time != b[i].time || a[i].at != b[i].at) {
pos = i;
break;
}
}
if (pos == -1 && (a.size() != b.size())) {
pos = max_index;
}
return pos;
}
bool DCIAOKernelStructs::run() {
MemoryManager& mm = simulator.getMemoryManager();
address_t minimal_ip = INT_MAX; // 1 Mbyte
address_t maximal_ip = 0;
address_t minimal_data = 0x100000; // 1 Mbyte
address_t maximal_data = 0;
for (ElfReader::section_iterator it = m_elf.sec_begin();
it != m_elf.sec_end(); ++it) {
const ElfSymbol &symbol = *it;
std::string prefix(".text");
if (symbol.getName().compare(0, prefix.size(), prefix) == 0) {
minimal_ip = std::min(minimal_ip, symbol.getStart());
maximal_ip = std::max(maximal_ip, symbol.getEnd());
} else {
minimal_data = std::min(minimal_data, symbol.getStart());
maximal_data = std::max(maximal_data, symbol.getEnd());
}
}
std::cout << "Code section from " << hex << minimal_ip << " to " << maximal_ip << std::endl;
std::cout << "Whole programm section from " << hex << minimal_data << " to " << maximal_data << std::endl;
//******* Boot, and store state *******//
m_log << "STARTING EXPERIMENT" << endl;
/* Read time Markers from list */
DCIAOKernelStructs::time_markers_t correct_time_markers = getTimeMarkerList();
m_log << "correct run is done:" << dec << std::endl;
m_log << " time_markers " << correct_time_markers.size() << std::endl;
// //******* Fault injection *******//
const ElfSymbol &s_time_marker_print = m_elf.getSymbol("time_marker_print");
assert(s_time_marker_print.isValid());
BPSingleListener l_time_marker_print(s_time_marker_print.getAddress());
const ElfSymbol &s_fail_virtual_port = m_elf.getSymbol("fail_virtual_port");
assert(s_fail_virtual_port.isValid());
MemAccessListener l_fail_virtual_port(s_fail_virtual_port.getAddress());
BPSingleListener bp;
unsigned executed_jobs = 0;
while (executed_jobs < 25 || m_jc.getNumberOfUndoneJobs() > 0) {
m_log << "asking jobserver for parameters" << endl;
DCIAOKernelExperimentData param;
if(!m_jc.getParam(param)){
m_log << "Dying." << endl; // We were told to die.
simulator.terminate(99);
}
// Get input data from Jobserver
unsigned injection_instr = param.msg.fsppilot().injection_instr();
address_t data_address = param.msg.fsppilot().data_address();
for (int bit_offset = 0; bit_offset < 8; ++bit_offset) {
// 8 results in one job
DCIAOKernelProtoMsg_Result *result = param.msg.add_result();
result->set_bitoffset(bit_offset);
result->set_invalid_memaccess_read(false);
result->set_invalid_memaccess_write(false);
result->set_invalid_jump(false);
m_log << "restoring state" << endl;
// Restore to the image, which starts at address(main)
simulator.restore("state");
executed_jobs ++;
m_log << "Trying to inject @ instr #" << dec << injection_instr << endl;
DCIAOKernelStructs::time_markers_t recorded_time_markers;
if (injection_instr > 0) {
simulator.clearListeners();
// XXX could be improved with intermediate states (reducing runtime until injection)
simulator.addListener(&l_time_marker_print);
bp.setWatchInstructionPointer(ANY_ADDR);
bp.setCounter(injection_instr);
simulator.addListener(&bp);
// Add vport listener
simulator.addListener(&l_fail_virtual_port);
bool inject = true;
while (1) {
fail::BaseListener * listener = simulator.resume();
// finish() before FI?
// Count kernel activations
if (listener == &l_time_marker_print) {
m_log << "experiment reached finish() before FI" << endl;
handleEvent(*result, result->NOINJECTION,
0,
"time_marker reached before injection_instr");
inject = false;
break;
} else if (listener == &l_fail_virtual_port) {
simulator.addListener(&l_fail_virtual_port);
assert(l_fail_virtual_port.getTriggerAccessType() == MemAccessEvent::MEM_WRITE);
unsigned int fail_virtual_port_data;
mm.getBytes(s_fail_virtual_port.getAddress(), 4, &fail_virtual_port_data);
uint32_t at = fail_virtual_port_data >> 16;
uint32_t time = fail_virtual_port_data & 0xffff;
DCIAOKernelStructs::time_marker marker;
assert(at != 0xffff);
marker.at = at;
marker.time = time;
recorded_time_markers.push_back(marker);
continue; // fast forward
} else if (listener == &bp) {
break;
} else {
inject = false;
handleEvent(*result, result->NOINJECTION, 0, "WTF");
break;
}
}
// Next experiment
if (!inject)
continue;
}
// Not a working sanitiy check. Because of instruction
// offsets!
if (param.msg.fsppilot().has_injection_instr_absolute()) {
address_t PC = param.msg.fsppilot().injection_instr_absolute();
if (simulator.getCPU(0).getInstructionPointer() != PC) {
m_log << "Invalid Injection address EIP=0x"
<< std::hex << simulator.getCPU(0).getInstructionPointer()
<< " != injection_instr_absolute=0x" << PC << std::endl;
simulator.terminate(1);
}
}
/// INJECT BITFLIP:
result->set_original_value(injectBitFlip(data_address, bit_offset));
TrapListener l_trap(ANY_TRAP);
unsigned int i_timeout = 100 * 1000;
TimerListener l_timeout(i_timeout); // miliseconds in
// microseconds
TimerListener l_timeout_hard(5 * 1000 * 1000); // miliseconds in
// microseconds
simulator.clearListeners();
simulator.addListener(&l_timeout);
simulator.addListener(&l_timeout_hard);
simulator.addListener(&l_trap);
simulator.addListener(&l_time_marker_print);
// jump outside text segment
BPRangeListener ev_below_text(ANY_ADDR, minimal_ip - 1);
BPRangeListener ev_beyond_text(maximal_ip + 1, ANY_ADDR);
simulator.addListener(&ev_below_text);
simulator.addListener(&ev_beyond_text);
// memory access outside of bound determined in the golden run [lowest_addr, highest_addr]
MemAccessListener ev_mem_low(0x0, MemAccessEvent::MEM_READWRITE);
ev_mem_low.setWatchWidth(minimal_data);
MemAccessListener ev_mem_high(maximal_data + 1, MemAccessEvent::MEM_READWRITE);
ev_mem_high.setWatchWidth(0xFFFFFFFFU - (maximal_data + 1));
simulator.addListener(&ev_mem_low);
simulator.addListener(&ev_mem_high);
// add listener for virtual fail port
simulator.addListener(&l_fail_virtual_port);
// resume and wait for results while counting kernel
// activations
fail::BaseListener* l;
time_t last_activity = time(NULL);
unsigned int last_timer;
while (1) {
l = simulator.resume();
if (l == &l_fail_virtual_port) {
simulator.addListener(l);
/* Virtual Port monitoring */
if(!l_fail_virtual_port.getTriggerAccessType() == MemAccessEvent::MEM_WRITE) {
handleEvent(*result, result->TRAP,
simulator.getTimerTicks(),
"Invalid read from vport");
break;
}
unsigned int fail_virtual_port_data;
mm.getBytes(s_fail_virtual_port.getAddress(), 4, &fail_virtual_port_data);
uint32_t at = fail_virtual_port_data >> 16;
uint32_t timer = fail_virtual_port_data & 0xffff;
if (at == 0xffff) {
/* Called error hook */
handleEvent(*result, result->ERR_ERROR_HOOK,
simulator.getTimerTicks(),
"called error hook");
break;
} else {
/* Reset timeout listener */
if (last_timer != timer) {
last_activity = time(NULL);
m_log << "Reset timeout @" << dec << at << " #" << timer << std::endl;
}
last_timer = timer;
DCIAOKernelStructs::time_marker marker;
marker.at = at;
marker.time = timer;
// m_log << "Marker " << at << " " << time << " " << l_fail_virtual_port.getTriggerInstructionPointer() << std::endl;
recorded_time_markers.push_back(marker);
continue; // till crash
}
} else if (l == &l_time_marker_print) {
m_log << "experiment ran to the end" << std::endl;
int pos = time_markers_compare(correct_time_markers, recorded_time_markers);
if (pos != -1) {
m_log << "Different activation scheme" << std::endl;
m_log << " size " << std::dec << recorded_time_markers.size() << std::endl;
m_log << " at " << std::dec << pos << std::endl;
stringstream sstr;
sstr << "diff after #" << pos;
handleEvent(*result, result->ERR_DIFFERENT_ACTIVATION,
simulator.getTimerTicks(),
sstr.str());
// In case of an error append the activation scheme
for (unsigned i = 0; i < recorded_time_markers.size(); ++i) {
result->add_activation_scheme( recorded_time_markers[i].time );
result->add_activation_scheme( recorded_time_markers[i].at );
m_log << i << " "
<< recorded_time_markers[i].time << " " << recorded_time_markers[i].at << " "
<< correct_time_markers[i].time << " " << correct_time_markers[i].at
<< std::endl;
}
} else {
stringstream sstr;
sstr << "calc done (markers #" << correct_time_markers.size() << ")";
handleEvent(*result, result->OK,
simulator.getTimerTicks(),
sstr.str());
}
// End of experiment
break;
} else if (l == &l_trap) {
stringstream sstr;
sstr << "trap #" << l_trap.getTriggerNumber();
handleEvent(*result, result->TRAP,
simulator.getTimerTicks(),
sstr.str());
break; // EOExperiment
} else if (l == &l_timeout || l == &l_timeout_hard){
time_t current_time = time(NULL);
if ((l == &l_timeout_hard) || (current_time - last_activity) > 4) {
handleEvent(*result, result->TIMEOUT,
simulator.getTimerTicks(),
"timeout: 5 second");
break; // EOExperiment
} else {
static int delta = 0;
if (delta != current_time) {
m_log << "Wait another n = " << (current_time - last_activity) << std::endl;
delta = current_time;
}
// Wait another i_timeout simulator time
simulator.removeListener(&l_timeout);
simulator.addListener(&l_timeout);
assert(l_timeout.getTimeout() == i_timeout);
continue; // till crash
}
} else if (l == &ev_below_text || l == &ev_beyond_text) {
/* Jump outside text segment. We do only note this
event and */
result->set_invalid_jump(true);
continue; // till crash
} else if (l == &ev_mem_low || l == &ev_mem_high) {
/* The target has done an access outside the
memory segment. Note this and continue; */
if (((MemAccessListener *)l)->getTriggerAccessType() == MemAccessEvent::MEM_READ) {
result->set_invalid_memaccess_read(true);
} else {
result->set_invalid_memaccess_write(true);
}
continue; //till crash
} else {
handleEvent(*result, result->UNKNOWN, 0,
"UNKNOWN event");
break; // EOExperiment
}
}
simulator.clearListeners();
} // injection done, continue with next bit
m_jc.sendResult(param);
} // end while (1)
// Explicitly terminate, or the simulator will continue to run.
simulator.terminate(0);
}