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