diff --git a/cmake/FindLLVM.cmake b/cmake/FindLLVM.cmake index 88d4a8bb..e13cda05 100644 --- a/cmake/FindLLVM.cmake +++ b/cmake/FindLLVM.cmake @@ -1,4 +1,4 @@ -find_program(LLVMCONFIG NAMES llvm-config llvm-config-3.3 llvm-config-3.2 llvm-config-3.1) +find_program(LLVMCONFIG NAMES llvm-config llvm-config-3.4 llvm-config-3.3 llvm-config-3.2 llvm-config-3.1) if( NOT LLVMCONFIG ) message(FATAL_ERROR "llvm-config not found, try installing llvm-dev llvm") diff --git a/cmake/FindLibIberty.cmake b/cmake/FindLibIberty.cmake new file mode 100644 index 00000000..a1ab1815 --- /dev/null +++ b/cmake/FindLibIberty.cmake @@ -0,0 +1,42 @@ +# - FindLibIberty.cmake +# This module can find libiberty on both Debian 7 and Ubuntu 14.04 +# (The libiberty headers moved from /usr/include to /usr/include/libiberty +# between Ubuntu 13.10 and 14.04, which made this search module necessary.) +# +# The following variables will be defined for your use: +# +# LibIberty_FOUND - TRUE if both library and headers were found +# LibIberty_INCLUDE_DIRS - Include directories +# LibIberty_LIBRARIES - Library path + +# set(LibIberty_PREFER_DYNAMIC True) if you want to prefer the dynamic library + +if(LibIberty_PREFER_DYNAMIC) + set(LibIberty_SEARCHORDER libiberty.so libiberty.a) +else() + set(LibIberty_SEARCHORDER libiberty.a libiberty.so) +endif() + +find_library(LibIberty_LIBRARIES NAMES ${LibIberty_SEARCHORDER}) + +find_path(LibIberty_INCLUDE_DIRS libiberty.h + PATHS + /usr/include /usr/include/libiberty + /usr/local/include /usr/local/include/libiberty + DOC "libiberty include directory containing libiberty.h") + +if(LibIberty_INCLUDE_DIRS AND LibIberty_LIBRARIES) + set(LibIberty_FOUND TRUE) +endif() + +if(LibIberty_FOUND) + if(NOT LibIberty_FIND_QUIETLY) + MESSAGE(STATUS "Found libiberty: ${LibIberty_LIBRARIES}") + endif() +else() + if(LibIberty_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find libiberty: install binutils-dev or libiberty-dev") + else() + MESSAGE(STATUS "libiberty not found") + endif() +endif() diff --git a/cmake/compilerconfig.cmake b/cmake/compilerconfig.cmake index 926fd283..91141b1b 100644 --- a/cmake/compilerconfig.cmake +++ b/cmake/compilerconfig.cmake @@ -3,7 +3,10 @@ option( VERBOSE_MAKE "Verbose Makefile output" OFF) # defaults to OFF set(CMAKE_VERBOSE_MAKEFILE ${VERBOSE_MAKE}) ### Additional compiler and linker flags ## -set(CMAKE_C_FLAGS "-g -Wall") +# -Wunused-local-typedefs is included in -Wall since GCC 4.8, and generates a +# flood of "typedef '...' locally defined but not used" warnings in +# ac++-1.2-generated code +set(CMAKE_C_FLAGS "-g -Wall -Wno-unused-local-typedefs") set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "-Wl,-gc-sections") diff --git a/doc/how-to-build.txt b/doc/how-to-build.txt index dd7fa8b5..4a49a25d 100644 --- a/doc/how-to-build.txt +++ b/doc/how-to-build.txt @@ -13,7 +13,7 @@ Required for Fail*: - protobuf-compiler - cmake 2.8.2 (2.8.11 preferred) - cmake-curses-gui - - binutils-dev + - binutils-dev, on newer systems libiberty-dev - AspectC++ (ag++, ac++): AspectC++ 1.1 or newer is known to work and can be obtained from ; nightlies can be downloaded from . Make sure you use the 64-bit version if running diff --git a/src/core/cpn/CMakeLists.txt b/src/core/cpn/CMakeLists.txt index 79fac5ba..f62c2c95 100644 --- a/src/core/cpn/CMakeLists.txt +++ b/src/core/cpn/CMakeLists.txt @@ -8,7 +8,7 @@ find_package(MySQL REQUIRED) include_directories(${MYSQL_INCLUDE_DIR}) add_library(fail-cpn ${SRCS}) -target_link_libraries(fail-cpn fail-comm ${MYSQL_LIBRARIES}) +target_link_libraries(fail-cpn fail-comm fail-util ${MYSQL_LIBRARIES}) # make sure protobufs are generated before we include them add_dependencies(fail-cpn fail-comm) diff --git a/src/core/efw/CMakeLists.txt b/src/core/efw/CMakeLists.txt index 3034c840..e0e2fe2c 100644 --- a/src/core/efw/CMakeLists.txt +++ b/src/core/efw/CMakeLists.txt @@ -9,6 +9,7 @@ set(SRCS add_library(fail-efw ${SRCS}) add_dependencies(fail-efw fail-comm) target_link_libraries(fail-efw fail-comm) +target_link_libraries(fail-efw fail-util) # WallclockTimer find_package(LibPCL REQUIRED) include_directories(${LIBPCL_INCLUDE_DIRS}) diff --git a/src/core/util/CMakeLists.txt b/src/core/util/CMakeLists.txt index 8492d0d9..19a8ff72 100644 --- a/src/core/util/CMakeLists.txt +++ b/src/core/util/CMakeLists.txt @@ -43,17 +43,19 @@ include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) # libiberty required by Demangler.cc: -find_library(LIB_IBERTY iberty) -mark_as_advanced(LIB_IBERTY) - -if(${LIB_IBERTY} STREQUAL LIB_IBERTY-NOTFOUND) - message(FATAL_ERROR "libiberty not found. Try installing binutils-dev: [ sudo aptitude install binutils-dev ]") -endif() +find_package(LibIberty REQUIRED) +include_directories(${LibIberty_INCLUDE_DIRS}) # libz required by gzstream find_package(ZLIB REQUIRED) include_directories(${ZLIB_INCLUDE_DIRS}) +# libelf and libdwarf required by ElfReader/DwarfReader +find_package(LibElf REQUIRED) +find_package(LibDwarf REQUIRED) +include_directories(${LIBELF_INCLUDE_DIRS}) +include_directories(${LIBDWARF_INCLUDE_DIRS}) + # objdump required by Diassembler.cc set(THE_OBJDUMP "${ARCH_TOOL_PREFIX}objdump") @@ -67,8 +69,7 @@ mark_as_advanced(FAIL_OBJDUMP) add_library(fail-util ${SRCS}) add_dependencies(fail-util fail-comm) -target_link_libraries(fail-util fail-comm) -target_link_libraries(fail-util ${PROTOBUF_LIBRARY} ${Boost_LIBRARIES} ${LIB_IBERTY} ${ZLIB_LIBRARIES} dwarf elf) +target_link_libraries(fail-util fail-comm ${PROTOBUF_LIBRARY} ${Boost_LIBRARIES} ${LibIberty_LIBRARIES} ${ZLIB_LIBRARIES} ${LIBDWARF_LIBRARIES} ${LIBELF_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/Database.cc b/src/core/util/Database.cc index 6ab7d182..398b0299 100644 --- a/src/core/util/Database.cc +++ b/src/core/util/Database.cc @@ -12,14 +12,21 @@ using namespace fail; boost::mutex Database::m_global_lock; #endif +static CommandLine::option_handle DATABASE, HOSTNAME, USERNAME, DBDEFAULTS; + Database::Database(const std::string &username, const std::string &host, const std::string &database) { #ifndef __puma boost::lock_guard guard(m_global_lock); #endif + CommandLine &cmd = CommandLine::Inst(); + std::string db_conf_file = "~/.my.cnf"; + if (cmd[DBDEFAULTS].count()) { + db_conf_file = cmd[DBDEFAULTS].first()->arg; + } handle = mysql_init(0); last_result = 0; - mysql_options(handle, MYSQL_READ_DEFAULT_FILE, "~/.my.cnf"); + mysql_options(handle, MYSQL_READ_DEFAULT_FILE, db_conf_file.c_str()); if (!mysql_real_connect(handle, host.c_str(), username.c_str(), 0, database.c_str(), 0, 0, 0)) { @@ -231,8 +238,6 @@ std::string Database::escape_string(const std::string unescaped_string) { return result; } -static CommandLine::option_handle DATABASE, HOSTNAME, USERNAME; - void Database::cmdline_setup() { CommandLine &cmd = CommandLine::Inst(); @@ -241,7 +246,9 @@ void Database::cmdline_setup() { HOSTNAME = cmd.addOption("H", "hostname", Arg::Required, "-h/--hostname \tMYSQL Hostname (default: taken from ~/.my.cnf)"); USERNAME = cmd.addOption("u", "username", Arg::Required, - "-u/--username \tMYSQL Username (default: taken from ~/.my.cnf, or your current user)\n"); + "-u/--username \tMYSQL Username (default: taken from ~/.my.cnf, or your current user)"); + DBDEFAULTS = cmd.addOption("", "database-option--file", Arg::Required, + "--database-option-file \toverride MySQL ~/.my.cnf option file (prepend with './' for files in the CWD)\n"); // should be called before any threads are spawned mysql_library_init(0, NULL, NULL); diff --git a/src/core/util/llvmdisassembler/LLVMDisassembler.cpp b/src/core/util/llvmdisassembler/LLVMDisassembler.cpp index 6e2977aa..c5d03da7 100644 --- a/src/core/util/llvmdisassembler/LLVMDisassembler.cpp +++ b/src/core/util/llvmdisassembler/LLVMDisassembler.cpp @@ -85,7 +85,10 @@ void LLVMDisassembler::disassemble() uint64_t End; // The end is either the size of the section or the beginning of the next // symbol. - if (si == se - 1) + if (Start >= SectSize) + // we are beyond the end of the section + break; + else if (si == se - 1) End = SectSize; // Make sure this symbol takes up space. else if (Symbols[si + 1].first != Start) @@ -98,7 +101,7 @@ void LLVMDisassembler::disassemble() MCInst Inst; if (disas->getInstruction(Inst, Size, memoryObject, Index, - nulls(), nulls())) { + nulls(), nulls()) == MCDisassembler::Success) { const MCInstrDesc &desc = this->instr_info->get(Inst.getOpcode()); // Inst.dump(); Instr instr_info; @@ -107,6 +110,8 @@ void LLVMDisassembler::disassemble() instr_info.address = SectionAddr + Index; instr_info.conditional_branch = desc.isConditionalBranch(); + assert( Size > 0 && "zero size instruction disassembled" ); + unsigned int pos = 0; for (MCInst::iterator it = Inst.begin(); it != Inst.end(); ++it) { diff --git a/src/experiments/ecos_kernel_test/ecos_kernel_test.proto b/src/experiments/ecos_kernel_test/ecos_kernel_test.proto index 7cf96dd4..41e17b21 100644 --- a/src/experiments/ecos_kernel_test/ecos_kernel_test.proto +++ b/src/experiments/ecos_kernel_test/ecos_kernel_test.proto @@ -36,13 +36,16 @@ message EcosKernelTestProtoMsg { // did ECC correct the fault? optional Flag error_corrected = 5; + // simulated runtime factor compared to golden run (1.000 = golden run runtime) + optional float sim_runtime_factor = 6; + // especially interesting for TRAP/UNKNOWN: latest IP - optional uint32 latest_ip = 6; + optional uint32 latest_ip = 7; // optional textual description of what happened - optional string details = 7; + optional string details = 8; // experiment runtime (FIXME: should be part of DatabaseCampaignMessage instead) - optional float runtime = 8; + optional float runtime = 9; } } diff --git a/src/experiments/ecos_kernel_test/experiment.cc b/src/experiments/ecos_kernel_test/experiment.cc index b484b3d7..79a902c2 100644 --- a/src/experiments/ecos_kernel_test/experiment.cc +++ b/src/experiments/ecos_kernel_test/experiment.cc @@ -49,7 +49,7 @@ using namespace fail; const std::string EcosKernelTestExperiment::dir_images(DIR_IMAGES); const std::string EcosKernelTestExperiment::dir_prerequisites(DIR_PREREQUISITES); -bool EcosKernelTestExperiment::writeTraceInfo(unsigned instr_counter, unsigned timeout, +bool EcosKernelTestExperiment::writeTraceInfo(unsigned instr_counter, unsigned long long runtime, unsigned mem1_low, unsigned mem1_high, // < 1M unsigned mem2_low, unsigned mem2_high, // >= 1M const std::string& variant, const std::string& benchmark) { @@ -58,7 +58,7 @@ bool EcosKernelTestExperiment::writeTraceInfo(unsigned instr_counter, unsigned t cout << "failed to open " << filename_traceinfo(variant, benchmark) << endl; return false; } - ti << instr_counter << endl << timeout << endl + ti << instr_counter << endl << runtime << endl << mem1_low << endl << mem1_high << endl << mem2_low << endl << mem2_high << endl; ti.flush(); @@ -66,7 +66,7 @@ bool EcosKernelTestExperiment::writeTraceInfo(unsigned instr_counter, unsigned t return true; } -bool EcosKernelTestExperiment::readTraceInfo(unsigned &instr_counter, unsigned &timeout, +bool EcosKernelTestExperiment::readTraceInfo(unsigned &instr_counter, unsigned long long &runtime, unsigned &mem1_low, unsigned &mem1_high, // < 1M unsigned &mem2_low, unsigned &mem2_high, // >= 1M const std::string& variant, const std::string& benchmark) { @@ -86,7 +86,7 @@ bool EcosKernelTestExperiment::readTraceInfo(unsigned &instr_counter, unsigned & ss >> instr_counter; break; case 1: - ss >> timeout; + ss >> runtime; break; case 2: ss >> mem1_low; @@ -370,15 +370,16 @@ bool EcosKernelTestExperiment::performTrace(guest_address_t addr_entry, guest_ad ev = simulator.resume(); } - unsigned long long estimated_timeout_ticks = - simulator.getTimerTicks() - time_start + simulator.getTimerTicksPerSecond() / 18.2; // 1s/18.2 + unsigned long long goldenrun_runtime_ticks = simulator.getTimerTicks() - time_start; // convert to microseconds - unsigned estimated_timeout = (unsigned) - (estimated_timeout_ticks * 1000000 / simulator.getTimerTicksPerSecond()); + unsigned goldenrun_runtime = (unsigned) + (goldenrun_runtime_ticks * 1000000.0 / simulator.getTimerTicksPerSecond()); log << dec << "tracing finished after " << instr_counter << " instructions" << endl; log << hex << "all memory accesses within [0x" << mem1_low << ", 0x" << mem1_high << "] u [0x" << mem2_low << ", 0x" << mem2_high << "] (ignoring VGA mem)" << endl; - log << dec << "elapsed simulated time (plus safety margin): " << (estimated_timeout / 1000000.0) << "s" << endl; + log << dec << "elapsed simulated time: " + << (goldenrun_runtime / 1000000.0) << "s (" + << goldenrun_runtime_ticks << " ticks)" << endl; // sanitize memory ranges if (mem1_low > mem1_high) { @@ -389,7 +390,7 @@ bool EcosKernelTestExperiment::performTrace(guest_address_t addr_entry, guest_ad } // save these values for experiment STEP 3 - writeTraceInfo(instr_counter, estimated_timeout, + writeTraceInfo(instr_counter, goldenrun_runtime_ticks, mem1_low, mem1_high, mem2_low, mem2_high, m_variant, m_benchmark); simulator.removeFlow(&tp); @@ -454,7 +455,9 @@ bool EcosKernelTestExperiment::faultInjection() { log << "STEP 3: The actual experiment." << endl; // trace info - unsigned goldenrun_instr_counter, estimated_timeout, mem1_low, mem1_high, mem2_low, mem2_high; + unsigned goldenrun_instr_counter, mem1_low, mem1_high, mem2_low, mem2_high; + unsigned long long goldenrun_runtime_ticks; + unsigned goldenrun_runtime, timeout_runtime; // ELF symbol addresses guest_address_t addr_entry, addr_finish, addr_testdata, addr_testdata_size, addr_test_output, addr_errors_corrected, @@ -502,8 +505,12 @@ bool EcosKernelTestExperiment::faultInjection() { unsigned instr_offset = param.msg.fsppilot().injection_instr(); unsigned mem_addr = param.msg.fsppilot().data_address(); - readTraceInfo(goldenrun_instr_counter, estimated_timeout, + readTraceInfo(goldenrun_instr_counter, goldenrun_runtime_ticks, mem1_low, mem1_high, mem2_low, mem2_high, m_variant, m_benchmark); + // convert to microseconds + goldenrun_runtime = (unsigned) + (goldenrun_runtime_ticks * 1000000.0 / simulator.getTimerTicksPerSecond()); + timeout_runtime = goldenrun_runtime + 1000000/18.2; // + 1 timer tick if (!readELFSymbols(addr_entry, addr_finish, addr_testdata, addr_testdata_size, addr_test_output, addr_errors_corrected, addr_panic, addr_text_start, addr_text_end, @@ -568,6 +575,9 @@ bool EcosKernelTestExperiment::faultInjection() { SerialOutputLogger sol(0x3f8, LIMIT_SERIAL); simulator.addFlow(&sol); + // measure elapsed time + simtime_t time_start = simulator.getTimerTicks(); + BaseListener* ev; // no need to wait if offset is 0 @@ -671,7 +681,7 @@ bool EcosKernelTestExperiment::faultInjection() { simulator.addListener(&ev_mem_outside4); // timeout (e.g., stuck in a HLT instruction) - TimerListener ev_timeout(estimated_timeout); + TimerListener ev_timeout(timeout_runtime); simulator.addListener(&ev_timeout); // grant generous (10x) more instructions before aborting to avoid false positives @@ -776,6 +786,9 @@ bool EcosKernelTestExperiment::faultInjection() { } } + result->set_sim_runtime_factor( + (simulator.getTimerTicks() - time_start) / (double) goldenrun_runtime_ticks); + if (ev == &func_finish && output_correct) { // do we reach finish? log << "experiment finished ordinarily" << endl; diff --git a/src/experiments/ecos_kernel_test/experiment.hpp b/src/experiments/ecos_kernel_test/experiment.hpp index 9ce811ee..8b83a93c 100644 --- a/src/experiments/ecos_kernel_test/experiment.hpp +++ b/src/experiments/ecos_kernel_test/experiment.hpp @@ -41,8 +41,8 @@ public: void handle_func_test_output(bool &test_failed, bool& test_passed); - static bool writeTraceInfo(unsigned instr_counter, unsigned timeout, unsigned mem1_low, unsigned mem1_high, unsigned mem2_low, unsigned mem2_high, const std::string& variant = "", const std::string& benchmark = ""); - static bool readTraceInfo(unsigned &instr_counter, unsigned &timeout, unsigned &mem1_low, unsigned &mem1_high, unsigned &mem2_low, unsigned &mem2_high, const std::string& variant = "", const std::string& benchmark = ""); + static bool writeTraceInfo(unsigned instr_counter, unsigned long long runtime, unsigned mem1_low, unsigned mem1_high, unsigned mem2_low, unsigned mem2_high, const std::string& variant = "", const std::string& benchmark = ""); + static bool readTraceInfo(unsigned &instr_counter, unsigned long long &runtime, unsigned &mem1_low, unsigned &mem1_high, unsigned &mem2_low, unsigned &mem2_high, const std::string& variant = "", const std::string& benchmark = ""); static std::string filename_memorymap(const std::string& variant = "", const std::string& benchmark = ""); static std::string filename_state(unsigned instr_offset, const std::string& variant = "", const std::string& benchmark = ""); static std::string filename_trace(const std::string& variant = "", const std::string& benchmark = ""); diff --git a/src/experiments/kesogc/CMakeLists.txt b/src/experiments/kesogc/CMakeLists.txt new file mode 100644 index 00000000..dfd5e336 --- /dev/null +++ b/src/experiments/kesogc/CMakeLists.txt @@ -0,0 +1,37 @@ +set(EXPERIMENT_NAME kesogc) +set(EXPERIMENT_TYPE KESOgc) +configure_file(../instantiate-experiment.ah.in + ${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY +) + +## Setup desired protobuf descriptions HERE ## +set(MY_PROTOS + kesogc.proto +) + +set(MY_CAMPAIGN_SRCS + experiment.hpp + experiment.cc + campaign.hpp + campaign.cc +) + +#### PROTOBUFS #### +find_package(Protobuf REQUIRED) +include_directories(${PROTOBUF_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +find_package(MySQL REQUIRED) +include_directories(${MYSQL_INCLUDE_DIR}) + +PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS}) + +## Build library +add_library(fail-${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS}) +add_dependencies(fail-${EXPERIMENT_NAME} fail-comm) +target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY}) + +## This is the example's campaign server distributing experiment parameters +add_executable(${EXPERIMENT_NAME}-server main.cc) +target_link_libraries(${EXPERIMENT_NAME}-server -Wl,--start-group fail-${EXPERIMENT_NAME} fail-sal fail-util fail-cpn fail-comm ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY} ${MYSQL_LIBRARIES} -Wl,--end-group) +install(TARGETS ${EXPERIMENT_NAME}-server RUNTIME DESTINATION bin) diff --git a/src/experiments/kesogc/campaign.cc b/src/experiments/kesogc/campaign.cc new file mode 100644 index 00000000..bb7c2aef --- /dev/null +++ b/src/experiments/kesogc/campaign.cc @@ -0,0 +1,20 @@ +#include +#include + +#include "campaign.hpp" +#include "experimentInfo.hpp" +#include "cpn/CampaignManager.hpp" +#include "util/Logger.hpp" +#include "util/ProtoStream.hpp" +#include "sal/SALConfig.hpp" + + +using namespace std; +using namespace fail; +using namespace google::protobuf; + +void KesoGcCampaign::cb_send_pilot(DatabaseCampaignMessage pilot) { + KesoGcExperimentData *data = new KesoGcExperimentData; + data->msg.mutable_fsppilot()->CopyFrom(pilot); + campaignmanager.addParam(data); +} diff --git a/src/experiments/kesogc/campaign.hpp b/src/experiments/kesogc/campaign.hpp new file mode 100644 index 00000000..716e9666 --- /dev/null +++ b/src/experiments/kesogc/campaign.hpp @@ -0,0 +1,24 @@ +#ifndef __KESOGCCAMPAIGN_HPP__ + #define __KESOGCCAMPAIGN_HPP__ + +#include "cpn/DatabaseCampaign.hpp" +#include "comm/ExperimentData.hpp" +#include +#include "kesogc.pb.h" + + +class KesoGcExperimentData : public fail::ExperimentData { +public: + KesoGcProtoMsg msg; + KesoGcExperimentData() : fail::ExperimentData(&msg) {} +}; + + +class KesoGcCampaign : public fail::DatabaseCampaign { + virtual const google::protobuf::Descriptor * cb_result_message() + { return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("KesoGcProtoMsg"); } + + virtual void cb_send_pilot(DatabaseCampaignMessage pilot); +}; + +#endif // __KESOGCCAMPAIGN_HPP__ diff --git a/src/experiments/kesogc/experiment.cc b/src/experiments/kesogc/experiment.cc new file mode 100644 index 00000000..19f60219 --- /dev/null +++ b/src/experiments/kesogc/experiment.cc @@ -0,0 +1,327 @@ +#include +#include + +// getpid +#include +#include + + +#include +#include "experiment.hpp" +#include "experimentInfo.hpp" +#include "sal/SALConfig.hpp" +#include "sal/SALInst.hpp" +#include "sal/Memory.hpp" +#include "sal/Listener.hpp" + +#include "sal/bochs/BochsListener.hpp" +#include +#include + +#include "campaign.hpp" +#include "kesogc.pb.h" +#include "util/Disassembler.hpp" + +const char *parity_types[] = { + "LIST", + "STACK", + "STACK_POINTER", + "DOMAIN", + "BITMAP", + "LLREFS", + "COLOR" +}; + +using namespace std; +using namespace fail; + +// Check if configuration dependencies are satisfied: +#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) + #error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration. +#endif + +unsigned KESOgc::injectBitFlip(address_t data_address, unsigned bitpos){ + + MemoryManager& mm = simulator.getMemoryManager(); + unsigned int value, injectedval; + + value = mm.getByte(data_address); + injectedval = value ^ (1 << bitpos); + mm.setByte(data_address, injectedval); + + m_log << "INJECTION at: 0x" << hex<< setw(2) << setfill('0') << data_address + << " value: 0x" << setw(2) << setfill('0') << value << " -> 0x" << setw(2) << setfill('0') << mm.getByte(data_address) << endl; + + return value; +} + + +void handleEvent(KesoGcProtoMsg_Result& result, KesoGcProtoMsg_Result_ResultType restype, const std::string &msg) { + cout << "Result details: " << msg << endl; + result.set_resulttype(restype); + result.set_details(msg); +} + + +void handleMemoryAccessEvent(KesoGcProtoMsg_Result& result, const fail::MemAccessListener& l_mem){ + stringstream sstr; + sstr << "mem access ("; + switch (l_mem.getTriggerAccessType()) { + case MemAccessEvent::MEM_READ: + sstr << "r"; + break; + case MemAccessEvent::MEM_WRITE: + sstr << "w"; + break; + default: break; + } + sstr << ") @ 0x" << hex << l_mem.getTriggerAddress(); + + sstr << " ip @ 0x" << hex << l_mem.getTriggerInstructionPointer(); + + handleEvent(result, result.MEMACCESS, sstr.str()); +} + +bool KESOgc::run() +{ + address_t minimal_ip = INT_MAX; // 1 Mbyte + address_t maximal_ip = 0; + address_t minimal_data = 0x100000; // 1 Mbyte + address_t maximal_data = 0; + + for (ElfReader::section_iterator it = m_elf.sec_begin(); + it != m_elf.sec_end(); ++it) { + const ElfSymbol &symbol = *it; + std::string prefix(".text"); + if (symbol.getName().compare(0, prefix.size(), prefix) == 0) { + minimal_ip = std::min(minimal_ip, symbol.getStart()); + maximal_ip = std::max(maximal_ip, symbol.getEnd()); + } else { + minimal_data = std::min(minimal_data, symbol.getStart()); + maximal_data = std::max(maximal_data, symbol.getEnd()); + } + } + + std::cout << "Code section from " << hex << minimal_ip << " to " << maximal_ip << std::endl; + std::cout << "Whole programm section from " << hex << minimal_data << " to " << maximal_data << std::endl; + + + + // m_dis.init(); + //******* Boot, and store state *******// + m_log << "STARTING EXPERIMENT" << endl; + + unsigned executed_jobs = 0; + + // Setup exit points + const ElfSymbol &s_error = m_elf.getSymbol("keso_throw_error"); + BPSingleListener l_error(s_error.getAddress()); + + const ElfSymbol &s_nullp = m_elf.getSymbol("keso_throw_nullpointer"); + BPSingleListener l_nullp(s_nullp.getAddress()); + + const ElfSymbol &s_oobounds = m_elf.getSymbol("keso_throw_index_out_of_bounds"); + BPSingleListener l_oobounds(s_oobounds.getAddress()); + + const ElfSymbol &s_gc_parity = m_elf.getSymbol("keso_throw_gc_parity"); + BPSingleListener l_gc_parity(s_gc_parity.getAddress()); + + const ElfSymbol &s_parity = m_elf.getSymbol("keso_throw_parity"); + BPSingleListener l_parity(s_parity.getAddress()); + + //BPSingleListener l_dump(m_elf.getSymbol("c17_Main_m4_dumpResults_console").getAddress()); + BPSingleListener l_end(m_elf.getSymbol("keso_end").getAddress()); + + MemAccessListener l_mem_text(minimal_ip, MemAccessEvent::MEM_WRITE); + l_mem_text.setWatchWidth(maximal_ip - minimal_ip); + + MemAccessListener l_mem_outerspace( maximal_data, MemAccessEvent::MEM_WRITE); + l_mem_outerspace.setWatchWidth(0xfffffff0); + + TrapListener l_trap(ANY_TRAP); + + TimerListener l_timeout(5 * 1000 * 1000); // 5 seconds in microseconds + + const ElfSymbol &s_resilient_heap_repaired = m_elf.getSymbol("keso_resilient_gcHeapPointer_repaired"); + BPSingleListener l_resilient_heap_repaired(s_resilient_heap_repaired.getAddress()); + + const ElfSymbol &s_ft_list_repaired = m_elf.getSymbol("keso_ft_list_repaired"); + BPSingleListener l_ft_list_repaired(s_ft_list_repaired.getAddress()); + + const ElfSymbol &s_ft_list_broken = m_elf.getSymbol("keso_ft_list_broken"); + BPSingleListener l_ft_list_broken(s_ft_list_broken.getAddress()); + + const ElfSymbol &s_resilient_stack_repaired = m_elf.getSymbol("keso_resilient_gcStackPointer_repaired"); + BPSingleListener l_resilient_stack_repaired(s_resilient_stack_repaired.getAddress()); + + while (executed_jobs < 25 || m_jc->getNumberOfUndoneJobs() > 0) { + m_log << "asking jobserver for parameters" << endl; + KesoGcExperimentData param; + if(!m_jc->getParam(param)){ + m_log << "Dying." << endl; // We were told to die. + simulator.terminate(1); + } + + // Get input data from Jobserver + unsigned injection_instr = param.msg.fsppilot().injection_instr(); + address_t data_address = param.msg.fsppilot().data_address(); + + for (int bit_offset = 0; bit_offset < 8; ++bit_offset) { + // 8 results in one job + KesoGcProtoMsg_Result *result = param.msg.add_result(); + result->set_bitoffset(bit_offset); + + m_log << "restoring state" << endl; + // Restore to the image, which starts at address(main) + simulator.restore("state"); + executed_jobs ++; + + m_log << "Trying to inject @ instr #" << dec << injection_instr << endl; + + + if (injection_instr > 0) { + simulator.clearListeners(); + // XXX could be improved with intermediate states (reducing runtime until injection) + simulator.addListener(&l_end); + + BPSingleListener bp; + bp.setWatchInstructionPointer(ANY_ADDR); + + // Fix offset by 1 error + bp.setCounter(injection_instr + 1); + + simulator.addListener(&bp); + + bool inject = true; + while (1) { + fail::BaseListener * listener = simulator.resume(); + // finish() before FI? + if (listener == &l_end) { + m_log << "experiment reached finish() before FI" << endl; + handleEvent(*result, result->NOINJECTION, "time_marker reached before instr2"); + inject = false; + break; + } else if (listener == &bp) { + break; + } else { + inject = false; + handleEvent(*result, result->NOINJECTION, "WTF"); + break; + } + } + + // Next experiment + if (!inject) + continue; + } + + address_t injection_instr_absolute = param.msg.fsppilot().injection_instr_absolute(); + if (simulator.getCPU(0).getInstructionPointer() != injection_instr_absolute) { + m_log << "Invalid Injection address EIP=0x" + << std::hex << simulator.getCPU(0).getInstructionPointer() + << " != 0x" << injection_instr_absolute << std::endl; + simulator.terminate(1); + } + + /// INJECT BITFLIP: + result->set_original_value(injectBitFlip(data_address, bit_offset)); + + cout << " outerspace : " << l_mem_outerspace.getWatchWidth() << " --- @ :" << l_mem_outerspace.getWatchAddress() << endl; + simulator.clearListeners(); + simulator.addListener(&l_trap); + if (s_error.isValid()) + simulator.addListener(&l_error); + if (s_nullp.isValid()) + simulator.addListener(&l_nullp); + if (s_oobounds.isValid()) + simulator.addListener(&l_oobounds); + if (s_gc_parity.isValid()) + simulator.addListener(&l_gc_parity); + if (s_parity.isValid()) + simulator.addListener(&l_parity); + simulator.addListener(&l_end); + simulator.addListener(&l_mem_text); + simulator.addListener(&l_mem_outerspace); + simulator.addListener(&l_timeout); + + if(s_resilient_heap_repaired.isValid()){ + simulator.addListener(&l_resilient_heap_repaired); + } + + if(s_resilient_stack_repaired.isValid()){ + simulator.addListener(&l_resilient_stack_repaired); + } + + if(s_ft_list_repaired.isValid()){ + simulator.addListener(&l_ft_list_repaired); + } + + if(s_ft_list_broken.isValid()){ + simulator.addListener(&l_ft_list_broken); + } + + m_log << "Resuming till the crash" << std::endl; + // resume and wait for results + fail::BaseListener* l = simulator.resume(); + m_log << "CDX has ended" << std::endl; + + // Evaluate result + if(l == &l_error) { + handleEvent(*result, result->EXC_ERROR, "exc error"); + } else if (l == &l_nullp) { + handleEvent(*result, result->EXC_NULLPOINTER, "exc nullpointer"); + + } else if ( l == &l_oobounds ) { + handleEvent(*result, result->EXC_OOBOUNDS, "exc out of bounds"); + + } else if (l == &l_gc_parity) { + address_t stack_pointer = simulator.getCPU(0).getStackPointer(); + int32_t parity_type = simulator.getMemoryManager().getByte(stack_pointer + 4); // 1st argument is at esp+4 + stringstream sstr; + sstr << "exc gc_parity of type: " << parity_types[parity_type]; + handleEvent(*result, result->EXC_GC_PARITY, sstr.str()); + + } else if (l == &l_parity) { + handleEvent(*result, result->EXC_PARITY, "exc parity"); + + } else if (l == &l_end) { + handleEvent(*result, result->CALCDONE, "calculation done"); + + } else if (l == &l_timeout) { + handleEvent(*result, result->TIMEOUT, "5s"); + + } else if (l == &l_trap) { + stringstream sstr; + sstr << "trap #" << l_trap.getTriggerNumber(); + handleEvent(*result, result->TRAP, sstr.str()); + + } else if (l == &l_mem_text){ + handleMemoryAccessEvent(*result, l_mem_text); + + } else if (l == &l_mem_outerspace){ + handleMemoryAccessEvent(*result, l_mem_outerspace); + + } else if (l == &l_resilient_heap_repaired){ + handleEvent(*result, result->RESILIENT_HEAP_REPAIRED, "resilient heap pointer repaired"); + + } else if (l == &l_resilient_stack_repaired){ + handleEvent(*result, result->RESILIENT_STACK_REPAIRED, "resilient stack pointer repaired"); + + } else if (l == &l_ft_list_repaired){ + handleEvent(*result, result->FT_LIST_REPAIRED, "ft list repaired"); + + } else if (l == &l_ft_list_broken){ + handleEvent(*result, result->FT_LIST_BROKEN, "ft list broken"); + + } else { + handleEvent(*result, result->UNKNOWN, "UNKNOWN event"); + } + simulator.clearListeners(); + } + + m_jc->sendResult(param); + } + // Explicitly terminate, or the simulator will continue to run. + simulator.terminate(); +} + diff --git a/src/experiments/kesogc/experiment.hpp b/src/experiments/kesogc/experiment.hpp new file mode 100644 index 00000000..c69c4956 --- /dev/null +++ b/src/experiments/kesogc/experiment.hpp @@ -0,0 +1,44 @@ +#ifndef __KESO_GC_EXPERIMENT_HPP__ + #define __KESO_GC_EXPERIMENT_HPP__ + + +#include "sal/SALInst.hpp" +#include "efw/ExperimentFlow.hpp" +#include "efw/JobClient.hpp" +#include "util/Logger.hpp" +#include "util/ElfReader.hpp" +#include "util/Disassembler.hpp" +#include +#include + + +class KESOgc : public fail::ExperimentFlow { + fail::JobClient *m_jc; + fail::Logger m_log; + fail::MemoryManager& m_mm; + fail::ElfReader m_elf; + fail::Disassembler m_dis; + + void printEIP(); + void setupExitBPs(const std::string&); + void enableBPs(); + void clearExitBPs(); + void showStaticRefs(); + + unsigned injectBitFlip(fail::address_t data_address, unsigned bitpos); + +public: + KESOgc() : m_log("KESOgc", false), m_mm(fail::simulator.getMemoryManager()) { + + 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(); + } + + }; + bool run(); +}; + +#endif // __KESO_GC_EXPERIMENT_HPP__ diff --git a/src/experiments/kesogc/experimentInfo.hpp b/src/experiments/kesogc/experimentInfo.hpp new file mode 100644 index 00000000..bcc57e18 --- /dev/null +++ b/src/experiments/kesogc/experimentInfo.hpp @@ -0,0 +1,24 @@ +#ifndef __EXPERIMENT_INFO_HPP__ + #define __EXPERIMENT_INFO_HPP__ + +// FIXME autogenerate this + + +// the task function's entry address: +// nm -C ecc.elf|fgrep main +#define OOSTUBS_FUNC_ENTRY 0x001009d0 +// empty function that is called explicitly when the experiment finished +// nm -C ecc.elf|fgrep "finished()" +#define OOSTUBS_FUNC_FINISH 0x001009d6 +// function executing HLT with no chance for further progress (after panic()) +// nm -C ecc.elf|fgrep cpu_halt +#define OOSTUBS_FUNC_CPU_HALT 0x001009f7 + +// nm -C ecc.elf | fgrep "_TEXT_" +#define OOSTUBS_TEXT_START 0x00100000 +#define OOSTUBS_TEXT_END 0x00100a1b + +#define OOSTUBS_NUMINSTR 5 + + +#endif // __EXPERIMENT_INFO_HPP__ diff --git a/src/experiments/kesogc/kesogc.proto b/src/experiments/kesogc/kesogc.proto new file mode 100644 index 00000000..03420a40 --- /dev/null +++ b/src/experiments/kesogc/kesogc.proto @@ -0,0 +1,31 @@ +import "DatabaseCampaignMessage.proto"; + +message KesoGcProtoMsg { + required DatabaseCampaignMessage fsppilot = 1; + + repeated group Result = 2 { + // make these optional to reduce overhead for server->client communication + enum ResultType { + CALCDONE = 1; + TIMEOUT = 2; + TRAP = 3; + EXC_ERROR = 4; + FT_LIST_REPAIRED = 5; + EXC_NULLPOINTER = 6; + RESILIENT_HEAP_REPAIRED = 7; + MEMACCESS = 8; + NOINJECTION = 9; + FT_LIST_BROKEN = 10; + UNKNOWN = 11; + RESILIENT_STACK_REPAIRED = 12; + EXC_PARITY = 13; + EXC_GC_PARITY = 14; + EXC_OOBOUNDS = 15; + } + // result type, see above + required ResultType resulttype = 4; + required uint32 original_value = 5; + required uint32 bitoffset = 6 [(sql_primary_key) = true]; + optional string details = 7; + } +} diff --git a/src/experiments/kesogc/main.cc b/src/experiments/kesogc/main.cc new file mode 100644 index 00000000..c00c7b3f --- /dev/null +++ b/src/experiments/kesogc/main.cc @@ -0,0 +1,20 @@ +#include +#include + +#include "cpn/CampaignManager.hpp" +#include "util/CommandLine.hpp" +#include "campaign.hpp" + +int main(int argc, char **argv) +{ + fail::CommandLine &cmd = fail::CommandLine::Inst(); + for (int i = 1; i < argc; ++i) + cmd.add_args(argv[i]); + + KesoGcCampaign c; + if (fail::campaignmanager.runCampaign(&c)) { + return 0; + } else { + return 1; + } +} diff --git a/src/experiments/vezs-example/CMakeLists.txt b/src/experiments/vezs-example/CMakeLists.txt index 81320d74..48e30790 100644 --- a/src/experiments/vezs-example/CMakeLists.txt +++ b/src/experiments/vezs-example/CMakeLists.txt @@ -5,15 +5,15 @@ configure_file(../instantiate-experiment.ah.in ) ## Setup desired protobuf descriptions HERE ## -set(MY_PROTOS -# vezs-example.proto +set(MY_PROTOS + vezs.proto ) set(MY_CAMPAIGN_SRCS experiment.hpp experiment.cc - #campaign.hpp - #campaign.cc + campaign.hpp + campaign.cc ) #### PROTOBUFS #### @@ -21,7 +21,10 @@ find_package(Protobuf REQUIRED) include_directories(${PROTOBUF_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -#PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS}) +find_package(MySQL REQUIRED) +include_directories(${MYSQL_INCLUDE_DIR}) + +PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS}) ## Build library add_library(fail-${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS}) @@ -30,6 +33,6 @@ target_link_libraries(fail-${EXPERIMENT_NAME} fail-comm) target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY}) ## This is the example's campaign server distributing experiment parameters -#add_executable(${EXPERIMENT_NAME}-server main.cc) -#target_link_libraries(${EXPERIMENT_NAME}-server fail-${EXPERIMENT_NAME} fail ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY}) -#install(TARGETS ${EXPERIMENT_NAME}-server RUNTIME DESTINATION bin) +add_executable(${EXPERIMENT_NAME}-server main.cc) +target_link_libraries(${EXPERIMENT_NAME}-server -Wl,--start-group fail-${EXPERIMENT_NAME} fail-sal fail-util fail-cpn fail-comm ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY} ${MYSQL_LIBRARIES} -Wl,--end-group) +install(TARGETS ${EXPERIMENT_NAME}-server RUNTIME DESTINATION bin) diff --git a/src/experiments/vezs-example/campaign.cc b/src/experiments/vezs-example/campaign.cc new file mode 100644 index 00000000..5066a812 --- /dev/null +++ b/src/experiments/vezs-example/campaign.cc @@ -0,0 +1,20 @@ +#include +#include + +#include "campaign.hpp" +#include "experimentInfo.hpp" +#include "cpn/CampaignManager.hpp" +#include "util/Logger.hpp" +#include "util/ProtoStream.hpp" +#include "sal/SALConfig.hpp" + + +using namespace std; +using namespace fail; +using namespace google::protobuf; + +void VEZSCampaign::cb_send_pilot(DatabaseCampaignMessage pilot) { + VEZSExperimentData *data = new VEZSExperimentData; + data->msg.mutable_fsppilot()->CopyFrom(pilot); + campaignmanager.addParam(data); +} diff --git a/src/experiments/vezs-example/campaign.hpp b/src/experiments/vezs-example/campaign.hpp new file mode 100644 index 00000000..e7fae4c2 --- /dev/null +++ b/src/experiments/vezs-example/campaign.hpp @@ -0,0 +1,24 @@ +#ifndef __KESOGCCAMPAIGN_HPP__ + #define __KESOGCCAMPAIGN_HPP__ + +#include "cpn/DatabaseCampaign.hpp" +#include "comm/ExperimentData.hpp" +#include +#include "vezs.pb.h" + + +class VEZSExperimentData : public fail::ExperimentData { +public: + VEZSProtoMsg msg; + VEZSExperimentData() : fail::ExperimentData(&msg) {} +}; + + +class VEZSCampaign : public fail::DatabaseCampaign { + virtual const google::protobuf::Descriptor * cb_result_message() + { return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("VEZSProtoMsg"); } + + virtual void cb_send_pilot(DatabaseCampaignMessage pilot); +}; + +#endif // __KESOGCCAMPAIGN_HPP__ diff --git a/src/experiments/vezs-example/experiment.cc b/src/experiments/vezs-example/experiment.cc index 37b7e14b..d5267793 100644 --- a/src/experiments/vezs-example/experiment.cc +++ b/src/experiments/vezs-example/experiment.cc @@ -1,3 +1,4 @@ +#include #include #include @@ -8,87 +9,228 @@ #include #include "experiment.hpp" -#include "experimentInfo.hpp" #include "sal/SALConfig.hpp" #include "sal/SALInst.hpp" #include "sal/Memory.hpp" #include "sal/Listener.hpp" +#include "sal/bochs/BochsListener.hpp" #include +#include +#include +#include + +#include "util/llvmdisassembler/LLVMtoFailTranslator.hpp" +#include "util/llvmdisassembler/LLVMtoFailBochs.hpp" +#include "campaign.hpp" +#include "vezs.pb.h" +#include "util/Disassembler.hpp" + using namespace std; using namespace fail; +// Check if configuration dependencies are satisfied: +#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) + #error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration. +#endif + +void VEZSExperiment::redecodeCurrentInstruction() { + /* Flush Instruction Caches and Prefetch queue */ + BX_CPU_C *cpu_context = simulator.getCPUContext(); + cpu_context->invalidate_prefetch_q(); + cpu_context->iCache.flushICacheEntries(); + + + guest_address_t pc = simulator.getCPU(0).getInstructionPointer(); + bxInstruction_c *currInstr = simulator.getCurrentInstruction(); + + cout << "REDECODE INSTRUCTION!" << endl; + + Bit32u eipBiased = pc + cpu_context->eipPageBias; + Bit8u instr_plain[32]; + + MemoryManager& mm = simulator.getMemoryManager(); + mm.getBytes(pc, 32, instr_plain); + + unsigned remainingInPage = cpu_context->eipPageWindowSize - eipBiased; + int ret; +#if BX_SUPPORT_X86_64 + if (cpu_context->cpu_mode == BX_MODE_LONG_64) + ret = cpu_context->fetchDecode64(instr_plain, currInstr, remainingInPage); + else +#endif + ret = cpu_context->fetchDecode32(instr_plain, currInstr, remainingInPage); + if (ret < 0) { + // handle instrumentation callback inside boundaryFetch + cpu_context->boundaryFetch(instr_plain, remainingInPage, currInstr); + } +} + +unsigned VEZSExperiment::injectBitFlip(address_t data_address, unsigned data_width, unsigned bitpos){ + MemoryManager& mm = simulator.getMemoryManager(); + unsigned int value, injectedval; + + value = mm.getByte(data_address); + injectedval = value ^ (1 << bitpos); + mm.setByte(data_address, injectedval); + + cout << "INJECTION at: 0x" << hex << setw(2) << setfill('0') << data_address + << " value: 0x" << setw(2) << setfill('0') << value << " -> 0x" << setw(2) << setfill('0') << injectedval << endl; + + /* If it is the current instruction redecode it */ + guest_address_t pc = simulator.getCPU(0).getInstructionPointer(); + bxInstruction_c *currInstr = simulator.getCurrentInstruction(); + if (currInstr) { + unsigned length_in_bytes = currInstr->ilen(); + + if (pc <= data_address && data_address <= (pc + length_in_bytes)) { + redecodeCurrentInstruction(); + } + } + + return value; +} + + +void handleEvent(VEZSProtoMsg_Result& result, VEZSProtoMsg_Result_ResultType restype, const std::string &msg) { + cout << "Result details: " << msg << endl; + result.set_resulttype(restype); + result.set_details(msg); +} + + bool VEZSExperiment::run() { - //MemoryManager& mm = simulator.getMemoryManager(); + // m_dis.init(); + //******* Boot, and store state *******// + cout << "STARTING EXPERIMENT" << endl; - //m_elf.printDemangled(); - m_log << "STARTING EXPERIMENT" << endl; - m_log << "Instruction Pointer: 0x" << hex << simulator.getCPU(0).getInstructionPointer() << endl; -// Test register access - Register* reg = simulator.getCPU(0).getRegister(RI_R1); - m_log << "Register R1: 0x" << hex << simulator.getCPU(0).getRegisterContent(reg) << endl; + unsigned executed_jobs = 0; - reg = simulator.getCPU(0).getRegister(RI_R2); - m_log << "Register R2: 0x" << hex << simulator.getCPU(0).getRegisterContent(reg) << endl; + // Setup exit points + const ElfSymbol &s_positive = m_elf.getSymbol("fail_marker_positive"); + BPSingleListener l_positive(s_positive.getAddress()); + + const ElfSymbol &s_negative = m_elf.getSymbol("fail_marker_negative"); + BPSingleListener l_negative(s_negative.getAddress()); + + const ElfSymbol &s_end = m_elf.getSymbol("fail_trace_stop"); + BPSingleListener l_end(s_end.getAddress()); + + TrapListener l_trap(ANY_TRAP); + + TimerListener l_timeout(500 * 1000); // 500 ms + + assert(s_positive.isValid() || "fail_marker_positive not found"); + assert(s_negative.isValid() || "fail_marker_negative not found"); + assert(s_end.isValid() ||"fail_trace_stop not found"); -// Test Listeners - address_t address = m_elf.getSymbol("incfoo").getAddress(); - address &= ~1; // Cortex M3 Thumb Mode has the first bit set.. - m_log << "incfoo() @ 0x" << std::hex << address << std::endl; + while (executed_jobs < 25 || m_jc->getNumberOfUndoneJobs() > 0) { + cout << "asking jobserver for parameters" << endl; + VEZSExperimentData param; + if(!m_jc->getParam(param)){ + cout << "Dying." << endl; // We were told to die. + simulator.terminate(1); + } - address_t pfoo = m_elf.getSymbol("foo").getAddress(); - //BPSingleListener bp(address); - //BPRangeListener bp(address-32, address + 32); - MemWriteListener l_foo( pfoo ); - //MemAccessListener l_foo( 0x20002074 ); l_foo.setWatchWidth(0x4); - reg = simulator.getCPU(0).getRegister(RI_R4); + // Get input data from Jobserver + unsigned injection_instr = param.msg.fsppilot().injection_instr(); + address_t data_address = param.msg.fsppilot().data_address(); + unsigned data_width = param.msg.fsppilot().data_width(); - //unsigned foo = 23; - for(int i = 0; i < 15; i++){ - simulator.addListenerAndResume(&l_foo); - //if(i == 0) mm.setBytes(pfoo, 4, (void*)&foo); - m_log << " Breakpoint hit! @ 0x" << std::hex << simulator.getCPU(0).getInstructionPointer() << std::endl; - m_log << " Trigger PC: 0x" << std::hex << l_foo.getTriggerInstructionPointer() << std::endl; - //m_log << " Register R3: 0x" << hex << simulator.getCPU(0).getRegisterContent(reg) << endl; - //mm.getBytes(pfoo, 4, (void*)&foo); - //m_log << " foo @ 0x"<< std::hex << pfoo << " = " << foo << std::endl; - } + for (int bit_offset = 0; bit_offset < 8; ++bit_offset) { + // 8 results in one job + VEZSProtoMsg_Result *result = param.msg.add_result(); + result->set_bitoffset(bit_offset); -/* - BPRangeListener rbp(0xef, 0xff); - simulator.addListener(&rbp); + cout << "restoring state" << endl; + // Restore to the image, which starts at address(main) + simulator.restore("state"); + executed_jobs ++; - MemAccessListener l_mem_w(0x1111, MemAccessEvent::MEM_WRITE); - l_mem_w.setWatchWidth(16); - simulator.addListener(&l_mem_w); + cout << "Trying to inject @ instr #" << dec << injection_instr << endl; - MemAccessListener l_mem_r(0x2222, MemAccessEvent::MEM_READ); - l_mem_r.setWatchWidth(16); - simulator.addListener(&l_mem_r); - MemAccessListener l_mem_rw(0x3333, MemAccessEvent::MEM_READWRITE); - l_mem_rw.setWatchWidth(16); - simulator.addListener(&l_mem_rw); + if (injection_instr > 0) { + simulator.clearListeners(); + // XXX could be improved with intermediate states (reducing runtime until injection) + simulator.addListener(&l_end); - simulator.clearListeners(); -// resume backend. -// simulator.resume(); + BPSingleListener bp; + bp.setWatchInstructionPointer(ANY_ADDR); + + // Fix offset by 1 error + bp.setCounter(injection_instr + 1); + + simulator.addListener(&bp); -// Test Memory access - address_t targetaddress = 0x12345678; - MemoryManager& mm = simulator.getMemoryManager(); - mm.setByte(targetaddress, 0x42); - mm.getByte(targetaddress); + bool inject = true; + while (1) { + fail::BaseListener * listener = simulator.resume(); + // finish() before FI? + if (listener == &l_end) { + cout << "experiment reached finish() before FI" << endl; + handleEvent(*result, result->NOINJECTION, "time_marker reached before instr2"); + inject = false; + break; + } else if (listener == &bp) { + break; + } else { + inject = false; + handleEvent(*result, result->NOINJECTION, "WTF"); + break; + } + } - uint8_t tb[] = {0xab, 0xbb, 0xcc, 0xdd}; - mm.setBytes(targetaddress, 4, tb); - *((uint32_t*)(tb)) = 0; // clear array. - // read back bytes - mm.getBytes(targetaddress, 4, tb); + // Next experiment + if (!inject) + continue; + } + + address_t injection_instr_absolute = param.msg.fsppilot().injection_instr_absolute(); + if (simulator.getCPU(0).getInstructionPointer() != injection_instr_absolute) { + cout << "Invalid Injection address EIP=0x" + << std::hex << simulator.getCPU(0).getInstructionPointer() + << " != 0x" << injection_instr_absolute << std::endl; + simulator.terminate(1); + } -*/ - // Explicitly terminate, or the simulator will continue to run. - simulator.terminate(); + /// INJECT BITFLIP: + result->set_original_value(injectBitFlip(data_address, data_width, bit_offset)); + + simulator.clearListeners(); + simulator.addListener(&l_timeout); + simulator.addListener(&l_trap); + simulator.addListener(&l_positive); + simulator.addListener(&l_negative); + + + cout << "Resuming till the crash" << std::endl; + // resume and wait for results + fail::BaseListener* l = simulator.resume(); + cout << "End of execution" << std::endl; + + // Evaluate result + if(l == &l_positive) { + handleEvent(*result, result->POSITIVE_MARKER, "fail_marker_positive()"); + } else if(l == &l_negative) { + handleEvent(*result, result->NEGATIVE_MARKER, "fail_marker_negative()"); + } else if (l == &l_timeout) { + handleEvent(*result, result->TIMEOUT, "500ms"); + } else if (l == &l_trap) { + stringstream sstr; + sstr << "trap #" << l_trap.getTriggerNumber(); + handleEvent(*result, result->TRAP, sstr.str()); + } else { + handleEvent(*result, result->UNKNOWN, "UNKNOWN event"); + } + simulator.clearListeners(); + } + + m_jc->sendResult(param); + } + // Explicitly terminate, or the simulator will continue to run. + simulator.terminate(); } + diff --git a/src/experiments/vezs-example/experiment.hpp b/src/experiments/vezs-example/experiment.hpp index 5045cba4..899e833c 100644 --- a/src/experiments/vezs-example/experiment.hpp +++ b/src/experiments/vezs-example/experiment.hpp @@ -9,14 +9,24 @@ #include "util/ElfReader.hpp" class VEZSExperiment : public fail::ExperimentFlow { + fail::JobClient *m_jc; + fail::MemoryManager& m_mm; + fail::ElfReader m_elf; - fail::JobClient m_jc; - fail::Logger m_log; - fail::ElfReader m_elf; + unsigned injectBitFlip(fail::address_t data_address, unsigned data_width, unsigned bitpos); + void redecodeCurrentInstruction(); public: - VEZSExperiment() : m_log("VEZS-example", false) {}; - bool run(); + VEZSExperiment() : m_mm(fail::simulator.getMemoryManager()) { + 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(); + } + }; + + bool run(); }; #endif // __CHECKSUM_OOSTUBS_EXPERIMENT_HPP__ diff --git a/src/experiments/vezs-example/experimentInfo.hpp b/src/experiments/vezs-example/experimentInfo.hpp index bcc57e18..de63bb28 100644 --- a/src/experiments/vezs-example/experimentInfo.hpp +++ b/src/experiments/vezs-example/experimentInfo.hpp @@ -1,24 +1,5 @@ #ifndef __EXPERIMENT_INFO_HPP__ #define __EXPERIMENT_INFO_HPP__ -// FIXME autogenerate this - - -// the task function's entry address: -// nm -C ecc.elf|fgrep main -#define OOSTUBS_FUNC_ENTRY 0x001009d0 -// empty function that is called explicitly when the experiment finished -// nm -C ecc.elf|fgrep "finished()" -#define OOSTUBS_FUNC_FINISH 0x001009d6 -// function executing HLT with no chance for further progress (after panic()) -// nm -C ecc.elf|fgrep cpu_halt -#define OOSTUBS_FUNC_CPU_HALT 0x001009f7 - -// nm -C ecc.elf | fgrep "_TEXT_" -#define OOSTUBS_TEXT_START 0x00100000 -#define OOSTUBS_TEXT_END 0x00100a1b - -#define OOSTUBS_NUMINSTR 5 - #endif // __EXPERIMENT_INFO_HPP__ diff --git a/src/experiments/vezs-example/main.cc b/src/experiments/vezs-example/main.cc index 16605ac5..2afd1c0f 100644 --- a/src/experiments/vezs-example/main.cc +++ b/src/experiments/vezs-example/main.cc @@ -2,11 +2,16 @@ #include #include "cpn/CampaignManager.hpp" +#include "util/CommandLine.hpp" #include "campaign.hpp" int main(int argc, char **argv) { - ChecksumOOStuBSCampaign c; + fail::CommandLine &cmd = fail::CommandLine::Inst(); + for (int i = 1; i < argc; ++i) + cmd.add_args(argv[i]); + + VEZSCampaign c; if (fail::campaignmanager.runCampaign(&c)) { return 0; } else { diff --git a/src/experiments/vezs-example/vezs.proto b/src/experiments/vezs-example/vezs.proto new file mode 100644 index 00000000..c63ef553 --- /dev/null +++ b/src/experiments/vezs-example/vezs.proto @@ -0,0 +1,23 @@ +import "DatabaseCampaignMessage.proto"; + +message VEZSProtoMsg { + required DatabaseCampaignMessage fsppilot = 1; + + repeated group Result = 2 { + // make these optional to reduce overhead for server->client communication + enum ResultType { + POSITIVE_MARKER = 1; + NEGATIVE_MARKER = 2; + NO_MARKER = 3; + TIMEOUT = 4; + TRAP = 5; + NOINJECTION = 6; + UNKNOWN = 7; + } + // result type, see above + required ResultType resulttype = 4; + required uint32 original_value = 5; + required uint32 bitoffset = 6 [(sql_primary_key) = true]; + optional string details = 7; + } +} diff --git a/tools/import-trace/CMakeLists.txt b/tools/import-trace/CMakeLists.txt index 583e397d..b8a04e49 100644 --- a/tools/import-trace/CMakeLists.txt +++ b/tools/import-trace/CMakeLists.txt @@ -1,6 +1,7 @@ set(SRCS Importer.cc MemoryImporter.cc + FullTraceImporter.cc ) if (BUILD_LLVM_DISASSEMBLER) diff --git a/tools/import-trace/FullTraceImporter.cc b/tools/import-trace/FullTraceImporter.cc new file mode 100644 index 00000000..5c4a939b --- /dev/null +++ b/tools/import-trace/FullTraceImporter.cc @@ -0,0 +1,72 @@ +#include +#include + +#include "util/Logger.hpp" +#include "util/Database.hpp" +#include "FullTraceImporter.hpp" + +using namespace fail; +static fail::Logger LOG("FullTraceImporter"); + +Database *db; + +bool FullTraceImporter::handle_ip_event(simtime_t curtime, instruction_count_t instr, + Trace_Event &ev) { + + margin_info_t right_margin; + right_margin.time = curtime; + right_margin.dyninstr = instr; // !< The current instruction + right_margin.ip = ev.ip(); + + // pass through potentially available extended trace information + if (!add_trace_event(right_margin, right_margin, ev)) { + LOG << "add_trace_event failed" << std::endl; + return false; + } + + return true; +} + +bool FullTraceImporter::handle_mem_event(simtime_t curtime, instruction_count_t instr, + Trace_Event &ev) { + return true; +} + +bool FullTraceImporter::create_database() { + std::stringstream create_statement; + create_statement << "CREATE TABLE IF NOT EXISTS fulltrace (" + " variant_id int(11) NOT NULL," + " instr int(10) unsigned NOT NULL," + " instr_absolute int(10) unsigned DEFAULT NULL," + " INDEX(instr)," + " PRIMARY KEY (variant_id,instr)" + ") engine=MyISAM "; + return db->query(create_statement.str().c_str()); +} + +bool FullTraceImporter::clear_database() { + std::stringstream ss; + ss << "DELETE FROM fulltrace WHERE variant_id = " << m_variant_id; + + bool ret = db->query(ss.str().c_str()) == 0 ? false : true; + LOG << "deleted " << db->affected_rows() << " rows from fulltrace table" << std::endl; + return ret; +} + +bool FullTraceImporter::add_trace_event(margin_info_t &begin, margin_info_t &end, + Trace_Event &event, bool is_fake) { + // Is event a fake mem-event? + if (is_fake) { + return true; + } + + // INSERT group entry + std::stringstream sql; + sql << "(" << m_variant_id << "," << end.dyninstr << "," << end.ip << ")"; + + return db->insert_multiple("INSERT INTO fulltrace (variant_id, instr, instr_absolute) VALUES ", sql.str().c_str()); +} + +bool FullTraceImporter::trace_end_reached() { + return db->insert_multiple(); +} diff --git a/tools/import-trace/FullTraceImporter.hpp b/tools/import-trace/FullTraceImporter.hpp new file mode 100644 index 00000000..2db2b193 --- /dev/null +++ b/tools/import-trace/FullTraceImporter.hpp @@ -0,0 +1,25 @@ +#ifndef __FULL_TRACE_IMPORTER_H__ +#define __FULL_TRACE_IMPORTER_H__ + +#include "Importer.hpp" +#include "util/CommandLine.hpp" + +/** + The FullTraceImporter imports every dynamic ip-event from the trace into the database. +*/ +class FullTraceImporter : public Importer { + +protected: + virtual bool handle_ip_event(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev); + virtual bool handle_mem_event(fail::simtime_t curtime, instruction_count_t instr, + Trace_Event &ev); + virtual bool create_database(); + virtual bool clear_database(); + using Importer::add_trace_event; + virtual bool add_trace_event(margin_info_t &begin, margin_info_t &end, + Trace_Event &event, bool is_fake = false); + virtual bool trace_end_reached(); +}; + +#endif diff --git a/tools/import-trace/main.cc b/tools/import-trace/main.cc index 14832c82..eb2e71cb 100644 --- a/tools/import-trace/main.cc +++ b/tools/import-trace/main.cc @@ -7,6 +7,7 @@ #include #include #include "MemoryImporter.hpp" +#include "FullTraceImporter.hpp" #ifdef BUILD_LLVM_DISASSEMBLER #include "InstructionImporter.hpp" @@ -129,6 +130,8 @@ int main(int argc, char *argv[]) { if (imp == "BasicImporter" || imp == "MemoryImporter" || imp == "memory" || imp == "mem") { imp = "MemoryImporter"; importer = new MemoryImporter(); + } else if (imp == "FullTraceImporter") { + importer = new FullTraceImporter(); #ifdef BUILD_LLVM_DISASSEMBLER } else if (imp == "InstructionImporter" || imp == "code") { imp = "InstructionImporter";