Merge remote-tracking branch 'origin/master' into sampling-wip
Change-Id: Iae5c02be5801d75e8adc55222ccb35c559f7ebf4
This commit is contained in:
@ -30,9 +30,11 @@ find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Let protobuf compiler find defined file (DatabaseCampaignMessage) in binary tree
|
||||
set(PROTOBUF_IMPORT_DIRS ${PROTOBUF_IMPORT_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
# The PROTOBUF_IMPORT_DIRS is set in src/CMakeLists.txt
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${PROTOS})
|
||||
|
||||
add_custom_target(fail-protoc
|
||||
DEPENDS ${PROTO_SRCS} ${PROTO_HDRS}
|
||||
)
|
||||
add_library(fail-comm ${SRCS} ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
target_link_libraries(fail-comm ${PROTOBUF_LIBRARY})
|
||||
|
||||
@ -21,4 +21,10 @@ message DatabaseCampaignMessage {
|
||||
required string benchmark = 9 [(sql_ignore) = true];
|
||||
|
||||
required InjectionPointMessage injection_point = 10 [(sql_ignore) = true];
|
||||
}
|
||||
}
|
||||
|
||||
message DatabaseExperimentMessage {
|
||||
required uint32 bitoffset = 1 [(sql_primary_key) = true];
|
||||
required uint32 original_value = 2;
|
||||
}
|
||||
|
||||
|
||||
@ -2,31 +2,31 @@
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/VariantConfig.hpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/VariantConfig.hpp)
|
||||
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS "Event source: Breakpoints" OFF)
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS_RANGE "Event source: Range Breakpoints" OFF)
|
||||
OPTION(CONFIG_EVENT_MEMREAD "Event source: Memory reads" OFF)
|
||||
OPTION(CONFIG_EVENT_MEMWRITE "Event source: Memory writes" OFF)
|
||||
OPTION(CONFIG_EVENT_GUESTSYS "Event source: Outbound guest-system communication" OFF)
|
||||
OPTION(CONFIG_EVENT_IOPORT "Event source: I/O port communication" OFF)
|
||||
OPTION(CONFIG_EVENT_INTERRUPT "Event source: Interrupts" OFF)
|
||||
OPTION(CONFIG_EVENT_TRAP "Event source: Traps" OFF)
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS "Event source: Breakpoints" ON)
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS_RANGE "Event source: Range Breakpoints" ON)
|
||||
OPTION(CONFIG_EVENT_MEMREAD "Event source: Memory reads" ON)
|
||||
OPTION(CONFIG_EVENT_MEMWRITE "Event source: Memory writes" ON)
|
||||
OPTION(CONFIG_EVENT_GUESTSYS "Event source: Outbound guest-system communication" ON)
|
||||
OPTION(CONFIG_EVENT_IOPORT "Event source: I/O port communication" ON)
|
||||
OPTION(CONFIG_EVENT_INTERRUPT "Event source: Interrupts" ON)
|
||||
OPTION(CONFIG_EVENT_TRAP "Event source: Traps" ON)
|
||||
OPTION(CONFIG_EVENT_JUMP "Event source: Branch instructions" OFF)
|
||||
OPTION(CONFIG_SR_RESTORE "Target backend: State restore" OFF)
|
||||
OPTION(CONFIG_SR_SAVE "Target backend: State saving" OFF)
|
||||
OPTION(CONFIG_SR_REBOOT "Target backend: Reboot" OFF)
|
||||
OPTION(CONFIG_SR_RESTORE "Target backend: State restore" ON)
|
||||
OPTION(CONFIG_SR_SAVE "Target backend: State saving" ON)
|
||||
OPTION(CONFIG_SR_REBOOT "Target backend: Reboot" ON)
|
||||
OPTION(CONFIG_BOCHS_NON_VERBOSE "Misc: Reduced verbosity (a lot faster for large campaigns)" OFF)
|
||||
OPTION(CONFIG_BOCHS_NO_ABORT "Misc: Do not abort or ask the user in case the simulator stumbles on unexpected events (e.g., panics)" ON)
|
||||
OPTION(CONFIG_BOCHS_COMPRESS_STATE "Misc: Reduce Bochs save/restore size by compressing memory images" OFF)
|
||||
OPTION(CONFIG_SUPPRESS_INTERRUPTS "Target backend: Suppress interrupts" OFF)
|
||||
OPTION(CONFIG_FIRE_INTERRUPTS "Target backend: Fire interrupts" OFF)
|
||||
OPTION(CONFIG_BOCHS_COMPRESS_STATE "Misc: Reduce Bochs save/restore size by compressing memory images" ON)
|
||||
OPTION(CONFIG_SUPPRESS_INTERRUPTS "Target backend: Suppress interrupts" ON)
|
||||
OPTION(CONFIG_FIRE_INTERRUPTS "Target backend: Fire interrupts" ON)
|
||||
OPTION(CONFIG_DISABLE_KEYB_INTERRUPTS "Target backend: Suppress keyboard interrupts" OFF)
|
||||
OPTION(SERVER_PERFORMANCE_MEASURE "Performance measurement in job-server" OFF)
|
||||
OPTION(CONFIG_FAST_BREAKPOINTS "Enable fast breakpoints (requires breakpoint events to be enabled)" OFF)
|
||||
OPTION(CONFIG_FAST_WATCHPOINTS "Enable fast watchpoints (requires memory access events to be enabled)" OFF)
|
||||
OPTION(CONFIG_FAST_BREAKPOINTS "Enable fast breakpoints (requires breakpoint events to be enabled)" ON)
|
||||
OPTION(CONFIG_FAST_WATCHPOINTS "Enable fast watchpoints (requires memory access events to be enabled)" ON)
|
||||
OPTION(CONFIG_INJECTIONPOINT_HOPS "Enable hop chain trace navigation to injection point" OFF)
|
||||
SET(SERVER_COMM_HOSTNAME "localhost" CACHE STRING "Job-server hostname or IP")
|
||||
SET(SERVER_COMM_TCP_PORT "1111" CACHE STRING "Job-server TCP port")
|
||||
SET(SERVER_OUT_QUEUE_SIZE "0" CACHE STRING "Queue size for outbound jobs (0 = unlimited)")
|
||||
SET(SERVER_OUT_QUEUE_SIZE "0" CACHE STRING "Queue size for outbound jobs (0 = unlimited)")
|
||||
SET(SERVER_PERF_LOG_PATH "perf.log" CACHE STRING "A file name for storing the server's performance log (CSV)")
|
||||
SET(SERVER_PERF_STEPPING_SEC "1" CACHE STRING "Stepping of performance measurements in seconds")
|
||||
SET(CLIENT_RAND_BACKOFF_TSTART "3" CACHE STRING "Lower limit of client's backoff phase in seconds")
|
||||
|
||||
@ -114,12 +114,15 @@ bool DatabaseCampaign::run() {
|
||||
boost::thread collect_thread(&DatabaseCampaign::collect_result_thread, this);
|
||||
#endif
|
||||
|
||||
load_completed_pilots();
|
||||
|
||||
std::vector<Database::Variant> variantlist =
|
||||
db->get_variants(variants, variants_exclude, benchmarks, benchmarks_exclude);
|
||||
|
||||
// Which Pilots were already processed?
|
||||
load_completed_pilots(variantlist);
|
||||
|
||||
for (std::vector<Database::Variant>::const_iterator it = variantlist.begin();
|
||||
it != variantlist.end(); ++it) {
|
||||
// Push all other variants to the queue
|
||||
if (!run_variant(*it)) {
|
||||
log_send << "run_variant failed for " << it->variant << "/" << it->benchmark <<std::endl;
|
||||
return false;
|
||||
@ -165,7 +168,7 @@ void DatabaseCampaign::collect_result_thread() {
|
||||
|
||||
bool DatabaseCampaign::run_variant(Database::Variant variant) {
|
||||
/* Gather jobs */
|
||||
int experiment_count;
|
||||
unsigned long experiment_count;
|
||||
std::stringstream ss;
|
||||
std::string sql_select = "SELECT p.id, p.injection_instr, p.injection_instr_absolute, p.data_address, p.data_width, t.instr1, t.instr2 ";
|
||||
ss << " FROM fsppilot p "
|
||||
@ -252,14 +255,29 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) {
|
||||
|
||||
}
|
||||
|
||||
void DatabaseCampaign::load_completed_pilots()
|
||||
void DatabaseCampaign::load_completed_pilots(std::vector<Database::Variant> &variants)
|
||||
{
|
||||
// If no variants were given, do nothing
|
||||
if (variants.size() == 0)
|
||||
return;
|
||||
|
||||
// load list of partially or completely finished pilots
|
||||
std::stringstream variant_str;
|
||||
bool comma = false;
|
||||
for (std::vector<Database::Variant>::const_iterator it = variants.begin();
|
||||
it != variants.end(); ++it) {
|
||||
if (comma) variant_str << ", ";
|
||||
variant_str << it->id;
|
||||
comma = true; // Next time we need a comma
|
||||
}
|
||||
log_send << "loading completed pilot IDs ..." << std::endl;
|
||||
|
||||
std::stringstream sql;
|
||||
sql << "SELECT pilot_id, COUNT(*) FROM " << db_connect.result_table()
|
||||
sql << "SELECT pilot_id, COUNT(*) FROM fsppilot p"
|
||||
<< " JOIN " << db_connect.result_table() << " r ON r.pilot_id = p.id"
|
||||
<< " WHERE variant_id in (" << variant_str.str() << ")"
|
||||
<< " GROUP BY pilot_id ";
|
||||
MYSQL_RES *ids = db->query_stream(sql.str().c_str());
|
||||
log_send << "loading completed pilot IDs ..." << std::endl;
|
||||
MYSQL_ROW row;
|
||||
unsigned rowcount = 0;
|
||||
while ((row = mysql_fetch_row(ids)) != 0) {
|
||||
|
||||
@ -29,7 +29,7 @@ class DatabaseCampaign : public Campaign {
|
||||
int fspmethod_id; // !< Which fspmethod should be put out to the clients
|
||||
|
||||
void collect_result_thread();
|
||||
void load_completed_pilots();
|
||||
void load_completed_pilots(std::vector<fail::Database::Variant> &);
|
||||
unsigned existing_results_for_pilot(unsigned pilot_id);
|
||||
|
||||
#ifndef __puma
|
||||
|
||||
@ -4,10 +4,12 @@ set(SRCS
|
||||
ExperimentFlow.hpp
|
||||
JobClient.hpp
|
||||
JobClient.cc
|
||||
DatabaseExperiment.hpp
|
||||
DatabaseExperiment.cc
|
||||
)
|
||||
|
||||
add_library(fail-efw ${SRCS})
|
||||
add_dependencies(fail-efw fail-comm)
|
||||
add_dependencies(fail-efw fail-protoc)
|
||||
target_link_libraries(fail-efw fail-comm)
|
||||
target_link_libraries(fail-efw fail-util) # WallclockTimer
|
||||
|
||||
|
||||
183
src/core/efw/DatabaseExperiment.cc
Normal file
183
src/core/efw/DatabaseExperiment.cc
Normal file
@ -0,0 +1,183 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "sal/SALConfig.hpp"
|
||||
#include "sal/Memory.hpp"
|
||||
#include "sal/Listener.hpp"
|
||||
#include "efw/DatabaseExperiment.hpp"
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/message.h>
|
||||
#include "comm/DatabaseCampaignMessage.pb.h"
|
||||
#include "sal/bochs/BochsListener.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
using namespace google::protobuf;
|
||||
|
||||
// Check if configuration dependencies are satisfied:
|
||||
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE)
|
||||
#error This experiment needs: breakpoints, restore. Enable these in the configuration.
|
||||
#endif
|
||||
|
||||
DatabaseExperiment::~DatabaseExperiment() {
|
||||
delete this->m_jc;
|
||||
}
|
||||
|
||||
unsigned DatabaseExperiment::injectBitFlip(address_t data_address, unsigned bitpos){
|
||||
unsigned int value, injectedval;
|
||||
|
||||
value = m_mm.getByte(data_address);
|
||||
injectedval = value ^ (1 << bitpos);
|
||||
m_mm.setByte(data_address, injectedval);
|
||||
|
||||
m_log << "INJECTION at: 0x" << hex<< setw(2) << setfill('0') << data_address
|
||||
<< " value: 0x" << setw(2) << setfill('0') << value << " -> 0x"
|
||||
<< setw(2) << setfill('0') << (unsigned) m_mm.getByte(data_address) << endl;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T * protobufFindSubmessageByTypename(Message *msg, const std::string &name) {
|
||||
T * submessage = 0;
|
||||
const Descriptor *msg_type = msg->GetDescriptor();
|
||||
const Message::Reflection *ref = msg->GetReflection();
|
||||
const Descriptor *database_desc =
|
||||
DescriptorPool::generated_pool()->FindMessageTypeByName(name);
|
||||
assert(database_desc != 0);
|
||||
|
||||
size_t count = msg_type->field_count();
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
const FieldDescriptor *field = msg_type->field(i);
|
||||
assert(field != 0);
|
||||
if (field->message_type() == database_desc) {
|
||||
submessage = dynamic_cast<T*>(ref->MutableMessage(msg, field));
|
||||
assert(submessage != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return submessage;
|
||||
}
|
||||
|
||||
|
||||
bool DatabaseExperiment::run()
|
||||
{
|
||||
m_log << "STARTING EXPERIMENT" << endl;
|
||||
|
||||
if (!this->cb_start_experiment()) {
|
||||
m_log << "Initialization failed. Exiting." << endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
|
||||
unsigned executed_jobs = 0;
|
||||
|
||||
while (executed_jobs < 25 || m_jc->getNumberOfUndoneJobs() > 0) {
|
||||
m_log << "asking jobserver for parameters" << endl;
|
||||
ExperimentData * param = this->cb_allocate_experiment_data();
|
||||
if (!m_jc->getParam(*param)){
|
||||
m_log << "Dying." << endl; // We were told to die.
|
||||
simulator.terminate(1);
|
||||
}
|
||||
m_current_param = param;
|
||||
|
||||
DatabaseCampaignMessage * fsppilot =
|
||||
protobufFindSubmessageByTypename<DatabaseCampaignMessage>(¶m->getMessage(), "DatabaseCampaignMessage");
|
||||
assert (fsppilot != 0);
|
||||
|
||||
unsigned injection_instr = fsppilot->injection_instr();
|
||||
address_t data_address = fsppilot->data_address();
|
||||
unsigned width = fsppilot->data_width();
|
||||
|
||||
for (unsigned bit_offset = 0; bit_offset < width * 8; ++bit_offset) {
|
||||
// 8 results in one job
|
||||
Message *outer_result = cb_new_result(param);
|
||||
m_current_result = outer_result;
|
||||
DatabaseExperimentMessage *result =
|
||||
protobufFindSubmessageByTypename<DatabaseExperimentMessage>(outer_result, "DatabaseExperimentMessage");
|
||||
result->set_bitoffset(bit_offset);
|
||||
m_log << "restoring state" << endl;
|
||||
// Restore to the image, which starts at address(main)
|
||||
simulator.restore(cb_state_directory());
|
||||
executed_jobs ++;
|
||||
|
||||
m_log << "Trying to inject @ instr #" << dec << injection_instr << endl;
|
||||
|
||||
simulator.clearListeners();
|
||||
|
||||
// Generate an experiment listener, that matches on any IP
|
||||
// event. It is used to forward to the injection
|
||||
// point. The +1 is needed, since even for the zeroth
|
||||
// dynamic instruction we need at least one breakpoint
|
||||
// event.
|
||||
BPSingleListener bp;
|
||||
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||
bp.setCounter(injection_instr + 1);
|
||||
simulator.addListener(&bp);
|
||||
|
||||
if (!this->cb_before_fast_forward()) {
|
||||
continue;
|
||||
}
|
||||
fail::BaseListener * listener;
|
||||
while (true) {
|
||||
listener = simulator.resume();
|
||||
if (listener == &bp) {
|
||||
break;
|
||||
} else {
|
||||
bool should_continue = this->cb_during_fast_forward(listener);
|
||||
if (!should_continue)
|
||||
break; // Stop fast forwarding
|
||||
}
|
||||
}
|
||||
if (!this->cb_after_fast_forward(listener)) {
|
||||
continue; // Continue to next injection experiment
|
||||
}
|
||||
|
||||
address_t injection_instr_absolute = fsppilot->injection_instr_absolute();
|
||||
bool found_eip;
|
||||
for (int i = 0; i < BX_SMP_PROCESSORS; i++) {
|
||||
address_t eip = simulator.getCPU(i).getInstructionPointer();
|
||||
if (eip == injection_instr_absolute) {
|
||||
found_eip = true;
|
||||
}
|
||||
}
|
||||
if (!found_eip) {
|
||||
m_log << "Invalid Injection address != 0x" << injection_instr_absolute << std::endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
|
||||
simulator.clearListeners();
|
||||
|
||||
/// INJECT BITFLIP:
|
||||
result->set_original_value(injectBitFlip(data_address, bit_offset));
|
||||
|
||||
if (!this->cb_before_resume()) {
|
||||
continue; // Continue to next experiment
|
||||
}
|
||||
|
||||
m_log << "Resuming till the crash" << std::endl;
|
||||
// resume and wait for results
|
||||
while (true) {
|
||||
listener = simulator.resume();
|
||||
bool should_continue = this->cb_during_resume(listener);
|
||||
if (!should_continue)
|
||||
break;
|
||||
}
|
||||
m_log << "Resume done" << std::endl;
|
||||
this->cb_after_resume(listener);
|
||||
|
||||
simulator.clearListeners();
|
||||
}
|
||||
m_jc->sendResult(*param);
|
||||
this->cb_free_experiment_data(param);
|
||||
}
|
||||
// Explicitly terminate, or the simulator will continue to run.
|
||||
simulator.terminate();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
150
src/core/efw/DatabaseExperiment.hpp
Normal file
150
src/core/efw/DatabaseExperiment.hpp
Normal file
@ -0,0 +1,150 @@
|
||||
#ifndef __DATABASE_EXPERIMENT_HPP__
|
||||
#define __DATABASE_EXPERIMENT_HPP__
|
||||
|
||||
#include <google/protobuf/message.h>
|
||||
#include "efw/ExperimentFlow.hpp"
|
||||
#include "efw/JobClient.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace fail {
|
||||
class ExperimentData;
|
||||
|
||||
class DatabaseExperiment : public fail::ExperimentFlow {
|
||||
fail::JobClient *m_jc;
|
||||
|
||||
unsigned injectBitFlip(fail::address_t data_address, unsigned bitpos);
|
||||
|
||||
/**
|
||||
The current experiment data as returned by the job client. This
|
||||
allocated by cb_allocate_experiment_data()
|
||||
*/
|
||||
ExperimentData *m_current_param;
|
||||
google::protobuf::Message *m_current_result;
|
||||
|
||||
public:
|
||||
DatabaseExperiment(const std::string &name)
|
||||
: m_log(name, false), m_mm(fail::simulator.getMemoryManager()) {
|
||||
|
||||
/* The fail server can be set with an environent variable,
|
||||
otherwise the JOBSERVER configured by cmake ist used */
|
||||
char *server_host = getenv("FAIL_SERVER_HOST");
|
||||
if (server_host != NULL){
|
||||
this->m_jc = new fail::JobClient(std::string(server_host));
|
||||
} else {
|
||||
this->m_jc = new fail::JobClient();
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~DatabaseExperiment();
|
||||
|
||||
bool run();
|
||||
|
||||
|
||||
protected:
|
||||
fail::Logger m_log;
|
||||
fail::MemoryManager& m_mm;
|
||||
|
||||
/** Returns the currently running experiment message as returned
|
||||
* by the job client
|
||||
*/
|
||||
ExperimentData * get_current_experiment_data() { return m_current_param; }
|
||||
|
||||
/** Returns the currently result message, that was allocated by
|
||||
* cb_allocate_new_result.
|
||||
*/
|
||||
google::protobuf::Message * get_current_result() { return m_current_result; }
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Can be overwritten by experiment
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Get path to the state directory
|
||||
*/
|
||||
virtual std::string cb_state_directory() { return "state"; }
|
||||
|
||||
/**
|
||||
* Callback that is called, before the actual experiment
|
||||
* starts. Simulation is terminated on false.
|
||||
* @param The current result message
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_start_experiment() { return true; };
|
||||
|
||||
/**
|
||||
* Allocate enough space to hold the incoming ExperimentData
|
||||
* message. The can be accessed during the experiment through
|
||||
* get_current_experiment_data()
|
||||
*/
|
||||
virtual ExperimentData* cb_allocate_experiment_data() = 0;
|
||||
virtual void cb_free_experiment_data(ExperimentData *) {};
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a new result slot in the given experiment data. The
|
||||
* returned pointer can be obtained by calling
|
||||
* get_current_result()
|
||||
*/
|
||||
virtual google::protobuf::Message* cb_new_result(ExperimentData*) = 0;
|
||||
|
||||
/**
|
||||
* Callback that is called before the fast forward is done. This
|
||||
* can be used to add additional event listeners during the fast
|
||||
* forward phase. If returning false, the experiment is canceled.
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_before_fast_forward() { return true; };
|
||||
|
||||
/**
|
||||
* Callback that is called during the fast forward, when an event
|
||||
* has triggered, but it was not the fast forward listener. This
|
||||
* can be used to collect additional information during the fast
|
||||
* forward If returning false, the fast forwarding is stopped.
|
||||
*
|
||||
* @return \c true on should continue, \c false stop ff
|
||||
*/
|
||||
virtual bool cb_during_fast_forward(fail::BaseListener *) { return false; };
|
||||
|
||||
/**
|
||||
* Callback that is called after the fast forward, with the last
|
||||
* triggered event forward If returning false, the experiment is
|
||||
* canceled.
|
||||
*
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_after_fast_forward(fail::BaseListener *) { return true; };
|
||||
|
||||
/**
|
||||
* Callback that is called before the resuming till crash has
|
||||
* started. This is called after the fault was injected. Here the
|
||||
* end listeners should be installed. Returns true on
|
||||
* success. Otherwise the experiment is canceled.
|
||||
|
||||
* @return \c true on success, \c false otherwise
|
||||
*/
|
||||
virtual bool cb_before_resume() = 0;
|
||||
|
||||
/**
|
||||
* Callback that is called during the resume-till-crash phase,
|
||||
* when an event has triggered, This can be used to collect
|
||||
* additional information during the resuming phse. If returning
|
||||
* false, the resuming has finished and the experiment has stopped.
|
||||
*
|
||||
* @return \c true on should continue ff, \c false stop ff
|
||||
*/
|
||||
virtual bool cb_during_resume(fail::BaseListener *) { return false; };
|
||||
|
||||
/**
|
||||
* Callback that is called after the resume-till-crash phase with
|
||||
* the last triggered listener. This callback should collect all data and
|
||||
*
|
||||
*/
|
||||
virtual void cb_after_resume(fail::BaseListener *) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __DATABASE_EXPERIMENT_HPP__
|
||||
@ -749,9 +749,20 @@ public:
|
||||
void setId(timer_id_t id) { m_Data.setId(id); }
|
||||
/**
|
||||
* Retrieves the timer's timeout value.
|
||||
* @return the timout in microseconds
|
||||
* @return the timeout in microseconds
|
||||
*/
|
||||
unsigned getTimeout() const { return m_Timeout; }
|
||||
/**
|
||||
* Set the timeout value. Returns the old timeout value. Be aware,
|
||||
* that this method might have no effects, after the listener was added.
|
||||
* @return the old timeout in microseconds
|
||||
*/
|
||||
unsigned setTimeout(unsigned timeout) {
|
||||
unsigned tmp = m_Timeout;
|
||||
m_Timeout = timeout;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
@ -12,29 +12,33 @@
|
||||
#include "../SALInst.hpp"
|
||||
#include "BochsHelpers.hpp"
|
||||
|
||||
// TODO: ATM only capturing bytewise output (most common, I suppose)
|
||||
// FIXME ATM only capturing the first byte of a multi-byte in/out
|
||||
|
||||
aspect IOPortCom {
|
||||
pointcut outInstruction() = "% ...::bx_cpu_c::OUT_DXAL(...)";
|
||||
pointcut devices_outp() = "% ...::bx_devices_c::outp(...)";
|
||||
|
||||
advice execution (outInstruction()) : after ()
|
||||
advice call (devices_outp()) && within ("...::bx_cpu_c") : before ()
|
||||
{
|
||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||
|
||||
// Detect the CPU that triggered the change:
|
||||
unsigned port = *(tjp->arg<0>());
|
||||
unsigned data = *(tjp->arg<1>());
|
||||
|
||||
// detect the CPU that triggered the access
|
||||
fail::ConcreteCPU& triggerCPU = fail::simulator.detectCPU(getCPU(tjp->that()));
|
||||
fail::simulator.onIOPort(&triggerCPU, rAL, rDX, true);
|
||||
// forward to SimulatorController
|
||||
fail::simulator.onIOPort(&triggerCPU, data, port, true);
|
||||
}
|
||||
|
||||
pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX(...)";
|
||||
pointcut devices_inp() = "% ...::bx_devices_c::inp(...)";
|
||||
|
||||
advice execution (inInstruction()) : after ()
|
||||
advice call (devices_inp()) && within ("...::bx_cpu_c") : after ()
|
||||
{
|
||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||
unsigned port = *(tjp->arg<0>());
|
||||
unsigned data = *(tjp->result());
|
||||
|
||||
// detect the CPU that triggered the access
|
||||
fail::ConcreteCPU& triggerCPU = fail::simulator.detectCPU(getCPU(tjp->that()));
|
||||
fail::simulator.onIOPort(&triggerCPU, rAL, rDX, false);
|
||||
// forward to SimulatorController
|
||||
fail::simulator.onIOPort(&triggerCPU, data, port, false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -91,4 +91,5 @@ target_link_libraries(memorymap-test fail-util)
|
||||
add_test(NAME memorymap-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testing COMMAND memorymap-test)
|
||||
|
||||
add_executable(sumtree-test testing/SumTreeTest.cc)
|
||||
target_link_libraries(sumtree-test fail-util)
|
||||
add_test(NAME sumtree-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testing COMMAND sumtree-test)
|
||||
|
||||
@ -43,8 +43,13 @@ bool CommandLine::parse() {
|
||||
option::Descriptor desc = {0, 0, 0, 0, 0, 0};
|
||||
this->options.push_back(desc);
|
||||
|
||||
// Generate the options stats
|
||||
option::Stats stats(this->options.data(), argv.size(), argv.data());
|
||||
// Copy argv to preserve original argument order
|
||||
// (for proper re-parsing after adding more options)
|
||||
argv_reordered = argv;
|
||||
|
||||
// Generate the options stats (GNU mode)
|
||||
option::Stats stats(true, this->options.data(),
|
||||
argv_reordered.size(), argv_reordered.data());
|
||||
|
||||
if (parsed_options)
|
||||
delete[] parsed_options;
|
||||
@ -56,9 +61,9 @@ bool CommandLine::parse() {
|
||||
parsed_options = new option::Option[stats.options_max];
|
||||
parsed_buffer = new option::Option[stats.buffer_max];
|
||||
|
||||
m_parser = new option::Parser(this->options.data(), argv.size(), argv.data(),
|
||||
parsed_options, parsed_buffer);
|
||||
|
||||
m_parser = new option::Parser(true, this->options.data(),
|
||||
argv_reordered.size(), argv_reordered.data(),
|
||||
parsed_options, parsed_buffer);
|
||||
|
||||
// Pop the terminating entry
|
||||
this->options.pop_back();
|
||||
|
||||
@ -30,7 +30,7 @@ public:
|
||||
private:
|
||||
static CommandLine m_instance;
|
||||
|
||||
std::vector<const char *> argv;
|
||||
std::vector<const char *> argv, argv_reordered;
|
||||
std::vector<option::Descriptor> options;
|
||||
option::Option *parsed_options, *parsed_buffer;
|
||||
option::Parser *m_parser;
|
||||
|
||||
@ -133,9 +133,11 @@ void DatabaseProtobufAdapter::TypeBridge_string::bind(MYSQL_BIND *bind, const go
|
||||
/* Handle the NULL case */
|
||||
if (insert_null(bind, msg)) return;
|
||||
|
||||
buffer = ref->GetString(*msg, desc);
|
||||
|
||||
bind->buffer_type = MYSQL_TYPE_STRING;
|
||||
bind->buffer = (void *) ref->GetString(*msg, desc).c_str();
|
||||
bind->buffer_length = ref->GetString(*msg, desc).length();
|
||||
bind->buffer = (void *) buffer.c_str();
|
||||
bind->buffer_length = buffer.length();
|
||||
}
|
||||
void DatabaseProtobufAdapter::TypeBridge_enum::bind(MYSQL_BIND *bind, const google::protobuf::Message *msg) {
|
||||
const google::protobuf::Reflection *ref = msg->GetReflection();
|
||||
|
||||
@ -80,7 +80,7 @@ class DatabaseProtobufAdapter {
|
||||
TypeBridge_uint32(const google::protobuf::FieldDescriptor *desc)
|
||||
: TypeBridge(desc){};
|
||||
|
||||
virtual std::string sql_type() { return "INT"; };
|
||||
virtual std::string sql_type() { return "INT UNSIGNED"; };
|
||||
virtual int element_size() { return 4; };
|
||||
virtual void copy_to(const google::protobuf::Message *msg, int i, void *);
|
||||
virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg);
|
||||
@ -101,7 +101,7 @@ class DatabaseProtobufAdapter {
|
||||
TypeBridge_uint64(const google::protobuf::FieldDescriptor *desc)
|
||||
: TypeBridge(desc){};
|
||||
|
||||
virtual std::string sql_type() { return "BIGINT"; };
|
||||
virtual std::string sql_type() { return "BIGINT UNSIGNED"; };
|
||||
virtual int element_size() { return 8; };
|
||||
virtual void copy_to(const google::protobuf::Message *msg, int i, void *);
|
||||
virtual void bind(MYSQL_BIND *bind, const google::protobuf::Message *msg);
|
||||
@ -138,6 +138,7 @@ class DatabaseProtobufAdapter {
|
||||
};
|
||||
|
||||
struct TypeBridge_string : TypeBridge {
|
||||
std::string buffer;
|
||||
TypeBridge_string(const google::protobuf::FieldDescriptor *desc)
|
||||
: TypeBridge(desc){};
|
||||
virtual std::string sql_type() { return "TEXT"; };
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
|
||||
#include "DwarfReader.hpp"
|
||||
#include "libdwarf.h"
|
||||
@ -128,8 +130,10 @@ bool DwarfReader::read_source_files(const std::string& fileName,std::list<std::s
|
||||
while (dwarf_next_cu_header(dbg,0,0,0,0,&header,0)==DW_DLV_OK) {
|
||||
// Access the die
|
||||
Dwarf_Die die;
|
||||
if (dwarf_siblingof(dbg,0,&die,0)!=DW_DLV_OK)
|
||||
if (dwarf_siblingof(dbg,0,&die,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the source lines
|
||||
Dwarf_Line* lineBuffer;
|
||||
@ -140,17 +144,25 @@ bool DwarfReader::read_source_files(const std::string& fileName,std::list<std::s
|
||||
// Store them
|
||||
for (int index=0;index<lineCount;index++) {
|
||||
Dwarf_Unsigned lineNo;
|
||||
if (dwarf_lineno(lineBuffer[index],&lineNo,0)!=DW_DLV_OK)
|
||||
if (dwarf_lineno(lineBuffer[index],&lineNo,0)!=DW_DLV_OK){
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
char* lineSource;
|
||||
if (dwarf_linesrc(lineBuffer[index],&lineSource,0)!=DW_DLV_OK)
|
||||
if (dwarf_linesrc(lineBuffer[index],&lineSource,0)!=DW_DLV_OK){
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
Dwarf_Bool isCode;
|
||||
if (dwarf_linebeginstatement(lineBuffer[index],&isCode,0)!=DW_DLV_OK)
|
||||
if (dwarf_linebeginstatement(lineBuffer[index],&isCode,0)!=DW_DLV_OK){
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
Dwarf_Addr addr;
|
||||
if (dwarf_lineaddr(lineBuffer[index],&addr,0)!=DW_DLV_OK)
|
||||
if (dwarf_lineaddr(lineBuffer[index],&addr,0)!=DW_DLV_OK){
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lineNo&&isCode) {
|
||||
//LOG << "lineNo: " << lineNo << " addr: " << reinterpret_cast<void*>(addr) << " line source:" << normalize(lineSource) << endl;
|
||||
@ -171,14 +183,20 @@ bool DwarfReader::read_source_files(const std::string& fileName,std::list<std::s
|
||||
lines.unique();
|
||||
|
||||
// Shut down libdwarf
|
||||
if (dwarf_finish(dbg,0)!=DW_DLV_OK)
|
||||
if (dwarf_finish(dbg,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addrToLineList) {
|
||||
bool DwarfReader::read_mapping(std::string fileName, std::list<DwarfLineMapping>& lineMapping) {
|
||||
// temporary mapping of instruction address to (file, line)
|
||||
// => static instruction addresses are sorted ascendingly for the whole binary
|
||||
// (i.e. all instructions from all CUs!)
|
||||
std::map<unsigned, SourceLine> instr_to_sourceline;
|
||||
|
||||
// Open The file
|
||||
int fd=open(fileName.c_str(),O_RDONLY);
|
||||
@ -200,10 +218,13 @@ bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addr
|
||||
|
||||
// Iterator over the headers
|
||||
Dwarf_Unsigned header;
|
||||
// iterate compilation unit headers
|
||||
while (dwarf_next_cu_header(dbg,0,0,0,0,&header,0)==DW_DLV_OK) {
|
||||
// Access the die
|
||||
Dwarf_Die die;
|
||||
// XXX: "if there are no sibling headers, die" | semantics unclear!
|
||||
if (dwarf_siblingof(dbg,0,&die,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -211,6 +232,7 @@ bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addr
|
||||
Dwarf_Line* lineBuffer;
|
||||
Dwarf_Signed lineCount;
|
||||
if (dwarf_srclines(die,&lineBuffer,&lineCount,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
continue; //return false;
|
||||
}
|
||||
|
||||
@ -218,24 +240,30 @@ bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addr
|
||||
for (int index=0;index<lineCount;index++) {
|
||||
Dwarf_Unsigned lineNo;
|
||||
if (dwarf_lineno(lineBuffer[index],&lineNo,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
char* lineSource;
|
||||
if (dwarf_linesrc(lineBuffer[index],&lineSource,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
Dwarf_Bool isCode;
|
||||
if (dwarf_linebeginstatement(lineBuffer[index],&isCode,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
Dwarf_Addr addr;
|
||||
if (dwarf_lineaddr(lineBuffer[index],&addr,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lineNo&&isCode) {
|
||||
struct addrToLine newLine = { (int) addr, (int) lineNo, normalize(lineSource) };
|
||||
addrToLineList.push_back(newLine);
|
||||
// wrap line_number and source_file into an object
|
||||
SourceLine tmp_sl(normalize(lineSource), lineNo);
|
||||
// store SourceLine object with static instr in the map
|
||||
instr_to_sourceline.insert(std::make_pair(addr, tmp_sl));
|
||||
}
|
||||
|
||||
dwarf_dealloc(dbg,lineSource,DW_DLA_STRING);
|
||||
@ -250,9 +278,30 @@ bool DwarfReader::read_mapping(std::string fileName, std::list<addrToLine>& addr
|
||||
|
||||
// Shut down libdwarf
|
||||
if (dwarf_finish(dbg,0)!=DW_DLV_OK) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// iterate instr_to_sourceline to determine the "line_range_size" for mapping
|
||||
std::map<unsigned, SourceLine>::iterator it;
|
||||
for (it = instr_to_sourceline.begin(); it != instr_to_sourceline.end(); it++) {
|
||||
unsigned addr = it->first;
|
||||
SourceLine sl = it->second;
|
||||
|
||||
/* Default the linetable's address range (->"size") to the maximum
|
||||
* possible value. This results in the last linetable entry having
|
||||
* maximum range. This entry will either be a dummy or a function's
|
||||
* epilogue, both of which are irrelevant for our (current) use cases.
|
||||
* All other entries' sizes are set properly. */
|
||||
DwarfLineMapping mapping(addr, (std::numeric_limits<unsigned>::max() - addr),
|
||||
sl.line_number, sl.source_file);
|
||||
if (!lineMapping.empty()) {
|
||||
DwarfLineMapping& back = lineMapping.back();
|
||||
back.line_range_size = addr - back.absolute_addr;
|
||||
}
|
||||
lineMapping.push_back(mapping);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7,6 +7,26 @@
|
||||
|
||||
namespace fail {
|
||||
|
||||
// temporary wrapper object for (file, linenumber)
|
||||
class SourceLine {
|
||||
public:
|
||||
std::string source_file;
|
||||
unsigned line_number;
|
||||
|
||||
SourceLine(std::string source, unsigned line) : source_file(source), line_number(line) {}
|
||||
};
|
||||
|
||||
// wrapper object for insertion into DB
|
||||
class DwarfLineMapping {
|
||||
public:
|
||||
unsigned absolute_addr;
|
||||
unsigned line_range_size;
|
||||
unsigned line_number;
|
||||
std::string line_source;
|
||||
|
||||
DwarfLineMapping(unsigned addr, unsigned size, unsigned number, std::string src)
|
||||
: absolute_addr(addr), line_range_size(size), line_number(number), line_source(src){}
|
||||
};
|
||||
|
||||
/**
|
||||
* This source code is based on bcov 0.2.
|
||||
@ -15,23 +35,16 @@ namespace fail {
|
||||
* GNU GENERAL PUBLIC LICENSE
|
||||
*/
|
||||
|
||||
struct addrToLine {
|
||||
int absoluteAddr;
|
||||
int lineNumber;
|
||||
std::string lineSource;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class DwarfReader
|
||||
* ToDO
|
||||
*/
|
||||
|
||||
class DwarfReader {
|
||||
/**
|
||||
* \class DwarfReader
|
||||
* ToDO
|
||||
*/
|
||||
class DwarfReader {
|
||||
|
||||
public:
|
||||
|
||||
bool read_source_files(const std::string& fileName, std::list<std::string>& lines);
|
||||
bool read_mapping(std::string fileName, std::list<addrToLine>& addrToLineList);
|
||||
bool read_mapping(std::string fileName, std::list<DwarfLineMapping>& lineMapping);
|
||||
};
|
||||
|
||||
} // end-of-namespace fail
|
||||
|
||||
@ -19,11 +19,15 @@ bool MemoryMap::readFromFile(char const * const filename)
|
||||
unsigned count = 0;
|
||||
|
||||
while (getline(file, buf)) {
|
||||
std::string addr, len;
|
||||
std::stringstream ss(buf, std::ios::in);
|
||||
ss >> guest_addr >> guest_len;
|
||||
ss >> addr >> len;
|
||||
guest_addr = strtoul(addr.c_str(), NULL, 0);
|
||||
guest_len = strtoul(len.c_str(), NULL, 0);
|
||||
add(guest_addr, guest_len);
|
||||
count++;
|
||||
}
|
||||
|
||||
// assertion kept from original code; usually something's fishy if the file
|
||||
// contains no entries
|
||||
assert(count > 0);
|
||||
|
||||
@ -11,8 +11,7 @@ set(SRCS
|
||||
|
||||
include(FindLLVM)
|
||||
|
||||
# compiling without -fno-rtti fails even when LLVM is not built with that flag
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_CXX_FLAGS} -fno-rtti" )
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_CXX_FLAGS}" )
|
||||
|
||||
add_library(fail-llvmdisassembler ${SRCS})
|
||||
target_link_libraries(fail-llvmdisassembler fail-sal)
|
||||
|
||||
@ -49,7 +49,10 @@ LLVMtoFailTranslator* LLVMtoFailTranslator::createFromBinary(const std::string e
|
||||
llvm::InitializeAllDisassemblers();
|
||||
|
||||
OwningPtr<Binary> binary;
|
||||
assert(createBinary(elf_path, binary) == 0);
|
||||
llvm::error_code ret = createBinary(elf_path, binary);
|
||||
assert (ret == 0);
|
||||
(void) ret; // unused in release builds
|
||||
assert (binary.get() != NULL);
|
||||
|
||||
#ifndef __puma
|
||||
LLVMDisassembler disas(dyn_cast<ObjectFile>(binary.get()));
|
||||
@ -57,4 +60,4 @@ LLVMtoFailTranslator* LLVMtoFailTranslator::createFromBinary(const std::string e
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
#ifndef __puma
|
||||
#include "../LLVMDisassembler.hpp"
|
||||
|
||||
using namespace llvm;
|
||||
@ -69,4 +68,3 @@ int main(int argc, char* argv[]) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
#include "util/SumTree.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#define LOG std::cerr
|
||||
|
||||
using std::endl;
|
||||
|
||||
fail::Logger LOG("SumTreeTest");
|
||||
|
||||
struct Pilot {
|
||||
uint32_t id;
|
||||
uint32_t instr2;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <stdlib.h>
|
||||
#include "util/MemoryMap.hpp"
|
||||
|
||||
using namespace fail;
|
||||
@ -16,6 +17,7 @@ uint32_t outside[] = { 0, 10, 16, 1, 20, 1, 25, 10 };
|
||||
void test_failed(std::string msg)
|
||||
{
|
||||
cerr << "MemoryMap test failed (" << msg << ")!" << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
// pass by value intentional
|
||||
@ -98,6 +100,7 @@ int main()
|
||||
char const *filename_tmp = "tmp.memorymap";
|
||||
char const *filename_test1 = "test1.memorymap";
|
||||
char const *filename_test2 = "test2.memorymap";
|
||||
char const *filename_test3 = "test3.memorymap";
|
||||
|
||||
for (unsigned i = 0; i < LEN(inside); i += 2) {
|
||||
mm.add(inside[i], inside[i+1]);
|
||||
@ -125,4 +128,8 @@ int main()
|
||||
mm.clear();
|
||||
mm.readFromFile(filename_test2);
|
||||
test(mm);
|
||||
|
||||
mm.clear();
|
||||
mm.readFromFile(filename_test3);
|
||||
test(mm);
|
||||
}
|
||||
|
||||
3
src/core/util/testing/test3.memorymap
Normal file
3
src/core/util/testing/test3.memorymap
Normal file
@ -0,0 +1,3 @@
|
||||
012 0x6
|
||||
0x11 3
|
||||
21 0x4
|
||||
Reference in New Issue
Block a user