Files
fail/src/experiments/dciao-kernelstructs/experiment.cc
Christian Dietrich d307dd2ecb dciao-kernelstructs: reuse sobres experiment for ISORC2014
Differences:

- the task activation order is determined in the faulty experiment as
  well as in the golden run (which is now done by
  fail-generic-tracing) by observing a variable fail_virtual_port.
- There is a panic value read from the fail_virtual_port
- The golden run task activation is determined by giving an extended
  trace to task_activation.py. The script collects all writes to
  fail_virtual_port, and determines the activation from this.

Change-Id: Id401b78933b45a4b2cf031fc0a8b5ac90151ec24
2014-01-27 10:32:09 +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+1);
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);
}