diff --git a/cmake/panda.cmake b/cmake/panda.cmake index 6fa24780..5aebc7e8 100644 --- a/cmake/panda.cmake +++ b/cmake/panda.cmake @@ -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) diff --git a/src/core/comm/CMakeLists.txt b/src/core/comm/CMakeLists.txt index 8f353689..154190d2 100644 --- a/src/core/comm/CMakeLists.txt +++ b/src/core/comm/CMakeLists.txt @@ -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}) diff --git a/src/core/comm/DatabaseCampaignMessage.proto b/src/core/comm/DatabaseCampaignMessage.proto.in similarity index 78% rename from src/core/comm/DatabaseCampaignMessage.proto rename to src/core/comm/DatabaseCampaignMessage.proto.in index d3996d34..442371f1 100644 --- a/src/core/comm/DatabaseCampaignMessage.proto +++ b/src/core/comm/DatabaseCampaignMessage.proto.in @@ -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]; } \ No newline at end of file diff --git a/src/core/comm/InjectionPointHopsMessage.proto b/src/core/comm/InjectionPointHopsMessage.proto new file mode 100644 index 00000000..6aa59370 --- /dev/null +++ b/src/core/comm/InjectionPointHopsMessage.proto @@ -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; + } +} \ No newline at end of file diff --git a/src/core/comm/InjectionPointStepsMessage.proto b/src/core/comm/InjectionPointStepsMessage.proto new file mode 100644 index 00000000..d875d0f6 --- /dev/null +++ b/src/core/comm/InjectionPointStepsMessage.proto @@ -0,0 +1,3 @@ +message InjectionPointMessage { + required int32 injection_instr = 1; +} \ No newline at end of file diff --git a/src/core/config/CMakeLists.txt b/src/core/config/CMakeLists.txt index 4ee279f9..38d139c9 100644 --- a/src/core/config/CMakeLists.txt +++ b/src/core/config/CMakeLists.txt @@ -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)") diff --git a/src/core/config/FailConfig.hpp.in b/src/core/config/FailConfig.hpp.in index 78fbe3c3..b3a518fe 100644 --- a/src/core/config/FailConfig.hpp.in +++ b/src/core/config/FailConfig.hpp.in @@ -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 diff --git a/src/core/cpn/CMakeLists.txt b/src/core/cpn/CMakeLists.txt index 81ac82cf..94dba088 100644 --- a/src/core/cpn/CMakeLists.txt +++ b/src/core/cpn/CMakeLists.txt @@ -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) diff --git a/src/core/cpn/DatabaseCampaign.cc b/src/core/cpn/DatabaseCampaign.cc index d42abb93..3b25b344 100644 --- a/src/core/cpn/DatabaseCampaign.cc +++ b/src/core/cpn/DatabaseCampaign.cc @@ -4,7 +4,7 @@ #include "util/Logger.hpp" #include "util/Database.hpp" #include "comm/ExperimentData.hpp" - +#include "InjectionPoint.hpp" #ifndef __puma #include @@ -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); diff --git a/src/core/cpn/InjectionPoint.hpp b/src/core/cpn/InjectionPoint.hpp new file mode 100644 index 00000000..f910d3fe --- /dev/null +++ b/src/core/cpn/InjectionPoint.hpp @@ -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_ */ diff --git a/src/core/cpn/InjectionPointHops.cc b/src/core/cpn/InjectionPointHops.cc new file mode 100644 index 00000000..9804929d --- /dev/null +++ b/src/core/cpn/InjectionPointHops.cc @@ -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 */ diff --git a/src/core/cpn/InjectionPointSteps.cc b/src/core/cpn/InjectionPointSteps.cc new file mode 100644 index 00000000..ab07688a --- /dev/null +++ b/src/core/cpn/InjectionPointSteps.cc @@ -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 */ diff --git a/src/core/util/CMakeLists.txt b/src/core/util/CMakeLists.txt index 77775dc0..5b78bf2d 100644 --- a/src/core/util/CMakeLists.txt +++ b/src/core/util/CMakeLists.txt @@ -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) diff --git a/src/core/util/smarthops/CMakeLists.txt b/src/core/util/smarthops/CMakeLists.txt new file mode 100644 index 00000000..d112f6d1 --- /dev/null +++ b/src/core/util/smarthops/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/src/core/util/smarthops/SmartHops.cc b/src/core/util/smarthops/SmartHops.cc new file mode 100644 index 00000000..07e3d4c3 --- /dev/null +++ b/src/core/util/smarthops/SmartHops.cc @@ -0,0 +1,247 @@ +#include "SmartHops.hpp" + +#include +#include +#include + +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, unsigned costs, InjectionPointMessage &ipm) { + + ipm.Clear(); + // If checkpoint at beginning of hop-chain, add its id to HopChain + std::vector::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::max(); + + bool hop_found = false; + bool checkpoint_forbidden = false; + + for (std::vector::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::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(*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::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 *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(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 diff --git a/src/core/util/smarthops/SmartHops.hpp b/src/core/util/smarthops/SmartHops.hpp new file mode 100644 index 00000000..72485337 --- /dev/null +++ b/src/core/util/smarthops/SmartHops.hpp @@ -0,0 +1,90 @@ +#ifndef SMART_ALGORITHM__HPP +#define SMART_ALGORITHM__HPP + +#include +#include + +#include "TraceReader.hpp" +#include "comm/InjectionPointHopsMessage.pb.h" +#include "util/Logger.hpp" + +namespace fail { + +typedef std::pair result_tuple; + +// COSTS NAVIGATIONAL RECORD +typedef std::pair > 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, 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 m_last_positions; + std::vector m_checkpoints; + + std::vector m_trace_events; + std::vector m_result; +}; + +} // end of namespace + +#endif // SMART_ALGORITHM__HPP + diff --git a/src/core/util/smarthops/TraceReader.cc b/src/core/util/smarthops/TraceReader.cc new file mode 100644 index 00000000..ceac1787 --- /dev/null +++ b/src/core/util/smarthops/TraceReader.cc @@ -0,0 +1,97 @@ +#include "TraceReader.hpp" + +#include +#include +#include + +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_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 diff --git a/src/core/util/smarthops/TraceReader.hpp b/src/core/util/smarthops/TraceReader.hpp new file mode 100644 index 00000000..3273e448 --- /dev/null +++ b/src/core/util/smarthops/TraceReader.hpp @@ -0,0 +1,54 @@ +#ifndef __TRACE_READER_HPP +#define __TRACE_READER_HPP + +#include +#include + +#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 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_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