Merge remote-tracking branch 'origin/master' into sampling-wip
Change-Id: Iae5c02be5801d75e8adc55222ccb35c559f7ebf4
This commit is contained in:
@ -7,7 +7,9 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/core)
|
||||
# protoc imports refer to the same root path. Otherwise the generated
|
||||
# protoc headers are not comptabile.
|
||||
SET(PROTOBUF_GENERATE_CPP_APPEND_PATH FALSE)
|
||||
SET(PROTOBUF_IMPORT_DIRS "/usr/include;${CMAKE_CURRENT_SOURCE_DIR}/core/comm")
|
||||
SET(PROTOBUF_IMPORT_DIRS "/usr/include;${CMAKE_CURRENT_SOURCE_DIR}/core/comm"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/core/comm")
|
||||
|
||||
|
||||
# Note: CMAKE_CURRENT_BINARY_DIR is needed to find "FailConfig.hpp", which
|
||||
# is generated by CMake from config/FailConfig.hpp.in and stored in
|
||||
|
||||
@ -30,9 +30,11 @@ find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Let protobuf compiler find defined file (DatabaseCampaignMessage) in binary tree
|
||||
set(PROTOBUF_IMPORT_DIRS ${PROTOBUF_IMPORT_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
# The PROTOBUF_IMPORT_DIRS is set in src/CMakeLists.txt
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${PROTOS})
|
||||
|
||||
add_custom_target(fail-protoc
|
||||
DEPENDS ${PROTO_SRCS} ${PROTO_HDRS}
|
||||
)
|
||||
add_library(fail-comm ${SRCS} ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
target_link_libraries(fail-comm ${PROTOBUF_LIBRARY})
|
||||
|
||||
@ -21,4 +21,10 @@ message DatabaseCampaignMessage {
|
||||
required string benchmark = 9 [(sql_ignore) = true];
|
||||
|
||||
required InjectionPointMessage injection_point = 10 [(sql_ignore) = true];
|
||||
}
|
||||
}
|
||||
|
||||
message DatabaseExperimentMessage {
|
||||
required uint32 bitoffset = 1 [(sql_primary_key) = true];
|
||||
required uint32 original_value = 2;
|
||||
}
|
||||
|
||||
|
||||
@ -2,31 +2,31 @@
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/VariantConfig.hpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/VariantConfig.hpp)
|
||||
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS "Event source: Breakpoints" OFF)
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS_RANGE "Event source: Range Breakpoints" OFF)
|
||||
OPTION(CONFIG_EVENT_MEMREAD "Event source: Memory reads" OFF)
|
||||
OPTION(CONFIG_EVENT_MEMWRITE "Event source: Memory writes" OFF)
|
||||
OPTION(CONFIG_EVENT_GUESTSYS "Event source: Outbound guest-system communication" OFF)
|
||||
OPTION(CONFIG_EVENT_IOPORT "Event source: I/O port communication" OFF)
|
||||
OPTION(CONFIG_EVENT_INTERRUPT "Event source: Interrupts" OFF)
|
||||
OPTION(CONFIG_EVENT_TRAP "Event source: Traps" OFF)
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS "Event source: Breakpoints" ON)
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS_RANGE "Event source: Range Breakpoints" ON)
|
||||
OPTION(CONFIG_EVENT_MEMREAD "Event source: Memory reads" ON)
|
||||
OPTION(CONFIG_EVENT_MEMWRITE "Event source: Memory writes" ON)
|
||||
OPTION(CONFIG_EVENT_GUESTSYS "Event source: Outbound guest-system communication" ON)
|
||||
OPTION(CONFIG_EVENT_IOPORT "Event source: I/O port communication" ON)
|
||||
OPTION(CONFIG_EVENT_INTERRUPT "Event source: Interrupts" ON)
|
||||
OPTION(CONFIG_EVENT_TRAP "Event source: Traps" ON)
|
||||
OPTION(CONFIG_EVENT_JUMP "Event source: Branch instructions" OFF)
|
||||
OPTION(CONFIG_SR_RESTORE "Target backend: State restore" OFF)
|
||||
OPTION(CONFIG_SR_SAVE "Target backend: State saving" OFF)
|
||||
OPTION(CONFIG_SR_REBOOT "Target backend: Reboot" OFF)
|
||||
OPTION(CONFIG_SR_RESTORE "Target backend: State restore" ON)
|
||||
OPTION(CONFIG_SR_SAVE "Target backend: State saving" ON)
|
||||
OPTION(CONFIG_SR_REBOOT "Target backend: Reboot" ON)
|
||||
OPTION(CONFIG_BOCHS_NON_VERBOSE "Misc: Reduced verbosity (a lot faster for large campaigns)" OFF)
|
||||
OPTION(CONFIG_BOCHS_NO_ABORT "Misc: Do not abort or ask the user in case the simulator stumbles on unexpected events (e.g., panics)" ON)
|
||||
OPTION(CONFIG_BOCHS_COMPRESS_STATE "Misc: Reduce Bochs save/restore size by compressing memory images" OFF)
|
||||
OPTION(CONFIG_SUPPRESS_INTERRUPTS "Target backend: Suppress interrupts" OFF)
|
||||
OPTION(CONFIG_FIRE_INTERRUPTS "Target backend: Fire interrupts" OFF)
|
||||
OPTION(CONFIG_BOCHS_COMPRESS_STATE "Misc: Reduce Bochs save/restore size by compressing memory images" ON)
|
||||
OPTION(CONFIG_SUPPRESS_INTERRUPTS "Target backend: Suppress interrupts" ON)
|
||||
OPTION(CONFIG_FIRE_INTERRUPTS "Target backend: Fire interrupts" ON)
|
||||
OPTION(CONFIG_DISABLE_KEYB_INTERRUPTS "Target backend: Suppress keyboard interrupts" OFF)
|
||||
OPTION(SERVER_PERFORMANCE_MEASURE "Performance measurement in job-server" OFF)
|
||||
OPTION(CONFIG_FAST_BREAKPOINTS "Enable fast breakpoints (requires breakpoint events to be enabled)" OFF)
|
||||
OPTION(CONFIG_FAST_WATCHPOINTS "Enable fast watchpoints (requires memory access events to be enabled)" OFF)
|
||||
OPTION(CONFIG_FAST_BREAKPOINTS "Enable fast breakpoints (requires breakpoint events to be enabled)" ON)
|
||||
OPTION(CONFIG_FAST_WATCHPOINTS "Enable fast watchpoints (requires memory access events to be enabled)" ON)
|
||||
OPTION(CONFIG_INJECTIONPOINT_HOPS "Enable hop chain trace navigation to injection point" OFF)
|
||||
SET(SERVER_COMM_HOSTNAME "localhost" CACHE STRING "Job-server hostname or IP")
|
||||
SET(SERVER_COMM_TCP_PORT "1111" CACHE STRING "Job-server TCP port")
|
||||
SET(SERVER_OUT_QUEUE_SIZE "0" CACHE STRING "Queue size for outbound jobs (0 = unlimited)")
|
||||
SET(SERVER_OUT_QUEUE_SIZE "0" CACHE STRING "Queue size for outbound jobs (0 = unlimited)")
|
||||
SET(SERVER_PERF_LOG_PATH "perf.log" CACHE STRING "A file name for storing the server's performance log (CSV)")
|
||||
SET(SERVER_PERF_STEPPING_SEC "1" CACHE STRING "Stepping of performance measurements in seconds")
|
||||
SET(CLIENT_RAND_BACKOFF_TSTART "3" CACHE STRING "Lower limit of client's backoff phase in seconds")
|
||||
|
||||
@ -114,12 +114,15 @@ bool DatabaseCampaign::run() {
|
||||
boost::thread collect_thread(&DatabaseCampaign::collect_result_thread, this);
|
||||
#endif
|
||||
|
||||
load_completed_pilots();
|
||||
|
||||
std::vector<Database::Variant> variantlist =
|
||||
db->get_variants(variants, variants_exclude, benchmarks, benchmarks_exclude);
|
||||
|
||||
// Which Pilots were already processed?
|
||||
load_completed_pilots(variantlist);
|
||||
|
||||
for (std::vector<Database::Variant>::const_iterator it = variantlist.begin();
|
||||
it != variantlist.end(); ++it) {
|
||||
// Push all other variants to the queue
|
||||
if (!run_variant(*it)) {
|
||||
log_send << "run_variant failed for " << it->variant << "/" << it->benchmark <<std::endl;
|
||||
return false;
|
||||
@ -165,7 +168,7 @@ void DatabaseCampaign::collect_result_thread() {
|
||||
|
||||
bool DatabaseCampaign::run_variant(Database::Variant variant) {
|
||||
/* Gather jobs */
|
||||
int experiment_count;
|
||||
unsigned long experiment_count;
|
||||
std::stringstream ss;
|
||||
std::string sql_select = "SELECT p.id, p.injection_instr, p.injection_instr_absolute, p.data_address, p.data_width, t.instr1, t.instr2 ";
|
||||
ss << " FROM fsppilot p "
|
||||
@ -252,14 +255,29 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) {
|
||||
|
||||
}
|
||||
|
||||
void DatabaseCampaign::load_completed_pilots()
|
||||
void DatabaseCampaign::load_completed_pilots(std::vector<Database::Variant> &variants)
|
||||
{
|
||||
// If no variants were given, do nothing
|
||||
if (variants.size() == 0)
|
||||
return;
|
||||
|
||||
// load list of partially or completely finished pilots
|
||||
std::stringstream variant_str;
|
||||
bool comma = false;
|
||||
for (std::vector<Database::Variant>::const_iterator it = variants.begin();
|
||||
it != variants.end(); ++it) {
|
||||
if (comma) variant_str << ", ";
|
||||
variant_str << it->id;
|
||||
comma = true; // Next time we need a comma
|
||||
}
|
||||
log_send << "loading completed pilot IDs ..." << std::endl;
|
||||
|
||||
std::stringstream sql;
|
||||
sql << "SELECT pilot_id, COUNT(*) FROM " << db_connect.result_table()
|
||||
sql << "SELECT pilot_id, COUNT(*) FROM fsppilot p"
|
||||
<< " JOIN " << db_connect.result_table() << " r ON r.pilot_id = p.id"
|
||||
<< " WHERE variant_id in (" << variant_str.str() << ")"
|
||||
<< " GROUP BY pilot_id ";
|
||||
MYSQL_RES *ids = db->query_stream(sql.str().c_str());
|
||||
log_send << "loading completed pilot IDs ..." << std::endl;
|
||||
MYSQL_ROW row;
|
||||
unsigned rowcount = 0;
|
||||
while ((row = mysql_fetch_row(ids)) != 0) {
|
||||
|
||||
@ -29,7 +29,7 @@ class DatabaseCampaign : public Campaign {
|
||||
int fspmethod_id; // !< Which fspmethod should be put out to the clients
|
||||
|
||||
void collect_result_thread();
|
||||
void load_completed_pilots();
|
||||
void load_completed_pilots(std::vector<fail::Database::Variant> &);
|
||||
unsigned existing_results_for_pilot(unsigned pilot_id);
|
||||
|
||||
#ifndef __puma
|
||||
|
||||
@ -4,10 +4,12 @@ set(SRCS
|
||||
ExperimentFlow.hpp
|
||||
JobClient.hpp
|
||||
JobClient.cc
|
||||
DatabaseExperiment.hpp
|
||||
DatabaseExperiment.cc
|
||||
)
|
||||
|
||||
add_library(fail-efw ${SRCS})
|
||||
add_dependencies(fail-efw fail-comm)
|
||||
add_dependencies(fail-efw fail-protoc)
|
||||
target_link_libraries(fail-efw fail-comm)
|
||||
target_link_libraries(fail-efw fail-util) # WallclockTimer
|
||||
|
||||
|
||||
183
src/core/efw/DatabaseExperiment.cc
Normal file
183
src/core/efw/DatabaseExperiment.cc
Normal file
@ -0,0 +1,183 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "sal/SALConfig.hpp"
|
||||
#include "sal/Memory.hpp"
|
||||
#include "sal/Listener.hpp"
|
||||
#include "efw/DatabaseExperiment.hpp"
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/message.h>
|
||||
#include "comm/DatabaseCampaignMessage.pb.h"
|
||||
#include "sal/bochs/BochsListener.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
using namespace google::protobuf;
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE)
|
||||
#error This experiment needs: breakpoints, restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
DatabaseExperiment::~DatabaseExperiment() {
|
||||
delete this->m_jc;
|
||||
}
|
||||
|
||||
unsigned DatabaseExperiment::injectBitFlip(address_t data_address, unsigned bitpos){
|
||||
unsigned int value, injectedval;
|
||||
|
||||
value = m_mm.getByte(data_address);
|
||||
injectedval = value ^ (1 << bitpos);
|
||||
m_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') << (unsigned) m_mm.getByte(data_address) << endl;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T * protobufFindSubmessageByTypename(Message *msg, const std::string &name) {
|
||||
T * submessage = 0;
|
||||
const Descriptor *msg_type = msg->GetDescriptor();
|
||||
const Message::Reflection *ref = msg->GetReflection();
|
||||
const Descriptor *database_desc =
|
||||
DescriptorPool::generated_pool()->FindMessageTypeByName(name);
|
||||
assert(database_desc != 0);
|
||||
|
||||
size_t count = msg_type->field_count();
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
const FieldDescriptor *field = msg_type->field(i);
|
||||
assert(field != 0);
|
||||
if (field->message_type() == database_desc) {
|
||||
submessage = dynamic_cast<T*>(ref->MutableMessage(msg, field));
|
||||
assert(submessage != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return submessage;
|
||||
}
|
||||
|
||||
|
||||
bool DatabaseExperiment::run()
|
||||
{
|
||||
m_log << "STARTING EXPERIMENT" << endl;
|
||||
|
||||
if (!this->cb_start_experiment()) {
|
||||
m_log << "Initialization failed. Exiting." << endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
|
||||
unsigned executed_jobs = 0;
|
||||
|
||||
while (executed_jobs < 25 || m_jc->getNumberOfUndoneJobs() > 0) {
|
||||
m_log << "asking jobserver for parameters" << endl;
|
||||
ExperimentData * param = this->cb_allocate_experiment_data();
|
||||
if (!m_jc->getParam(*param)){
|
||||
m_log << "Dying." << endl; // We were told to die.
|
||||
simulator.terminate(1);
|
||||
}
|
||||
m_current_param = param;
|
||||
|
||||
DatabaseCampaignMessage * fsppilot =
|
||||
protobufFindSubmessageByTypename<DatabaseCampaignMessage>(¶m->getMessage(), "DatabaseCampaignMessage");
|
||||
assert (fsppilot != 0);
|
||||
|
||||
unsigned injection_instr = fsppilot->injection_instr();
|
||||
address_t data_address = fsppilot->data_address();
|
||||
unsigned width = fsppilot->data_width();
|
||||
|
||||
for (unsigned bit_offset = 0; bit_offset < width * 8; ++bit_offset) {
|
||||
// 8 results in one job
|
||||
Message *outer_result = cb_new_result(param);
|
||||
m_current_result = outer_result;
|
||||
DatabaseExperimentMessage *result =
|
||||
protobufFindSubmessageByTypename<DatabaseExperimentMessage>(outer_result, "DatabaseExperimentMessage");
|
||||
result->set_bitoffset(bit_offset);
|
||||
m_log << "restoring state" << endl;
|
||||
// Restore to the image, which starts at address(main)
|
||||
simulator.restore(cb_state_directory());
|
||||
executed_jobs ++;
|
||||
|
||||
m_log << "Trying to inject @ instr #" << dec << injection_instr << endl;
|
||||
|
||||
simulator.clearListeners();
|
||||
|
||||
// Generate an experiment listener, that matches on any IP
|
||||
// event. It is used to forward to the injection
|
||||
// point. The +1 is needed, since even for the zeroth
|
||||
// dynamic instruction we need at least one breakpoint
|
||||
// event.
|
||||
BPSingleListener bp;
|
||||
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||
bp.setCounter(injection_instr + 1);
|
||||
simulator.addListener(&bp);
|
||||
|
||||
if (!this->cb_before_fast_forward()) {
|
||||
continue;
|
||||
}
|
||||
fail::BaseListener * listener;
|
||||
while (true) {
|
||||
listener = simulator.resume();
|
||||
if (listener == &bp) {
|
||||
break;
|
||||
} else {
|
||||
bool should_continue = this->cb_during_fast_forward(listener);
|
||||
if (!should_continue)
|
||||
break; // Stop fast forwarding
|
||||
}
|
||||
}
|
||||
if (!this->cb_after_fast_forward(listener)) {
|
||||
continue; // Continue to next injection experiment
|
||||
}
|
||||
|
||||
address_t injection_instr_absolute = fsppilot->injection_instr_absolute();
|
||||
bool found_eip;
|
||||
for (int i = 0; i < BX_SMP_PROCESSORS; i++) {
|
||||
address_t eip = simulator.getCPU(i).getInstructionPointer();
|
||||
if (eip == injection_instr_absolute) {
|
||||
found_eip = true;
|
||||
}
|
||||
}
|
||||
if (!found_eip) {
|
||||
m_log << "Invalid Injection address != 0x" << injection_instr_absolute << std::endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
|
||||
simulator.clearListeners();
|
||||
|
||||
/// INJECT BITFLIP:
|
||||
result->set_original_value(injectBitFlip(data_address, bit_offset));
|
||||
|
||||
if (!this->cb_before_resume()) {
|
||||
continue; // Continue to next experiment
|
||||
}
|
||||
|
||||
m_log << "Resuming till the crash" << std::endl;
|
||||
// resume and wait for results
|
||||
while (true) {
|
||||
listener = simulator.resume();
|
||||
bool should_continue = this->cb_during_resume(listener);
|
||||
if (!should_continue)
|
||||
break;
|
||||
}
|
||||
m_log << "Resume done" << std::endl;
|
||||
this->cb_after_resume(listener);
|
||||
|
||||
simulator.clearListeners();
|
||||
}
|
||||
m_jc->sendResult(*param);
|
||||
this->cb_free_experiment_data(param);
|
||||
}
|
||||
// Explicitly terminate, or the simulator will continue to run.
|
||||
simulator.terminate();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
150
src/core/efw/DatabaseExperiment.hpp
Normal file
150
src/core/efw/DatabaseExperiment.hpp
Normal file
@ -0,0 +1,150 @@
|
||||
#ifndef __DATABASE_EXPERIMENT_HPP__
|
||||
#define __DATABASE_EXPERIMENT_HPP__
|
||||
|
||||
#include <google/protobuf/message.h>
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace fail {
|
||||
class ExperimentData;
|
||||
|
||||
class DatabaseExperiment : public fail::ExperimentFlow {
|
||||
fail::JobClient *m_jc;
|
||||
|
||||
unsigned injectBitFlip(fail::address_t data_address, unsigned bitpos);
|
||||
|
||||
/**
|
||||
The current experiment data as returned by the job client. This
|
||||
allocated by cb_allocate_experiment_data()
|
||||
*/
|
||||
ExperimentData *m_current_param;
|
||||
google::protobuf::Message *m_current_result;
|
||||
|
||||
public:
|
||||
DatabaseExperiment(const std::string &name)
|
||||
: m_log(name, false), m_mm(fail::simulator.getMemoryManager()) {
|
||||
|
||||
/* The fail server can be set with an environent variable,
|
||||
otherwise the JOBSERVER configured by cmake ist used */
|
||||
char *server_host = getenv("FAIL_SERVER_HOST");
|
||||
if (server_host != NULL){
|
||||
this->m_jc = new fail::JobClient(std::string(server_host));
|
||||
} else {
|
||||
this->m_jc = new fail::JobClient();
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~DatabaseExperiment();
|
||||
|
||||
bool run();
|
||||
|
||||
|
||||
protected:
|
||||
fail::Logger m_log;
|
||||
fail::MemoryManager& m_mm;
|
||||
|
||||
/** Returns the currently running experiment message as returned
|
||||
* by the job client
|
||||
*/
|
||||
ExperimentData * get_current_experiment_data() { return m_current_param; }
|
||||
|
||||
/** Returns the currently result message, that was allocated by
|
||||
* cb_allocate_new_result.
|
||||
*/
|
||||
google::protobuf::Message * get_current_result() { return m_current_result; }
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Can be overwritten by experiment
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Get path to the state directory
|
||||
*/
|
||||
virtual std::string cb_state_directory() { return "state"; }
|
||||
|
||||
/**
|
||||
* Callback that is called, before the actual experiment
|
||||
* starts. Simulation is terminated on false.
|
||||
* @param The current result message
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_start_experiment() { return true; };
|
||||
|
||||
/**
|
||||
* Allocate enough space to hold the incoming ExperimentData
|
||||
* message. The can be accessed during the experiment through
|
||||
* get_current_experiment_data()
|
||||
*/
|
||||
virtual ExperimentData* cb_allocate_experiment_data() = 0;
|
||||
virtual void cb_free_experiment_data(ExperimentData *) {};
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a new result slot in the given experiment data. The
|
||||
* returned pointer can be obtained by calling
|
||||
* get_current_result()
|
||||
*/
|
||||
virtual google::protobuf::Message* cb_new_result(ExperimentData*) = 0;
|
||||
|
||||
/**
|
||||
* Callback that is called before the fast forward is done. This
|
||||
* can be used to add additional event listeners during the fast
|
||||
* forward phase. If returning false, the experiment is canceled.
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_before_fast_forward() { return true; };
|
||||
|
||||
/**
|
||||
* Callback that is called during the fast forward, when an event
|
||||
* has triggered, but it was not the fast forward listener. This
|
||||
* can be used to collect additional information during the fast
|
||||
* forward If returning false, the fast forwarding is stopped.
|
||||
*
|
||||
* @return \c true on should continue, \c false stop ff
|
||||
*/
|
||||
virtual bool cb_during_fast_forward(fail::BaseListener *) { return false; };
|
||||
|
||||
/**
|
||||
* Callback that is called after the fast forward, with the last
|
||||
* triggered event forward If returning false, the experiment is
|
||||
* canceled.
|
||||
*
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_after_fast_forward(fail::BaseListener *) { return true; };
|
||||
|
||||
/**
|
||||
* Callback that is called before the resuming till crash has
|
||||
* started. This is called after the fault was injected. Here the
|
||||
* end listeners should be installed. Returns true on
|
||||
* success. Otherwise the experiment is canceled.
|
||||
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_before_resume() = 0;
|
||||
|
||||
/**
|
||||
* Callback that is called during the resume-till-crash phase,
|
||||
* when an event has triggered, This can be used to collect
|
||||
* additional information during the resuming phse. If returning
|
||||
* false, the resuming has finished and the experiment has stopped.
|
||||
*
|
||||
* @return \c true on should continue ff, \c false stop ff
|
||||
*/
|
||||
virtual bool cb_during_resume(fail::BaseListener *) { return false; };
|
||||
|
||||
/**
|
||||
* Callback that is called after the resume-till-crash phase with
|
||||
* the last triggered listener. This callback should collect all data and
|
||||
*
|
||||
*/
|
||||
virtual void cb_after_resume(fail::BaseListener *) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __DATABASE_EXPERIMENT_HPP__
|
||||
@ -749,9 +749,20 @@ public:
|
||||
void setId(timer_id_t id) { m_Data.setId(id); }
|
||||
/**
|
||||
* Retrieves the timer's timeout value.
|
||||
* @return the timout in microseconds
|
||||
* @return the timeout in microseconds
|
||||
*/
|
||||
unsigned getTimeout() const { return m_Timeout; }
|
||||
/**
|
||||
* Set the timeout value. Returns the old timeout value. Be aware,
|
||||
* that this method might have no effects, after the listener was added.
|
||||
* @return the old timeout in microseconds
|
||||
*/
|
||||
unsigned setTimeout(unsigned timeout) {
|
||||
unsigned tmp = m_Timeout;
|
||||
m_Timeout = timeout;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
@ -12,29 +12,33 @@
|
||||
#include "../SALInst.hpp"
|
||||
#include "BochsHelpers.hpp"
|
||||
|
||||
// TODO: ATM only capturing bytewise output (most common, I suppose)
|
||||
// FIXME ATM only capturing the first byte of a multi-byte in/out
|
||||
|
||||
aspect IOPortCom {
|
||||
pointcut outInstruction() = "% ...::bx_cpu_c::OUT_DXAL(...)";
|
||||
pointcut devices_outp() = "% ...::bx_devices_c::outp(...)";
|
||||
|
||||
advice execution (outInstruction()) : after ()
|
||||
advice call (devices_outp()) && within ("...::bx_cpu_c") : before ()
|
||||
{
|
||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||
|
||||
// Detect the CPU that triggered the change:
|
||||
unsigned port = *(tjp->arg<0>());
|
||||
unsigned data = *(tjp->arg<1>());
|
||||
|
||||
// detect the CPU that triggered the access
|
||||
fail::ConcreteCPU& triggerCPU = fail::simulator.detectCPU(getCPU(tjp->that()));
|
||||
fail::simulator.onIOPort(&triggerCPU, rAL, rDX, true);
|
||||
// forward to SimulatorController
|
||||
fail::simulator.onIOPort(&triggerCPU, data, port, true);
|
||||
}
|
||||
|
||||
pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX(...)";
|
||||
pointcut devices_inp() = "% ...::bx_devices_c::inp(...)";
|
||||
|
||||
advice execution (inInstruction()) : after ()
|
||||
advice call (devices_inp()) && within ("...::bx_cpu_c") : after ()
|
||||
{
|
||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||
unsigned port = *(tjp->arg<0>());
|
||||
unsigned data = *(tjp->result());
|
||||
|
||||
// detect the CPU that triggered the access
|
||||
fail::ConcreteCPU& triggerCPU = fail::simulator.detectCPU(getCPU(tjp->that()));
|
||||
fail::simulator.onIOPort(&triggerCPU, rAL, rDX, false);
|
||||
// forward to SimulatorController
|
||||
fail::simulator.onIOPort(&triggerCPU, data, port, false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -91,4 +91,5 @@ target_link_libraries(memorymap-test fail-util)
|
||||
add_test(NAME memorymap-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testing COMMAND memorymap-test)
|
||||
|
||||
add_executable(sumtree-test testing/SumTreeTest.cc)
|
||||
target_link_libraries(sumtree-test fail-util)
|
||||
add_test(NAME sumtree-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testing COMMAND sumtree-test)
|
||||
|
||||
@ -43,8 +43,13 @@ bool CommandLine::parse() {
|
||||
option::Descriptor desc = {0, 0, 0, 0, 0, 0};
|
||||
this->options.push_back(desc);
|
||||
|
||||
// Generate the options stats
|
||||
option::Stats stats(this->options.data(), argv.size(), argv.data());
|
||||
// Copy argv to preserve original argument order
|
||||
// (for proper re-parsing after adding more options)
|
||||
argv_reordered = argv;
|
||||
|
||||
// Generate the options stats (GNU mode)
|
||||
option::Stats stats(true, this->options.data(),
|
||||
argv_reordered.size(), argv_reordered.data());
|
||||
|
||||
if (parsed_options)
|
||||
delete[] parsed_options;
|
||||
@ -56,9 +61,9 @@ bool CommandLine::parse() {
|
||||
parsed_options = new option::Option[stats.options_max];
|
||||
parsed_buffer = new option::Option[stats.buffer_max];
|
||||
|
||||
m_parser = new option::Parser(this->options.data(), argv.size(), argv.data(),
|
||||
parsed_options, parsed_buffer);
|
||||
|
||||
m_parser = new option::Parser(true, this->options.data(),
|
||||
argv_reordered.size(), argv_reordered.data(),
|
||||
parsed_options, parsed_buffer);
|
||||
|
||||
// Pop the terminating entry
|
||||
this->options.pop_back();
|
||||
|
||||
@ -30,7 +30,7 @@ public:
|
||||
private:
|
||||
static CommandLine m_instance;
|
||||
|
||||
std::vector<const char *> argv;
|
||||
std::vector<const char *> argv, argv_reordered;
|
||||
std::vector<option::Descriptor> options;
|
||||
option::Option *parsed_options, *parsed_buffer;
|
||||
option::Parser *m_parser;
|
||||
|
||||
@ -133,9 +133,11 @@ void DatabaseProtobufAdapter::TypeBridge_string::bind(MYSQL_BIND *bind, const go
|
||||
/* Handle the NULL case */
|
||||
if (insert_null(bind, msg)) return;
|
||||
|
||||
buffer = ref->GetString(*msg, desc);
|
||||
|
||||
bind->buffer_type = MYSQL_TYPE_STRING;
|
||||
bind->buffer = (void *) ref->GetString(*msg, desc).c_str();
|
||||
bind->buffer_length = ref->GetString(*msg, desc).length();
|
||||
bind->buffer = (void *) buffer.c_str();
|
||||
bind->buffer_length = buffer.length();
|
||||
}
|
||||
void DatabaseProtobufAdapter::TypeBridge_enum::bind(MYSQL_BIND *bind, const google::protobuf::Message *msg) {
|
||||
const google::protobuf::Reflection *ref = msg->GetReflection();
|
||||
|
||||
@ -80,7 +80,7 @@ class DatabaseProtobufAdapter {
|
||||
TypeBridge_uint32(const google::protobuf::FieldDescriptor *desc)
|
||||
: TypeBridge(desc){};
|
||||
|
||||
virtual std::string sql_type() { return "INT"; };
|
||||
virtual std::string sql_type() { return "INT UNSIGNED"; };
|
||||
virtual int element_size() { return 4; };
|
||||
virtual void copy_to(const google::protobuf::Message *msg, int i, void *);
|
||||
virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg);
|
||||
@ -101,7 +101,7 @@ class DatabaseProtobufAdapter {
|
||||
TypeBridge_uint64(const google::protobuf::FieldDescriptor *desc)
|
||||
: TypeBridge(desc){};
|
||||
|
||||
virtual std::string sql_type() { return "BIGINT"; };
|
||||
virtual std::string sql_type() { return "BIGINT UNSIGNED"; };
|
||||
virtual int element_size() { return 8; };
|
||||
virtual void copy_to(const google::protobuf::Message *msg, int i, void *);
|
||||
virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg);
|
||||
@ -138,6 +138,7 @@ class DatabaseProtobufAdapter {
|
||||
};
|
||||
|
||||
struct TypeBridge_string : TypeBridge {
|
||||
std::string buffer;
|
||||
TypeBridge_string(const google::protobuf::FieldDescriptor *desc)
|
||||
: TypeBridge(desc){};
|
||||
virtual std::string sql_type() { return "TEXT"; };
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
|
||||
#include "DwarfReader.hpp"
|
||||
#include "libdwarf.h"
|
||||
@ -128,8 +130,10 @@ bool DwarfReader::read_source_files(const std::string& fileName,std::list<std::s
|
||||
while (dwarf_next_cu_header(dbg,0,0,0,0,&header,0)==DW_DLV_OK) {
|
||||
// Access the die
|
||||
Dwarf_Die die;
|
||||
if (dwarf_siblingof(dbg,0,&die,0)!=DW_DLV_OK)
|
||||
if (dwarf_siblingof(dbg,0,&die,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the source lines
|
||||
Dwarf_Line* lineBuffer;
|
||||
@ -140,17 +144,25 @@ bool DwarfReader::read_source_files(const std::string& fileName,std::list<std::s
|
||||
// Store them
|
||||
for (int index=0;index<lineCount;index++) {
|
||||
Dwarf_Unsigned lineNo;
|
||||
if (dwarf_lineno(lineBuffer[index],&lineNo,0)!=DW_DLV_OK)
|
||||
if (dwarf_lineno(lineBuffer[index],&lineNo,0)!=DW_DLV_OK){
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
char* lineSource;
|
||||
if (dwarf_linesrc(lineBuffer[index],&lineSource,0)!=DW_DLV_OK)
|
||||
if (dwarf_linesrc(lineBuffer[index],&lineSource,0)!=DW_DLV_OK){
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
Dwarf_Bool isCode;
|
||||
if (dwarf_linebeginstatement(lineBuffer[index],&isCode,0)!=DW_DLV_OK)
|
||||
if (dwarf_linebeginstatement(lineBuffer[index],&isCode,0)!=DW_DLV_OK){
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
Dwarf_Addr addr;
|
||||
if (dwarf_lineaddr(lineBuffer[index],&addr,0)!=DW_DLV_OK)
|
||||
if (dwarf_lineaddr(lineBuffer[index],&addr,0)!=DW_DLV_OK){
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lineNo&&isCode) {
|
||||
//LOG << "lineNo: " << lineNo << " addr: " << reinterpret_cast<void*>(addr) << " line source:" << normalize(lineSource) << endl;
|
||||
@ -171,14 +183,20 @@ bool DwarfReader::read_source_files(const std::string& fileName,std::list<std::s
|
||||
lines.unique();
|
||||
|
||||
// Shut down libdwarf
|
||||
if (dwarf_finish(dbg,0)!=DW_DLV_OK)
|
||||
if (dwarf_finish(dbg,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addrToLineList) {
|
||||
bool DwarfReader::read_mapping(std::string fileName, std::list<DwarfLineMapping>& lineMapping) {
|
||||
// temporary mapping of instruction address to (file, line)
|
||||
// => static instruction addresses are sorted ascendingly for the whole binary
|
||||
// (i.e. all instructions from all CUs!)
|
||||
std::map<unsigned, SourceLine> instr_to_sourceline;
|
||||
|
||||
// Open The file
|
||||
int fd=open(fileName.c_str(),O_RDONLY);
|
||||
@ -200,10 +218,13 @@ bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addr
|
||||
|
||||
// Iterator over the headers
|
||||
Dwarf_Unsigned header;
|
||||
// iterate compilation unit headers
|
||||
while (dwarf_next_cu_header(dbg,0,0,0,0,&header,0)==DW_DLV_OK) {
|
||||
// Access the die
|
||||
Dwarf_Die die;
|
||||
// XXX: "if there are no sibling headers, die" | semantics unclear!
|
||||
if (dwarf_siblingof(dbg,0,&die,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -211,6 +232,7 @@ bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addr
|
||||
Dwarf_Line* lineBuffer;
|
||||
Dwarf_Signed lineCount;
|
||||
if (dwarf_srclines(die,&lineBuffer,&lineCount,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
continue; //return false;
|
||||
}
|
||||
|
||||
@ -218,24 +240,30 @@ bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addr
|
||||
for (int index=0;index<lineCount;index++) {
|
||||
Dwarf_Unsigned lineNo;
|
||||
if (dwarf_lineno(lineBuffer[index],&lineNo,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
char* lineSource;
|
||||
if (dwarf_linesrc(lineBuffer[index],&lineSource,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
Dwarf_Bool isCode;
|
||||
if (dwarf_linebeginstatement(lineBuffer[index],&isCode,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
Dwarf_Addr addr;
|
||||
if (dwarf_lineaddr(lineBuffer[index],&addr,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lineNo&&isCode) {
|
||||
struct addrToLine newLine = { (int) addr, (int) lineNo, normalize(lineSource) };
|
||||
addrToLineList.push_back(newLine);
|
||||
// wrap line_number and source_file into an object
|
||||
SourceLine tmp_sl(normalize(lineSource), lineNo);
|
||||
// store SourceLine object with static instr in the map
|
||||
instr_to_sourceline.insert(std::make_pair(addr, tmp_sl));
|
||||
}
|
||||
|
||||
dwarf_dealloc(dbg,lineSource,DW_DLA_STRING);
|
||||
@ -250,9 +278,30 @@ bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addr
|
||||
|
||||
// Shut down libdwarf
|
||||
if (dwarf_finish(dbg,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// iterate instr_to_sourceline to determine the "line_range_size" for mapping
|
||||
std::map<unsigned, SourceLine>::iterator it;
|
||||
for (it = instr_to_sourceline.begin(); it != instr_to_sourceline.end(); it++) {
|
||||
unsigned addr = it->first;
|
||||
SourceLine sl = it->second;
|
||||
|
||||
/* Default the linetable's address range (->"size") to the maximum
|
||||
* possible value. This results in the last linetable entry having
|
||||
* maximum range. This entry will either be a dummy or a function's
|
||||
* epilogue, both of which are irrelevant for our (current) use cases.
|
||||
* All other entries' sizes are set properly. */
|
||||
DwarfLineMapping mapping(addr, (std::numeric_limits<unsigned>::max() - addr),
|
||||
sl.line_number, sl.source_file);
|
||||
if (!lineMapping.empty()) {
|
||||
DwarfLineMapping& back = lineMapping.back();
|
||||
back.line_range_size = addr - back.absolute_addr;
|
||||
}
|
||||
lineMapping.push_back(mapping);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7,6 +7,26 @@
|
||||
|
||||
namespace fail {
|
||||
|
||||
// temporary wrapper object for (file, linenumber)
|
||||
class SourceLine {
|
||||
public:
|
||||
std::string source_file;
|
||||
unsigned line_number;
|
||||
|
||||
SourceLine(std::string source, unsigned line) : source_file(source), line_number(line) {}
|
||||
};
|
||||
|
||||
// wrapper object for insertion into DB
|
||||
class DwarfLineMapping {
|
||||
public:
|
||||
unsigned absolute_addr;
|
||||
unsigned line_range_size;
|
||||
unsigned line_number;
|
||||
std::string line_source;
|
||||
|
||||
DwarfLineMapping(unsigned addr, unsigned size, unsigned number, std::string src)
|
||||
: absolute_addr(addr), line_range_size(size), line_number(number), line_source(src){}
|
||||
};
|
||||
|
||||
/**
|
||||
* This source code is based on bcov 0.2.
|
||||
@ -15,23 +35,16 @@ namespace fail {
|
||||
* GNU GENERAL PUBLIC LICENSE
|
||||
*/
|
||||
|
||||
struct addrToLine {
|
||||
int absoluteAddr;
|
||||
int lineNumber;
|
||||
std::string lineSource;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class DwarfReader
|
||||
* ToDO
|
||||
*/
|
||||
|
||||
class DwarfReader {
|
||||
/**
|
||||
* \class DwarfReader
|
||||
* ToDO
|
||||
*/
|
||||
class DwarfReader {
|
||||
|
||||
public:
|
||||
|
||||
bool read_source_files(const std::string& fileName, std::list<std::string>& lines);
|
||||
bool read_mapping(std::string fileName, std::list<addrToLine>& addrToLineList);
|
||||
bool read_mapping(std::string fileName, std::list<DwarfLineMapping>& lineMapping);
|
||||
};
|
||||
|
||||
} // end-of-namespace fail
|
||||
|
||||
@ -19,11 +19,15 @@ bool MemoryMap::readFromFile(char const * const filename)
|
||||
unsigned count = 0;
|
||||
|
||||
while (getline(file, buf)) {
|
||||
std::string addr, len;
|
||||
std::stringstream ss(buf, std::ios::in);
|
||||
ss >> guest_addr >> guest_len;
|
||||
ss >> addr >> len;
|
||||
guest_addr = strtoul(addr.c_str(), NULL, 0);
|
||||
guest_len = strtoul(len.c_str(), NULL, 0);
|
||||
add(guest_addr, guest_len);
|
||||
count++;
|
||||
}
|
||||
|
||||
// assertion kept from original code; usually something's fishy if the file
|
||||
// contains no entries
|
||||
assert(count > 0);
|
||||
|
||||
@ -11,8 +11,7 @@ set(SRCS
|
||||
|
||||
include(FindLLVM)
|
||||
|
||||
# compiling without -fno-rtti fails even when LLVM is not built with that flag
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_CXX_FLAGS} -fno-rtti" )
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_CXX_FLAGS}" )
|
||||
|
||||
add_library(fail-llvmdisassembler ${SRCS})
|
||||
target_link_libraries(fail-llvmdisassembler fail-sal)
|
||||
|
||||
@ -49,7 +49,10 @@ LLVMtoFailTranslator* LLVMtoFailTranslator::createFromBinary(const std::string e
|
||||
llvm::InitializeAllDisassemblers();
|
||||
|
||||
OwningPtr<Binary> binary;
|
||||
assert(createBinary(elf_path, binary) == 0);
|
||||
llvm::error_code ret = createBinary(elf_path, binary);
|
||||
assert (ret == 0);
|
||||
(void) ret; // unused in release builds
|
||||
assert (binary.get() != NULL);
|
||||
|
||||
#ifndef __puma
|
||||
LLVMDisassembler disas(dyn_cast<ObjectFile>(binary.get()));
|
||||
@ -57,4 +60,4 @@ LLVMtoFailTranslator* LLVMtoFailTranslator::createFromBinary(const std::string e
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
#ifndef __puma
|
||||
#include "../LLVMDisassembler.hpp"
|
||||
|
||||
using namespace llvm;
|
||||
@ -69,4 +68,3 @@ int main(int argc, char* argv[]) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
#include "util/SumTree.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#define LOG std::cerr
|
||||
|
||||
using std::endl;
|
||||
|
||||
fail::Logger LOG("SumTreeTest");
|
||||
|
||||
struct Pilot {
|
||||
uint32_t id;
|
||||
uint32_t instr2;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <stdlib.h>
|
||||
#include "util/MemoryMap.hpp"
|
||||
|
||||
using namespace fail;
|
||||
@ -16,6 +17,7 @@ uint32_t outside[] = { 0, 10, 16, 1, 20, 1, 25, 10 };
|
||||
void test_failed(std::string msg)
|
||||
{
|
||||
cerr << "MemoryMap test failed (" << msg << ")!" << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
// pass by value intentional
|
||||
@ -98,6 +100,7 @@ int main()
|
||||
char const *filename_tmp = "tmp.memorymap";
|
||||
char const *filename_test1 = "test1.memorymap";
|
||||
char const *filename_test2 = "test2.memorymap";
|
||||
char const *filename_test3 = "test3.memorymap";
|
||||
|
||||
for (unsigned i = 0; i < LEN(inside); i += 2) {
|
||||
mm.add(inside[i], inside[i+1]);
|
||||
@ -125,4 +128,8 @@ int main()
|
||||
mm.clear();
|
||||
mm.readFromFile(filename_test2);
|
||||
test(mm);
|
||||
|
||||
mm.clear();
|
||||
mm.readFromFile(filename_test3);
|
||||
test(mm);
|
||||
}
|
||||
|
||||
3
src/core/util/testing/test3.memorymap
Normal file
3
src/core/util/testing/test3.memorymap
Normal file
@ -0,0 +1,3 @@
|
||||
012 0x6
|
||||
0x11 3
|
||||
21 0x4
|
||||
37
src/experiments/cored-tester/CMakeLists.txt
Normal file
37
src/experiments/cored-tester/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
||||
set(EXPERIMENT_NAME cored-tester)
|
||||
set(EXPERIMENT_TYPE CoredTester)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
cored-tester.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})
|
||||
|
||||
set(PROTOBUF_IMPORT_DIRS ${PROTOBUF_IMPORT_DIRS} ${CMAKE_CURRENT_BINARY_DIR}/../../core/comm)
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
|
||||
|
||||
## Build library
|
||||
add_library(fail-${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
add_dependencies(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY} fail-llvmdisassembler)
|
||||
|
||||
## This is the example's campaign server distributing experiment parameters
|
||||
add_executable(${EXPERIMENT_NAME}-server main.cc)
|
||||
target_link_libraries(${EXPERIMENT_NAME}-server -Wl,--start-group fail-${EXPERIMENT_NAME} fail-sal fail-util fail-cpn fail-comm ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} -lmysqlclient -Wl,--end-group)
|
||||
install(TARGETS ${EXPERIMENT_NAME}-server RUNTIME DESTINATION bin)
|
||||
|
||||
20
src/experiments/cored-tester/campaign.cc
Normal file
20
src/experiments/cored-tester/campaign.cc
Normal file
@ -0,0 +1,20 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/ElfReader.hpp"
|
||||
#include "util/ProtoStream.hpp"
|
||||
#include "sal/SALConfig.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
using namespace google::protobuf;
|
||||
|
||||
void CoredTesterCampaign::cb_send_pilot(DatabaseCampaignMessage pilot) {
|
||||
CoredTesterExperimentData *data = new CoredTesterExperimentData;
|
||||
data->msg.mutable_fsppilot()->CopyFrom(pilot);
|
||||
campaignmanager.addParam(data);
|
||||
}
|
||||
|
||||
30
src/experiments/cored-tester/campaign.hpp
Normal file
30
src/experiments/cored-tester/campaign.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef __DCIAOCAMPAIGN_HPP__
|
||||
#define __DCIAOCAMPAIGN_HPP__
|
||||
|
||||
#include "cpn/DatabaseCampaign.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "cored-tester.pb.h"
|
||||
#include "util/ElfReader.hpp"
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
class CoredTesterExperimentData : public fail::ExperimentData {
|
||||
public:
|
||||
CoredTesterProtoMsg msg;
|
||||
CoredTesterExperimentData() : fail::ExperimentData(&msg) {}
|
||||
};
|
||||
|
||||
class CoredTesterCampaign : public fail::DatabaseCampaign {
|
||||
virtual const google::protobuf::Descriptor * cb_result_message()
|
||||
{ return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("CoredTesterProtoMsg"); }
|
||||
|
||||
virtual void cb_send_pilot(DatabaseCampaignMessage pilot);
|
||||
virtual int expected_number_of_results(std::string variant, std::string benchmark) {
|
||||
if (benchmark.find("jump") != std::string::npos)
|
||||
return 1;
|
||||
else
|
||||
return 8;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // __KESOREFCAMPAIGN_HPP__
|
||||
39
src/experiments/cored-tester/cored-tester.proto
Normal file
39
src/experiments/cored-tester/cored-tester.proto
Normal file
@ -0,0 +1,39 @@
|
||||
import "DatabaseCampaignMessage.proto";
|
||||
|
||||
message CoredTesterProtoMsg {
|
||||
required DatabaseCampaignMessage fsppilot = 1;
|
||||
|
||||
repeated group Result = 2 {
|
||||
enum ResultType {
|
||||
/* Test did the right thing */
|
||||
OK = 1;
|
||||
OK_DETECTED_ERROR = 3;
|
||||
|
||||
/* Test did the wrong thing */
|
||||
SDC_WRONG_RESULT = 4;
|
||||
|
||||
/* Test detected the error */
|
||||
ERR_TRAP = 5;
|
||||
ERR_TIMEOUT = 6;
|
||||
|
||||
ERR_ASSERT_UNKOWN = 7;
|
||||
ERR_ASSERT_SYSTEM_STATE = 8;
|
||||
ERR_ASSERT_CFG_REGION = 9;
|
||||
ERR_ASSERT_SPURIOUS = 10;
|
||||
|
||||
UNKNOWN = 20;
|
||||
NOINJECTION = 21;
|
||||
}
|
||||
|
||||
required int32 bitoffset = 1 [(sql_primary_key) = true];
|
||||
required ResultType resulttype = 2;
|
||||
required uint32 experiment_number = 3;
|
||||
|
||||
/* At which point in time did the crash occur */
|
||||
optional uint64 time_crash = 4;
|
||||
|
||||
optional uint32 original_value = 5;
|
||||
|
||||
optional string details = 6;
|
||||
}
|
||||
}
|
||||
586
src/experiments/cored-tester/experiment.cc
Normal file
586
src/experiments/cored-tester/experiment.cc
Normal file
@ -0,0 +1,586 @@
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
#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 "sal/bochs/BochsMemory.hpp"
|
||||
#include "sal/bochs/BochsCPU.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
#include "util/llvmdisassembler/LLVMtoFailTranslator.hpp"
|
||||
#include "util/llvmdisassembler/LLVMtoFailBochs.hpp"
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "cored-tester.pb.h"
|
||||
|
||||
#include "../plugins/randomgenerator/RandomGenerator.hpp"
|
||||
#include "../plugins/checkpoint/Checkpoint.hpp"
|
||||
|
||||
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
|
||||
#if !defined(CONFIG_EVENT_MEMREAD) || !defined(CONFIG_EVENT_MEMWRITE)
|
||||
#error This experiment needs: MemRead and MemWrite. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
void CoredTester::redecodeCurrentInstruction() {
|
||||
/* Flush Instruction Caches and Prefetch queue */
|
||||
BX_CPU_C *cpu_context = simulator.getCPUContext();
|
||||
cpu_context->invalidate_prefetch_q();
|
||||
cpu_context->iCache.flushICacheEntries();
|
||||
|
||||
|
||||
guest_address_t pc = simulator.getCPU(0).getInstructionPointer();
|
||||
bxInstruction_c *currInstr = simulator.getCurrentInstruction();
|
||||
//unsigned length_in_bytes = currInstr->ilen();
|
||||
|
||||
m_log << "REDECODE INSTRUCTION @ IP 0x" << std::hex << pc << endl;
|
||||
|
||||
Bit32u eipBiased = pc + cpu_context->eipPageBias;
|
||||
Bit8u instr_plain[15];
|
||||
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
for(unsigned i=0; i<sizeof(instr_plain); i++) {
|
||||
if(!mm.isMapped(pc+i)) {
|
||||
m_log << "REDECODE: 0x" << std::hex << pc+i << "UNMAPPED" << endl;
|
||||
// TODO: error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mm.getBytes(pc, sizeof(instr_plain), instr_plain);
|
||||
|
||||
unsigned remainingInPage = cpu_context->eipPageWindowSize - eipBiased;
|
||||
int ret;
|
||||
#if BX_SUPPORT_X86_64
|
||||
if (cpu_context->cpu_mode == BX_MODE_LONG_64)
|
||||
ret = cpu_context->fetchDecode64(instr_plain, currInstr, remainingInPage);
|
||||
else
|
||||
#endif
|
||||
ret = cpu_context->fetchDecode32(instr_plain, currInstr, remainingInPage);
|
||||
if (ret < 0) {
|
||||
// handle instrumentation callback inside boundaryFetch
|
||||
cpu_context->boundaryFetch(instr_plain, remainingInPage, currInstr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned CoredTester::injectBitFlip(address_t data_address, unsigned data_width, unsigned bitpos) {
|
||||
/* First 32 Registers, this might neeed adaption */
|
||||
if (data_address < (32 << 8)) {
|
||||
LLVMtoFailTranslator::reginfo_t reginfo = LLVMtoFailTranslator::reginfo_t::fromDataAddress(data_address, data_width);
|
||||
|
||||
unsigned int value, injectedval;
|
||||
|
||||
value = m_ltof->getRegisterContent(simulator.getCPU(0), reginfo);
|
||||
injectedval = value ^ (1 << bitpos);
|
||||
m_ltof->setRegisterContent(simulator.getCPU(0), reginfo, injectedval);
|
||||
|
||||
m_log << "INJECTING register (" << dec << reginfo.id
|
||||
<< " offset " << (int) reginfo.offset
|
||||
<< ") bitpos: " << bitpos
|
||||
<< " value: 0x" << hex << setw(2) << setfill('0') << value << " -> 0x" << setw(2) << setfill('0') << injectedval
|
||||
<< dec << endl;
|
||||
if (reginfo.id == RID_PC)
|
||||
redecodeCurrentInstruction();
|
||||
|
||||
return value;
|
||||
} else {
|
||||
unsigned int value, injectedval;
|
||||
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
if(!mm.isMapped(data_address)) {
|
||||
m_log << "DATA_ADDRESS NOT MAPPED" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* If it is the current instruction redecode it */
|
||||
guest_address_t pc = simulator.getCPU(0).getInstructionPointer();
|
||||
m_log << "IP: 0x" << std::hex << pc << std::endl;
|
||||
bxInstruction_c *currInstr = simulator.getCurrentInstruction();
|
||||
assert(currInstr != NULL && "FATAL ERROR: current instruction was NULL (not expected)!");
|
||||
unsigned length_in_bytes = currInstr->ilen();
|
||||
|
||||
if (currInstr == NULL || (pc <= data_address && data_address <= (pc + currInstr->ilen()))) {
|
||||
redecodeCurrentInstruction();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void handleEvent(CoredTesterProtoMsg_Result& result, CoredTesterProtoMsg_Result_ResultType restype, const std::string &msg) {
|
||||
cout << msg << endl;
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
const ElfSymbol& CoredTester::getELFSymbol(const std::string name) {
|
||||
const ElfSymbol &symbol = m_elf.getSymbol(name);
|
||||
if (!symbol.isValid()) {
|
||||
m_log << "Couldn't find symbol: " << name << std::endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
bool CoredTester::run() {
|
||||
m_log << "STARTING EXPERIMENT" << endl;
|
||||
|
||||
// seed random number generator
|
||||
timeval start;
|
||||
gettimeofday(&start, NULL);
|
||||
srand(start.tv_usec);
|
||||
|
||||
// get symbols
|
||||
const ElfSymbol &s_trace_end_marker = getELFSymbol("test_finish");
|
||||
BPSingleListener l_trace_end_marker(s_trace_end_marker.getAddress());
|
||||
|
||||
const ElfSymbol &s_stext_app = getELFSymbol("_stext_application");
|
||||
const ElfSymbol &s_etext_app = getELFSymbol("_etext_application");
|
||||
|
||||
const ElfSymbol &s_panic_handler = getELFSymbol("irq_handler_2");
|
||||
|
||||
const ElfSymbol &s_fail_trace = getELFSymbol("fail_trace");
|
||||
MemAccessListener l_fail_trace(s_fail_trace.getAddress());
|
||||
|
||||
const ElfSymbol &s_random_source = m_elf.getSymbol("random_source");
|
||||
|
||||
const ElfSymbol &s_color_assert_port = m_elf.getSymbol("color_assert_port");
|
||||
MemAccessListener l_color_assert_port(s_color_assert_port, MemAccessEvent::MEM_WRITE);
|
||||
|
||||
// allowed regions
|
||||
address_t text_tasks_start = s_stext_app.getAddress();
|
||||
address_t text_tasks_end = s_etext_app.getAddress();
|
||||
|
||||
m_log << "not injecting in application: " << std::hex << text_tasks_start << " -- " << text_tasks_end << std::endl;
|
||||
|
||||
// listeners for traps, code region
|
||||
//InterruptListener l_interrupt(2); // NMI interrupt
|
||||
//TrapListener l_trap(2); // NMI trap?
|
||||
BPSingleListener l_panic(s_panic_handler.getAddress());
|
||||
m_log << "PANIC handler @ " << std::hex << s_panic_handler.getAddress() << std::endl;
|
||||
|
||||
unsigned i_timeout = 50 * 1000;
|
||||
TimerListener l_timeout(i_timeout); // 1s in microseconds
|
||||
TimerListener l_timeout_hard(1 * 1000 * 1000); // 1s in microseconds
|
||||
|
||||
// initialize LLVM disassembler
|
||||
m_ltof = LLVMtoFailTranslator::createFromBinary(m_elf.getFilename());
|
||||
|
||||
// memory manager
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
|
||||
// job client with environment parameters
|
||||
char* server = getenv("FAIL_SERVER_HOST");
|
||||
if(server == NULL) server = (char*) SERVER_COMM_HOSTNAME;
|
||||
char* cport = getenv("FAIL_SERVER_PORT");
|
||||
int port = (cport != NULL) ? atoi(cport) : SERVER_COMM_TCP_PORT;
|
||||
fail::JobClient jobclient(server, port);
|
||||
|
||||
// execute jobs
|
||||
unsigned executed_jobs = 0;
|
||||
while (executed_jobs < 25 || jobclient.getNumberOfUndoneJobs() > 0) {
|
||||
// get next parameters from jobserver
|
||||
m_log << "asking jobserver for parameters" << endl;
|
||||
CoredTesterExperimentData param;
|
||||
if(!jobclient.getParam(param)){
|
||||
m_log << "Dying." << endl; // We were told to die.
|
||||
simulator.terminate(0);
|
||||
}
|
||||
|
||||
// extract parameters
|
||||
unsigned injection_instr = param.msg.fsppilot().injection_instr();
|
||||
address_t data_address = param.msg.fsppilot().data_address();
|
||||
unsigned data_width = param.msg.fsppilot().data_width();
|
||||
|
||||
// detect wheter we should inject the PC
|
||||
bool pc_injection = param.msg.fsppilot().benchmark().find("jump") != std::string::npos;
|
||||
|
||||
// setup experiments
|
||||
int experiments = pc_injection ? 1 : 8;
|
||||
|
||||
// run experiments
|
||||
for (int experiment_id = 0; experiment_id < experiments; ++experiment_id) {
|
||||
CoredTesterProtoMsg_Result *result = 0;
|
||||
|
||||
// restore to the image
|
||||
m_log << "restoring state" << endl;
|
||||
simulator.clearListeners(this);
|
||||
simulator.restore("state");
|
||||
m_log << "state restored" << endl;
|
||||
executed_jobs++;
|
||||
|
||||
// Check for Read Only Memory
|
||||
if (0x100000 <= data_address && data_address < 0x200000) {
|
||||
result = param.msg.add_result();
|
||||
handleEvent(*result, result->NOINJECTION, "ROM");
|
||||
|
||||
result->set_experiment_number(0);
|
||||
result->set_bitoffset(experiment_id);
|
||||
result->set_original_value(0);
|
||||
continue; // Produce experiments results
|
||||
}
|
||||
|
||||
// Extract stack ranges for checkpoint plugin
|
||||
Checkpoint::range_vector check_ranges;
|
||||
ElfReader::symbol_iterator it = m_elf.sym_begin();
|
||||
for( ; it != m_elf.sym_end(); ++it) {
|
||||
const std::string name = it->getName();
|
||||
|
||||
size_t pos = name.rfind("_stack");
|
||||
if((pos == std::string::npos) || (pos != (name.size() - 6))) continue;
|
||||
|
||||
const ElfSymbol &s_end = m_elf.getSymbol(name); // *it ?
|
||||
const std::string ptr_name = "OS_" + name + "ptr";
|
||||
stringstream ptrstr;
|
||||
ptrstr << "_ZN4arch";
|
||||
ptrstr << ptr_name.size();
|
||||
ptrstr << ptr_name;
|
||||
ptrstr << "E";
|
||||
const ElfSymbol &s_sptr = m_elf.getSymbol(ptrstr.str());
|
||||
if(!s_sptr.isValid()) {
|
||||
m_log << "no stack end symbol for " << name << " (" << ptrstr.str() << "), skipping!" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_log << "found task stack symbol: " << name << std::endl;
|
||||
|
||||
Checkpoint::indirectable_address_t start = std::make_pair(s_sptr.getAddress(), true);
|
||||
Checkpoint::indirectable_address_t end = std::make_pair(s_end.getEnd(), false);
|
||||
check_ranges.push_back(std::make_pair(start, end));
|
||||
}
|
||||
|
||||
// Init Plugins
|
||||
Checkpoint cpoint(check_ranges, "checkpoint.trace");
|
||||
|
||||
RandomGenerator* rgen;
|
||||
if (s_random_source.isValid()) {
|
||||
const unsigned seed = 12342;
|
||||
rgen = new RandomGenerator(s_random_source, seed);
|
||||
simulator.addFlow(rgen);
|
||||
}
|
||||
|
||||
// fast forward to injection address
|
||||
m_log << "Trying to inject @ instr #" << dec << injection_instr << endl;
|
||||
simulator.clearListeners(this);
|
||||
simulator.addListener(&l_fail_trace);
|
||||
|
||||
BPSingleListener bp;
|
||||
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||
// TODO: why does this need a +1?
|
||||
bp.setCounter(injection_instr+1);
|
||||
simulator.addListener(&bp);
|
||||
|
||||
fail::BaseListener * listener = simulator.resume();
|
||||
bool ok = true;
|
||||
|
||||
while ( ok && (listener == &l_fail_trace) ) {
|
||||
// m_log << "CP IP 0x" << std::hex << simulator.getCPU(0).getInstructionPointer() << std::endl;
|
||||
ok = cpoint.check(s_fail_trace, l_fail_trace.getTriggerInstructionPointer()) == Checkpoint::IDENTICAL;
|
||||
if(ok) listener = simulator.addListenerAndResume(&l_fail_trace);
|
||||
}
|
||||
|
||||
unsigned experiment_number = cpoint.getCount();
|
||||
|
||||
if(!ok) {
|
||||
result = param.msg.add_result();
|
||||
handleEvent(*result, result->NOINJECTION, "CHECKPOINT FAIL BEFORE INJECTION?!");
|
||||
|
||||
result->set_experiment_number((unsigned int) experiment_number);
|
||||
result->set_bitoffset(experiment_id);
|
||||
result->set_original_value(0);
|
||||
|
||||
break;
|
||||
}
|
||||
if (listener != &bp) {
|
||||
result = param.msg.add_result();
|
||||
handleEvent(*result, result->NOINJECTION, "WTF");
|
||||
|
||||
result->set_experiment_number((unsigned int) experiment_number);
|
||||
result->set_bitoffset(experiment_id);
|
||||
result->set_original_value(0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// program counter sanity check
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
m_log << "Trace " << std::dec << experiment_number << std::endl;
|
||||
result = param.msg.add_result();
|
||||
result->set_experiment_number((unsigned int) experiment_number);
|
||||
result->set_bitoffset(experiment_id);
|
||||
result->set_original_value(0);
|
||||
|
||||
// abort if outside targeted region
|
||||
address_t PC = simulator.getCPU(0).getInstructionPointer();
|
||||
//if(PC < min_code || PC > max_code) {
|
||||
if(PC < text_tasks_end && PC >= text_tasks_start) {
|
||||
std::stringstream ss;
|
||||
ss << "0x" << hex << PC;
|
||||
ss << " inside task text: ";
|
||||
ss << "0x" << hex << text_tasks_start << " - 0x" << hex << text_tasks_end;
|
||||
handleEvent(*result, result->NOINJECTION, ss.str());
|
||||
continue; // next experiment
|
||||
}
|
||||
|
||||
// perform injection
|
||||
if (pc_injection) {
|
||||
// jump to "data" address
|
||||
address_t current_PC = simulator.getCPU(0).getInstructionPointer();
|
||||
address_t new_PC = param.msg.fsppilot().data_address();
|
||||
m_log << "jump from 0x" << hex << current_PC << " to 0x" << new_PC << std::endl;
|
||||
simulator.getCPU(0).setRegisterContent(simulator.getCPU(0).getRegister(RID_PC), new_PC);
|
||||
|
||||
// set program counter
|
||||
result->set_bitoffset(0);
|
||||
result->set_original_value(current_PC);
|
||||
redecodeCurrentInstruction();
|
||||
} else {
|
||||
// inject random bitflip
|
||||
// experiment_id == bitpos
|
||||
result->set_original_value(injectBitFlip(data_address, data_width, experiment_id));
|
||||
}
|
||||
|
||||
|
||||
// add listeners
|
||||
simulator.clearListeners(this);
|
||||
simulator.addListener(&l_panic);
|
||||
simulator.addListener(&l_timeout);
|
||||
simulator.addListener(&l_fail_trace);
|
||||
simulator.addListener(&l_color_assert_port);
|
||||
simulator.addListener(&l_trace_end_marker);
|
||||
|
||||
// BPSingleListener single_step;
|
||||
// single_step.setWatchInstructionPointer(ANY_ADDR);
|
||||
// simulator.addListener(&single_step);
|
||||
// fail::BaseListener* ll = simulator.resume();
|
||||
// while (ll == &single_step || ll == &l_fail_trace) {
|
||||
// if (ll == &l_fail_trace) {
|
||||
// Checkpoint::check_result res = cpoint.check(s_fail_trace, l_fail_trace.getTriggerInstructionPointer());
|
||||
|
||||
// if(res == Checkpoint::DIFFERENT_IP) {
|
||||
// std::stringstream ss;
|
||||
// ss << "different IP";
|
||||
// ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||
// ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||
// std::cout << ss.str() << endl;
|
||||
// break;
|
||||
// } else if(res == Checkpoint::DIFFERENT_VALUE) {
|
||||
// std::stringstream ss;
|
||||
// ss << "different value";
|
||||
// ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||
// ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||
// std::cout << ss.str() << endl;
|
||||
// break;
|
||||
// } else if(res == Checkpoint::DIFFERENT_DIGEST) {
|
||||
// std::stringstream ss;
|
||||
// ss << "different digest";
|
||||
// ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||
// ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||
// std::cout << ss.str() << endl;
|
||||
// break;
|
||||
// } else if(res == Checkpoint::INVALID) {
|
||||
// std::stringstream ss;
|
||||
// ss << "invalid checkpoint";
|
||||
// ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||
// ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||
// std::cout << ss.str() << endl;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// std::stringstream ss;
|
||||
// ss << "@ IP " << std::hex << simulator.getCPU(0).getInstructionPointer();
|
||||
// const ElfSymbol &sym = m_elf.getSymbol(simulator.getCPU(0).getInstructionPointer());
|
||||
// ss << " " << sym.getName();
|
||||
// std::cout << ss.str() << endl;
|
||||
// ll = simulator.addListenerAndResume(ll);
|
||||
// }
|
||||
|
||||
// std::cout << (ll == &l_color_assert_port) << " COLOR ASSERT" << endl;
|
||||
|
||||
// //continue;
|
||||
// simulator.terminate(0);
|
||||
|
||||
// resume and wait for results
|
||||
m_log << "Resuming till the crash (time: " << simulator.getTimerTicks() << ")"<< std::endl;
|
||||
bool reached_check_start = false;
|
||||
fail::BaseListener* l = simulator.resume();
|
||||
|
||||
while(l == &l_fail_trace) {
|
||||
Checkpoint::check_result res = cpoint.check(s_fail_trace, l_fail_trace.getTriggerInstructionPointer());
|
||||
if(res == Checkpoint::DIFFERENT_IP) {
|
||||
std::stringstream ss;
|
||||
ss << "different IP";
|
||||
ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||
ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||
handleEvent(*result, result->SDC_WRONG_RESULT, ss.str());
|
||||
break;
|
||||
} else if(res == Checkpoint::DIFFERENT_VALUE) {
|
||||
std::stringstream ss;
|
||||
ss << "different value";
|
||||
ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||
ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||
handleEvent(*result, result->SDC_WRONG_RESULT, ss.str());
|
||||
break;
|
||||
} else if(res == Checkpoint::DIFFERENT_DIGEST) {
|
||||
std::stringstream ss;
|
||||
ss << "different digest";
|
||||
ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||
ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||
handleEvent(*result, result->SDC_WRONG_RESULT, ss.str());
|
||||
break;
|
||||
} else if(res == Checkpoint::INVALID) {
|
||||
std::stringstream ss;
|
||||
ss << "invalid checkpoint";
|
||||
ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||
ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||
handleEvent(*result, result->SDC_WRONG_RESULT, ss.str());
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset the soft timeout listener
|
||||
simulator.removeListener(&l_timeout);
|
||||
simulator.addListener(&l_timeout);
|
||||
assert(l_timeout.getTimeout() == i_timeout);
|
||||
|
||||
l = simulator.addListenerAndResume(&l_fail_trace);
|
||||
}
|
||||
|
||||
// End of Injection Phase. Now we have crashed
|
||||
m_log << "Crashed (time: " << simulator.getTimerTicks() << ")"<< std::endl;
|
||||
result->set_time_crash(simulator.getTimerTicks());
|
||||
|
||||
if(l == &l_fail_trace) {
|
||||
// already reported invalid checkpoint
|
||||
} else if(l == &l_trace_end_marker) {
|
||||
// trace ended successfully
|
||||
std::stringstream ss;
|
||||
ss << "correct end after " << cpoint.getCount() << " checkpoints";
|
||||
handleEvent(*result, result->OK, ss.str());
|
||||
} else if (l == &l_panic) {
|
||||
// error detected
|
||||
stringstream sstr;
|
||||
sstr << "PANIC";
|
||||
|
||||
// CoRedOS specific trap information
|
||||
const Register *reg_eax = simulator.getCPU(0).getRegister(RID_CAX);
|
||||
uint32_t trap = simulator.getCPU(0).getRegisterContent(reg_eax);
|
||||
sstr << " trap " << dec << trap;
|
||||
|
||||
//address_t sp = simulator.getCPU(0).getStackPointer();
|
||||
//if(mm.isMapped(sp)) {
|
||||
// uint32_t ip;
|
||||
// mm.getBytes(sp, 4, &ip);
|
||||
// sstr << " @ 0x" << hex << ip;
|
||||
|
||||
const Register *reg_esi = simulator.getCPU(0).getRegister(RID_CSI);
|
||||
address_t sp = simulator.getCPU(0).getRegisterContent(reg_esi) + 4;
|
||||
if(mm.isMapped(sp) && mm.isMapped(sp+1) && mm.isMapped(sp+2) && mm.isMapped(sp+3)) {
|
||||
|
||||
uint32_t ip;
|
||||
mm.getBytes(sp, 4, &ip);
|
||||
sstr << " from 0x" << hex << ip;
|
||||
}
|
||||
|
||||
handleEvent(*result, result->OK_DETECTED_ERROR, sstr.str());
|
||||
} else if ( l == &l_color_assert_port ) {
|
||||
// A colored assert has occured
|
||||
uint32_t color = 0;
|
||||
mm.getBytes(s_color_assert_port.getAddress(), 4, &color);
|
||||
std::stringstream ss;
|
||||
ss << "@ IP" << std::hex << simulator.getCPU(0).getInstructionPointer();
|
||||
|
||||
if (color == 0xb83829de) {
|
||||
handleEvent(*result, result->ERR_ASSERT_UNKOWN, ss.str());
|
||||
} else if (color == 0xf4d9f7ca) {
|
||||
handleEvent(*result, result->ERR_ASSERT_CFG_REGION, ss.str());
|
||||
} else if (color == 0x9451210d) {
|
||||
handleEvent(*result, result->ERR_ASSERT_SYSTEM_STATE, ss.str());
|
||||
} else {
|
||||
handleEvent(*result, result->ERR_ASSERT_SPURIOUS, ss.str());
|
||||
}
|
||||
} else if ( l == &l_timeout || l == &l_timeout_hard) {
|
||||
// timeout, probably infinite loop
|
||||
handleEvent(*result, result->ERR_TIMEOUT, "");
|
||||
} else {
|
||||
// what happened?
|
||||
handleEvent(*result, result->UNKNOWN, "WTF");
|
||||
}
|
||||
|
||||
// For RandomJump do only one experiment not 8
|
||||
if (pc_injection) break;
|
||||
}
|
||||
|
||||
// send results
|
||||
jobclient.sendResult(param);
|
||||
}
|
||||
|
||||
// explicitly terminate, or the simulator will continue to run
|
||||
simulator.terminate();
|
||||
}
|
||||
|
||||
33
src/experiments/cored-tester/experiment.hpp
Normal file
33
src/experiments/cored-tester/experiment.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __CORED_TESTER_EXPERIMENT_HPP__
|
||||
#define __CORED_TESTER_EXPERIMENT_HPP__
|
||||
|
||||
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/ElfReader.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "util/llvmdisassembler/LLVMtoFailTranslator.hpp"
|
||||
|
||||
class CoredTester : public fail::ExperimentFlow {
|
||||
public:
|
||||
|
||||
private:
|
||||
fail::Logger m_log;
|
||||
fail::MemoryManager& m_mm;
|
||||
fail::ElfReader m_elf;
|
||||
fail::LLVMtoFailTranslator* m_ltof;
|
||||
|
||||
unsigned injectBitFlip(fail::address_t data_address, unsigned data_width, unsigned bitpos);
|
||||
void redecodeCurrentInstruction();
|
||||
const fail::ElfSymbol& getELFSymbol(const std::string name);
|
||||
|
||||
public:
|
||||
CoredTester() : m_log("CoredTester", false), m_mm(fail::simulator.getMemoryManager()) {}
|
||||
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif
|
||||
20
src/experiments/cored-tester/main.cc
Normal file
20
src/experiments/cored-tester/main.cc
Normal file
@ -0,0 +1,20 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/CommandLine.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
fail::CommandLine &cmd = fail::CommandLine::Inst();
|
||||
for (int i = 1; i < argc; ++i)
|
||||
cmd.add_args(argv[i]);
|
||||
|
||||
CoredTesterCampaign c;
|
||||
if (fail::campaignmanager.runCampaign(&c)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
18
src/experiments/cored-tracing/CMakeLists.txt
Normal file
18
src/experiments/cored-tracing/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
set(EXPERIMENT_NAME cored-tracing)
|
||||
set(EXPERIMENT_TYPE CoRedTracing)
|
||||
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(fail-${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
|
||||
add_dependencies(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} fail-tracing fail-randomgenerator fail-checkpoint fail-comm)
|
||||
292
src/experiments/cored-tracing/experiment.cc
Normal file
292
src/experiments/cored-tracing/experiment.cc
Normal file
@ -0,0 +1,292 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "sal/Register.hpp"
|
||||
#include "sal/Listener.hpp"
|
||||
#include "experiment.hpp"
|
||||
#include "util/CommandLine.hpp"
|
||||
#include "util/gzstream/gzstream.h"
|
||||
|
||||
// required (enabled) plugins
|
||||
#include "../plugins/tracing/TracingPlugin.hpp"
|
||||
#include "../plugins/randomgenerator/RandomGenerator.hpp"
|
||||
#include "../plugins/checkpoint/Checkpoint.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
void CoRedTracing::parseOptions() {
|
||||
CommandLine &cmd = CommandLine::Inst();
|
||||
cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] -Wf,[option] ... <BochsOptions...>\n\n");
|
||||
CommandLine::option_handle HELP = cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit");
|
||||
|
||||
|
||||
CommandLine::option_handle ELF_FILE = cmd.addOption("", "elf-file", Arg::Required,
|
||||
"--elf-file \tELF Binary File (default: $FAIL_ELF_PATH)");
|
||||
CommandLine::option_handle START_SYMBOL = cmd.addOption("s", "start-symbol", Arg::Required,
|
||||
"-s,--start-symbol \tELF symbol to start tracing (default: main)");
|
||||
CommandLine::option_handle STOP_SYMBOL = cmd.addOption("e", "end-symbol", Arg::Required,
|
||||
"-e,--end-symbol \tELF symbol to end tracing");
|
||||
CommandLine::option_handle SAVE_SYMBOL = cmd.addOption("S", "save-symbol", Arg::Required,
|
||||
"-S,--save-symbol \tELF symbol to save the state of the machine (default: main)\n");
|
||||
CommandLine::option_handle STATE_FILE = cmd.addOption("f", "state-file", Arg::Required,
|
||||
"-f,--state-file \tFile/dir to save the state to (default: state)");
|
||||
CommandLine::option_handle TRACE_FILE = cmd.addOption("t", "trace-file", Arg::Required,
|
||||
"-t,--trace-file \tFile to save the execution trace to (default: trace.pb)\n");
|
||||
|
||||
CommandLine::option_handle FULL_TRACE = cmd.addOption("", "full-trace", Arg::None, "--full-trace \tDo a full trace (more data, default: off)");
|
||||
CommandLine::option_handle MEM_SYMBOL = cmd.addOption("m", "memory-symbol", Arg::Required,
|
||||
"-m,--memory-symbol \tELF symbol(s) to trace accesses (default: all mem read/writes are traced)");
|
||||
CommandLine::option_handle MEM_REGION = cmd.addOption("M", "memory-region", Arg::Required,
|
||||
"-M,--memory-region \trestrict memory region which is traced"
|
||||
" (Possible formats: 0x<address>, 0x<address>:0x<address>, 0x<address>:<length>)");
|
||||
|
||||
if (!cmd.parse()) {
|
||||
cerr << "Error parsing arguments." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (cmd[HELP]) {
|
||||
cmd.printUsage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (cmd[ELF_FILE].count() > 0)
|
||||
elf_file = cmd[ELF_FILE].first()->arg;
|
||||
else {
|
||||
char * elfpath = getenv("FAIL_ELF_PATH");
|
||||
if (elfpath == NULL) {
|
||||
m_log << "FAIL_ELF_PATH not set :( (alternative: --elf-file) " << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
elf_file = elfpath;
|
||||
}
|
||||
m_elf = new ElfReader(elf_file.c_str());
|
||||
|
||||
if (cmd[START_SYMBOL].count() > 0)
|
||||
start_symbol = cmd[START_SYMBOL].first()->arg;
|
||||
else
|
||||
start_symbol = "main";
|
||||
|
||||
if (cmd[STOP_SYMBOL].count() > 0)
|
||||
stop_symbol = std::string(cmd[STOP_SYMBOL].first()->arg);
|
||||
else {
|
||||
m_log << "You have to give an end symbol (-e,--end-symbol)!" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (cmd[SAVE_SYMBOL].count() > 0)
|
||||
save_symbol = std::string(cmd[SAVE_SYMBOL].first()->arg);
|
||||
else
|
||||
save_symbol = "main";
|
||||
|
||||
if (cmd[STATE_FILE].count() > 0)
|
||||
state_file = std::string(cmd[STATE_FILE].first()->arg);
|
||||
else
|
||||
state_file = "state";
|
||||
|
||||
if (cmd[TRACE_FILE].count() > 0)
|
||||
trace_file = std::string(cmd[TRACE_FILE].first()->arg);
|
||||
else
|
||||
trace_file = "trace.pb";
|
||||
|
||||
use_memory_map = false;
|
||||
|
||||
if (cmd[MEM_SYMBOL].count() > 0) {
|
||||
use_memory_map = true;
|
||||
option::Option *opt = cmd[MEM_SYMBOL].first();
|
||||
|
||||
while (opt != 0) {
|
||||
const ElfSymbol &symbol = m_elf->getSymbol(opt->arg);
|
||||
assert(symbol.isValid());
|
||||
|
||||
m_log << "Adding '" << opt->arg << "' == 0x" << std::hex << symbol.getAddress()
|
||||
<< "+" << std::dec << symbol.getSize() << " to trace map" << std::endl;
|
||||
traced_memory_map.add(symbol.getAddress(), symbol.getSize());
|
||||
|
||||
opt = opt->next();
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd[MEM_REGION].count() > 0) {
|
||||
use_memory_map = true;
|
||||
option::Option *opt = cmd[MEM_REGION].first();
|
||||
|
||||
while (opt != 0) {
|
||||
char *endptr;
|
||||
guest_address_t begin = strtol(opt->arg, &endptr, 16);
|
||||
guest_address_t size;
|
||||
if (endptr == opt->arg) {
|
||||
m_log << "Couldn't parse " << opt->arg << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
char delim = *endptr;
|
||||
if (delim == 0) {
|
||||
size = 1;
|
||||
} else if (delim == ':') {
|
||||
char *p = endptr +1;
|
||||
size = strtol(p, &endptr, 16) - begin;
|
||||
if (p == endptr || *endptr != 0) {
|
||||
m_log << "Couldn't parse " << opt->arg << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
} else if (delim == '+') {
|
||||
char *p = endptr +1;
|
||||
size = strtol(p, &endptr, 10);
|
||||
if (p == endptr || *endptr != 0) {
|
||||
m_log << "Couldn't parse " << opt->arg << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
m_log << "Couldn't parse " << opt->arg << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
traced_memory_map.add(begin, size);
|
||||
|
||||
m_log << "Adding " << opt->arg << " 0x" << std::hex << begin
|
||||
<< "+" << std::dec << size << " to trace map" << std::endl;
|
||||
|
||||
opt = opt->next();
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd[FULL_TRACE]) {
|
||||
this->full_trace = true;
|
||||
}
|
||||
|
||||
assert(m_elf->getSymbol(start_symbol).isValid());
|
||||
assert(m_elf->getSymbol(stop_symbol).isValid());
|
||||
assert(m_elf->getSymbol(save_symbol).isValid());
|
||||
|
||||
m_log << "start symbol: " << start_symbol << " 0x" << std::hex << m_elf->getSymbol(start_symbol).getAddress() << std::endl;
|
||||
m_log << "save symbol: " << save_symbol << " 0x" << std::hex << m_elf->getSymbol(save_symbol).getAddress() << std::endl;
|
||||
m_log << "stop symbol: " << stop_symbol << " 0x" << std::hex << m_elf->getSymbol(stop_symbol).getAddress() << std::endl;
|
||||
|
||||
m_log << "state file: " << state_file << std::endl;
|
||||
m_log << "trace file: " << trace_file << std::endl;
|
||||
m_log << "full-trace: " << this->full_trace << std::endl;
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool CoRedTracing::run()
|
||||
{
|
||||
parseOptions();
|
||||
|
||||
BPSingleListener l_start_symbol(m_elf->getSymbol(start_symbol).getAddress());
|
||||
BPSingleListener l_save_symbol (m_elf->getSymbol(save_symbol).getAddress());
|
||||
BPSingleListener l_stop_symbol (m_elf->getSymbol(stop_symbol).getAddress());
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// STEP 1: run until interesting function starts, start the tracing
|
||||
simulator.addListenerAndResume(&l_start_symbol);
|
||||
m_log << start_symbol << " reached, start tracing" << std::endl;
|
||||
|
||||
// restrict memory access logging to injection target
|
||||
TracingPlugin tp;
|
||||
tp.setFullTrace(this->full_trace);
|
||||
|
||||
if (use_memory_map) {
|
||||
m_log << "Use restricted memory map for tracing" << std::endl;
|
||||
tp.restrictMemoryAddresses(&traced_memory_map);
|
||||
}
|
||||
|
||||
ogzstream of(trace_file.c_str());
|
||||
if (of.bad()) {
|
||||
m_log << "Couldn't open trace file: " << trace_file << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
tp.setTraceFile(&of);
|
||||
|
||||
// this must be done *after* configuring the plugin:
|
||||
simulator.addFlow(&tp);
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// STEP 2: continue to the save point, and save state
|
||||
if (start_symbol != save_symbol) {
|
||||
simulator.addListenerAndResume(&l_save_symbol);
|
||||
}
|
||||
m_log << start_symbol << " reached, save state" << std::endl;
|
||||
simulator.save(state_file);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Step 3: add plugins
|
||||
// symbol to trigger checkpoints
|
||||
const ElfSymbol &s_fail_trace = m_elf->getSymbol("fail_trace");
|
||||
|
||||
Checkpoint *cpoint;
|
||||
if(s_fail_trace.isValid()) {
|
||||
Checkpoint::range_vector check_ranges;
|
||||
|
||||
ElfReader::symbol_iterator it = m_elf->sym_begin();
|
||||
for( ; it != m_elf->sym_end(); ++it) {
|
||||
const std::string name = it->getName();
|
||||
|
||||
size_t pos = name.rfind("_stack");
|
||||
if((pos == std::string::npos) || (pos != (name.size() - 6))) continue;
|
||||
|
||||
const ElfSymbol &s_end = m_elf->getSymbol(name); // *it ?
|
||||
const std::string ptr_name = "OS_" + name + "ptr";
|
||||
stringstream ptrstr;
|
||||
ptrstr << "_ZN4arch";
|
||||
ptrstr << ptr_name.size();
|
||||
ptrstr << ptr_name;
|
||||
ptrstr << "E";
|
||||
const ElfSymbol &s_sptr = m_elf->getSymbol(ptrstr.str());
|
||||
if(!s_sptr.isValid()) {
|
||||
m_log << "no stack end symbol for " << name << " (" << ptrstr.str() << "), skipping!" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_log << "found task stack symbol: " << name << std::endl;
|
||||
|
||||
Checkpoint::indirectable_address_t start = std::make_pair(s_sptr.getAddress(), true);
|
||||
Checkpoint::indirectable_address_t end = std::make_pair(s_end.getEnd(), false);
|
||||
check_ranges.push_back(std::make_pair(start, end));
|
||||
}
|
||||
|
||||
cpoint = new Checkpoint(s_fail_trace, check_ranges, "checkpoint.trace");
|
||||
simulator.addFlow(cpoint);
|
||||
} else {
|
||||
m_log << "Checkpoint plugin NOT added to simulation" << std::endl;
|
||||
}
|
||||
|
||||
// symbol to read random values from
|
||||
const ElfSymbol &s_random_source = m_elf->getSymbol("random_source");
|
||||
RandomGenerator *rgen;
|
||||
if (s_random_source.isValid()) {
|
||||
const unsigned seed = 12342;
|
||||
rgen = new RandomGenerator(s_random_source, seed);
|
||||
simulator.addFlow(rgen);
|
||||
} else {
|
||||
m_log << "Randomgenerator plugin NOT added to simulation" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Step 4: Continue to the stop point
|
||||
simulator.addListener(&l_stop_symbol);
|
||||
simulator.resume();
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Step 5: tear down the tracing
|
||||
|
||||
simulator.removeFlow(&tp);
|
||||
// serialize trace to file
|
||||
if (of.fail()) {
|
||||
m_log << "failed to write " << trace_file << std::endl;
|
||||
return false;
|
||||
}
|
||||
of.close();
|
||||
|
||||
simulator.clearListeners();
|
||||
simulator.terminate();
|
||||
|
||||
return true;
|
||||
}
|
||||
37
src/experiments/cored-tracing/experiment.hpp
Normal file
37
src/experiments/cored-tracing/experiment.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef __CORED_TRACING_HPP__
|
||||
#define __CORED_TRACING_HPP__
|
||||
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/ElfReader.hpp"
|
||||
#include "util/MemoryMap.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
class CoRedTracing : public fail::ExperimentFlow {
|
||||
std::string start_symbol;
|
||||
std::string stop_symbol;
|
||||
std::string save_symbol;
|
||||
|
||||
std::string state_file;
|
||||
std::string trace_file;
|
||||
std::string elf_file;
|
||||
|
||||
bool use_memory_map;
|
||||
fail::MemoryMap traced_memory_map;
|
||||
|
||||
bool full_trace;
|
||||
|
||||
fail::Logger m_log;
|
||||
fail::ElfReader *m_elf;
|
||||
|
||||
public:
|
||||
void parseOptions();
|
||||
bool run();
|
||||
|
||||
CoRedTracing() : full_trace(false), m_log("CoRedTracing", false) {};
|
||||
};
|
||||
|
||||
#endif // __CORED_TRACING_HPP__
|
||||
@ -16,6 +16,5 @@ set(MY_CAMPAIGN_SRCS
|
||||
## Build library
|
||||
add_library(fail-${EXPERIMENT_NAME} ${MY_CAMPAIGN_SRCS})
|
||||
add_dependencies(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME})
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} fail-comm fail-util)
|
||||
|
||||
|
||||
42
src/experiments/fiascoFail/CMakeLists.txt
Normal file
42
src/experiments/fiascoFail/CMakeLists.txt
Normal file
@ -0,0 +1,42 @@
|
||||
set(EXPERIMENT_NAME fiascoFail)
|
||||
set(EXPERIMENT_TYPE FiascoFailExperiment)
|
||||
configure_file(../instantiate-experiment-indirect.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
fiascofail.proto
|
||||
)
|
||||
|
||||
set(MY_CAMPAIGN_SRCS
|
||||
instantiateExperiment.cc
|
||||
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})
|
||||
|
||||
find_package(MySQL REQUIRED)
|
||||
include_directories(${MYSQL_INCLUDE_DIR})
|
||||
|
||||
set(PROTOBUF_IMPORT_DIRS ${PROTOBUF_IMPORT_DIRS} ${CMAKE_CURRENT_BINARY_DIR}/../../core/comm)
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
|
||||
|
||||
## Build library
|
||||
add_library(fail-${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
add_dependencies(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
#target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY} ${MYSQL_LIBRARIES})
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} fail)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY} ${MYSQL_LIBRARIES})
|
||||
|
||||
## This is the example's campaign server distributing experiment parameters
|
||||
add_executable(${EXPERIMENT_NAME}-server main.cc)
|
||||
target_link_libraries(${EXPERIMENT_NAME}-server fail-${EXPERIMENT_NAME} fail ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY} ${MYSQL_LIBRARIES})
|
||||
install(TARGETS ${EXPERIMENT_NAME}-server RUNTIME DESTINATION bin)
|
||||
9
src/experiments/fiascoFail/campaign.cc
Normal file
9
src/experiments/fiascoFail/campaign.cc
Normal file
@ -0,0 +1,9 @@
|
||||
#include "campaign.hpp"
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
|
||||
void FiascoFailCampaign::cb_send_pilot(DatabaseCampaignMessage pilot)
|
||||
{
|
||||
FiascoFailExperimentData *data = new FiascoFailExperimentData;
|
||||
data->msg.mutable_fsppilot()->CopyFrom(pilot);
|
||||
fail::campaignmanager.addParam(data);
|
||||
}
|
||||
32
src/experiments/fiascoFail/campaign.hpp
Normal file
32
src/experiments/fiascoFail/campaign.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "cpn/DatabaseCampaign.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "fiascofail.pb.h"
|
||||
//#include <google/protobuf/descriptor.h>
|
||||
|
||||
class FiascoFailExperimentData : public fail::ExperimentData
|
||||
{
|
||||
public:
|
||||
FiascofailProtoMsg msg;
|
||||
FiascoFailExperimentData() : fail::ExperimentData(&msg) {}
|
||||
};
|
||||
|
||||
class FiascoFailCampaign : public fail::DatabaseCampaign
|
||||
{
|
||||
virtual const google::protobuf::Descriptor * cb_result_message()
|
||||
{
|
||||
return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("FiascofailProtoMsg");
|
||||
}
|
||||
virtual void cb_send_pilot(DatabaseCampaignMessage pilot);
|
||||
|
||||
virtual int expected_number_of_results(std::string variant, std::string benchmark)
|
||||
{
|
||||
#if FIASCO_FAULTMODEL_BURST
|
||||
return 1;
|
||||
#else
|
||||
return 8;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
575
src/experiments/fiascoFail/experiment.cc
Normal file
575
src/experiments/fiascoFail/experiment.cc
Normal file
@ -0,0 +1,575 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
#include "sal/SALConfig.hpp"
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "sal/Memory.hpp"
|
||||
#include "sal/Listener.hpp"
|
||||
#include <sal/bochs/BochsMemory.hpp>
|
||||
#include "util/WallclockTimer.hpp"
|
||||
#include "config/FailConfig.hpp"
|
||||
#include "util/CommandLine.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
#define LOCAL 0
|
||||
|
||||
void FiascoFailExperiment::parseOptions()
|
||||
{
|
||||
CommandLine &cmd = CommandLine::Inst();
|
||||
cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] ... <BochsOptions...>\n\n");
|
||||
CommandLine::option_handle GOLDEN = cmd.addOption("g", "golden", Arg::None, "-g,--golden \tExecute golden-run experiment?");
|
||||
CommandLine::option_handle HELP = cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit");
|
||||
CommandLine::option_handle END_ADDRESS = cmd.addOption("E", "end", Arg::Required, "-E,--end \tEnd-Address of experiment");
|
||||
CommandLine::option_handle TOTAL_TIMER = cmd.addOption("T", "time", Arg::Required, "-T,--time \tTotal timer ticks of the golden run experiment from restoring point");
|
||||
CommandLine::option_handle TOTAL_INSTR = cmd.addOption("t", "total", Arg::Required, "-t,--total \tTotal instructions of the golden run experiment from restoring point");
|
||||
CommandLine::option_handle ECC_PANIC_FUNC = cmd.addOption("p", "panic", Arg::Required, "-p--panic \tAddress of the ecc_panic function");
|
||||
CommandLine::option_handle ERROR_CORRECTED_ADDR = cmd.addOption("c", "corrected", Arg::Required, "-c--corected \tAddress of the errors_corrected variable");
|
||||
|
||||
if(!cmd.parse())
|
||||
{
|
||||
cerr << "Error parsing arguments." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if(cmd[HELP])
|
||||
{
|
||||
cmd.printUsage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(cmd[GOLDEN].count() > 0)
|
||||
{
|
||||
_golden_run = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_golden_run = false;
|
||||
}
|
||||
|
||||
// Check if end-address is given
|
||||
if(cmd[END_ADDRESS].count() > 0)
|
||||
{
|
||||
endAddress = strtoul(cmd[END_ADDRESS].first()->arg, NULL, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log << "You have to give an end address!" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Check if number of golden run timer ticks is given
|
||||
if(cmd[TOTAL_TIMER].count() > 0)
|
||||
{
|
||||
golden_run_timer_ticks = strtoull(cmd[TOTAL_TIMER].first()->arg, NULL, 10);
|
||||
}
|
||||
else if(!_golden_run)
|
||||
{
|
||||
m_log << "You hava to give the number of total timer ticks of the golden run" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Check if number of total instructions is given
|
||||
if(cmd[TOTAL_INSTR].count() > 0)
|
||||
{
|
||||
golden_run_instructions = strtoul(cmd[TOTAL_INSTR].first()->arg, NULL, 10);
|
||||
}
|
||||
else if(!_golden_run)
|
||||
{
|
||||
m_log << "You have to give the number of total instructions of the golden run" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Check if ecc_panic function address is given
|
||||
if(cmd[ECC_PANIC_FUNC].count() > 0)
|
||||
{
|
||||
ecc_panic_address = strtoul(cmd[ECC_PANIC_FUNC].first()->arg, NULL, 16);
|
||||
}
|
||||
else if(!_golden_run)
|
||||
{
|
||||
m_log << "You have to give the address of the ecc_panic function" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Check if errors_corrected variable address is given
|
||||
if(cmd[ERROR_CORRECTED_ADDR].count() > 0)
|
||||
{
|
||||
addr_errors_corrected = strtoul(cmd[ERROR_CORRECTED_ADDR].first()->arg, NULL, 16);
|
||||
}
|
||||
else if(!_golden_run)
|
||||
{
|
||||
m_log << "You have to give the address of the errors_corrected variable" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void FiascoFailExperiment::readGoldenRun(string& target)
|
||||
{
|
||||
ifstream golden_run_file("golden.out");
|
||||
|
||||
if(!golden_run_file.good())
|
||||
{
|
||||
m_log << "Could not open file golden.out" << endl;
|
||||
simulator.terminate();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
target.assign((istreambuf_iterator<char>(golden_run_file)), istreambuf_iterator<char>());
|
||||
golden_run_file.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to record every output on the VGA-Output-Port in m_CurrentOutput
|
||||
* Runs until any breakpoint different from the VGA-Port is reached
|
||||
*/
|
||||
BaseListener* FiascoFailExperiment::waitIOOrOther(bool clear_output)
|
||||
{
|
||||
IOPortListener ev_ioport(0x3F8, true); // VGA-Output-Port: 0x3F8
|
||||
BaseListener* ev = NULL;
|
||||
if(clear_output)
|
||||
{
|
||||
m_CurrentOutput.clear();
|
||||
}
|
||||
while(true)
|
||||
{
|
||||
simulator.addListener(&ev_ioport); // Add VGA-Port to the current listeners...
|
||||
ev = simulator.resume(); // ...and continue
|
||||
if(ev == &ev_ioport) // If current breakpoint == VGA-Port...
|
||||
{
|
||||
m_CurrentOutput += ev_ioport.getData(); // ... add output to m_CurrentOutput
|
||||
}
|
||||
else // Else: Breakpoint different from VGA-Port reached, break
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ev; // Return the current breakpoint
|
||||
}
|
||||
|
||||
bool FiascoFailExperiment::run()
|
||||
{
|
||||
m_log << "startup" << endl;
|
||||
parseOptions();
|
||||
|
||||
if(_golden_run)
|
||||
{
|
||||
// Do the golden-run experiment
|
||||
goldenRun();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do the actual fault injection
|
||||
faultInjection();
|
||||
}
|
||||
|
||||
// Experiment finished
|
||||
simulator.terminate();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FiascoFailExperiment::faultInjection()
|
||||
{
|
||||
string golden_run;
|
||||
readGoldenRun(golden_run); // Read the output string from the golden run
|
||||
|
||||
BPSingleListener bp;
|
||||
int experiments = 0;
|
||||
#if !LOCAL
|
||||
for(experiments = 0; experiments < 500 || (m_jc.getNumberOfUndoneJobs() != 0); )
|
||||
{
|
||||
#endif
|
||||
m_log << "asking job server for experiment parameters" << endl;
|
||||
FiascoFailExperimentData param;
|
||||
#if !LOCAL
|
||||
if(!m_jc.getParam(param))
|
||||
{
|
||||
m_log << "Dying." << endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
#else
|
||||
// XXX debug
|
||||
param.msg.mutable_fsppilot()->set_injection_instr(96);
|
||||
param.msg.mutable_fsppilot()->set_injection_instr_absolute(4026670033);
|
||||
param.msg.mutable_fsppilot()->set_data_address(4237508604);
|
||||
param.msg.mutable_fsppilot()->set_data_width(1);
|
||||
param.msg.mutable_fsppilot()->set_variant("baseline");
|
||||
param.msg.mutable_fsppilot()->set_benchmark("shared_ds");
|
||||
#endif
|
||||
|
||||
if(param.msg.fsppilot().data_width() != 1)
|
||||
{
|
||||
m_log << "cannot deal with data_width = " << param.msg.fsppilot().data_width() << endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
|
||||
// Get the experiment data from the Job-Server
|
||||
int id = param.getWorkloadID();
|
||||
m_variant = param.msg.fsppilot().variant();
|
||||
m_benchmark = param.msg.fsppilot().benchmark();
|
||||
unsigned instr_offset = param.msg.fsppilot().injection_instr(); // Offset to the IP where the fault injection has to be done
|
||||
guest_address_t mem_addr = param.msg.fsppilot().data_address(); // Memory address wehre the fault injection has to be done
|
||||
|
||||
// for each job with the SINGLEBITFLIP fault model we're actually doing *8*
|
||||
// experiments (one for each bit)
|
||||
for(int bit_offset = 0; bit_offset < 8; ++bit_offset)
|
||||
{
|
||||
++experiments;
|
||||
|
||||
WallclockTimer timer; // Timer to log the actual time of the experiment
|
||||
timer.startTimer();
|
||||
|
||||
FiascofailProtoMsg_Result *result = param.msg.add_result(); // Protobuf object for the result
|
||||
result->set_bit_offset(bit_offset); // Set the bit offset (1 if FAULTMODEL_BURST is active)
|
||||
m_log << dec << "job " << id << " @bit: " << bit_offset << " " << m_variant << "/" << m_benchmark
|
||||
<< " instr-offset " << hex << instr_offset
|
||||
<< " mem " << hex << mem_addr << "+" << dec << bit_offset << endl;
|
||||
|
||||
m_log << "restoring state" << endl;
|
||||
simulator.restore("state"); // Restore the state (Entry point is the first IP of the main-function from the application)
|
||||
m_log << "restore @ip " << hex << simulator.getCPU(0).getInstructionPointer() << " finished!" << endl;
|
||||
|
||||
// convert to microseconds (simulator.getTimerTicksPerSecond() only
|
||||
// works reliably when simulation has begun)
|
||||
unsigned goldenrun_runtime = (unsigned)(golden_run_timer_ticks * 1000000.0 / simulator.getTimerTicksPerSecond());
|
||||
unsigned timeout_runtime = goldenrun_runtime + 1000000/18.2; // + 1 timer tick
|
||||
|
||||
BPSingleListener func_finish(endAddress); // Add the last IP of the main-function to the listeners (end of experiment)
|
||||
simulator.addListener(&func_finish);
|
||||
|
||||
|
||||
simtime_t time_start = simulator.getTimerTicks(); // measure elapsed time
|
||||
|
||||
if(instr_offset > 0)
|
||||
{
|
||||
bp.setWatchInstructionPointer(ANY_ADDR); // Create new Breakpoint...
|
||||
bp.setCounter(instr_offset + 1); // ...to break when the IP for the fault injection is reached...
|
||||
simulator.addListener(&bp); // ...and add it to the actual listeners
|
||||
|
||||
BaseListener *go = waitIOOrOther(true); // Resume simulation and log VGA-Output
|
||||
if(go == &func_finish) // If func_finish has triggerd the break, something went wong...
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "experiment reached finish() before FI";
|
||||
m_log << ss.str() << endl;
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
result->set_details(ss.str());
|
||||
result->set_runtime(timer);
|
||||
m_jc.sendResult(param);
|
||||
|
||||
continue; // ... so continue with next experiment
|
||||
}
|
||||
else if(go != &bp) // Else if the breakpoint for the fault injection is not reached, something went wrong...
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "experiment didn't reach bp";
|
||||
m_log << ss.str() << endl;
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
result->set_details(ss.str());
|
||||
result->set_latest_ip(simulator.getCPU(0).getInstructionPointer());
|
||||
result->set_runtime(timer);
|
||||
#if !LOCAL
|
||||
m_jc.sendResult(param);
|
||||
#endif
|
||||
|
||||
#if FIASCO_FAULTMODEL_BURST
|
||||
bit_offset = 8;
|
||||
#endif
|
||||
|
||||
continue; // ... so continue with the next experiment
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check (check if actual IP equals the trced IP)
|
||||
uint32_t injection_ip = simulator.getCPU(0).getInstructionPointer();
|
||||
if(param.msg.fsppilot().has_injection_instr_absolute() &&
|
||||
injection_ip != param.msg.fsppilot().injection_instr_absolute())
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "SANITY CHECK FAILED: " << hex << injection_ip
|
||||
<< " != " << hex << param.msg.fsppilot().injection_instr_absolute();
|
||||
m_log << ss.str() << endl;
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
result->set_latest_ip(injection_ip);
|
||||
result->set_details(ss.str());
|
||||
result->set_runtime(timer);
|
||||
#if !LOCAL
|
||||
m_jc.sendResult(param);
|
||||
#endif
|
||||
|
||||
#if FIASCO_FAULTMODEL_BURST
|
||||
bit_offset = 8;
|
||||
#endif
|
||||
|
||||
continue; // If sanity check fails: next experiment
|
||||
}
|
||||
if(param.msg.fsppilot().has_injection_instr_absolute())
|
||||
{
|
||||
m_log << "Absolute IP sanity check OK" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log << "Absolute IP sanity check skipped (job parameters insufficient)" << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- fault injection ---
|
||||
MemoryManager& mm = simulator.getMemoryManager(); // Get the memory manager from Bochs
|
||||
host_address_t addr = reinterpret_cast<BochsMemoryManager*>(&mm)->guestToHost(mem_addr); // check if the fault-address is mapped (guestToHost returns ADDR_INV if not)
|
||||
if (addr == (host_address_t)ADDR_INV)
|
||||
{
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
result->set_latest_ip(injection_ip);
|
||||
result->set_runtime(timer);
|
||||
stringstream ss;
|
||||
ss << "INVALID DATA-ADDRESS " << hex << mem_addr << " @ ip " << injection_ip;
|
||||
result->set_details(ss.str());
|
||||
m_jc.sendResult(param);
|
||||
|
||||
continue; // Faul-address is not mapped so continue with the next experiment
|
||||
}
|
||||
byte_t data = mm.getByte(mem_addr); // Get tha actual value stored in the fault-addres
|
||||
byte_t newdata;
|
||||
#if FIASCO_FAULTMODEL_BURST
|
||||
newdata = data ^ 0xff; // If Faultmode burst is active: Flip every 8 bits...
|
||||
bit_offset = 8; // ...and continue with the next byte
|
||||
#else
|
||||
newdata = data ^ (1 << bit_offset); // Else: Flip the bit according to the actual bit-offset and continue with next bit
|
||||
#endif
|
||||
mm.setByte(mem_addr, newdata); // Store the new data in the actual faut-address
|
||||
m_log << "fault injected @ ip " << injection_ip
|
||||
<< " 0x" << hex << ((int)data) << " -> 0x" << ((int)newdata) << endl;
|
||||
|
||||
|
||||
|
||||
|
||||
// --- aftermath ---
|
||||
// catch traps as "extraordinary" ending
|
||||
TrapListener ev_trap(ANY_TRAP);
|
||||
simulator.addListener(&ev_trap);
|
||||
|
||||
// jump outside text segment (TODO: text segments for multiple elf-files + paging)
|
||||
/*
|
||||
BPRangeListener ev_below_text(ANY_ADDR, addr_text_start -1); // TODO
|
||||
BPRangeListener ev_beyond_text(addr_text_end + 1, ANY_ADDR); // TODO
|
||||
simulator.addListener(&ev_below_text);
|
||||
simulator.addListener(&ev_beyond_text);
|
||||
*/
|
||||
|
||||
// timeout (e.g., stuck in a HLT instruction)
|
||||
TimerListener ev_timeout(timeout_runtime);
|
||||
simulator.addListener(&ev_timeout);
|
||||
|
||||
// grant generous (10x) more instructions before aborting to avoid false positives
|
||||
BPSingleListener ev_dyninstructions(ANY_ADDR);
|
||||
// FIXME overflow possible
|
||||
ev_dyninstructions.setCounter(golden_run_instructions * 10);
|
||||
simulator.addListener(&ev_dyninstructions);
|
||||
|
||||
// incomplete (e.g. cursors blinks so nothing happens any more or a longjump occurs)
|
||||
BPSingleListener ev_blink(FIASCO_BREAK_BLINK);
|
||||
simulator.addListener(&ev_blink);
|
||||
BPSingleListener ev_longjmp(FIASCO_BREAK_LONGJMP);
|
||||
simulator.addListener(&ev_longjmp);
|
||||
|
||||
// function called by ecc apsects, when an uncorrectable error is detected
|
||||
BPSingleListener func_ecc_panic(ecc_panic_address);
|
||||
if(ecc_panic_address != ADDR_INV)
|
||||
{
|
||||
simulator.addListener(&func_ecc_panic);
|
||||
}
|
||||
|
||||
// wait until experiment-terminating event occurs
|
||||
bool finished = false;
|
||||
BaseListener *go;
|
||||
while(!finished)
|
||||
{
|
||||
go = waitIOOrOther(false); // resume experiment until func_finish or any other BP is reached and log the output
|
||||
if(go == &ev_trap) // if a trap is triggered, check which one
|
||||
{
|
||||
// Traps that occour in golden run are considered as deliberate
|
||||
if(ev_trap.getTriggerNumber() == 14) // Page fault trap
|
||||
{
|
||||
finished = false;
|
||||
simulator.addListener(&ev_trap); // Trap considered as deliberate so continue
|
||||
}
|
||||
else
|
||||
{
|
||||
finished = true; // Trap not considered as deliberate so break
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
finished = true; // Experiment reached BP so break
|
||||
}
|
||||
}
|
||||
|
||||
// record latest IP regardless of result
|
||||
// TODO: consider recording latest IP within text segment, too, which
|
||||
// would make this usable for the jump-outside case
|
||||
result->set_latest_ip(simulator.getCPU(0).getInstructionPointer());
|
||||
|
||||
// record error_corrected regardless of result
|
||||
if (addr_errors_corrected != ADDR_INV)
|
||||
{
|
||||
int32_t error_corrected = mm.getByte(addr_errors_corrected);
|
||||
result->set_error_corrected(error_corrected ? result->TRUE : result->FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// not setting this yields NULL in the DB
|
||||
//result->set_error_corrected(0);
|
||||
}
|
||||
|
||||
result->set_sim_runtime_factor(
|
||||
(simulator.getTimerTicks() - time_start) / (double) golden_run_timer_ticks); // Get the runtime factor compared to the golden run
|
||||
|
||||
|
||||
// Look for result
|
||||
if(go == &func_finish) // If BP == func_finished...
|
||||
{
|
||||
if(strcmp(m_CurrentOutput.c_str(), golden_run.c_str()) == 0) // ...and output is equal to the golden run: Result: OK
|
||||
{
|
||||
m_log << "experiment finished ordinarily" << endl;
|
||||
result->set_resulttype(result->OK);
|
||||
}
|
||||
else // ...or output is different from the golden run: Result: SDC
|
||||
{
|
||||
m_log << "experiment finished, but output incorrect" << endl;
|
||||
result->set_resulttype(result->SDC);
|
||||
result->set_details(m_CurrentOutput.c_str());
|
||||
}
|
||||
}
|
||||
else if(go == &ev_trap) // If BP == trap: Result: Trap
|
||||
{
|
||||
m_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(go == &func_ecc_panic) // If BP == ecc_panic_function: Result: Detected (but not corrected)
|
||||
{
|
||||
m_log << "ECC Panic: uncorrectable error" << endl;
|
||||
result->set_resulttype(result->DETECTED); // DETECTED <=> ECC_PANIC <=> reboot
|
||||
}
|
||||
// TODO (see above)
|
||||
/*else if(go == &ev_below_text || go == &ev_beyond_text)
|
||||
{
|
||||
m_log << "Result Trap #" << ev_trap.getTriggerNumber() << endl;
|
||||
result->set_jump_outside(result->TRUE);
|
||||
result->set_resulttype(result->TRAP);
|
||||
|
||||
stringstream ss;
|
||||
ss << ev_trap.getTriggerNumber();
|
||||
result->set_details(ss.str());
|
||||
}*/
|
||||
else if(go == &ev_timeout || go == &ev_dyninstructions || go == &ev_blink || go == &ev_longjmp) // Result: Timeout if any of these BP occur
|
||||
{
|
||||
m_log << "Result TIMEOUT" << endl;
|
||||
result->set_resulttype(result->TIMEOUT);
|
||||
if(go == &ev_dyninstructions)
|
||||
{
|
||||
result->set_details("i");
|
||||
}
|
||||
else if(go == &ev_blink)
|
||||
{
|
||||
result->set_details("b");
|
||||
}
|
||||
else if(go == &ev_longjmp)
|
||||
{
|
||||
result->set_details("l");
|
||||
}
|
||||
else
|
||||
{
|
||||
result->set_details("t");
|
||||
}
|
||||
}
|
||||
else // None of the above BPs reached so obviously something went wrong
|
||||
{
|
||||
m_log << "Result WTF?" << endl;
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
|
||||
stringstream ss;
|
||||
ss << "event addr: " << go << " EIP " << simulator.getCPU(0).getInstructionPointer();
|
||||
result->set_details(ss.str());
|
||||
}
|
||||
result->set_runtime(timer);
|
||||
}
|
||||
|
||||
#if !LOCAL
|
||||
m_jc.sendResult(param); // Send the result back to the job server and continue with next experiment (if there is one)
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void FiascoFailExperiment::goldenRun()
|
||||
{
|
||||
std::vector<int> m_lTraps;
|
||||
|
||||
|
||||
simulator.restore("state"); // Restore state (Continues from the first IP in the main function of the Application)
|
||||
|
||||
BPSingleListener l_stop_address(endAddress); // Add the last IP of the main function to the actual listeners
|
||||
m_log << "Golden-Run start, Stop-Address: 0x" << hex << endAddress << endl;
|
||||
|
||||
std::string golden_output;
|
||||
ofstream golden_output_file("golden.out"); // Save the logged output in "golden.out"
|
||||
|
||||
simulator.addListener(&l_stop_address);
|
||||
|
||||
TrapListener ev_trap(ANY_TRAP); // Trap listeners, break if any trap is triggered
|
||||
simulator.addListener(&ev_trap);
|
||||
|
||||
bool finished = false;
|
||||
m_CurrentOutput = "";
|
||||
while(!finished) // Continue and log output
|
||||
{
|
||||
BaseListener* ev = waitIOOrOther(false);
|
||||
if(ev == &ev_trap) // if trap is triggered, log the number
|
||||
{
|
||||
m_lTraps.push_back(ev_trap.getTriggerNumber());
|
||||
simulator.addListener(&ev_trap);
|
||||
}
|
||||
else if(ev == &l_stop_address) // if stop address is reached, save the output and finish the experiment
|
||||
{
|
||||
golden_output.assign(m_CurrentOutput.c_str());
|
||||
golden_output_file << m_CurrentOutput.c_str();
|
||||
m_log << "Output successfully logged..." << endl;
|
||||
finished = true;
|
||||
}
|
||||
else // something went wrong
|
||||
{
|
||||
m_log << "Error on logging Output, terminating..." << endl;
|
||||
golden_output_file.close();
|
||||
simulator.clearListeners();
|
||||
simulator.terminate();
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
m_log << "Saving..." << endl;
|
||||
golden_output_file.close();
|
||||
m_log << "Done." << endl;
|
||||
|
||||
stringstream ss;
|
||||
ss << "triggered traps: ";
|
||||
for(std::vector<int>::iterator lIterator = m_lTraps.begin(); lIterator != m_lTraps.end(); ++lIterator)
|
||||
{
|
||||
ss << *lIterator << " ";
|
||||
}
|
||||
m_log << ss.str() << endl;
|
||||
}
|
||||
34
src/experiments/fiascoFail/experiment.hpp
Normal file
34
src/experiments/fiascoFail/experiment.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "util/Logger.hpp"
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
#include "sal/Listener.hpp"
|
||||
|
||||
class FiascoFailExperiment : public fail::ExperimentFlow
|
||||
{
|
||||
private:
|
||||
fail::Logger m_log;
|
||||
fail::JobClient m_jc;
|
||||
std::string m_variant, m_benchmark;
|
||||
void readGoldenRun(std::string &);
|
||||
fail::BaseListener* waitIOOrOther(bool);
|
||||
void parseOptions();
|
||||
bool faultInjection();
|
||||
|
||||
std::string m_CurrentOutput;
|
||||
fail::guest_address_t endAddress;
|
||||
unsigned golden_run_instructions;
|
||||
unsigned long long golden_run_timer_ticks;
|
||||
fail::guest_address_t ecc_panic_address;
|
||||
fail::guest_address_t addr_errors_corrected;
|
||||
bool _golden_run;
|
||||
void goldenRun();
|
||||
|
||||
public:
|
||||
FiascoFailExperiment() : m_log("FiascoFail", false){}
|
||||
bool run();
|
||||
};
|
||||
9
src/experiments/fiascoFail/experimentInfo.hpp
Normal file
9
src/experiments/fiascoFail/experimentInfo.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef __FIASCOFAIL_EXPERIMENT_INFO_HPP
|
||||
#define __FIASCOFAIL_EXPERIMENT_INFO_HPP
|
||||
|
||||
#define FIASCO_BREAK_BLINK 0xf004b800
|
||||
#define FIASCO_BREAK_LONGJMP 0xf004c88e
|
||||
|
||||
#define FIASCO_FAULTMODEL_BURST 1
|
||||
|
||||
#endif
|
||||
51
src/experiments/fiascoFail/fiascofail.proto
Normal file
51
src/experiments/fiascoFail/fiascofail.proto
Normal file
@ -0,0 +1,51 @@
|
||||
import "DatabaseCampaignMessage.proto";
|
||||
|
||||
message FiascofailProtoMsg {
|
||||
required DatabaseCampaignMessage fsppilot = 1;
|
||||
|
||||
repeated group Result = 2 {
|
||||
// single experiment bit offset
|
||||
required uint32 bit_offset = 1 [(sql_primary_key) = true];
|
||||
|
||||
enum ResultType {
|
||||
OK = 0;
|
||||
SDC = 1;
|
||||
DETECTED = 2;
|
||||
TRAP = 3;
|
||||
TIMEOUT = 4;
|
||||
UNKNOWN = 5;
|
||||
}
|
||||
required ResultType resulttype = 2;
|
||||
|
||||
// all subsequent fields are optional, resulting in NULLable DB
|
||||
// columns, saving space when unused; NULL should be interpreted as 0
|
||||
// for these cases
|
||||
enum MemAccessOutside {
|
||||
NONE = 0;
|
||||
READ = 1;
|
||||
WRITE = 2;
|
||||
}
|
||||
optional MemAccessOutside memaccess_outside = 3;
|
||||
|
||||
enum Flag {
|
||||
FALSE = 0;
|
||||
TRUE = 1;
|
||||
}
|
||||
optional Flag jump_outside = 4;
|
||||
|
||||
// did ECC correct the fault?
|
||||
optional Flag error_corrected = 5;
|
||||
|
||||
// simulated runtime factor compared to golden run (1.000 = golden run runtime)
|
||||
optional float sim_runtime_factor = 6;
|
||||
|
||||
// especially interesting for TRAP/UNKNOWN: latest IP
|
||||
optional uint32 latest_ip = 7;
|
||||
|
||||
// optional textual description of what happened
|
||||
optional string details = 8;
|
||||
|
||||
// experiment runtime (FIXME: should be part of DatabaseCampaignMessage instead)
|
||||
optional float runtime = 9;
|
||||
}
|
||||
}
|
||||
7
src/experiments/fiascoFail/instantiateExperiment.cc
Normal file
7
src/experiments/fiascoFail/instantiateExperiment.cc
Normal file
@ -0,0 +1,7 @@
|
||||
#include "experiment.hpp"
|
||||
#include "sal/SALInst.hpp"
|
||||
|
||||
void instantiateFiascoFailExperiment()
|
||||
{
|
||||
fail::simulator.addFlow(new FiascoFailExperiment);
|
||||
}
|
||||
25
src/experiments/fiascoFail/main.cc
Normal file
25
src/experiments/fiascoFail/main.cc
Normal file
@ -0,0 +1,25 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/CommandLine.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
fail::CommandLine &cmd = fail::CommandLine::Inst();
|
||||
for(int i = 1; i < argc; ++i)
|
||||
{
|
||||
cmd.add_args(argv[i]);
|
||||
}
|
||||
|
||||
FiascoFailCampaign c;
|
||||
if(fail::campaignmanager.runCampaign(&c))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
37
src/experiments/generic-experiment/CMakeLists.txt
Normal file
37
src/experiments/generic-experiment/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
||||
set(EXPERIMENT_NAME generic-experiment)
|
||||
set(EXPERIMENT_TYPE GenericExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
generic-experiment.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})
|
||||
|
||||
find_package(MySQL REQUIRED)
|
||||
include_directories(${MYSQL_INCLUDE_DIR})
|
||||
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
|
||||
|
||||
## Build library
|
||||
add_library(fail-${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
add_dependencies(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY} fail-sal)
|
||||
|
||||
## This is the example's campaign server distributing experiment parameters
|
||||
add_executable(${EXPERIMENT_NAME}-server main.cc)
|
||||
target_link_libraries(${EXPERIMENT_NAME}-server -Wl,--start-group fail-${EXPERIMENT_NAME} fail-sal fail-util fail-cpn fail-comm ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY} ${MYSQL_LIBRARIES} -Wl,--end-group)
|
||||
install(TARGETS ${EXPERIMENT_NAME}-server RUNTIME DESTINATION bin)
|
||||
21
src/experiments/generic-experiment/campaign.cc
Normal file
21
src/experiments/generic-experiment/campaign.cc
Normal file
@ -0,0 +1,21 @@
|
||||
#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"
|
||||
|
||||
#include "experimentInfo.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
using namespace google::protobuf;
|
||||
|
||||
void GenericExperimentCampaign::cb_send_pilot(DatabaseCampaignMessage pilot) {
|
||||
GenericExperimentData *data = new GenericExperimentData;
|
||||
data->msg.mutable_fsppilot()->CopyFrom(pilot);
|
||||
campaignmanager.addParam(data);
|
||||
}
|
||||
16
src/experiments/generic-experiment/campaign.hpp
Normal file
16
src/experiments/generic-experiment/campaign.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __KESOGCCAMPAIGN_HPP__
|
||||
#define __KESOGCCAMPAIGN_HPP__
|
||||
|
||||
#include "cpn/DatabaseCampaign.hpp"
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
class GenericExperimentCampaign : public fail::DatabaseCampaign {
|
||||
virtual const google::protobuf::Descriptor * cb_result_message() {
|
||||
return google::protobuf::DescriptorPool::generated_pool()
|
||||
->FindMessageTypeByName("GenericExperimentMessage");
|
||||
}
|
||||
|
||||
virtual void cb_send_pilot(DatabaseCampaignMessage pilot);
|
||||
};
|
||||
|
||||
#endif // __KESOGCCAMPAIGN_HPP__
|
||||
2
src/experiments/generic-experiment/config.cmake
Normal file
2
src/experiments/generic-experiment/config.cmake
Normal file
@ -0,0 +1,2 @@
|
||||
SET(bochs_configure_params "--enable-a20-pin;--enable-x86-64;--enable-cpu-level=6;--enable-ne2000;--enable-acpi;--enable-pci;--enable-usb;--enable-trace-cache;--enable-fast-function-calls;--enable-host-specific-asms;--enable-readline;--enable-clgd54xx;--enable-fpu;--enable-vmx=2;--enable-monitor-mwait;--enable-cdrom;--enable-sb16=linux;--enable-gdb-stub;--with-nogui" CACHE STRING "")
|
||||
|
||||
266
src/experiments/generic-experiment/experiment.cc
Normal file
266
src/experiments/generic-experiment/experiment.cc
Normal file
@ -0,0 +1,266 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
// getpid
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "experiment.hpp"
|
||||
#include "experimentInfo.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 "campaign.hpp"
|
||||
#include "generic-experiment.pb.h"
|
||||
#include "util/CommandLine.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
|
||||
GenericExperiment::~GenericExperiment() {}
|
||||
|
||||
static GenericExperimentData space_for_param;
|
||||
ExperimentData* GenericExperiment::cb_allocate_experiment_data() {
|
||||
return &space_for_param;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new result slot in the given experiment data
|
||||
*/
|
||||
google::protobuf::Message* GenericExperiment::cb_new_result(ExperimentData* data) {
|
||||
GenericExperimentData *param = static_cast<GenericExperimentData *>(data);
|
||||
GenericExperimentMessage_Result *result = param->msg.add_result();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void handleEvent(GenericExperimentMessage_Result& result,
|
||||
GenericExperimentMessage_Result_ResultType restype,
|
||||
unsigned int details) {
|
||||
cout << "Result details: " << restype << " "<< details << endl;
|
||||
result.set_resulttype(restype);
|
||||
result.set_details(details);
|
||||
}
|
||||
|
||||
void GenericExperiment::parseSymbols(const std::string &args, std::set<fail::BaseListener *> * into) {
|
||||
std::vector<std::string> elems;
|
||||
std::stringstream ss(args);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, ',')) {
|
||||
const ElfSymbol * symbol = &m_elf.getSymbol(item);
|
||||
if (!symbol->isValid()) {
|
||||
m_log << "ELF Symbol not found: " << item << endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
m_log << "Adding symbol " << item << " at 0x" << hex << symbol->getAddress() << endl;
|
||||
BPSingleListener *l = new BPSingleListener(symbol->getAddress());
|
||||
into->insert(l);
|
||||
end_markers.insert(l);
|
||||
listener_to_symbol[l] = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GenericExperiment::cb_start_experiment() {
|
||||
CommandLine &cmd = CommandLine::Inst();
|
||||
cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] -Wf,[option] ... <BochsOptions...>\n\n");
|
||||
CommandLine::option_handle HELP = cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit");
|
||||
|
||||
CommandLine::option_handle STATE_DIR = cmd.addOption("", "state-dir", Arg::Required,
|
||||
"--state-dir \t Path to the state directory");
|
||||
|
||||
// catch any trap
|
||||
CommandLine::option_handle TRAP = cmd.addOption("", "trap", Arg::None,
|
||||
"--trap \tCatch Traps");
|
||||
CommandLine::option_handle WRITE_MEM_TEXT = cmd.addOption("", "catch-write-textsegment", Arg::None,
|
||||
"--catch-write-textsegment \tCatch writes to the text segment");
|
||||
|
||||
CommandLine::option_handle WRITE_MEM_OUTERSPACE
|
||||
= cmd.addOption("", "catch-write-outerspace", Arg::None,
|
||||
"--catch-write-outerspace \tCatch writes to the outerspace");
|
||||
|
||||
CommandLine::option_handle TIMEOUT = cmd.addOption("", "timeout", Arg::Required,
|
||||
"--timeout \t Experiment Timeout in uS");
|
||||
|
||||
|
||||
std::map<std::string, CommandLine::option_handle> option_handles;
|
||||
for (std::map<std::string, ListenerSet *>::iterator it = end_marker_groups.begin();
|
||||
it != end_marker_groups.end(); ++it) {
|
||||
CommandLine::option_handle handle =
|
||||
cmd.addOption("", it->first, Arg::Required,
|
||||
"--" + it->first + " \tList of symbols (comma separated)");
|
||||
option_handles[it->first] = handle;
|
||||
}
|
||||
|
||||
if (!cmd.parse()) {
|
||||
cerr << "Error parsing arguments." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (cmd[HELP]) {
|
||||
cmd.printUsage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
address_t minimal_ip = INT_MAX; // Every address is lower
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd[WRITE_MEM_TEXT]) {
|
||||
m_log << "Catch writes to text segment from " << hex << minimal_ip << " to " << maximal_ip << std::endl;
|
||||
enabled_mem_text = true;
|
||||
|
||||
l_mem_text.setWatchAddress(minimal_ip);
|
||||
l_mem_text.setTriggerAccessType(MemAccessEvent::MEM_WRITE);
|
||||
l_mem_text.setWatchWidth(maximal_ip - minimal_ip);
|
||||
}
|
||||
|
||||
if (cmd[WRITE_MEM_OUTERSPACE]) {
|
||||
m_log << "Catch writes to outerspace from " << hex << " from " << maximal_data << std::endl;
|
||||
enabled_mem_outerspace = true;
|
||||
|
||||
l_mem_outerspace.setWatchAddress(maximal_data);
|
||||
l_mem_outerspace.setTriggerAccessType(MemAccessEvent::MEM_WRITE);
|
||||
l_mem_outerspace.setWatchWidth(0xfffffff0);
|
||||
}
|
||||
|
||||
if (cmd[TRAP]) {
|
||||
m_log << "Catch all traps" << endl;
|
||||
enabled_trap = true;
|
||||
}
|
||||
|
||||
if (cmd[STATE_DIR]) {
|
||||
std::string value(cmd[STATE_DIR].first()->arg);
|
||||
m_state_dir = value;
|
||||
m_log << "Set state dir to " << value << endl;
|
||||
}
|
||||
|
||||
if (cmd[TIMEOUT]) {
|
||||
std::string value(cmd[TIMEOUT].first()->arg);
|
||||
std::stringstream ss(value);
|
||||
ss >> m_Timeout;
|
||||
if (ss.bad()) {
|
||||
m_log << "Could not parse --timeout argument" << endl;
|
||||
return false; // Initialization failed
|
||||
}
|
||||
l_timeout.setTimeout(m_Timeout);
|
||||
enabled_timeout = true;
|
||||
m_log << "Enabled Experiment Timeout of " << dec << m_Timeout << " microseconds" << endl;
|
||||
}
|
||||
|
||||
for (std::map<std::string, CommandLine::option_handle>::iterator it = option_handles.begin();
|
||||
it != option_handles.end(); ++it) {
|
||||
if (cmd[option_handles[it->first]]) {
|
||||
option::Option *opt = cmd[option_handles[it->first]].first();
|
||||
while (opt != 0) {
|
||||
parseSymbols(std::string(opt->arg), end_marker_groups[it->first]);
|
||||
opt = opt->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Everything OK
|
||||
}
|
||||
|
||||
|
||||
bool GenericExperiment::cb_before_resume() {
|
||||
if (enabled_trap)
|
||||
simulator.addListener(&l_trap);
|
||||
|
||||
if (enabled_mem_text)
|
||||
simulator.addListener(&l_mem_text);
|
||||
|
||||
if (enabled_mem_outerspace) {
|
||||
std::cout << "enabled mem outerspace " << endl;
|
||||
simulator.addListener(&l_mem_outerspace);
|
||||
}
|
||||
|
||||
if (enabled_timeout)
|
||||
simulator.addListener(&l_timeout);
|
||||
|
||||
for (std::set<BaseListener *>::iterator it = end_markers.begin();
|
||||
it != end_markers.end(); ++it) {
|
||||
simulator.addListener(*it);
|
||||
}
|
||||
|
||||
return true; // everything OK
|
||||
}
|
||||
|
||||
void GenericExperiment::cb_after_resume(fail::BaseListener *event) {
|
||||
GenericExperimentMessage_Result * result = static_cast<GenericExperimentMessage_Result *>(this->get_current_result());
|
||||
|
||||
// Record the crash time
|
||||
result->set_crash_time(simulator.getTimerTicks());
|
||||
|
||||
|
||||
if (event == &l_timeout) {
|
||||
handleEvent(*result, result->TIMEOUT, m_Timeout);
|
||||
} else if (event == &l_trap) {
|
||||
handleEvent(*result, result->TRAP, l_trap.getTriggerNumber());
|
||||
} else if (event == &l_mem_text) {
|
||||
handleEvent(*result, result->WRITE_TEXTSEGMENT,
|
||||
l_mem_text.getTriggerAddress());
|
||||
|
||||
} else if (event == &l_mem_outerspace){
|
||||
handleEvent(*result, result->WRITE_OUTERSPACE,
|
||||
l_mem_outerspace.getTriggerAddress());
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// End Marker Groups
|
||||
//////////////////////////////////////////////////
|
||||
} else if (OK_marker.find(event) != OK_marker.end()) {
|
||||
const ElfSymbol *symbol = listener_to_symbol[event];
|
||||
handleEvent(*result, result->OK_MARKER, symbol->getAddress());
|
||||
|
||||
} else if (FAIL_marker.find(event) != FAIL_marker.end()) {
|
||||
const ElfSymbol *symbol = listener_to_symbol[event];
|
||||
handleEvent(*result, result->FAIL_MARKER, symbol->getAddress());
|
||||
|
||||
} else if (DETECTED_marker.find(event) != DETECTED_marker.end()) {
|
||||
const ElfSymbol *symbol = listener_to_symbol[event];
|
||||
handleEvent(*result, result->DETECTED_MARKER, symbol->getAddress());
|
||||
|
||||
} else if (GROUP1_marker.find(event) != GROUP1_marker.end()) {
|
||||
const ElfSymbol *symbol = listener_to_symbol[event];
|
||||
handleEvent(*result, result->GROUP1_MARKER, symbol->getAddress());
|
||||
|
||||
} else if (GROUP2_marker.find(event) != GROUP2_marker.end()) {
|
||||
const ElfSymbol *symbol = listener_to_symbol[event];
|
||||
handleEvent(*result, result->GROUP2_MARKER, symbol->getAddress());
|
||||
|
||||
} else if (GROUP3_marker.find(event) != GROUP3_marker.end()) {
|
||||
const ElfSymbol *symbol = listener_to_symbol[event];
|
||||
handleEvent(*result, result->GROUP3_MARKER, symbol->getAddress());
|
||||
|
||||
} else if (GROUP4_marker.find(event) != GROUP4_marker.end()) {
|
||||
const ElfSymbol *symbol = listener_to_symbol[event];
|
||||
handleEvent(*result, result->GROUP4_MARKER, symbol->getAddress());
|
||||
|
||||
} else {
|
||||
handleEvent(*result, result->UNKNOWN, 0);
|
||||
}
|
||||
}
|
||||
111
src/experiments/generic-experiment/experiment.hpp
Normal file
111
src/experiments/generic-experiment/experiment.hpp
Normal file
@ -0,0 +1,111 @@
|
||||
#ifndef __GENERIC_EXPERIMENT_EXPERIMENT_HPP__
|
||||
#define __GENERIC_EXPERIMENT_EXPERIMENT_HPP__
|
||||
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "efw/DatabaseExperiment.hpp"
|
||||
#include "sal/Listener.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/ElfReader.hpp"
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
|
||||
class GenericExperiment : public fail::DatabaseExperiment {
|
||||
fail::ElfReader m_elf;
|
||||
|
||||
std::string m_state_dir;
|
||||
|
||||
bool enabled_mem_text;
|
||||
fail::MemAccessListener l_mem_text;
|
||||
|
||||
bool enabled_mem_outerspace;
|
||||
fail::MemAccessListener l_mem_outerspace;
|
||||
|
||||
bool enabled_trap;
|
||||
fail::TrapListener l_trap;
|
||||
|
||||
bool enabled_timeout;
|
||||
unsigned m_Timeout;
|
||||
fail::TimerListener l_timeout;
|
||||
|
||||
std::map<fail::BaseListener *, const fail::ElfSymbol *> listener_to_symbol;
|
||||
|
||||
typedef std::set<fail::BaseListener *> ListenerSet;
|
||||
|
||||
ListenerSet end_markers;
|
||||
ListenerSet OK_marker;
|
||||
ListenerSet FAIL_marker;
|
||||
ListenerSet DETECTED_marker;
|
||||
ListenerSet GROUP1_marker;
|
||||
ListenerSet GROUP2_marker;
|
||||
ListenerSet GROUP3_marker;
|
||||
ListenerSet GROUP4_marker;
|
||||
|
||||
std::map<std::string, ListenerSet * > end_marker_groups;
|
||||
|
||||
void parseSymbols(const std::string &args, std::set<fail::BaseListener *> *into);
|
||||
|
||||
public:
|
||||
GenericExperiment() : DatabaseExperiment("GenericExperiment"),
|
||||
m_state_dir("state"),
|
||||
l_trap(fail::ANY_TRAP), l_timeout(0) {
|
||||
enabled_mem_text = false;
|
||||
enabled_mem_outerspace = false;
|
||||
enabled_trap = false;
|
||||
enabled_timeout = false;
|
||||
|
||||
end_marker_groups["ok-marker"] = &OK_marker;
|
||||
end_marker_groups["fail-marker"] = &FAIL_marker;
|
||||
end_marker_groups["detected-marker"] = &FAIL_marker;
|
||||
end_marker_groups["group1-marker"] = &GROUP1_marker;
|
||||
end_marker_groups["group2-marker"] = &GROUP2_marker;
|
||||
end_marker_groups["group3-marker"] = &GROUP3_marker;
|
||||
end_marker_groups["group4-marker"] = &GROUP4_marker;
|
||||
}
|
||||
virtual ~GenericExperiment();
|
||||
|
||||
/**
|
||||
* Get path to the state directory
|
||||
*/
|
||||
virtual std::string cb_state_directory() { return m_state_dir; }
|
||||
|
||||
/**
|
||||
* Allocate enough space to hold the incoming ExperimentData message.
|
||||
*/
|
||||
virtual fail::ExperimentData* cb_allocate_experiment_data();
|
||||
|
||||
/**
|
||||
* Allocate a new result slot in the given experiment data
|
||||
*/
|
||||
virtual google::protobuf::Message* cb_new_result(fail::ExperimentData* data);
|
||||
|
||||
/**
|
||||
* Callback that is called, before the actual experiment
|
||||
* starts. Simulation is terminated on false.
|
||||
* @param The current result message
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_start_experiment();
|
||||
|
||||
/**
|
||||
* Callback that is called before the resuming till crash has
|
||||
* started. This is called after the fault was injected. Here the
|
||||
* end listeners should be installed. Returns true on
|
||||
* success. Otherwise the experiment is canceled.
|
||||
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_before_resume();
|
||||
|
||||
/**
|
||||
* Callback that is called after the resume-till-crash phase with
|
||||
* the last triggered listener. This callback should collect all
|
||||
* data and fill up the result message.
|
||||
*/
|
||||
virtual void cb_after_resume(fail::BaseListener *event);
|
||||
};
|
||||
|
||||
#endif // __GENERIC_EXPERIMENT_EXPERIMENT_HPP__
|
||||
15
src/experiments/generic-experiment/experimentInfo.hpp
Normal file
15
src/experiments/generic-experiment/experimentInfo.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __EXPERIMENT_INFO_HPP__
|
||||
#define __EXPERIMENT_INFO_HPP__
|
||||
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "generic-experiment.pb.h"
|
||||
|
||||
class GenericExperimentData : public fail::ExperimentData {
|
||||
public:
|
||||
GenericExperimentMessage msg;
|
||||
GenericExperimentData() : fail::ExperimentData(&msg) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // __EXPERIMENT_INFO_HPP__
|
||||
36
src/experiments/generic-experiment/generic-experiment.proto
Normal file
36
src/experiments/generic-experiment/generic-experiment.proto
Normal file
@ -0,0 +1,36 @@
|
||||
import "DatabaseCampaignMessage.proto";
|
||||
|
||||
message GenericExperimentMessage {
|
||||
required DatabaseCampaignMessage fsppilot = 1;
|
||||
|
||||
repeated group Result = 2 {
|
||||
// This submessage is required by the database experiment and
|
||||
// is filled with standard experiment result values
|
||||
required DatabaseExperimentMessage base_result = 1;
|
||||
|
||||
// make these optional to reduce overhead for server->client communication
|
||||
enum ResultType {
|
||||
OK_MARKER = 1;
|
||||
FAIL_MARKER = 2;
|
||||
DETECTED_MARKER = 3;
|
||||
|
||||
GROUP1_MARKER = 4;
|
||||
GROUP2_MARKER = 5;
|
||||
GROUP3_MARKER = 6;
|
||||
GROUP4_MARKER = 7;
|
||||
|
||||
TIMEOUT = 8;
|
||||
TRAP = 9;
|
||||
WRITE_TEXTSEGMENT = 10;
|
||||
WRITE_OUTERSPACE = 11;
|
||||
|
||||
UNKNOWN = 100;
|
||||
}
|
||||
// result type, see above
|
||||
required ResultType resulttype = 4;
|
||||
|
||||
required uint64 crash_time = 5;
|
||||
|
||||
optional uint64 details = 7;
|
||||
}
|
||||
}
|
||||
20
src/experiments/generic-experiment/main.cc
Normal file
20
src/experiments/generic-experiment/main.cc
Normal file
@ -0,0 +1,20 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/CommandLine.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
fail::CommandLine &cmd = fail::CommandLine::Inst();
|
||||
for (int i = 1; i < argc; ++i)
|
||||
cmd.add_args(argv[i]);
|
||||
|
||||
GenericExperimentCampaign c;
|
||||
if (fail::campaignmanager.runCampaign(&c)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
10
src/experiments/generic-tracing/config.cmake
Normal file
10
src/experiments/generic-tracing/config.cmake
Normal file
@ -0,0 +1,10 @@
|
||||
SET(PLUGINS_ACTIVATED "tracing" CACHE STRING "")
|
||||
|
||||
SET(bochs_configure_params "--enable-a20-pin;--enable-x86-64;--enable-cpu-level=6;--enable-ne2000;--enable-acpi;--enable-pci;--enable-usb;--enable-trace-cache;--enable-fast-function-calls;--enable-host-specific-asms;--enable-readline;--enable-clgd54xx;--enable-fpu;--enable-vmx=2;--enable-monitor-mwait;--enable-cdrom;--enable-sb16=linux;--enable-gdb-stub;--with-nogui" CACHE STRING "")
|
||||
|
||||
# Build the import and prune trace tools always
|
||||
SET(BUILD_IMPORT_TRACE ON CACHE BOOL "" FORCE)
|
||||
SET(BUILD_PRUNE_TRACE ON CACHE BOOL "" FORCE)
|
||||
SET(BUILD_CONVERT_TRACE ON CACHE BOOL "" FORCE)
|
||||
SET(BUILD_DUMP_TRACE ON CACHE BOOL "" FORCE)
|
||||
SET(BUILD_LLVM_DISASSEMBLER ON CACHE BOOL "" FORCE)
|
||||
@ -15,3 +15,6 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
## build library
|
||||
add_library(fail-${EXPERIMENT_NAME} ${MY_EXPERIMENT_SRCS})
|
||||
add_dependencies(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} fail-tracing fail-serialoutput fail-comm fail-util)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY})
|
||||
|
||||
@ -27,8 +27,8 @@ PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
|
||||
|
||||
## Build library
|
||||
add_library(fail-${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
add_dependencies(fail-${EXPERIMENT_NAME} fail-comm)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} fail-tracing fail-comm)
|
||||
add_dependencies(fail-${EXPERIMENT_NAME} fail-comm fail-util fail-sal fail-tracing)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} fail-comm fail-util fail-sal fail-tracing)
|
||||
target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY})
|
||||
|
||||
## This is the example's campaign server distributing experiment parameters
|
||||
|
||||
4
src/experiments/weather-monitor/config.cmake
Normal file
4
src/experiments/weather-monitor/config.cmake
Normal file
@ -0,0 +1,4 @@
|
||||
## Configuration
|
||||
SET(PLUGINS_ACTIVATED "tracing" CACHE STRING "")
|
||||
|
||||
SET(bochs_configure_params "--enable-a20-pin;--enable-x86-64;--enable-cpu-level=6;--enable-ne2000;--enable-acpi;--enable-pci;--enable-usb;--enable-trace-cache;--enable-fast-function-calls;--enable-host-specific-asms;--enable-disasm;--enable-readline;--enable-clgd54xx;--enable-fpu;--enable-vmx=2;--enable-monitor-mwait;--enable-cdrom;--enable-sb16=linux;--enable-gdb-stub;--with-nogui" CACHE STRING "")
|
||||
@ -5,6 +5,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/ElfReader.hpp"
|
||||
#include "util/CommandLine.hpp"
|
||||
#include "util/gzstream/gzstream.h"
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
@ -25,6 +28,8 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
const std::string WeatherMonitorExperiment::dir_images(DIR_IMAGES);
|
||||
const std::string WeatherMonitorExperiment::dir_prerequisites(DIR_PREREQUISITES);
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \
|
||||
@ -32,61 +37,183 @@ using namespace fail;
|
||||
#error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
bool WeatherMonitorExperiment::run()
|
||||
bool WeatherMonitorExperiment::readElfSymbols(
|
||||
guest_address_t& entry,
|
||||
guest_address_t& text_start,
|
||||
guest_address_t& text_end,
|
||||
guest_address_t& data_start,
|
||||
guest_address_t& data_end,
|
||||
guest_address_t& wait_begin,
|
||||
guest_address_t& wait_end,
|
||||
guest_address_t& vptr_panic)
|
||||
{
|
||||
char const *statename = "bochs.state" WEATHER_SUFFIX;
|
||||
Logger log("Weathermonitor", false);
|
||||
BPSingleListener bp;
|
||||
ElfReader elfreader(filename_elf(m_variant, m_benchmark).c_str());
|
||||
|
||||
log << "startup" << endl;
|
||||
entry = elfreader.getSymbol("main").getAddress();
|
||||
text_start = elfreader.getSymbol("___TEXT_START__").getAddress();
|
||||
text_end = elfreader.getSymbol("___TEXT_END__").getAddress();
|
||||
data_start = elfreader.getSymbol("___DATA_START__").getAddress();
|
||||
data_end = elfreader.getSymbol("___BSS_END__").getAddress();
|
||||
wait_begin = elfreader.getSymbol("wait_begin").getAddress();
|
||||
wait_end = elfreader.getSymbol("wait_end").getAddress();
|
||||
|
||||
/*
|
||||
* this does not work as the guestsys doesn't output anything
|
||||
* albeit that, it's no longer needed in this experiment
|
||||
*/
|
||||
|
||||
#if 0
|
||||
// STEP 0: record memory map with vptr addresses
|
||||
ofstream mmap;
|
||||
mmap.open ("memory.map");
|
||||
GuestListener g;
|
||||
while (true) {
|
||||
simulator.addListenerAndResume(&g);
|
||||
mmap << g.getData() << flush;
|
||||
// vptr_panic only exists in guarded version
|
||||
vptr_panic = elfreader.getSymbol("vptr_panic").getAddress();
|
||||
// use a dummy address, in case the symbol cannot be found
|
||||
if (vptr_panic == ADDR_INV) {
|
||||
vptr_panic = 99999999;
|
||||
}
|
||||
mmap.close();
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
if (entry == ADDR_INV || text_start == ADDR_INV || text_end == ADDR_INV ||
|
||||
data_start == ADDR_INV || data_end == ADDR_INV ||
|
||||
wait_begin == ADDR_INV || wait_end == ADDR_INV) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string WeatherMonitorExperiment::filename_elf(const std::string& variant, const std::string& benchmark)
|
||||
{
|
||||
if (variant.size() && benchmark.size()) {
|
||||
return dir_images + "/" + variant + ".elf";
|
||||
}
|
||||
return "weather.elf";
|
||||
}
|
||||
|
||||
std::string WeatherMonitorExperiment::filename_state(const std::string& variant, const std::string& benchmark)
|
||||
{
|
||||
if (variant.size() && benchmark.size()) {
|
||||
return dir_prerequisites + "/" + variant + ".state";
|
||||
}
|
||||
return "state.weather";
|
||||
}
|
||||
|
||||
std::string WeatherMonitorExperiment::filename_trace(const std::string& variant, const std::string& benchmark)
|
||||
{
|
||||
if (variant.size() && benchmark.size()) {
|
||||
return dir_prerequisites + "/" + variant + ".trace";
|
||||
}
|
||||
return "trace.weather";
|
||||
}
|
||||
|
||||
std::string WeatherMonitorExperiment::filename_traceinfo(const std::string& variant, const std::string& benchmark)
|
||||
{
|
||||
if (variant.size() && benchmark.size()) {
|
||||
return dir_prerequisites + "/" + variant + ".info";
|
||||
}
|
||||
return "weather.info";
|
||||
}
|
||||
|
||||
bool WeatherMonitorExperiment::writeTraceInfo(unsigned numinstr_tracing, unsigned numinstr_after)
|
||||
{
|
||||
ofstream ti(filename_traceinfo(m_variant, m_benchmark).c_str(), ios::out);
|
||||
if (!ti.is_open()) {
|
||||
cout << "failed to open " << filename_traceinfo(m_variant, m_benchmark) << endl;
|
||||
return false;
|
||||
}
|
||||
ti << numinstr_tracing << endl << numinstr_after << endl;
|
||||
ti.flush();
|
||||
ti.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WeatherMonitorExperiment::readTraceInfo(unsigned& numinstr_tracing, unsigned& numinstr_after)
|
||||
{
|
||||
ifstream file(filename_traceinfo(m_variant, m_benchmark).c_str());
|
||||
if (!file.is_open()) {
|
||||
cout << "failed to open " << filename_traceinfo(m_variant, m_benchmark) << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
string buf;
|
||||
unsigned count = 0;
|
||||
|
||||
while (getline(file, buf)) {
|
||||
stringstream ss(buf, ios::in);
|
||||
switch (count) {
|
||||
case 0:
|
||||
ss >> numinstr_tracing;
|
||||
break;
|
||||
case 1:
|
||||
ss >> numinstr_after;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
file.close();
|
||||
assert(count == 2);
|
||||
return (count == 2);
|
||||
}
|
||||
|
||||
|
||||
void WeatherMonitorExperiment::parseOptions()
|
||||
{
|
||||
CommandLine &cmd = CommandLine::Inst();
|
||||
cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] -Wf,[option] ... <BochsOptions...>");
|
||||
CommandLine::option_handle HELP =
|
||||
cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit");
|
||||
CommandLine::option_handle VARIANT =
|
||||
cmd.addOption("", "variant", Arg::Required, "--variant v \texperiment variant");
|
||||
CommandLine::option_handle BENCHMARK =
|
||||
cmd.addOption("", "benchmark", Arg::Required, "--benchmark b \tbenchmark");
|
||||
|
||||
if (!cmd.parse()) {
|
||||
cerr << "Error parsing arguments." << endl;
|
||||
simulator.terminate(1);
|
||||
} else if (cmd[HELP]) {
|
||||
cmd.printUsage();
|
||||
simulator.terminate(0);
|
||||
}
|
||||
|
||||
if (cmd[VARIANT].count() > 0 && cmd[BENCHMARK].count() > 0) {
|
||||
m_variant = std::string(cmd[VARIANT].first()->arg);
|
||||
m_benchmark = std::string(cmd[BENCHMARK].first()->arg);
|
||||
} else {
|
||||
cerr << "Please supply parameters for --variant and --benchmark." << endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool WeatherMonitorExperiment::establishState(guest_address_t& entry)
|
||||
{
|
||||
// STEP 1: run until interesting function starts, and save state
|
||||
bp.setWatchInstructionPointer(WEATHER_FUNC_MAIN);
|
||||
bp.setWatchInstructionPointer(entry);
|
||||
simulator.addListenerAndResume(&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.getCPU(0).getInstructionPointer() == WEATHER_FUNC_MAIN);
|
||||
LOG << "test function entry reached, saving state" << endl;
|
||||
LOG << "EIP = " << hex << bp.getTriggerInstructionPointer() << endl;
|
||||
simulator.save(filename_state(m_variant, m_benchmark).c_str());
|
||||
assert(bp.getTriggerInstructionPointer() == entry);
|
||||
assert(simulator.getCPU(0).getInstructionPointer() == entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WeatherMonitorExperiment::performTrace(
|
||||
guest_address_t& entry,
|
||||
guest_address_t& data_start,
|
||||
guest_address_t& data_end,
|
||||
guest_address_t& wait_end)
|
||||
{
|
||||
// STEP 2: record trace for fault-space pruning
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(statename);
|
||||
log << "EIP = " << hex << simulator.getCPU(0).getInstructionPointer() << endl;
|
||||
assert(simulator.getCPU(0).getInstructionPointer() == WEATHER_FUNC_MAIN);
|
||||
LOG << "STEP 2 restoring state" << endl;
|
||||
simulator.restore(filename_state(m_variant, m_benchmark).c_str());
|
||||
LOG << "EIP = " << hex << simulator.getCPU(0).getInstructionPointer() << endl;
|
||||
assert(simulator.getCPU(0).getInstructionPointer() == entry);
|
||||
|
||||
log << "enabling tracing" << endl;
|
||||
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);
|
||||
mm.add(data_start, data_end - data_start);
|
||||
tp.restrictMemoryAddresses(&mm);
|
||||
//tp.setLogIPOnly(true);
|
||||
|
||||
// record trace
|
||||
char const *tracefile = "trace.tc" WEATHER_SUFFIX;
|
||||
ofstream of(tracefile);
|
||||
ogzstream of(filename_trace(m_variant, m_benchmark).c_str());
|
||||
tp.setTraceFile(&of);
|
||||
|
||||
// this must be done *after* configuring the plugin:
|
||||
@ -95,7 +222,7 @@ bool WeatherMonitorExperiment::run()
|
||||
#if 1
|
||||
// trace WEATHER_NUMITER_TRACING measurement loop iterations
|
||||
// -> calibration
|
||||
bp.setWatchInstructionPointer(WEATHER_FUNC_WAIT_END);
|
||||
bp.setWatchInstructionPointer(wait_end);
|
||||
bp.setCounter(WEATHER_NUMITER_TRACING);
|
||||
#else
|
||||
// FIXME this doesn't work properly: trace is one instruction too short as
|
||||
@ -111,54 +238,61 @@ bool WeatherMonitorExperiment::run()
|
||||
|
||||
// count instructions
|
||||
// FIXME add SAL functionality for this?
|
||||
int instr_counter = 0;
|
||||
unsigned numinstr_tracing = 0;
|
||||
while (simulator.resume() == &ev_count) {
|
||||
++instr_counter;
|
||||
++numinstr_tracing;
|
||||
simulator.addListener(&ev_count);
|
||||
}
|
||||
|
||||
log << dec << "tracing finished after " << instr_counter
|
||||
LOG << dec << "tracing finished after " << numinstr_tracing
|
||||
<< " 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;
|
||||
LOG << "failed to write " << filename_trace(m_variant, m_benchmark) << endl;
|
||||
simulator.clearListeners(this); // cleanup
|
||||
return false;
|
||||
}
|
||||
of.close();
|
||||
log << "trace written to " << tracefile << endl;
|
||||
LOG << "trace written to " << filename_trace(m_variant, m_benchmark) << endl;
|
||||
|
||||
// wait another WEATHER_NUMITER_AFTER measurement loop iterations
|
||||
bp.setWatchInstructionPointer(WEATHER_FUNC_WAIT_END);
|
||||
bp.setWatchInstructionPointer(wait_end);
|
||||
bp.setCounter(WEATHER_NUMITER_AFTER);
|
||||
simulator.addListener(&bp);
|
||||
|
||||
// count instructions
|
||||
// FIXME add SAL functionality for this?
|
||||
instr_counter = 0;
|
||||
unsigned numinstr_after = 0;
|
||||
while (simulator.resume() == &ev_count) {
|
||||
++instr_counter;
|
||||
++numinstr_after;
|
||||
simulator.addListener(&ev_count);
|
||||
}
|
||||
|
||||
log << dec << "experiment finished after " << instr_counter
|
||||
LOG << dec << "experiment finished after " << numinstr_after
|
||||
<< " instructions, seeing wait_end " << WEATHER_NUMITER_AFTER << " times" << endl;
|
||||
|
||||
#elif 0
|
||||
// STEP 3: The actual experiment.
|
||||
if (!writeTraceInfo(numinstr_tracing, numinstr_after)) {
|
||||
LOG << "failed to write " << filename_traceinfo(m_variant, m_benchmark) << endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WeatherMonitorExperiment::faultInjection()
|
||||
{
|
||||
#if !LOCAL
|
||||
for (int i = 0; i < 50 || (m_jc.getNumberOfUndoneJobs() != 0) ; ++i) { // only do 50 sequential experiments, to prevent swapping
|
||||
// 50 exp ~ 0.5GB RAM usage per instance (linearly increasing)
|
||||
#endif
|
||||
|
||||
// get an experiment parameter set
|
||||
log << "asking job server for experiment parameters" << endl;
|
||||
LOG << "asking job server for experiment parameters" << endl;
|
||||
WeatherMonitorExperimentData param;
|
||||
#if !LOCAL
|
||||
if (!m_jc.getParam(param)) {
|
||||
log << "Dying." << endl;
|
||||
LOG << "Dying." << endl;
|
||||
// communicate that we were told to die
|
||||
simulator.terminate(1);
|
||||
}
|
||||
@ -168,9 +302,29 @@ bool WeatherMonitorExperiment::run()
|
||||
param.msg.fsppilot().set_data_address(0x00103bdc);
|
||||
#endif
|
||||
|
||||
|
||||
int id = param.getWorkloadID();
|
||||
m_variant = param.msg.fsppilot().variant();
|
||||
m_benchmark = param.msg.fsppilot().benchmark();
|
||||
unsigned injection_instr = param.msg.fsppilot().injection_instr();
|
||||
|
||||
/* get symbols from ELF */
|
||||
LOG << "retrieving ELF addresses..." << endl;
|
||||
guest_address_t entry, text_start, text_end, data_start, data_end, wait_begin, wait_end, vptr_panic;
|
||||
if (!readElfSymbols(entry, text_start, text_end, data_start, data_end, wait_begin, wait_end, vptr_panic)) {
|
||||
LOG << "failed, essential symbols are missing!" << endl;
|
||||
simulator.terminate(1);
|
||||
} else {
|
||||
LOG << "successfully retrieved ELF's addresses." << endl;
|
||||
}
|
||||
|
||||
/* get NUMINSTR_TRACING and NUMINSTR_AFTER */
|
||||
unsigned numinstr_tracing, numinstr_after;
|
||||
if (!readTraceInfo(numinstr_tracing, numinstr_after)) {
|
||||
LOG << "failed to read trace info from " << filename_traceinfo(m_variant, m_benchmark) << endl;
|
||||
simulator.terminate(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
address_t data_address = param.msg.fsppilot().data_address();
|
||||
|
||||
//old data. now it resides in the DatabaseCampaignMessage
|
||||
@ -180,11 +334,11 @@ bool WeatherMonitorExperiment::run()
|
||||
// 8 results in one job
|
||||
WeathermonitorProtoMsg_Result *result = param.msg.add_result();
|
||||
result->set_bitoffset(bit_offset);
|
||||
log << dec << "job " << id << " instr " << injection_instr
|
||||
LOG << dec << "job " << id << " instr " << injection_instr
|
||||
<< " mem " << data_address << "+" << bit_offset << endl;
|
||||
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(statename);
|
||||
LOG << "restoring state" << endl;
|
||||
simulator.restore(filename_state(m_variant, m_benchmark).c_str());
|
||||
|
||||
// XXX debug
|
||||
/*
|
||||
@ -197,13 +351,13 @@ bool WeatherMonitorExperiment::run()
|
||||
|
||||
// this marks THE END
|
||||
BPSingleListener ev_end(ANY_ADDR);
|
||||
ev_end.setCounter(WEATHER_NUMINSTR_TRACING + WEATHER_NUMINSTR_AFTER);
|
||||
ev_end.setCounter(numinstr_tracing + numinstr_after);
|
||||
simulator.addListener(&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"
|
||||
BPSingleListener ev_wait_begin(WEATHER_FUNC_WAIT_BEGIN);
|
||||
BPSingleListener ev_wait_begin(wait_begin);
|
||||
simulator.addListener(&ev_wait_begin);
|
||||
int count_loop_iter_before = 0;
|
||||
|
||||
@ -229,7 +383,7 @@ bool WeatherMonitorExperiment::run()
|
||||
// note at what IP we did it
|
||||
uint32_t injection_ip = simulator.getCPU(0).getInstructionPointer();
|
||||
result->set_iter_before_fi(count_loop_iter_before);
|
||||
log << "fault injected @ ip " << injection_ip
|
||||
LOG << "fault injected @ ip " << injection_ip
|
||||
<< " 0x" << hex << ((int)data) << " -> 0x" << ((int)newdata) << endl;
|
||||
// sanity check
|
||||
if (param.msg.fsppilot().has_injection_instr_absolute() &&
|
||||
@ -237,7 +391,7 @@ bool WeatherMonitorExperiment::run()
|
||||
stringstream ss;
|
||||
ss << "SANITY CHECK FAILED: " << injection_ip
|
||||
<< " != " << param.msg.fsppilot().injection_instr_absolute();
|
||||
log << ss.str() << endl;
|
||||
LOG << ss.str() << endl;
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
result->set_latest_ip(injection_ip);
|
||||
result->set_details(ss.str());
|
||||
@ -264,12 +418,12 @@ bool WeatherMonitorExperiment::run()
|
||||
TrapListener ev_trap(ANY_TRAP);
|
||||
simulator.addListener(&ev_trap);
|
||||
// jump outside text segment
|
||||
BPRangeListener ev_below_text(ANY_ADDR, WEATHER_TEXT_START - 1);
|
||||
BPRangeListener ev_beyond_text(WEATHER_TEXT_END + 1, ANY_ADDR);
|
||||
BPRangeListener ev_below_text(ANY_ADDR, text_start - 1);
|
||||
BPRangeListener ev_beyond_text(text_end + 1, ANY_ADDR);
|
||||
simulator.addListener(&ev_below_text);
|
||||
simulator.addListener(&ev_beyond_text);
|
||||
// error detected
|
||||
BPSingleListener ev_detected(WEATHER_FUNC_VPTR_PANIC);
|
||||
BPSingleListener ev_detected(vptr_panic);
|
||||
simulator.addListener(&ev_detected);
|
||||
// timeout (e.g., stuck in a HLT instruction)
|
||||
// 10000us = 500000 instructions
|
||||
@ -278,7 +432,7 @@ bool WeatherMonitorExperiment::run()
|
||||
|
||||
#if LOCAL && 0
|
||||
// XXX debug
|
||||
log << "enabling tracing" << endl;
|
||||
LOG << "enabling tracing" << endl;
|
||||
TracingPlugin tp;
|
||||
tp.setLogIPOnly(true);
|
||||
tp.setOstream(&cout);
|
||||
@ -300,28 +454,28 @@ bool WeatherMonitorExperiment::run()
|
||||
result->set_latest_ip(simulator.getCPU(0).getInstructionPointer());
|
||||
|
||||
if (ev == &ev_end) {
|
||||
log << "Result FINISHED (" << dec
|
||||
LOG << "Result FINISHED (" << dec
|
||||
<< count_loop_iter_before << "+" << count_loop_iter_after << ")" << endl;
|
||||
result->set_resulttype(result->FINISHED);
|
||||
} else if (ev == &ev_timeout) {
|
||||
log << "Result TIMEOUT (" << dec
|
||||
LOG << "Result TIMEOUT (" << dec
|
||||
<< count_loop_iter_before << "+" << count_loop_iter_after << ")" << endl;
|
||||
result->set_resulttype(result->TIMEOUT);
|
||||
} else if (ev == &ev_below_text || ev == &ev_beyond_text) {
|
||||
log << "Result OUTSIDE" << endl;
|
||||
LOG << "Result OUTSIDE" << endl;
|
||||
result->set_resulttype(result->OUTSIDE);
|
||||
} else if (ev == &ev_trap) {
|
||||
log << dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl;
|
||||
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;
|
||||
LOG << dec << "Result DETECTED" << endl;
|
||||
result->set_resulttype(result->DETECTED);
|
||||
} else {
|
||||
log << "Result WTF?" << endl;
|
||||
LOG << "Result WTF?" << endl;
|
||||
result->set_resulttype(result->UNKNOWN);
|
||||
|
||||
stringstream ss;
|
||||
@ -331,7 +485,7 @@ bool WeatherMonitorExperiment::run()
|
||||
}
|
||||
// sanity check: do we have exactly 8 results?
|
||||
if (param.msg.result_size() != 8) {
|
||||
log << "WTF? param.msg.result_size() != 8" << endl;
|
||||
LOG << "WTF? param.msg.result_size() != 8" << endl;
|
||||
} else {
|
||||
#if !LOCAL
|
||||
m_jc.sendResult(param);
|
||||
@ -342,7 +496,44 @@ bool WeatherMonitorExperiment::run()
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WeatherMonitorExperiment::run()
|
||||
{
|
||||
LOG << "startup" << endl;
|
||||
#if PREREQUISITES
|
||||
parseOptions();
|
||||
|
||||
/* get symbols from ELF */
|
||||
LOG << "retrieving ELF addresses..." << endl;
|
||||
guest_address_t entry, text_start, text_end, data_start, data_end, wait_begin, wait_end, vptr_panic;
|
||||
if (!readElfSymbols(entry, text_start, text_end, data_start, data_end, wait_begin, wait_end, vptr_panic)) {
|
||||
LOG << "failed, essential symbols are missing!" << endl;
|
||||
simulator.terminate(1);
|
||||
} else {
|
||||
LOG << "successfully retrieved ELF's addresses." << endl;
|
||||
}
|
||||
|
||||
//STEP 1
|
||||
if (establishState(entry)) {
|
||||
LOG << "STEP 1 (establish state) finished." << endl;
|
||||
} else {
|
||||
LOG << "STEP 1 (establish state) failed!" << endl;
|
||||
}
|
||||
|
||||
//STEP 2
|
||||
if (performTrace(entry, data_start, data_end, wait_end)) {
|
||||
LOG << "STEP 2 (perform trace) finished." << endl;
|
||||
} else {
|
||||
LOG << "STEP 2 (perform trace) failed!" << endl;
|
||||
}
|
||||
|
||||
#else // !PREREQUISITES i.e. STEP 3 "the actual experiment"
|
||||
faultInjection();
|
||||
|
||||
#endif
|
||||
|
||||
// Explicitly terminate, or the simulator will continue to run.
|
||||
simulator.terminate();
|
||||
}
|
||||
|
||||
@ -4,10 +4,39 @@
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/ElfReader.hpp"
|
||||
|
||||
#include "sal/Listener.hpp"
|
||||
#include "sal/SALConfig.hpp"
|
||||
|
||||
class WeatherMonitorExperiment : public fail::ExperimentFlow {
|
||||
fail::JobClient m_jc;
|
||||
std::string m_variant, m_benchmark;
|
||||
static const std::string dir_images;
|
||||
static const std::string dir_prerequisites;
|
||||
fail::Logger LOG;
|
||||
fail::BPSingleListener bp;
|
||||
|
||||
std::string filename_elf(const std::string& variant, const std::string& benchmark);
|
||||
std::string filename_state(const std::string& variant, const std::string& benchmark);
|
||||
std::string filename_trace(const std::string& variant, const std::string& benchmark);
|
||||
std::string filename_traceinfo(const std::string& variant, const std::string& benchmark);
|
||||
bool writeTraceInfo(unsigned numinstr_tracing, unsigned numinstr_after);
|
||||
bool readTraceInfo(unsigned& numinstr_tracing, unsigned& numinstr_after);
|
||||
bool readElfSymbols(fail::guest_address_t& entry, fail::guest_address_t& text_start,
|
||||
fail::guest_address_t& text_end, fail::guest_address_t& data_start,
|
||||
fail::guest_address_t& data_end, fail::guest_address_t& wait_begin,
|
||||
fail::guest_address_t& wait_end, fail::guest_address_t& vptr_panic);
|
||||
bool establishState(fail::guest_address_t& entry);
|
||||
bool performTrace(fail::guest_address_t& entry, fail::guest_address_t& data_start,
|
||||
fail::guest_address_t& data_end, fail::guest_address_t& wait_end);
|
||||
bool faultInjection();
|
||||
|
||||
public:
|
||||
WeatherMonitorExperiment() : LOG("Weathermonitor", false) {}
|
||||
bool run();
|
||||
void parseOptions(void);
|
||||
};
|
||||
|
||||
#endif // __WEATHERMONITOR_EXPERIMENT_HPP__
|
||||
|
||||
@ -1,127 +1,9 @@
|
||||
#ifndef __WEATHERMONITOR_EXPERIMENT_INFO_HPP__
|
||||
#define __WEATHERMONITOR_EXPERIMENT_INFO_HPP__
|
||||
#pragma once
|
||||
|
||||
// autogenerated, don't edit!
|
||||
#define DIR_IMAGES "oostubs"
|
||||
#define DIR_PREREQUISITES "prerequisites"
|
||||
#define PREREQUISITES 0 //1 == STEP1/2
|
||||
|
||||
// 0 = vanilla, 1 = guarded, 2 = plausibility
|
||||
#define WEATHERMONITOR_VARIANT 0
|
||||
#define WEATHER_NUMITER_TRACING 4 //same for all variants
|
||||
#define WEATHER_NUMITER_AFTER 2 //same for all variants
|
||||
|
||||
#if WEATHERMONITOR_VARIANT == 0 // without vptr guards
|
||||
|
||||
// suffix for simulator state, trace file
|
||||
#define WEATHER_SUFFIX ".vanilla"
|
||||
// main() address:
|
||||
// nm -C vanilla.elf|fgrep main
|
||||
#define WEATHER_FUNC_MAIN 0x00100d70
|
||||
// wait_begin address
|
||||
#define WEATHER_FUNC_WAIT_BEGIN 0x00100d68
|
||||
// wait_end address
|
||||
#define WEATHER_FUNC_WAIT_END 0x00100d6c
|
||||
// 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 20599
|
||||
// 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 10272
|
||||
// data/BSS begin:
|
||||
// nm -C vanilla.elf|fgrep ___DATA_START__
|
||||
#define WEATHER_DATA_START 0x00101558
|
||||
// data/BSS end:
|
||||
// nm -C vanilla.elf|fgrep ___BSS_END__
|
||||
#define WEATHER_DATA_END 0x00102e48
|
||||
// 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 0x0010139f
|
||||
|
||||
#elif WEATHERMONITOR_VARIANT == 1 // with guards
|
||||
|
||||
// suffix for simulator state, trace file
|
||||
#define WEATHER_SUFFIX ".guarded"
|
||||
// main() address:
|
||||
// nm -C guarded.elf|fgrep main
|
||||
#define WEATHER_FUNC_MAIN 0x00100dac
|
||||
// wait_begin address
|
||||
#define WEATHER_FUNC_WAIT_BEGIN 0x00100da4
|
||||
// wait_end address
|
||||
#define WEATHER_FUNC_WAIT_END 0x00100da8
|
||||
// vptr_panic address (only exists in guarded variant)
|
||||
#define WEATHER_FUNC_VPTR_PANIC 0x0010104c
|
||||
// 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 20599
|
||||
// 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 10272
|
||||
// data/BSS begin:
|
||||
// nm -C guarded.elf|fgrep ___DATA_START__
|
||||
#define WEATHER_DATA_START 0x00101878
|
||||
// data/BSS end:
|
||||
// nm -C guarded.elf|fgrep ___BSS_END__
|
||||
#define WEATHER_DATA_END 0x00103198
|
||||
// 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 0x001016af
|
||||
|
||||
#elif WEATHERMONITOR_VARIANT == 2 // with guards + plausibility check
|
||||
|
||||
// suffix for simulator state, trace file
|
||||
#define WEATHER_SUFFIX ".plausibility"
|
||||
// main() address:
|
||||
// nm -C plausibility.elf|fgrep main
|
||||
#define WEATHER_FUNC_MAIN 0x00100dbc
|
||||
// wait_begin address
|
||||
#define WEATHER_FUNC_WAIT_BEGIN 0x00100db4
|
||||
// wait_end address
|
||||
#define WEATHER_FUNC_WAIT_END 0x00100db8
|
||||
// vptr_panic address (only exists in guarded variant)
|
||||
#define WEATHER_FUNC_VPTR_PANIC 0x001010f0
|
||||
// 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 20599
|
||||
// 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 10272
|
||||
// data/BSS begin:
|
||||
// nm -C plausibility.elf|fgrep ___DATA_START__
|
||||
#define WEATHER_DATA_START 0x00101998
|
||||
// data/BSS end:
|
||||
// nm -C plausibility.elf|fgrep ___BSS_END__
|
||||
#define WEATHER_DATA_END 0x001032b8
|
||||
// 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 0x001017cb
|
||||
|
||||
#else
|
||||
#error Unknown WEATHERMONITOR_VARIANT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
#!/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
|
||||
// suffix for simulator state, trace file
|
||||
#define WEATHER_SUFFIX ".`basename $1|sed s/\\\\..*$//`"
|
||||
// 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 20599
|
||||
// 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 10272
|
||||
// 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/plugins/checkpoint/CMakeLists.txt
Normal file
11
src/plugins/checkpoint/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
set(PLUGIN_NAME checkpoint)
|
||||
|
||||
set(MY_PLUGIN_SRCS
|
||||
Checkpoint.cc
|
||||
Checkpoint.hpp
|
||||
sha1.c
|
||||
)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
## Build library
|
||||
add_library(fail-${PLUGIN_NAME} ${MY_PLUGIN_SRCS})
|
||||
230
src/plugins/checkpoint/Checkpoint.cc
Normal file
230
src/plugins/checkpoint/Checkpoint.cc
Normal file
@ -0,0 +1,230 @@
|
||||
#include "Checkpoint.hpp"
|
||||
#include "sal/Listener.hpp"
|
||||
#include "sal/Memory.hpp"
|
||||
#include "sha1.h"
|
||||
#include <cstring>
|
||||
#include "sal/bochs/BochsCPU.hpp"
|
||||
#include "sal/SALConfig.hpp"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
bool Checkpoint::run()
|
||||
{
|
||||
assert(!m_checking && "FATAL: Checkpoint plugin must not be added to simulation in checking mode");
|
||||
|
||||
// log information
|
||||
m_log << "Checkpoint Logger started." << std::endl;
|
||||
m_log << "Triggering on: " << m_symbol << std::endl;
|
||||
m_log << "Writing output to: " << m_file << std::endl;
|
||||
|
||||
/*
|
||||
std::vector<address_range>::const_iterator it = m_check_ranges.begin();
|
||||
for( ; it != m_check_ranges.end(); ++it) {
|
||||
m_log << "Checksumming: " <<
|
||||
<< ((it->first.second) ? "*" : "")
|
||||
<< "0x" << std::hex << it->first.first
|
||||
<< " - " <<
|
||||
<< (it->second.second) ? "*" : ""
|
||||
<< "0x" << std::hex << it->second.first
|
||||
<< std::endl;
|
||||
}
|
||||
*/
|
||||
|
||||
if (!m_ostream.is_open()) {
|
||||
m_log << "No output file." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// listen for memory writes and save checkpoints
|
||||
MemWriteListener ev_mem(m_symbol.getAddress());
|
||||
while (true) {
|
||||
simulator.addListenerAndResume(&ev_mem);
|
||||
|
||||
save_checkpoint(ev_mem.getTriggerInstructionPointer());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
address_t Checkpoint::resolve_address(const indirectable_address_t &addr) {
|
||||
if(addr.second) {
|
||||
const address_t paddr = addr.first;
|
||||
address_t value = 0;
|
||||
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
|
||||
if(mm.isMapped(paddr) && mm.isMapped(paddr+1) && mm.isMapped(paddr+2) && mm.isMapped(paddr+3)) {
|
||||
simulator.getMemoryManager().getBytes(paddr, 4, &value);
|
||||
}
|
||||
|
||||
// HACK/WORKAROUND for dOSEK, which uses bit 31 for parity!
|
||||
// This fixes checkpoint ranges for dOSEK, but breaks other systems *if*
|
||||
// addresses with bit 31 set are used as the limit for checkpoint regions
|
||||
return value & ~(1<<31);
|
||||
} else {
|
||||
return addr.first;
|
||||
}
|
||||
}
|
||||
|
||||
void Checkpoint::checksum(uint8_t (&Message_Digest)[20])
|
||||
{
|
||||
SHA1Context sha;
|
||||
int err;
|
||||
|
||||
// prepare SHA1 hash
|
||||
err = SHA1Reset(&sha);
|
||||
assert(err == 0);
|
||||
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
|
||||
// disable paging on x86
|
||||
#ifdef BUILD_X86
|
||||
const Register *reg_cr0 = simulator.getCPU(0).getRegister(RID_CR0);
|
||||
uint32_t cr0 = simulator.getCPU(0).getRegisterContent(reg_cr0);
|
||||
simulator.getCPU(0).setRegisterContent(reg_cr0, cr0 & ~(1<<31));
|
||||
#endif
|
||||
|
||||
// iterate memory regions
|
||||
std::vector<address_range>::const_iterator it = m_check_ranges.begin();
|
||||
for( ; it != m_check_ranges.end(); ++it) {
|
||||
fail::address_t start = resolve_address(it->first);
|
||||
fail::address_t end = resolve_address(it->second);
|
||||
|
||||
if((start == 0) || (end == 0)) {
|
||||
m_log << std::hex << "invalid checksum range pointer" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
//m_log << std::hex << "checksumming 0x" << start << " - 0x" << end << std::endl;
|
||||
|
||||
for(fail::address_t addr = start; addr < end; addr++) {
|
||||
if(mm.isMapped(addr)) {
|
||||
// read byte
|
||||
uint8_t data = mm.getByte(addr);
|
||||
// add to hash
|
||||
err = SHA1Input(&sha, &data, 1);
|
||||
} else {
|
||||
err = SHA1Input(&sha, (uint8_t*) &addr, 4);
|
||||
}
|
||||
|
||||
assert(err == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// restore paging on x86
|
||||
#ifdef BUILD_X86
|
||||
simulator.getCPU(0).setRegisterContent(reg_cr0, cr0);
|
||||
#endif
|
||||
|
||||
// complete hash
|
||||
err = SHA1Result(&sha, Message_Digest);
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
void Checkpoint::checkpoint(const fail::ElfSymbol symbol,
|
||||
uint32_t &value,
|
||||
fail::simtime_t &simtime,
|
||||
std::string &digest_str)
|
||||
{
|
||||
// increment checkpoint count
|
||||
m_count++;
|
||||
|
||||
// timestamp
|
||||
simtime = simulator.getTimerTicks();
|
||||
|
||||
// written value
|
||||
address_t addr = symbol.getAddress();
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
if(mm.isMapped(addr) && mm.isMapped(addr+1) && mm.isMapped(addr+2) && mm.isMapped(addr+3)) {
|
||||
mm.getBytes(symbol.getAddress(), symbol.getSize(), &value);
|
||||
} else {
|
||||
value = 0xDEADBEEF; // TODO: invalid value?
|
||||
}
|
||||
|
||||
// checksum
|
||||
uint8_t digest[20];
|
||||
checksum(digest);
|
||||
|
||||
// checksum to string
|
||||
std::stringstream s;
|
||||
s.fill('0');
|
||||
for ( size_t i = 0 ; i < 20 ; ++i )
|
||||
s << std::setw(2) << std::hex <<(unsigned short)digest[i];
|
||||
digest_str = s.str();
|
||||
}
|
||||
|
||||
void Checkpoint::save_checkpoint(fail::address_t ip)
|
||||
{
|
||||
uint32_t value;
|
||||
fail::simtime_t simtime;
|
||||
std::string digest;
|
||||
|
||||
// get checkpoint info
|
||||
checkpoint(m_symbol, value, simtime, digest);
|
||||
|
||||
// log checkpoint
|
||||
m_log << std::dec << "Checkpoint " << m_count << " @ " << simtime << std::endl;
|
||||
|
||||
// write checkpoint
|
||||
if (m_ostream.is_open()) {
|
||||
m_ostream << std::hex << ip << "\t" << std::dec << simtime << "\t" << value << "\t" << digest << std::endl;
|
||||
} else {
|
||||
m_log << "Output error" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Checkpoint::check_result Checkpoint::check(const fail::ElfSymbol symbol, fail::address_t ip)
|
||||
{
|
||||
uint32_t value;
|
||||
fail::simtime_t simtime;
|
||||
std::string digest;
|
||||
|
||||
address_t golden_ip;
|
||||
uint32_t golden_timestamp;
|
||||
uint32_t golden_value;
|
||||
char golden_digest_hex[41];
|
||||
|
||||
assert(m_checking && "FATAL: Checkpoint plugin cannot check in tracing mode");
|
||||
|
||||
// get checkpoint info
|
||||
checkpoint(symbol, value, simtime, digest);
|
||||
|
||||
// check with log
|
||||
if (!m_istream.is_open()) {
|
||||
m_log << "Input file not open!" << std::endl;
|
||||
return INVALID;
|
||||
}
|
||||
if (m_istream.eof()) {
|
||||
m_log << "Checkpoint after last golden checkpoint!" << std::endl;
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
// read golden values
|
||||
m_istream >> std::hex >> golden_ip;
|
||||
m_istream >> std::dec >> golden_timestamp;
|
||||
m_istream >> std::dec >> golden_value;
|
||||
m_istream.width(41);
|
||||
m_istream >> golden_digest_hex;
|
||||
std::string golden_digest = golden_digest_hex;
|
||||
|
||||
|
||||
//bool same_timestamp = simtime == golden_timestamp);
|
||||
bool same_ip = ip == golden_ip;
|
||||
bool same_value = value == golden_value;
|
||||
bool same_digest = digest == golden_digest;
|
||||
|
||||
if (!same_ip || !same_value || !same_digest) {
|
||||
// log
|
||||
m_log << "GOLDEN:" << std::hex << golden_ip << "\t" << std::dec << golden_timestamp << "\t" << golden_value << "\t" << golden_digest << std::endl;
|
||||
m_log << "TEST: " << std::hex << ip << "\t" << std::dec << simtime << "\t" << value << "\t" << digest << std::endl;
|
||||
}
|
||||
|
||||
|
||||
if(!same_value) return DIFFERENT_VALUE;
|
||||
if(!same_ip) return DIFFERENT_IP;
|
||||
if(!same_digest) return DIFFERENT_DIGEST;
|
||||
|
||||
return IDENTICAL;
|
||||
}
|
||||
117
src/plugins/checkpoint/Checkpoint.hpp
Normal file
117
src/plugins/checkpoint/Checkpoint.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef __Checkpoint_HPP__
|
||||
#define __Checkpoint_HPP__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "config/FailConfig.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include <fstream>
|
||||
#include "util/ElfReader.hpp"
|
||||
|
||||
/**
|
||||
* @class Checkpoint
|
||||
* @brief Listens to a memory location and outputs instruction pointer, written value,
|
||||
* timestamp and SHA1 hash of memory regions to file on each write access from SUT
|
||||
*/
|
||||
class Checkpoint : public fail::ExperimentFlow
|
||||
{
|
||||
public:
|
||||
typedef std::pair<fail::address_t,bool> indirectable_address_t; //!< fixed address or pointer to address
|
||||
typedef std::pair<indirectable_address_t,indirectable_address_t> address_range; //!< contiguous memory region
|
||||
typedef std::vector<address_range> range_vector; //!< vector of memory regions
|
||||
|
||||
enum check_result {
|
||||
IDENTICAL,
|
||||
DIFFERENT_IP,
|
||||
DIFFERENT_VALUE,
|
||||
DIFFERENT_DIGEST,
|
||||
INVALID
|
||||
};
|
||||
|
||||
private:
|
||||
const fail::ElfSymbol m_symbol; //!< target memory symbol triggering checkpoints
|
||||
const range_vector m_check_ranges; //!< address ranges to checksum
|
||||
const bool m_checking; //!< checking mode (when false: tracing mode)
|
||||
std::string m_file; //!< the input/output filename
|
||||
fail::Logger m_log; //!< debug output
|
||||
std::ofstream m_ostream; //!< outputfile stream
|
||||
std::ifstream m_istream; //!< inputfile stream
|
||||
unsigned m_count;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct Checkpoint Logger in tracing (output) mode.
|
||||
*
|
||||
* @param symbol The global memory address the plugin listens to
|
||||
* @param check_ranges Address ranges which are included in the saved checksum
|
||||
* @param outputfile The path to the file to write the checkpoints
|
||||
*/
|
||||
Checkpoint(const fail::ElfSymbol & symbol,
|
||||
const std::vector<address_range> check_ranges,
|
||||
const std::string& outputfile) :
|
||||
m_symbol(symbol), m_check_ranges(check_ranges), m_checking(false),
|
||||
m_file(outputfile) , m_log("CPLogger", false), m_count(0)
|
||||
{
|
||||
m_ostream.open(m_file.c_str() );
|
||||
if (!m_ostream.is_open()) {
|
||||
m_log << "Could not open " << m_file.c_str() << " for writing." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct Checkpoint Logger in checking (input) mode.
|
||||
*
|
||||
* @param symbol The global memory address the plugin listens to
|
||||
* @param check_ranges Address ranges which are compared to the saved checksum
|
||||
* @param inputfile The path to the file to read in checkpoints
|
||||
*/
|
||||
Checkpoint(const std::vector<address_range> check_ranges,
|
||||
const std::string& inputfile) :
|
||||
m_check_ranges(check_ranges), m_checking(true), m_file(inputfile),
|
||||
m_log("CPLogger", false), m_count(0)
|
||||
{
|
||||
m_istream.open(m_file.c_str() );
|
||||
if (!m_istream.is_open()) {
|
||||
m_log << "Could not open " << m_file.c_str() << " for reading." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//! How many checkpoints have been triggered so far
|
||||
unsigned getCount() const {
|
||||
return m_count;
|
||||
}
|
||||
|
||||
//! Start plugin control flow for tracing mode. Do not call in checking mode!
|
||||
bool run();
|
||||
|
||||
/**
|
||||
* Perform a check against saved checkpoints. Call this method in experiment when
|
||||
* a MemWriteBreakpoint on symbol triggers.
|
||||
*
|
||||
* @param symbol (memory) symbol which triggered checkpoint
|
||||
* @param ip instruction pointer which triggered checkpoint
|
||||
* @return check result
|
||||
*/
|
||||
check_result check(const fail::ElfSymbol symbol, fail::address_t ip);
|
||||
|
||||
private:
|
||||
//! calulate checksum over memory regions
|
||||
void checksum(uint8_t (&Message_Digest)[20]);
|
||||
|
||||
//! extract checkpoint information from simulation
|
||||
void checkpoint(const fail::ElfSymbol symbol,
|
||||
uint32_t &value,
|
||||
fail::simtime_t &simtime,
|
||||
std::string &digest_str);
|
||||
|
||||
//! save checkpoint to file
|
||||
void save_checkpoint(fail::address_t ip);
|
||||
|
||||
//! get value of indirectable_address_t
|
||||
fail::address_t resolve_address(const indirectable_address_t &addr);
|
||||
};
|
||||
|
||||
#endif // __Checkpoint_HPP__
|
||||
392
src/plugins/checkpoint/sha1.c
Normal file
392
src/plugins/checkpoint/sha1.c
Normal file
@ -0,0 +1,392 @@
|
||||
/*
|
||||
* sha1.c
|
||||
*
|
||||
* Copyright (C) The Internet Society (2001). All Rights Reserved.
|
||||
* This file is taken from RFC3174.
|
||||
*
|
||||
* Description:
|
||||
* This file implements the Secure Hashing Algorithm 1 as
|
||||
* defined in FIPS PUB 180-1 published April 17, 1995.
|
||||
*
|
||||
* The SHA-1, produces a 160-bit message digest for a given
|
||||
* data stream. It should take about 2**n steps to find a
|
||||
* message with the same digest as a given message and
|
||||
* 2**(n/2) to find any two messages with the same digest,
|
||||
* when n is the digest size in bits. Therefore, this
|
||||
* algorithm can serve as a means of providing a
|
||||
* "fingerprint" for a message.
|
||||
*
|
||||
* Portability Issues:
|
||||
* SHA-1 is defined in terms of 32-bit "words". This code
|
||||
* uses <stdint.h> (included via "sha1.h" to define 32 and 8
|
||||
* bit unsigned integer types. If your C compiler does not
|
||||
* support 32 bit unsigned integers, this code is not
|
||||
* appropriate.
|
||||
*
|
||||
* Caveats:
|
||||
* SHA-1 is designed to work with messages less than 2^64 bits
|
||||
* long. Although SHA-1 allows a message digest to be generated
|
||||
* for messages of any number of bits less than 2^64, this
|
||||
* implementation only works with messages with a length that is
|
||||
* a multiple of the size of an 8-bit character.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
/*
|
||||
* Define the SHA1 circular left shift macro
|
||||
*/
|
||||
#define SHA1CircularShift(bits,word) \
|
||||
(((word) << (bits)) | ((word) >> (32-(bits))))
|
||||
|
||||
/* Local Function Prototyptes */
|
||||
void SHA1PadMessage(SHA1Context *);
|
||||
void SHA1ProcessMessageBlock(SHA1Context *);
|
||||
|
||||
/*
|
||||
* SHA1Reset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the SHA1Context in preparation
|
||||
* for computing a new SHA1 message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Reset(SHA1Context *context)
|
||||
{
|
||||
if (!context)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
context->Length_Low = 0;
|
||||
context->Length_High = 0;
|
||||
context->Message_Block_Index = 0;
|
||||
|
||||
context->Intermediate_Hash[0] = 0x67452301;
|
||||
context->Intermediate_Hash[1] = 0xEFCDAB89;
|
||||
context->Intermediate_Hash[2] = 0x98BADCFE;
|
||||
context->Intermediate_Hash[3] = 0x10325476;
|
||||
context->Intermediate_Hash[4] = 0xC3D2E1F0;
|
||||
|
||||
context->Computed = 0;
|
||||
context->Corrupted = 0;
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Result
|
||||
*
|
||||
* Description:
|
||||
* This function will return the 160-bit message digest into the
|
||||
* Message_Digest array provided by the caller.
|
||||
* NOTE: The first octet of hash is stored in the 0th element,
|
||||
* the last octet of hash in the 19th element.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to use to calculate the SHA-1 hash.
|
||||
* Message_Digest: [out]
|
||||
* Where the digest is returned.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Result( SHA1Context *context,
|
||||
uint8_t Message_Digest[SHA1HashSize])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!context || !Message_Digest)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
if (context->Corrupted)
|
||||
{
|
||||
return context->Corrupted;
|
||||
}
|
||||
|
||||
if (!context->Computed)
|
||||
{
|
||||
SHA1PadMessage(context);
|
||||
for(i=0; i<64; ++i)
|
||||
{
|
||||
/* message may be sensitive, clear it out */
|
||||
context->Message_Block[i] = 0;
|
||||
}
|
||||
context->Length_Low = 0; /* and clear length */
|
||||
context->Length_High = 0;
|
||||
context->Computed = 1;
|
||||
|
||||
}
|
||||
|
||||
for(i = 0; i < SHA1HashSize; ++i)
|
||||
{
|
||||
Message_Digest[i] = context->Intermediate_Hash[i>>2]
|
||||
>> 8 * ( 3 - ( i & 0x03 ) );
|
||||
}
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion
|
||||
* of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update
|
||||
* message_array: [in]
|
||||
* An array of characters representing the next portion of
|
||||
* the message.
|
||||
* length: [in]
|
||||
* The length of the message in message_array
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Input( SHA1Context *context,
|
||||
const uint8_t *message_array,
|
||||
unsigned length)
|
||||
{
|
||||
if (!length)
|
||||
{
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
if (!context || !message_array)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
if (context->Computed)
|
||||
{
|
||||
context->Corrupted = shaStateError;
|
||||
|
||||
return shaStateError;
|
||||
}
|
||||
|
||||
if (context->Corrupted)
|
||||
{
|
||||
return context->Corrupted;
|
||||
}
|
||||
while(length-- && !context->Corrupted)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] =
|
||||
(*message_array & 0xFF);
|
||||
|
||||
context->Length_Low += 8;
|
||||
if (context->Length_Low == 0)
|
||||
{
|
||||
context->Length_High++;
|
||||
if (context->Length_High == 0)
|
||||
{
|
||||
/* Message is too long */
|
||||
context->Corrupted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (context->Message_Block_Index == 64)
|
||||
{
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
|
||||
message_array++;
|
||||
}
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1ProcessMessageBlock
|
||||
*
|
||||
* Description:
|
||||
* This function will process the next 512 bits of the message
|
||||
* stored in the Message_Block array.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
|
||||
* Many of the variable names in this code, especially the
|
||||
* single character names, were used because those were the
|
||||
* names used in the publication.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
{
|
||||
const uint32_t K[] = { /* Constants defined in SHA-1 */
|
||||
0x5A827999,
|
||||
0x6ED9EBA1,
|
||||
0x8F1BBCDC,
|
||||
0xCA62C1D6
|
||||
};
|
||||
int t; /* Loop counter */
|
||||
uint32_t temp; /* Temporary word value */
|
||||
uint32_t W[80]; /* Word sequence */
|
||||
uint32_t A, B, C, D, E; /* Word buffers */
|
||||
|
||||
/*
|
||||
* Initialize the first 16 words in the array W
|
||||
*/
|
||||
for(t = 0; t < 16; t++)
|
||||
{
|
||||
W[t] = context->Message_Block[t * 4] << 24;
|
||||
W[t] |= context->Message_Block[t * 4 + 1] << 16;
|
||||
W[t] |= context->Message_Block[t * 4 + 2] << 8;
|
||||
W[t] |= context->Message_Block[t * 4 + 3];
|
||||
}
|
||||
|
||||
for(t = 16; t < 80; t++)
|
||||
{
|
||||
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
||||
}
|
||||
|
||||
A = context->Intermediate_Hash[0];
|
||||
B = context->Intermediate_Hash[1];
|
||||
C = context->Intermediate_Hash[2];
|
||||
D = context->Intermediate_Hash[3];
|
||||
E = context->Intermediate_Hash[4];
|
||||
|
||||
for(t = 0; t < 20; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | ((~B) & D)) + E + W[t] + K[0];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 20; t < 40; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 40; t < 60; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 60; t < 80; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
context->Intermediate_Hash[0] += A;
|
||||
context->Intermediate_Hash[1] += B;
|
||||
context->Intermediate_Hash[2] += C;
|
||||
context->Intermediate_Hash[3] += D;
|
||||
context->Intermediate_Hash[4] += E;
|
||||
|
||||
context->Message_Block_Index = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1PadMessage
|
||||
*
|
||||
|
||||
* Description:
|
||||
* According to the standard, the message must be padded to an even
|
||||
* 512 bits. The first padding bit must be a '1'. The last 64
|
||||
* bits represent the length of the original message. All bits in
|
||||
* between should be 0. This function will pad the message
|
||||
* according to those rules by filling the Message_Block array
|
||||
* accordingly. It will also call the ProcessMessageBlock function
|
||||
* provided appropriately. When it returns, it can be assumed that
|
||||
* the message digest has been computed.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to pad
|
||||
* ProcessMessageBlock: [in]
|
||||
* The appropriate SHA*ProcessMessageBlock function
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
*/
|
||||
|
||||
void SHA1PadMessage(SHA1Context *context)
|
||||
{
|
||||
/*
|
||||
* Check to see if the current message block is too small to hold
|
||||
* the initial padding bits and length. If so, we will pad the
|
||||
* block, process it, and then continue padding into a second
|
||||
* block.
|
||||
*/
|
||||
if (context->Message_Block_Index > 55)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||
while(context->Message_Block_Index < 64)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
|
||||
while(context->Message_Block_Index < 56)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||
while(context->Message_Block_Index < 56)
|
||||
{
|
||||
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the message length as the last 8 octets
|
||||
*/
|
||||
context->Message_Block[56] = context->Length_High >> 24;
|
||||
context->Message_Block[57] = context->Length_High >> 16;
|
||||
context->Message_Block[58] = context->Length_High >> 8;
|
||||
context->Message_Block[59] = context->Length_High;
|
||||
context->Message_Block[60] = context->Length_Low >> 24;
|
||||
context->Message_Block[61] = context->Length_Low >> 16;
|
||||
context->Message_Block[62] = context->Length_Low >> 8;
|
||||
context->Message_Block[63] = context->Length_Low;
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
72
src/plugins/checkpoint/sha1.h
Normal file
72
src/plugins/checkpoint/sha1.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* sha1.h
|
||||
*
|
||||
* Description:
|
||||
* This is the header file for code which implements the Secure
|
||||
* Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
|
||||
* April 17, 1995.
|
||||
*
|
||||
* Many of the variable names in this code, especially the
|
||||
* single character names, were used because those were the names
|
||||
* used in the publication.
|
||||
*
|
||||
* Please read the file sha1.c for more information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SHA1_H_
|
||||
#define _SHA1_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef _SHA_enum_
|
||||
#define _SHA_enum_
|
||||
enum
|
||||
{
|
||||
shaSuccess = 0,
|
||||
shaNull, /* Null pointer parameter */
|
||||
shaInputTooLong, /* input data too long */
|
||||
shaStateError /* called Input after Result */
|
||||
};
|
||||
#endif
|
||||
#define SHA1HashSize 20
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-1
|
||||
* hashing operation
|
||||
*/
|
||||
typedef struct SHA1Context
|
||||
{
|
||||
uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
|
||||
|
||||
uint32_t Length_Low; /* Message length in bits */
|
||||
uint32_t Length_High; /* Message length in bits */
|
||||
|
||||
/* Index into message block array */
|
||||
int_least16_t Message_Block_Index;
|
||||
uint8_t Message_Block[64]; /* 512-bit message blocks */
|
||||
|
||||
int Computed; /* Is the digest computed? */
|
||||
int Corrupted; /* Is the message digest corrupted? */
|
||||
} SHA1Context;
|
||||
|
||||
/*
|
||||
* Function Prototypes
|
||||
*/
|
||||
|
||||
int SHA1Reset( SHA1Context *);
|
||||
int SHA1Input( SHA1Context *,
|
||||
const uint8_t *,
|
||||
unsigned int);
|
||||
int SHA1Result( SHA1Context *,
|
||||
uint8_t Message_Digest[SHA1HashSize]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -10,7 +10,7 @@ bool SerialOutputLogger::run()
|
||||
while (true) {
|
||||
simulator.addListener(&ev_ioport);
|
||||
simulator.resume();
|
||||
if (m_output.size() < m_limit) {
|
||||
if (m_limit == 0 || m_output.size() < m_limit) {
|
||||
m_output += ev_ioport.getData();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user