cpn: Generic wrapper for injection point
As for the pandaboard to navigate fast to the injection instruction we need to deliver a hop chain to the fail-client, this commit adds a generic wrapper for a injection point. For now we have only the two options hop chain and instruction offset, so it is activated via a cmake ON/OFF switch. Change-Id: Ic01a07a30ac386d4316e6d6d271baf1549db966a
This commit is contained in:
@ -43,7 +43,17 @@ if(BUILD_PANDA)
|
||||
target_link_libraries(fail-client ${openocd_src_dir}/src/.libs/libopenocd.a ${openocd_src_dir}/jimtcl/libjim.a fail ${openocd_library_dependencies})
|
||||
add_dependencies(fail-client libfailopenocd_external)
|
||||
|
||||
#copy the conf-files to build directory
|
||||
# ensure, elf path is set for enabling openocd to read elf symbols
|
||||
if(EXISTS $ENV{FAIL_ELF_PATH})
|
||||
SET(PANDA_ELF_PATH $ENV{FAIL_ELF_PATH})
|
||||
message(STATUS "[Fail*] PandaBoard ELF under test: ${PANDA_ELF_PATH}")
|
||||
else()
|
||||
message(FATAL_ERROR "Please set the FAIL_ELF_PATH enviroment variable to the binary under test.")
|
||||
endif()
|
||||
|
||||
|
||||
# copy the conf-files to build directory
|
||||
# TODO: copy to install path on install!?
|
||||
file(COPY ${openocd_src_dir}/tcl/ DESTINATION oocd_conf/ PATTERN openocd EXCLUDE)
|
||||
get_filename_component(OOCD_CONF_FILES_PATH ${CMAKE_BINARY_DIR}/oocd_conf/ REALPATH)
|
||||
|
||||
|
||||
@ -7,15 +7,31 @@ set(SRCS
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(PROTOS
|
||||
FailControlMessage.proto
|
||||
DatabaseCampaignMessage.proto
|
||||
${CMAKE_CURRENT_BINARY_DIR}/DatabaseCampaignMessage.proto
|
||||
TracePlugin.proto
|
||||
)
|
||||
|
||||
## Set concrete implementation of InjectionPointMessage
|
||||
# ToDo: Define seperate symbol, so it can also be used in other build types
|
||||
if(CONFIG_INJECTIONPOINT_HOPS)
|
||||
set(PROTOS ${PROTOS} InjectionPointHopsMessage.proto)
|
||||
set(CONCRETE_INJECTION_POINT InjectionPointHopsMessage.proto)
|
||||
else(CONFIG_INJECTIONPOINT_HOPS)
|
||||
set(PROTOS ${PROTOS} InjectionPointStepsMessage.proto)
|
||||
set(CONCRETE_INJECTION_POINT InjectionPointStepsMessage.proto)
|
||||
endif(CONFIG_INJECTIONPOINT_HOPS)
|
||||
|
||||
# Configure concrete protobuf message to be included by campaign message
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/DatabaseCampaignMessage.proto.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/DatabaseCampaignMessage.proto)
|
||||
|
||||
#### PROTOBUFS ####
|
||||
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})
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${PROTOS})
|
||||
|
||||
add_library(fail-comm ${SRCS} ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
|
||||
@ -4,14 +4,22 @@ extend google.protobuf.FieldOptions {
|
||||
optional bool sql_ignore = 32383 [ default = false];
|
||||
}
|
||||
|
||||
|
||||
import "@CONCRETE_INJECTION_POINT@";
|
||||
|
||||
message DatabaseCampaignMessage {
|
||||
required int32 pilot_id = 1 [(sql_primary_key) = true];
|
||||
required int32 variant_id = 2 [(sql_ignore) = true];
|
||||
required int32 fspmethod_id = 3 [(sql_ignore) = true];
|
||||
|
||||
// ToDo: injection_instr can be deleted if all experiments switched to
|
||||
// using generic InjectionPointMessage
|
||||
required int32 injection_instr = 4 [(sql_ignore) = true];
|
||||
optional int32 injection_instr_absolute = 5 [(sql_ignore) = true];
|
||||
required int32 data_address = 6 [(sql_ignore) = true];
|
||||
required int32 data_width = 7 [(sql_ignore) = true];
|
||||
required string variant = 8 [(sql_ignore) = true];
|
||||
required string benchmark = 9 [(sql_ignore) = true];
|
||||
|
||||
required InjectionPointMessage injection_point = 10 [(sql_ignore) = true];
|
||||
}
|
||||
23
src/core/comm/InjectionPointHopsMessage.proto
Normal file
23
src/core/comm/InjectionPointHopsMessage.proto
Normal file
@ -0,0 +1,23 @@
|
||||
message InjectionPointMessage {
|
||||
// Costs of the hop chain on the PandaBoard
|
||||
// ToDo: Could be eliminated, but it is nice for evaluation
|
||||
optional uint32 costs = 3;
|
||||
|
||||
// If checkpoint must be used for this hop chain, id is set properly
|
||||
optional uint32 checkpoint_id = 1;
|
||||
|
||||
// Repeated groups can't be defined as notempty, so a manual
|
||||
// non-empty check is required at usage
|
||||
|
||||
// As we assume hops to always watch addresses of length 1, we
|
||||
// don't encode the length in here
|
||||
repeated group Hops = 2 {
|
||||
required uint64 address = 1;
|
||||
enum AccessType {
|
||||
EXECUTE = 1;
|
||||
READ = 2;
|
||||
WRITE = 3;
|
||||
}
|
||||
required AccessType accesstype = 2;
|
||||
}
|
||||
}
|
||||
3
src/core/comm/InjectionPointStepsMessage.proto
Normal file
3
src/core/comm/InjectionPointStepsMessage.proto
Normal file
@ -0,0 +1,3 @@
|
||||
message InjectionPointMessage {
|
||||
required int32 injection_instr = 1;
|
||||
}
|
||||
@ -23,6 +23,7 @@ OPTION(CONFIG_DISABLE_KEYB_INTERRUPTS "Target backend: Suppress keyboard inter
|
||||
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_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)")
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
// Performance options
|
||||
#cmakedefine CONFIG_FAST_BREAKPOINTS
|
||||
#cmakedefine CONFIG_FAST_WATCHPOINTS
|
||||
#cmakedefine CONFIG_INJECTIONPOINT_HOPS
|
||||
|
||||
// Save/restore functionality
|
||||
#cmakedefine CONFIG_SR_RESTORE
|
||||
|
||||
@ -4,6 +4,19 @@ set(SRCS
|
||||
DatabaseCampaign.cc
|
||||
)
|
||||
|
||||
# only compile concrete implementation of InjectionPoint
|
||||
if(CONFIG_INJECTIONPOINT_HOPS)
|
||||
set (SRCS ${SRCS} InjectionPointHops.cc)
|
||||
else(CONFIG_INJECTIONPOINT_HOPS)
|
||||
set (SRCS ${SRCS} InjectionPointSteps.cc)
|
||||
endif(CONFIG_INJECTIONPOINT_HOPS)
|
||||
|
||||
add_library(fail-cpn ${SRCS})
|
||||
|
||||
# if hop-chains need to be calculated by the server, we
|
||||
# the smarthopping module
|
||||
if(CONFIG_INJECTIONPOINT_HOPS)
|
||||
add_dependencies(fail-cpn fail-smarthopping)
|
||||
endif(CONFIG_INJECTIONPOINT_HOPS)
|
||||
|
||||
add_dependencies(fail-cpn fail-comm)
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
#include "util/Logger.hpp"
|
||||
#include "util/Database.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
|
||||
#include "InjectionPoint.hpp"
|
||||
|
||||
#ifndef __puma
|
||||
#include <boost/thread.hpp>
|
||||
@ -135,6 +135,11 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) {
|
||||
log_send << "Found " << experiment_count << " unfinished experiments in database. ("
|
||||
<< variant.variant << "/" << variant.benchmark << ")" << std::endl;
|
||||
|
||||
// abstraction of injection point
|
||||
// must not be initialized in loop, because hop chain calculator would loose state after loop pass
|
||||
// and so for every hop chain it would have to begin calculating at trace instruction zero
|
||||
ConcreteInjectionPoint ip;
|
||||
|
||||
sent_pilots = 0;
|
||||
while ((row = mysql_fetch_row(pilots)) != 0) {
|
||||
unsigned pilot_id = atoi(row[0]);
|
||||
@ -147,10 +152,14 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) {
|
||||
pilot.set_pilot_id(pilot_id);
|
||||
pilot.set_fspmethod_id(fspmethod_id);
|
||||
pilot.set_variant_id(variant.id);
|
||||
// ToDo: Remove this, if all experiments work with abstract API (InjectionPoint)
|
||||
pilot.set_injection_instr(injection_instr);
|
||||
pilot.set_variant(variant.variant);
|
||||
pilot.set_benchmark(variant.benchmark);
|
||||
|
||||
ip.parseFromInjectionInstr(injection_instr);
|
||||
ip.addToCampaignMessage(pilot);
|
||||
|
||||
if (row[4]) {
|
||||
unsigned injection_instr_absolute = atoi(row[4]);
|
||||
pilot.set_injection_instr_absolute(injection_instr_absolute);
|
||||
|
||||
104
src/core/cpn/InjectionPoint.hpp
Normal file
104
src/core/cpn/InjectionPoint.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
#ifndef INJECTIONPOINT_HPP_
|
||||
#define INJECTIONPOINT_HPP_
|
||||
|
||||
#include "comm/DatabaseCampaignMessage.pb.h"
|
||||
#include "util/Logger.hpp"
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class InjectionPointBase
|
||||
*
|
||||
* Interface for a generic InjectionPoint (e.g. trace instruction offset or
|
||||
* hop chain) for use in the generic DatabaseCampaign. Every
|
||||
* DataBaseCampaignMessage contains a concrete InjectionPoint.
|
||||
*/
|
||||
class InjectionPointBase {
|
||||
protected:
|
||||
InjectionPointMessage m_ip; // !< Protobuf message of concrete injection point, this class wraps
|
||||
Logger m_log;
|
||||
public:
|
||||
InjectionPointBase() : m_log("InjectionPoint") {}
|
||||
|
||||
/**
|
||||
* Parses generic representation from a injection instruction (trace offset).
|
||||
* Used by server.
|
||||
* @param inj_instr trace instruction offset to be parsed
|
||||
*/
|
||||
virtual void parseFromInjectionInstr(unsigned inj_instr) = 0;
|
||||
|
||||
/**
|
||||
* Parses (extracts) generic representation from a DatabaseCampaignMessage.
|
||||
* Used by client.
|
||||
* @param pilot DatabaseCampaignMessage which contains a InjectionPoint
|
||||
*/
|
||||
void parseFromCampaignMessage(const DatabaseCampaignMessage &pilot) {m_ip.CopyFrom(pilot.injection_point());}
|
||||
|
||||
/**
|
||||
* Adds generic representation to a DatabaseCampaignMessage
|
||||
* @param pilot DatabaseCampaignMessage to which the generic protobuf representation will be added
|
||||
*/
|
||||
void addToCampaignMessage(DatabaseCampaignMessage &pilot) {pilot.mutable_injection_point()->CopyFrom(m_ip);}
|
||||
|
||||
/**
|
||||
* Get a copy of the contained InjectionPointMessage
|
||||
* @param ipm Generic InjectionPointMessage to which the content will be copied
|
||||
*/
|
||||
void copyInjectionPointMessage(InjectionPointMessage &ipm) {ipm.CopyFrom(m_ip);}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_INJECTIONPOINT_HOPS
|
||||
#include "comm/InjectionPointHopsMessage.pb.h"
|
||||
|
||||
class SmartHops;
|
||||
|
||||
/**
|
||||
* \class InjectionPointHops
|
||||
*
|
||||
* Concrete injection point which contains a hop chain to the target
|
||||
* trace instruction.
|
||||
*/
|
||||
class InjectionPointHops : public InjectionPointBase {
|
||||
private:
|
||||
SmartHops *m_sa; // !< Hop calculator which generates the hop chain
|
||||
uint32_t m_curr_inst; // !< Instruction for which currently a hop chain is available
|
||||
public:
|
||||
InjectionPointHops();
|
||||
virtual ~InjectionPointHops();
|
||||
|
||||
/**
|
||||
* Parses a hop chain from a injection instruction (trace offset).
|
||||
* @param inj_instr trace instruction offset to be parsed
|
||||
*/
|
||||
virtual void parseFromInjectionInstr(unsigned inj_instr);
|
||||
};
|
||||
|
||||
typedef InjectionPointHops ConcreteInjectionPoint;
|
||||
|
||||
#else
|
||||
#include "comm/InjectionPointStepsMessage.pb.h"
|
||||
|
||||
/**
|
||||
* \class InjectionPointSteps
|
||||
*
|
||||
* Concrete injection point which contains a trace instruction offset.
|
||||
*/
|
||||
class InjectionPointSteps : public InjectionPointBase {
|
||||
public:
|
||||
virtual ~InjectionPointSteps() {}
|
||||
|
||||
/**
|
||||
* Parses a trace offset from a injection instruction (trace offset),
|
||||
* so it effectively just stores the value in the protobuf message.
|
||||
* @param inj_instr trace instruction offset to be parsed
|
||||
*/
|
||||
virtual void parseFromInjectionInstr(unsigned inj_instr);
|
||||
};
|
||||
|
||||
typedef InjectionPointSteps ConcreteInjectionPoint;
|
||||
#endif
|
||||
|
||||
} /* namespace fail */
|
||||
|
||||
#endif /* INJECTIONPOINT_HPP_ */
|
||||
40
src/core/cpn/InjectionPointHops.cc
Normal file
40
src/core/cpn/InjectionPointHops.cc
Normal file
@ -0,0 +1,40 @@
|
||||
#include "InjectionPoint.hpp"
|
||||
#include "util/smarthops/SmartHops.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
InjectionPointHops::InjectionPointHops() : InjectionPointBase(), m_sa(new SmartHops()), m_curr_inst(0) {
|
||||
|
||||
char * elfpath = getenv("FAIL_TRACE_PATH");
|
||||
if(elfpath == NULL){
|
||||
m_log << "FAIL_TRACE_PATH not set :(" << std::endl;
|
||||
exit(-1);
|
||||
}else{
|
||||
m_sa->init((const char*)elfpath);
|
||||
}
|
||||
}
|
||||
|
||||
InjectionPointHops::~InjectionPointHops() {
|
||||
delete m_sa;
|
||||
}
|
||||
|
||||
void InjectionPointHops::parseFromInjectionInstr(unsigned inj_instr) {
|
||||
// Already calculated result needed?
|
||||
if (m_curr_inst == inj_instr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Non monotonic ascending injection instruction given?
|
||||
if (m_curr_inst > inj_instr) {
|
||||
m_log << "FATAL ERROR: Got not monotonic ascending values of injection instruction" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!m_sa->calculateFollowingHop(m_ip, inj_instr)) {
|
||||
m_log << "FATAL ERROR: Trace does not contain enough instructions (" << inj_instr << ")" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
m_curr_inst = inj_instr;
|
||||
}
|
||||
|
||||
} /* namespace fail */
|
||||
10
src/core/cpn/InjectionPointSteps.cc
Normal file
10
src/core/cpn/InjectionPointSteps.cc
Normal file
@ -0,0 +1,10 @@
|
||||
#include "InjectionPoint.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
void InjectionPointSteps::parseFromInjectionInstr(unsigned inj_instr) {
|
||||
// compute hops
|
||||
m_ip.set_injection_instr(inj_instr);
|
||||
}
|
||||
|
||||
} /* namespace fail */
|
||||
@ -63,9 +63,16 @@ else()
|
||||
endif()
|
||||
mark_as_advanced(FAIL_OBJDUMP)
|
||||
|
||||
# compile smarthops calculator if needed
|
||||
|
||||
if(CONFIG_INJECTIONPOINT_HOPS)
|
||||
add_subdirectory(smarthops)
|
||||
set(ADDITIONAL_LIBS fail-smarthops)
|
||||
endif(CONFIG_INJECTIONPOINT_HOPS)
|
||||
|
||||
add_library(fail-util ${SRCS})
|
||||
add_dependencies(fail-util fail-comm)
|
||||
target_link_libraries(fail-util ${PROTOBUF_LIBRARY} ${Boost_LIBRARIES} ${LIB_IBERTY} ${ZLIB_LIBRARIES})
|
||||
target_link_libraries(fail-util ${ADDITIONAL_LIBS} ${PROTOBUF_LIBRARY} ${Boost_LIBRARIES} ${LIB_IBERTY} ${ZLIB_LIBRARIES})
|
||||
|
||||
option(BUILD_LLVM_DISASSEMBLER "Build the LLVM-based disassembler (LLVM 3.3 preferred, for 3.1 and 3.2 read doc/how-to-build.txt)" OFF)
|
||||
if (BUILD_LLVM_DISASSEMBLER)
|
||||
|
||||
10
src/core/util/smarthops/CMakeLists.txt
Normal file
10
src/core/util/smarthops/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
set(SRCS
|
||||
SmartHops.cc
|
||||
SmartHops.hpp
|
||||
TraceReader.cc
|
||||
TraceReader.hpp
|
||||
)
|
||||
|
||||
add_library(fail-smarthops ${SRCS})
|
||||
# ToDo: Do we really need this dependency?
|
||||
add_dependencies(fail-smarthops fail-comm)
|
||||
247
src/core/util/smarthops/SmartHops.cc
Normal file
247
src/core/util/smarthops/SmartHops.cc
Normal file
@ -0,0 +1,247 @@
|
||||
#include "SmartHops.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace fail {
|
||||
|
||||
#define COST_CHANGE 1
|
||||
#define COST_NO_CHANGE 2
|
||||
|
||||
|
||||
void SmartHops::init(const char *filename) {
|
||||
m_trace_reader.openTraceFile(filename);
|
||||
}
|
||||
|
||||
void SmartHops::convertToIPM(std::vector<result_tuple > &result, unsigned costs, InjectionPointMessage &ipm) {
|
||||
|
||||
ipm.Clear();
|
||||
// If checkpoint at beginning of hop-chain, add its id to HopChain
|
||||
std::vector<result_tuple >::iterator it_hop = result.begin();
|
||||
if (it_hop != result.end() && it_hop->first.second == ACCESS_CHECKPOINT) {
|
||||
ipm.set_checkpoint_id(it_hop->first.first);
|
||||
it_hop++;
|
||||
}
|
||||
|
||||
ipm.set_costs(costs);
|
||||
|
||||
for(;it_hop != result.end();
|
||||
it_hop++) {
|
||||
InjectionPointMessage_Hops *hop = ipm.add_hops();
|
||||
hop->set_address(it_hop->first.first);
|
||||
|
||||
enum InjectionPointMessage_Hops_AccessType at;
|
||||
switch (it_hop->first.second) {
|
||||
case ACCESS_NONE:
|
||||
at = InjectionPointMessage_Hops_AccessType_EXECUTE;
|
||||
break;
|
||||
case ACCESS_READ:
|
||||
at = InjectionPointMessage_Hops_AccessType_READ;
|
||||
break;
|
||||
case ACCESS_WRITE:
|
||||
at = InjectionPointMessage_Hops_AccessType_WRITE;
|
||||
break;
|
||||
case ACCESS_READORWRITE:
|
||||
m_log << "ReadOrWrite memory access event not yet"
|
||||
" covered" << std::endl;
|
||||
exit(-1);
|
||||
break;
|
||||
case ACCESS_CHECKPOINT:
|
||||
m_log << "Checkpoint not allowed after beginning of hop chain" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
hop->set_accesstype(at);
|
||||
}
|
||||
}
|
||||
|
||||
bool SmartHops::calculateFollowingHop(InjectionPointMessage &ip, unsigned instruction_offset) {
|
||||
|
||||
while (m_trace_pos < instruction_offset) {
|
||||
if (!m_trace_reader.getNextTraceEvents(m_trace_pos, m_trace_events)) {
|
||||
return false;
|
||||
}
|
||||
// Policy when multiple trace events: Choose the one with smallest last_pos
|
||||
trace_event_tuple_t *best_te = NULL;
|
||||
trace_pos_t last_pos = std::numeric_limits<trace_pos_t>::max();
|
||||
|
||||
bool hop_found = false;
|
||||
bool checkpoint_forbidden = false;
|
||||
|
||||
for (std::vector<trace_event_tuple_t >::iterator it_te = m_trace_events.begin();
|
||||
it_te != m_trace_events.end();
|
||||
it_te++) {
|
||||
|
||||
// Don't use watchpoints?
|
||||
if (!m_use_watchpoints && it_te->second != ACCESS_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find last pos of current trace event
|
||||
std::map<trace_event_tuple_t, trace_pos_t>::iterator it_lp = m_last_positions.find(*it_te);
|
||||
|
||||
// Not known? => Single hop, result calculation complete
|
||||
if (it_lp == m_last_positions.end()) {
|
||||
m_last_positions.insert(std::pair<trace_event_tuple_t, trace_pos_t>(*it_te, m_trace_pos));
|
||||
// New instruction => single hop
|
||||
if (!hop_found) {
|
||||
m_result.clear();
|
||||
m_result.push_back(result_tuple(*it_te, m_trace_pos));
|
||||
m_costs = COST_CHANGE;
|
||||
hop_found = true;
|
||||
}
|
||||
} else {
|
||||
if (it_lp->second < last_pos) {
|
||||
last_pos = it_lp->second;
|
||||
best_te = (trace_event_tuple_t*)&(it_lp->first);
|
||||
}
|
||||
it_lp->second = m_trace_pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (hop_found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Deltion of unnecessary hops
|
||||
|
||||
/*
|
||||
* |----------------|
|
||||
* |pos bigger than |
|
||||
* |last pos of new | --> delete -->-|
|
||||
* |trace_event? | |
|
||||
* |----------------| |
|
||||
* ^ |
|
||||
* | v
|
||||
* A B A C <<< Last result
|
||||
* ^ ^
|
||||
* Reverse Reverse
|
||||
* iterator iterator
|
||||
* rit_pre_last rit_last
|
||||
*
|
||||
*/
|
||||
|
||||
std::vector<result_tuple >::reverse_iterator rit_pre_last, rit_last, end;
|
||||
|
||||
|
||||
rit_last = m_result.rbegin();
|
||||
rit_pre_last = m_result.rbegin();
|
||||
rit_pre_last++;
|
||||
|
||||
while (rit_last != m_result.rend()) {
|
||||
|
||||
trace_pos_t left_pos;
|
||||
|
||||
// Policy switch:
|
||||
// No weights => last pos <= pos of rit_pre_last
|
||||
// Weights => last pos < pos of rit_pre_last
|
||||
|
||||
if ((rit_pre_last == m_result.rend())) {
|
||||
// First node in hop list is Checkpoint? => Look at result before CP-Creation
|
||||
if (rit_last->first.second == ACCESS_CHECKPOINT) {
|
||||
rit_pre_last = (m_checkpoints[rit_last->first.first]).second.rbegin();
|
||||
|
||||
// This should never happen:
|
||||
if (rit_pre_last == (m_checkpoints[rit_last->first.first]).second.rend()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't allow deletion of CP, if afterwards
|
||||
// too few of the olf hops would be deleted.
|
||||
// (past before to be deleted CP)
|
||||
|
||||
// Should not exceed vector boundaries, if
|
||||
// g_rollback_thresh*2 < (g_cp_thresh - g_cost_cp)
|
||||
if ((*(rit_pre_last + m_rollback_thresh)).second < last_pos) {
|
||||
checkpoint_forbidden = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
left_pos = rit_pre_last->second;
|
||||
|
||||
if ((!m_use_weights && !(last_pos <= left_pos))
|
||||
|| (m_use_weights && !(last_pos < left_pos))) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Hop a->b is not allowed if last pos of b is at a
|
||||
// but a is not b
|
||||
// e.g. instruction x with mem access at y
|
||||
// a is WP y, and b is BP x
|
||||
// OR a is BP x, and b is WP y but instruction
|
||||
// at position a has also memory access at y
|
||||
if (rit_pre_last != m_result.rend()) {
|
||||
if ((last_pos == rit_pre_last->second) &&
|
||||
rit_pre_last->first != *best_te) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Right hop will be deleted
|
||||
// => Recalculate costs
|
||||
|
||||
// If to be deleted hop is a checkpoint:
|
||||
// There will be no past in the trace
|
||||
// Reconfigure result and iterators
|
||||
if (rit_last->first.second == ACCESS_CHECKPOINT) {
|
||||
m_costs = m_checkpoints[rit_last->first.first].first;
|
||||
|
||||
m_result.clear();
|
||||
std::vector<result_tuple > *tmp = &(m_checkpoints[rit_last->first.first].second);
|
||||
m_result.insert(m_result.end(), tmp->begin(), tmp->end());
|
||||
|
||||
rit_last = m_result.rbegin();
|
||||
rit_pre_last = m_result.rbegin();
|
||||
rit_pre_last++;
|
||||
|
||||
// CP could be removed from m_checkpoints, but to do this
|
||||
// m_checkpoints needs to be a map (CP-ID is implicit as vector index)
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rit_pre_last->first == rit_last->first) {
|
||||
m_costs -= COST_NO_CHANGE;
|
||||
} else {
|
||||
m_costs -= COST_CHANGE;
|
||||
}
|
||||
|
||||
// Delete element described by rit_last
|
||||
// Deletion of elements described by reverse iterator works like this
|
||||
m_result.erase((rit_last+1).base());
|
||||
rit_last = rit_pre_last;
|
||||
rit_pre_last++;
|
||||
}
|
||||
|
||||
|
||||
// Add costs for new hop
|
||||
if (*best_te == (m_result.back()).first) {
|
||||
m_costs += COST_NO_CHANGE;
|
||||
} else {
|
||||
m_costs += COST_CHANGE;
|
||||
}
|
||||
|
||||
// Check if Checkpoint needed
|
||||
if (m_use_checkpoints && (m_costs > m_cp_thresh) && !checkpoint_forbidden) {
|
||||
checkpoint_tuple_t new_cp(m_costs, std::vector<result_tuple >(m_result));
|
||||
m_checkpoints.push_back(new_cp);
|
||||
m_result.clear();
|
||||
m_result.push_back(result_tuple(trace_event_tuple_t(m_next_cp_id++,
|
||||
ACCESS_CHECKPOINT),
|
||||
m_trace_pos));
|
||||
m_costs = m_cost_cp;
|
||||
} else {
|
||||
// Add new hop
|
||||
m_result.push_back(result_tuple(*best_te, m_trace_pos));
|
||||
}
|
||||
}
|
||||
InjectionPointMessage ipm;
|
||||
convertToIPM(m_result, m_costs, ip);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
90
src/core/util/smarthops/SmartHops.hpp
Normal file
90
src/core/util/smarthops/SmartHops.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
#ifndef SMART_ALGORITHM__HPP
|
||||
#define SMART_ALGORITHM__HPP
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "TraceReader.hpp"
|
||||
#include "comm/InjectionPointHopsMessage.pb.h"
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
typedef std::pair<trace_event_tuple_t, trace_pos_t > result_tuple;
|
||||
|
||||
// COSTS NAVIGATIONAL RECORD
|
||||
typedef std::pair<unsigned int, std::vector<result_tuple > > checkpoint_tuple_t;
|
||||
|
||||
/**
|
||||
* \class SmartHops
|
||||
*
|
||||
* Calculator for (optimal) hop chains on the basis of a simple cost model.
|
||||
* Currently hardcoded cost model for PandaBoard (horizontal hops cost
|
||||
* twice as much as diagonal hops).
|
||||
* Calculates stream based (only regarding the next trace event) and so
|
||||
* it can only calculate for ascending trace instruction offsets.
|
||||
*/
|
||||
class SmartHops {
|
||||
public:
|
||||
|
||||
// ToDo: If you want to use checkoints as additional hop-chain-element, you may switch
|
||||
// m_use_checkpoints to true, but there will be additional changes needed for this to fully work.
|
||||
// The sal must, for example, generate CPs at the given positions
|
||||
SmartHops() : m_trace_pos(0), m_costs(0), m_next_cp_id(0), m_log("SmartHops", false), m_use_watchpoints(true),
|
||||
m_use_weights(true), m_use_checkpoints(false), m_cp_thresh(0), m_cost_cp(0), m_rollback_thresh(0){}
|
||||
|
||||
/**
|
||||
* Initializes the used TraceReader with given trace file path
|
||||
* @param filename Path to the trace file
|
||||
*/
|
||||
void init(const char *filename);
|
||||
|
||||
/**
|
||||
* Initializes the used TraceReader with given trace file path
|
||||
* @param filename Path to the trace file
|
||||
* @returns \c true if calculation succeeded and \c false if it did not
|
||||
*/
|
||||
bool calculateFollowingHop(InjectionPointMessage &ip, unsigned instruction_offset);
|
||||
private:
|
||||
|
||||
/**
|
||||
* Converts internal representation of a hop chain to a
|
||||
* InjectionPointMessage. The delivered InjectionPointMessage is
|
||||
* cleared before parsing.
|
||||
* @param result Internal representation of a hop chain
|
||||
* @param costs Costs of the hop chain (extracted from cost model)
|
||||
* @param ipm InjectionPointMessage to which the hop chain is parsed
|
||||
*/
|
||||
void convertToIPM(std::vector<result_tuple > &result, unsigned costs, InjectionPointMessage &ipm);
|
||||
|
||||
unsigned int m_trace_pos;
|
||||
unsigned int m_costs;
|
||||
|
||||
TraceReader m_trace_reader;
|
||||
unsigned int m_next_cp_id;
|
||||
Logger m_log;
|
||||
|
||||
bool m_use_watchpoints;
|
||||
bool m_use_weights;
|
||||
bool m_use_checkpoints;
|
||||
|
||||
unsigned int m_cp_thresh;
|
||||
unsigned int m_cost_cp;
|
||||
|
||||
// Must be smaller than ((COST_CP_THRESH - COST_CP) / 2)
|
||||
// If too high: Costs per dynamic instruction will approach
|
||||
// COST_CP_THRESH but number of CPs can be reduced drastically
|
||||
unsigned int m_rollback_thresh;
|
||||
|
||||
|
||||
std::map<trace_event_tuple_t, trace_pos_t> m_last_positions;
|
||||
std::vector<checkpoint_tuple_t > m_checkpoints;
|
||||
|
||||
std::vector<trace_event_tuple_t > m_trace_events;
|
||||
std::vector<result_tuple > m_result;
|
||||
};
|
||||
|
||||
} // end of namespace
|
||||
|
||||
#endif // SMART_ALGORITHM__HPP
|
||||
|
||||
97
src/core/util/smarthops/TraceReader.cc
Normal file
97
src/core/util/smarthops/TraceReader.cc
Normal file
@ -0,0 +1,97 @@
|
||||
#include "TraceReader.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
using std::endl;
|
||||
using fail::ProtoIStream;
|
||||
|
||||
namespace fail {
|
||||
|
||||
TraceReader::~TraceReader()
|
||||
{
|
||||
delete ps;
|
||||
delete normal_stream;
|
||||
delete gz_stream;
|
||||
}
|
||||
|
||||
std::istream& openStream(const char *input_file,
|
||||
std::ifstream& normal_stream, igzstream& gz_stream, Logger &out_stream) {
|
||||
normal_stream.open(input_file);
|
||||
if (!normal_stream) {
|
||||
out_stream << "FATAL ERROR: couldn't open " << input_file << endl;
|
||||
exit(-1);
|
||||
}
|
||||
unsigned char b1, b2;
|
||||
normal_stream >> b1 >> b2;
|
||||
|
||||
if (b1 == 0x1f && b2 == 0x8b) {
|
||||
normal_stream.close();
|
||||
gz_stream.open(input_file);
|
||||
if (!gz_stream) {
|
||||
out_stream << "couldn't open " << input_file << endl;
|
||||
exit(-1);
|
||||
}
|
||||
return gz_stream;
|
||||
}
|
||||
|
||||
normal_stream.seekg(0);
|
||||
|
||||
return normal_stream;
|
||||
}
|
||||
|
||||
void TraceReader::openTraceFile(const char *filename)
|
||||
{
|
||||
normal_stream = new std::ifstream();
|
||||
gz_stream = new igzstream();
|
||||
ps = new fail::ProtoIStream(&openStream(filename, *normal_stream, *gz_stream, m_log));
|
||||
}
|
||||
|
||||
bool TraceReader::getNextTraceEvents(trace_pos_t& trace_pos,
|
||||
std::vector<trace_event_tuple_t >& trace_events)
|
||||
{
|
||||
trace_pos = m_current_position;
|
||||
|
||||
// Delivered trace_events vector does not have to be
|
||||
// empty, so it must be cleared
|
||||
trace_events.clear();
|
||||
|
||||
if (m_current_position == 1) {
|
||||
if (!ps->getNext(&ev)) {
|
||||
return false;
|
||||
}
|
||||
ev_avail = true;
|
||||
}
|
||||
|
||||
if (!ev_avail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
trace_events.push_back(trace_event_tuple_t(ev.ip(), ACCESS_NONE));
|
||||
ev_avail = false;
|
||||
|
||||
// read possible memory accesses and the next instruction
|
||||
while (ps->getNext(&ev)) {
|
||||
if (!ev.has_memaddr()) {
|
||||
ev_avail = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Add a trace_event for every byte in memory access.
|
||||
// This breaks down the calculations to multiple
|
||||
// memory accesses of length 1. No more complexity
|
||||
// is needed in hop calculations.
|
||||
if (ev.has_width()) {
|
||||
for (unsigned int i = 0; i < ev.width(); i++) {
|
||||
trace_events.push_back(
|
||||
trace_event_tuple_t(ev.memaddr() + i,
|
||||
ev.accesstype() == ev.READ ? ACCESS_READ : ACCESS_WRITE));
|
||||
}
|
||||
}
|
||||
}
|
||||
m_current_position++;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
54
src/core/util/smarthops/TraceReader.hpp
Normal file
54
src/core/util/smarthops/TraceReader.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef __TRACE_READER_HPP
|
||||
#define __TRACE_READER_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include "../ProtoStream.hpp"
|
||||
#include "comm/TracePlugin.pb.h"
|
||||
|
||||
#include "../gzstream/gzstream.h"
|
||||
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
typedef enum {
|
||||
ACCESS_NONE ,
|
||||
ACCESS_READ ,
|
||||
ACCESS_WRITE ,
|
||||
ACCESS_READORWRITE , // some architectures can't distinguish read and write WPs in general (e.g. x86)
|
||||
ACCESS_CHECKPOINT ,
|
||||
} mem_access_type_e;
|
||||
|
||||
typedef uint32_t address_t;
|
||||
typedef uint32_t trace_pos_t;
|
||||
typedef std::pair<address_t, mem_access_type_e> trace_event_tuple_t;
|
||||
|
||||
class TraceReader {
|
||||
public:
|
||||
|
||||
TraceReader() : m_current_position(1), ps(0), normal_stream(0),
|
||||
gz_stream(0), ev_avail(false), m_log("TraceReader", false) {}
|
||||
|
||||
~TraceReader();
|
||||
|
||||
// Returns ACCESS_NONE in mem_access if current instruction does not access memory
|
||||
bool getNextTraceEvents(trace_pos_t& trace_pos,
|
||||
std::vector<trace_event_tuple_t >& trace_events);
|
||||
|
||||
void openTraceFile(const char *filename);
|
||||
private:
|
||||
unsigned int m_current_position;
|
||||
ProtoIStream* ps;
|
||||
std::ifstream *normal_stream;
|
||||
igzstream *gz_stream;
|
||||
Trace_Event ev;
|
||||
bool ev_avail;
|
||||
|
||||
Logger m_log;
|
||||
};
|
||||
|
||||
} // end of namespace
|
||||
|
||||
#endif //__TRACE_HPP
|
||||
Reference in New Issue
Block a user