Merge remote-tracking branch 'origin/master' into sampling-wip

Change-Id: Iae5c02be5801d75e8adc55222ccb35c559f7ebf4
This commit is contained in:
Horst Schirmeier
2015-01-21 00:19:52 +01:00
121 changed files with 16634 additions and 611 deletions

View File

@ -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

View File

@ -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})

View File

@ -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;
}

View File

@ -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")

View File

@ -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) {

View File

@ -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

View File

@ -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

View 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>(&param->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;
}

View 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__

View File

@ -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

View File

@ -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);
}
};

View File

@ -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)

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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"; };

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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
}
}

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -0,0 +1,3 @@
012 0x6
0x11 3
21 0x4

View 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)

View 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);
}

View 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__

View 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;
}
}

View 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();
}

View 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

View 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;
}
}

View 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)

View 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;
}

View 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__

View File

@ -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)

View 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)

View 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);
}

View 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
}
};

View 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;
}

View 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();
};

View 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

View 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;
}
}

View File

@ -0,0 +1,7 @@
#include "experiment.hpp"
#include "sal/SALInst.hpp"
void instantiateFiascoFailExperiment()
{
fail::simulator.addFlow(new FiascoFailExperiment);
}

View 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;
}
}

View 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)

View 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);
}

View 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__

View 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 "")

View 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);
}
}

View 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__

View 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__

View 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;
}
}

View 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;
}
}

View 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)

View File

@ -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})

View File

@ -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

View 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 "")

View File

@ -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();
}

View File

@ -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__

View File

@ -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

View File

@ -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

View 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})

View 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;
}

View 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__

View 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);
}

View 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

View File

@ -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();
}
}