experiments: experiment for tracing and testing ERIKA
ERIKA Enterprise is a OSEK conforming embedded RTOS. The supplied tracer
and experiment are similar to the cored-{tracing,tester} experiment, but
checks the integrity of the RTOS application in a different
manner. Stacks and stackpointers are located differently in ERIKA. This
experiment was used in RTAS'15 Hoffmann et al.
Change-Id: Idc8d874eb4d4ef15837f903270cfa521bc9514a2
This commit is contained in:
36
src/experiments/erika-tester/CMakeLists.txt
Normal file
36
src/experiments/erika-tester/CMakeLists.txt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
set(EXPERIMENT_NAME erika-tester)
|
||||||
|
set(EXPERIMENT_TYPE ErikaTester)
|
||||||
|
configure_file(../instantiate-experiment.ah.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
## Setup desired protobuf descriptions HERE ##
|
||||||
|
set(MY_PROTOS
|
||||||
|
erika-tester.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})
|
||||||
|
|
||||||
|
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} fail-comm)
|
||||||
|
target_link_libraries(fail-${EXPERIMENT_NAME} ${PROTOBUF_LIBRARY} fail-llvmdisassembler)
|
||||||
|
|
||||||
|
## 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} ${Boost_SYSTEM_LIBRARY} -lmysqlclient -Wl,--end-group)
|
||||||
|
install(TARGETS ${EXPERIMENT_NAME}-server RUNTIME DESTINATION bin)
|
||||||
|
|
||||||
20
src/experiments/erika-tester/campaign.cc
Normal file
20
src/experiments/erika-tester/campaign.cc
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "campaign.hpp"
|
||||||
|
#include "cpn/CampaignManager.hpp"
|
||||||
|
#include "util/Logger.hpp"
|
||||||
|
#include "util/ElfReader.hpp"
|
||||||
|
#include "util/ProtoStream.hpp"
|
||||||
|
#include "sal/SALConfig.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace fail;
|
||||||
|
using namespace google::protobuf;
|
||||||
|
|
||||||
|
void ErikaTesterCampaign::cb_send_pilot(DatabaseCampaignMessage pilot) {
|
||||||
|
ErikaTesterExperimentData *data = new ErikaTesterExperimentData;
|
||||||
|
data->msg.mutable_fsppilot()->CopyFrom(pilot);
|
||||||
|
campaignmanager.addParam(data);
|
||||||
|
}
|
||||||
|
|
||||||
30
src/experiments/erika-tester/campaign.hpp
Normal file
30
src/experiments/erika-tester/campaign.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef __DCIAOCAMPAIGN_HPP__
|
||||||
|
#define __DCIAOCAMPAIGN_HPP__
|
||||||
|
|
||||||
|
#include "cpn/DatabaseCampaign.hpp"
|
||||||
|
#include "comm/ExperimentData.hpp"
|
||||||
|
#include "erika-tester.pb.h"
|
||||||
|
#include "util/ElfReader.hpp"
|
||||||
|
#include <google/protobuf/descriptor.h>
|
||||||
|
|
||||||
|
class ErikaTesterExperimentData : public fail::ExperimentData {
|
||||||
|
public:
|
||||||
|
ErikaTesterProtoMsg msg;
|
||||||
|
ErikaTesterExperimentData() : fail::ExperimentData(&msg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ErikaTesterCampaign : public fail::DatabaseCampaign {
|
||||||
|
virtual const google::protobuf::Descriptor * cb_result_message()
|
||||||
|
{ return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("ErikaTesterProtoMsg"); }
|
||||||
|
|
||||||
|
virtual void cb_send_pilot(DatabaseCampaignMessage pilot);
|
||||||
|
virtual int expected_number_of_results(std::string variant, std::string benchmark) {
|
||||||
|
if (benchmark.find("jump") != std::string::npos)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __KESOREFCAMPAIGN_HPP__
|
||||||
6
src/experiments/erika-tester/config.cmake
Normal file
6
src/experiments/erika-tester/config.cmake
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
SET(bochs_configure_params --enable-a20-pin;--enable-x86-64;--enable-cpu-level=6;--enable-ne2000;--enable-acpi;--enable-pci;--enable-usb;--enable-trace-cache;--enable-fast-function-calls;--enable-host-specific-asms;--enable-readline;--enable-clgd54xx;--enable-fpu;--enable-vmx=2;--enable-monitor-mwait;--enable-cdrom;--enable-sb16=linux;--enable-gdb-stub;--with-nogui CACHE STRING "")
|
||||||
|
|
||||||
|
SET(PLUGINS_ACTIVATED "checkpoint;randomgenerator" CACHE STRING "")
|
||||||
|
|
||||||
|
SET(BUILD_LLVM_DISASSEMBLER ON CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
33
src/experiments/erika-tester/erika-tester.proto
Normal file
33
src/experiments/erika-tester/erika-tester.proto
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import "DatabaseCampaignMessage.proto";
|
||||||
|
|
||||||
|
message ErikaTesterProtoMsg {
|
||||||
|
required DatabaseCampaignMessage fsppilot = 1;
|
||||||
|
|
||||||
|
repeated group Result = 2 {
|
||||||
|
enum ResultType {
|
||||||
|
/* Test did the right thing */
|
||||||
|
OK = 1;
|
||||||
|
OK_WRONG_CONTROL_FLOW = 2;
|
||||||
|
OK_DETECTED_ERROR = 3;
|
||||||
|
|
||||||
|
/* Test did the wrong thing */
|
||||||
|
ERR_WRONG_RESULT = 4;
|
||||||
|
|
||||||
|
ERR_TRAP = 5;
|
||||||
|
ERR_TIMEOUT = 6;
|
||||||
|
ERR_OUTSIDE_TEXT = 7;
|
||||||
|
ERR_OUTSIDE_DATA = 8;
|
||||||
|
|
||||||
|
UNKNOWN = 9;
|
||||||
|
NOINJECTION = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
required int32 bitoffset = 1 [(sql_primary_key) = true];
|
||||||
|
required ResultType resulttype = 2;
|
||||||
|
required uint32 experiment_number = 3;
|
||||||
|
|
||||||
|
optional uint32 original_value = 4;
|
||||||
|
|
||||||
|
optional string details = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
572
src/experiments/erika-tester/experiment.cc
Normal file
572
src/experiments/erika-tester/experiment.cc
Normal file
@ -0,0 +1,572 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "experiment.hpp"
|
||||||
|
#include "sal/SALConfig.hpp"
|
||||||
|
#include "sal/SALInst.hpp"
|
||||||
|
#include "sal/Memory.hpp"
|
||||||
|
#include "sal/Listener.hpp"
|
||||||
|
|
||||||
|
#include "sal/bochs/BochsListener.hpp"
|
||||||
|
#include "sal/bochs/BochsMemory.hpp"
|
||||||
|
#include "sal/bochs/BochsCPU.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "util/llvmdisassembler/LLVMtoFailTranslator.hpp"
|
||||||
|
#include "util/llvmdisassembler/LLVMtoFailBochs.hpp"
|
||||||
|
|
||||||
|
#include "campaign.hpp"
|
||||||
|
#include "erika-tester.pb.h"
|
||||||
|
|
||||||
|
#include "../plugins/randomgenerator/RandomGenerator.hpp"
|
||||||
|
#include "../plugins/checkpoint/Checkpoint.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace fail;
|
||||||
|
|
||||||
|
#define SAFESTATE (1)
|
||||||
|
|
||||||
|
// Check if configuration dependencies are satisfied:
|
||||||
|
#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \
|
||||||
|
!defined(CONFIG_SR_SAVE)
|
||||||
|
#error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration.
|
||||||
|
#endif
|
||||||
|
#if !defined(CONFIG_EVENT_MEMREAD) || !defined(CONFIG_EVENT_MEMWRITE)
|
||||||
|
#error This experiment needs: MemRead and MemWrite. Enable these in the configuration.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ErikaTester::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();
|
||||||
|
//unsigned length_in_bytes = currInstr->ilen();
|
||||||
|
|
||||||
|
m_log << "REDECODE INSTRUCTION @ IP 0x" << std::hex << pc << endl;
|
||||||
|
|
||||||
|
Bit32u eipBiased = pc + cpu_context->eipPageBias;
|
||||||
|
Bit8u instr_plain[15];
|
||||||
|
|
||||||
|
MemoryManager& mm = simulator.getMemoryManager();
|
||||||
|
for(unsigned i=0; i<sizeof(instr_plain); i++) {
|
||||||
|
if(!mm.isMapped(pc+i)) {
|
||||||
|
m_log << "REDECODE: 0x" << std::hex << pc+i << "UNMAPPED" << endl;
|
||||||
|
// TODO: error?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mm.getBytes(pc, sizeof(instr_plain), 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 ErikaTester::injectBitFlip(address_t data_address, unsigned data_width, unsigned bitpos) {
|
||||||
|
/* First 32 Registers, this might neeed adaption */
|
||||||
|
if (data_address < (32 << 8)) {
|
||||||
|
LLVMtoFailTranslator::reginfo_t reginfo = LLVMtoFailTranslator::reginfo_t::fromDataAddress(data_address, data_width);
|
||||||
|
|
||||||
|
unsigned int value, injectedval;
|
||||||
|
|
||||||
|
value = m_ltof->getRegisterContent(simulator.getCPU(0), reginfo);
|
||||||
|
injectedval = value ^ (1 << bitpos);
|
||||||
|
m_ltof->setRegisterContent(simulator.getCPU(0), reginfo, injectedval);
|
||||||
|
|
||||||
|
m_log << "INJECTING register (" << dec << reginfo.id
|
||||||
|
<< " offset " << (int) reginfo.offset
|
||||||
|
<< ") bitpos: " << bitpos
|
||||||
|
<< " value: 0x" << hex << setw(2) << setfill('0') << value << " -> 0x" << setw(2) << setfill('0') << injectedval
|
||||||
|
<< dec << endl;
|
||||||
|
if (reginfo.id == RID_PC)
|
||||||
|
redecodeCurrentInstruction();
|
||||||
|
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
unsigned int value, injectedval;
|
||||||
|
|
||||||
|
MemoryManager& mm = simulator.getMemoryManager();
|
||||||
|
if(!mm.isMapped(data_address)) {
|
||||||
|
m_log << "DATA_ADDRESS NOT MAPPED" << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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') << injectedval << endl;
|
||||||
|
|
||||||
|
/* If it is the current instruction redecode it */
|
||||||
|
guest_address_t pc = simulator.getCPU(0).getInstructionPointer();
|
||||||
|
m_log << "IP: 0x" << std::hex << pc << std::endl;
|
||||||
|
bxInstruction_c *currInstr = simulator.getCurrentInstruction();
|
||||||
|
assert(currInstr != NULL && "FATAL ERROR: current instruction was NULL (not expected)!");
|
||||||
|
unsigned length_in_bytes = currInstr->ilen();
|
||||||
|
|
||||||
|
if (currInstr == NULL || (pc <= data_address && data_address <= (pc + currInstr->ilen()))) {
|
||||||
|
redecodeCurrentInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void handleEvent(ErikaTesterProtoMsg_Result& result, ErikaTesterProtoMsg_Result_ResultType restype, const std::string &msg) {
|
||||||
|
cout << msg << endl;
|
||||||
|
result.set_resulttype(restype);
|
||||||
|
result.set_details(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string handleMemoryAccessEvent(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();
|
||||||
|
|
||||||
|
return sstr.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ElfSymbol& ErikaTester::getELFSymbol(const std::string name) {
|
||||||
|
const ElfSymbol &symbol = m_elf.getSymbol(name);
|
||||||
|
if (!symbol.isValid()) {
|
||||||
|
m_log << "Couldn't find symbol: " << name << std::endl;
|
||||||
|
simulator.terminate(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ErikaTester::run() {
|
||||||
|
m_log << "STARTING EXPERIMENT" << endl;
|
||||||
|
|
||||||
|
// seed random number generator
|
||||||
|
timeval start;
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
srand(start.tv_usec);
|
||||||
|
|
||||||
|
// get symbols
|
||||||
|
const ElfSymbol &s_trace_end_marker = getELFSymbol("test_finish");
|
||||||
|
BPSingleListener l_trace_end_marker(s_trace_end_marker.getAddress());
|
||||||
|
|
||||||
|
const ElfSymbol &s_stext_app = getELFSymbol("_stext_application");
|
||||||
|
const ElfSymbol &s_etext_app = getELFSymbol("_etext_application");
|
||||||
|
|
||||||
|
const ElfSymbol &s_panic_handler = m_elf.getSymbol("panic_handler");
|
||||||
|
assert (s_panic_handler.isValid());
|
||||||
|
|
||||||
|
const ElfSymbol &s_fail_trace = getELFSymbol("fail_trace");
|
||||||
|
MemAccessListener l_fail_trace(s_fail_trace.getAddress());
|
||||||
|
|
||||||
|
const ElfSymbol &s_random_source = m_elf.getSymbol("random_source");
|
||||||
|
const ElfSymbol &s_tos = getELFSymbol("EE_x86_system_tos");
|
||||||
|
|
||||||
|
|
||||||
|
// allowed regions
|
||||||
|
address_t text_tasks_start = s_stext_app.getAddress();
|
||||||
|
address_t text_tasks_end = s_etext_app.getAddress();
|
||||||
|
|
||||||
|
m_log << "not injecting in application: " << std::hex << text_tasks_start << " -- " << text_tasks_end << std::endl;
|
||||||
|
|
||||||
|
// listeners for traps, code region
|
||||||
|
//InterruptListener l_interrupt(2); // NMI interrupt
|
||||||
|
//TrapListener l_trap(2); // NMI trap?
|
||||||
|
BPSingleListener* l_panic;
|
||||||
|
if(s_panic_handler.isValid()) {
|
||||||
|
l_panic = new BPSingleListener(s_panic_handler.getAddress());
|
||||||
|
m_log << "PANIC handler @ " << std::hex << s_panic_handler.getAddress() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned i_timeout = 2000 * 1000;
|
||||||
|
TimerListener l_timeout(i_timeout); // 1s in microseconds
|
||||||
|
TimerListener l_timeout_hard(2 * 1000 * 1000); // 2s in microseconds
|
||||||
|
|
||||||
|
|
||||||
|
// initialize LLVM disassembler
|
||||||
|
m_ltof = LLVMtoFailTranslator::createFromBinary(m_elf.getFilename());
|
||||||
|
|
||||||
|
// memory manager
|
||||||
|
MemoryManager& mm = simulator.getMemoryManager();
|
||||||
|
|
||||||
|
// job client with environment parameters
|
||||||
|
char* server = getenv("FAIL_SERVER_HOST");
|
||||||
|
if(server == NULL) server = (char*) SERVER_COMM_HOSTNAME;
|
||||||
|
char* cport = getenv("FAIL_SERVER_PORT");
|
||||||
|
int port = (cport != NULL) ? atoi(cport) : SERVER_COMM_TCP_PORT;
|
||||||
|
fail::JobClient jobclient(server, port);
|
||||||
|
|
||||||
|
// execute jobs
|
||||||
|
unsigned executed_jobs = 0;
|
||||||
|
while (executed_jobs < 25 || jobclient.getNumberOfUndoneJobs() > 0) {
|
||||||
|
// get next parameters from jobserver
|
||||||
|
m_log << "asking jobserver for parameters" << endl;
|
||||||
|
ErikaTesterExperimentData param;
|
||||||
|
if(!jobclient.getParam(param)){
|
||||||
|
m_log << "Dying." << endl; // We were told to die.
|
||||||
|
simulator.terminate(128); // special return code to signal campaign finish
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract parameters
|
||||||
|
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();
|
||||||
|
|
||||||
|
// detect wheter we should inject the PC
|
||||||
|
bool pc_injection = param.msg.fsppilot().benchmark().find("jump") != std::string::npos;
|
||||||
|
|
||||||
|
// setup experiments
|
||||||
|
int experiments = pc_injection ? 1 : 8;
|
||||||
|
|
||||||
|
// run experiments
|
||||||
|
for (int experiment_id = 0; experiment_id < experiments; ++experiment_id) {
|
||||||
|
ErikaTesterProtoMsg_Result *result = 0;
|
||||||
|
|
||||||
|
// restore to the image
|
||||||
|
m_log << "restoring state" << endl;
|
||||||
|
simulator.clearListeners(this);
|
||||||
|
simulator.restore("state");
|
||||||
|
m_log << "state restored" << endl;
|
||||||
|
executed_jobs++;
|
||||||
|
|
||||||
|
// init plugins
|
||||||
|
Checkpoint::range_vector check_ranges;
|
||||||
|
|
||||||
|
// The idle stack
|
||||||
|
std::map<fail::address_t, fail::address_t> stackpointers;
|
||||||
|
const ElfSymbol &s_os_stack = m_elf.getSymbol("os_stack");
|
||||||
|
assert (s_os_stack.isValid());
|
||||||
|
m_log << "found task stack: " << "os_stack" << std::endl;
|
||||||
|
fail::address_t paddr;
|
||||||
|
paddr = s_tos.getAddress() + 0*4;
|
||||||
|
Checkpoint::indirectable_address_t start = std::make_pair(paddr, true);
|
||||||
|
Checkpoint::indirectable_address_t end = std::make_pair(s_os_stack.getEnd(), false);
|
||||||
|
check_ranges.push_back(std::make_pair(start, end));
|
||||||
|
stackpointers[paddr+0] = paddr;
|
||||||
|
stackpointers[paddr+1] = paddr;
|
||||||
|
stackpointers[paddr+2] = paddr;
|
||||||
|
stackpointers[paddr+3] = paddr;
|
||||||
|
|
||||||
|
|
||||||
|
for(int i=1; i<255; i++) {
|
||||||
|
stringstream stackname;
|
||||||
|
stackname << "EE_x86_stack_";
|
||||||
|
stackname << i;
|
||||||
|
const ElfSymbol &s_stack = m_elf.getSymbol(stackname.str());
|
||||||
|
if(!s_stack.isValid()) {
|
||||||
|
m_log << "found " << std::dec << (i-1) << " task stacks" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
paddr = s_tos.getAddress() + i*4;
|
||||||
|
Checkpoint::indirectable_address_t start = std::make_pair(paddr, true);
|
||||||
|
Checkpoint::indirectable_address_t end = std::make_pair(s_stack.getEnd(), false);
|
||||||
|
check_ranges.push_back(std::make_pair(start, end));
|
||||||
|
|
||||||
|
stackpointers[paddr+0] = paddr;
|
||||||
|
stackpointers[paddr+1] = paddr;
|
||||||
|
stackpointers[paddr+2] = paddr;
|
||||||
|
stackpointers[paddr+3] = paddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Checkpoint cpoint(check_ranges, "checkpoint.trace");
|
||||||
|
|
||||||
|
RandomGenerator* rgen;
|
||||||
|
if (s_random_source.isValid()) {
|
||||||
|
const unsigned seed = 12342;
|
||||||
|
rgen = new RandomGenerator(s_random_source, seed);
|
||||||
|
simulator.addFlow(rgen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fast forward to injection address
|
||||||
|
m_log << "Trying to inject @ instr #" << dec << injection_instr << endl;
|
||||||
|
simulator.clearListeners(this);
|
||||||
|
simulator.addListener(&l_fail_trace);
|
||||||
|
if (injection_instr != 0) {
|
||||||
|
|
||||||
|
BPSingleListener bp;
|
||||||
|
bp.setWatchInstructionPointer(ANY_ADDR);
|
||||||
|
// TODO: why does this need a +1?
|
||||||
|
bp.setCounter(injection_instr);
|
||||||
|
simulator.addListener(&bp);
|
||||||
|
|
||||||
|
fail::BaseListener * listener = simulator.resume();
|
||||||
|
bool ok = true;
|
||||||
|
|
||||||
|
while ( ok && (listener == &l_fail_trace) ) {
|
||||||
|
m_log << "CP IP 0x" << std::hex << simulator.getCPU(0).getInstructionPointer() << std::endl;
|
||||||
|
ok = cpoint.check(s_fail_trace, l_fail_trace.getTriggerInstructionPointer()) == Checkpoint::IDENTICAL;
|
||||||
|
if(ok) listener = simulator.addListenerAndResume(&l_fail_trace);
|
||||||
|
}
|
||||||
|
if(!ok) {
|
||||||
|
result = param.msg.add_result();
|
||||||
|
handleEvent(*result, result->NOINJECTION, "CHECKPOINT FAIL BEFORE INJECTION?!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (listener != &bp) {
|
||||||
|
result = param.msg.add_result();
|
||||||
|
handleEvent(*result, result->NOINJECTION, "WTF");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// program counter sanity check
|
||||||
|
if (param.msg.fsppilot().has_injection_instr_absolute()) {
|
||||||
|
address_t PC = param.msg.fsppilot().injection_instr_absolute();
|
||||||
|
if (simulator.getCPU(0).getInstructionPointer() != PC) {
|
||||||
|
m_log << "Invalid injection address EIP=0x"
|
||||||
|
<< std::hex << simulator.getCPU(0).getInstructionPointer()
|
||||||
|
<< " != injection_instr_absolute=0x" << PC << std::endl;
|
||||||
|
simulator.terminate(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned experiment_number = cpoint.getCount();
|
||||||
|
m_log << "Trace " << std::dec << experiment_number << std::endl;
|
||||||
|
result = param.msg.add_result();
|
||||||
|
result->set_experiment_number((unsigned int) experiment_number);
|
||||||
|
result->set_bitoffset(experiment_id);
|
||||||
|
result->set_original_value(0);
|
||||||
|
|
||||||
|
// abort if outside targeted region
|
||||||
|
address_t PC = simulator.getCPU(0).getInstructionPointer();
|
||||||
|
//if(PC < min_code || PC > max_code) {
|
||||||
|
if(PC < text_tasks_end && PC >= text_tasks_start) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "0x" << hex << PC;
|
||||||
|
ss << " inside task text: ";
|
||||||
|
ss << "0x" << hex << text_tasks_start << " - 0x" << hex << text_tasks_end;
|
||||||
|
handleEvent(*result, result->NOINJECTION, ss.str());
|
||||||
|
continue; // next experiment
|
||||||
|
}
|
||||||
|
|
||||||
|
MemWriteListener *stackpointer_event = 0;
|
||||||
|
bool stackpointer_event_valid = false;
|
||||||
|
bool check_digest = true;
|
||||||
|
|
||||||
|
// perform injection
|
||||||
|
if (pc_injection) {
|
||||||
|
// jump to "data" address
|
||||||
|
address_t current_PC = simulator.getCPU(0).getInstructionPointer();
|
||||||
|
address_t new_PC = param.msg.fsppilot().data_address();
|
||||||
|
m_log << "jump from 0x" << hex << current_PC << " to 0x" << new_PC << std::endl;
|
||||||
|
simulator.getCPU(0).setRegisterContent(simulator.getCPU(0).getRegister(RID_PC), new_PC);
|
||||||
|
|
||||||
|
// set program counter
|
||||||
|
result->set_bitoffset(0);
|
||||||
|
result->set_original_value(current_PC);
|
||||||
|
redecodeCurrentInstruction();
|
||||||
|
} else {
|
||||||
|
// inject random bitflip
|
||||||
|
// experiment_id == bitpos
|
||||||
|
// We cache the stackpointer value inside the
|
||||||
|
// checkpoint plugin, if we inject it. This prevents
|
||||||
|
// the Checkpoint plugin to read invalid values.
|
||||||
|
if (stackpointers.find(data_address) != stackpointers.end()) {
|
||||||
|
guest_address_t paddr = stackpointers[data_address];
|
||||||
|
guest_address_t value = 0;
|
||||||
|
simulator.getMemoryManager().getBytes(paddr, 4, &value);
|
||||||
|
cpoint.cache_stackpointer_variable(paddr, value);
|
||||||
|
stackpointer_event = new MemWriteListener(paddr);
|
||||||
|
stackpointer_event_valid = true;
|
||||||
|
m_log << "Injected Stackpointer; saved old stackpointer value ("
|
||||||
|
<< hex << "0x" << paddr << "="<< value << dec << ")"<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpoint.in_range(data_address)) {
|
||||||
|
m_log << "Injected value is in checksummed range, ignore stack digest results" << endl;
|
||||||
|
check_digest = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->set_original_value(injectBitFlip(data_address, data_width, experiment_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add listeners
|
||||||
|
simulator.clearListeners(this);
|
||||||
|
|
||||||
|
if(s_panic_handler.isValid()) {
|
||||||
|
simulator.addListener(l_panic);
|
||||||
|
}
|
||||||
|
i_timeout = 2000 * 1000;
|
||||||
|
l_timeout.setTimeout(i_timeout);
|
||||||
|
simulator.addListener(&l_timeout);
|
||||||
|
simulator.addListener(&l_timeout_hard);
|
||||||
|
BPSingleListener l_timeout_instr;
|
||||||
|
l_timeout_instr.setWatchInstructionPointer(ANY_ADDR);
|
||||||
|
// Simulate only further 500 000 instructions
|
||||||
|
l_timeout_instr.setCounter(500000);
|
||||||
|
simulator.addListener(&l_timeout_instr);
|
||||||
|
|
||||||
|
simulator.addListener(&l_fail_trace);
|
||||||
|
simulator.addListener(&l_trace_end_marker);
|
||||||
|
|
||||||
|
if (stackpointer_event_valid) {
|
||||||
|
m_log << "Injected Stackpointer; add listener 0x"
|
||||||
|
<< hex << stackpointer_event->getWatchAddress() << dec
|
||||||
|
<< " " << stackpointer_event->getTriggerAccessType() << endl;
|
||||||
|
simulator.addListener(stackpointer_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resume and wait for results
|
||||||
|
m_log << "Resuming till the crash" << std::endl;
|
||||||
|
bool reached_check_start = false;
|
||||||
|
fail::BaseListener* l = simulator.resume();
|
||||||
|
|
||||||
|
int checkpoint_count = 0;
|
||||||
|
|
||||||
|
while(l == &l_fail_trace || l == stackpointer_event) {
|
||||||
|
if (l == stackpointer_event) {
|
||||||
|
cpoint.uncache_stackpointer_variable(stackpointer_event->getWatchAddress());
|
||||||
|
m_log << "Injected Stackpointer; cleared stackpointer cache" << std::endl;
|
||||||
|
l = simulator.resume();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Checkpoint::check_result res = cpoint.check(s_fail_trace, l_fail_trace.getTriggerInstructionPointer());
|
||||||
|
checkpoint_count ++;
|
||||||
|
if(res == Checkpoint::DIFFERENT_IP) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "different IP";
|
||||||
|
ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||||
|
ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||||
|
handleEvent(*result, result->ERR_WRONG_RESULT, ss.str());
|
||||||
|
break;
|
||||||
|
} else if(res == Checkpoint::DIFFERENT_VALUE) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "different value";
|
||||||
|
ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||||
|
ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||||
|
handleEvent(*result, result->ERR_WRONG_RESULT, ss.str());
|
||||||
|
break;
|
||||||
|
} else if(check_digest && res == Checkpoint::DIFFERENT_DIGEST) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "different digest";
|
||||||
|
ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||||
|
ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||||
|
|
||||||
|
// IGNORE for ISORC'14 experiments
|
||||||
|
handleEvent(*result, result->ERR_WRONG_RESULT, ss.str());
|
||||||
|
break;
|
||||||
|
} else if(res == Checkpoint::INVALID) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "invalid checkpoint";
|
||||||
|
ss << "@ IP 0x" << std::hex << l_fail_trace.getTriggerInstructionPointer();
|
||||||
|
ss << " (checkpoint " << std::dec << cpoint.getCount() << ")";
|
||||||
|
handleEvent(*result, result->ERR_WRONG_RESULT, ss.str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_log << "i " << checkpoint_count << " " << simulator.getTimerTicks() << " " << i_timeout << endl;
|
||||||
|
|
||||||
|
// Reset the soft timeout listener
|
||||||
|
simulator.removeListener(&l_timeout);
|
||||||
|
if (checkpoint_count > 5) {
|
||||||
|
i_timeout = 59 * 1000;
|
||||||
|
}
|
||||||
|
l_timeout.setTimeout(i_timeout);
|
||||||
|
simulator.addListener(&l_timeout);
|
||||||
|
assert(l_timeout.getTimeout() == i_timeout);
|
||||||
|
|
||||||
|
l = simulator.addListenerAndResume(&l_fail_trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l == &l_fail_trace) {
|
||||||
|
// already reported invalid checkpoint
|
||||||
|
} else if(l == &l_trace_end_marker) {
|
||||||
|
// trace ended successfully
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "correct end after " << cpoint.getCount() << " checkpoints";
|
||||||
|
handleEvent(*result, result->OK, ss.str());
|
||||||
|
} else if (l == l_panic) {
|
||||||
|
// error detected
|
||||||
|
stringstream sstr;
|
||||||
|
sstr << "PANIC";
|
||||||
|
|
||||||
|
// ErikaOS specific trap information
|
||||||
|
const Register *reg_eax = simulator.getCPU(0).getRegister(RID_CAX);
|
||||||
|
uint32_t trap = simulator.getCPU(0).getRegisterContent(reg_eax);
|
||||||
|
sstr << " trap " << dec << trap;
|
||||||
|
|
||||||
|
//address_t sp = simulator.getCPU(0).getStackPointer();
|
||||||
|
//if(mm.isMapped(sp)) {
|
||||||
|
// uint32_t ip;
|
||||||
|
// mm.getBytes(sp, 4, &ip);
|
||||||
|
// sstr << " @ 0x" << hex << ip;
|
||||||
|
|
||||||
|
const Register *reg_esi = simulator.getCPU(0).getRegister(RID_CSI);
|
||||||
|
address_t sp = simulator.getCPU(0).getRegisterContent(reg_esi) + 4;
|
||||||
|
if(mm.isMapped(sp) && mm.isMapped(sp+1) && mm.isMapped(sp+2) && mm.isMapped(sp+3)) {
|
||||||
|
uint32_t ip;
|
||||||
|
mm.getBytes(sp, 4, &ip);
|
||||||
|
sstr << " from 0x" << hex << ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(*result, result->OK_DETECTED_ERROR, sstr.str());
|
||||||
|
} else if ( l == &l_timeout || l == &l_timeout_hard || l == &l_timeout_instr) {
|
||||||
|
if (l == &l_timeout) {
|
||||||
|
handleEvent(*result, result->ERR_TIMEOUT, "soft timeout");
|
||||||
|
} else if (l == &l_timeout_hard) {
|
||||||
|
handleEvent(*result, result->ERR_TIMEOUT, "hard timeout");
|
||||||
|
} else if (l == &l_timeout_instr) {
|
||||||
|
handleEvent(*result, result->ERR_TIMEOUT, "instr timeout");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// what happened?
|
||||||
|
handleEvent(*result, result->UNKNOWN, "WTF");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stackpointer_event_valid) {
|
||||||
|
delete stackpointer_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For RandomJump do only one experiment not 8
|
||||||
|
if (pc_injection) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send results
|
||||||
|
jobclient.sendResult(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicitly terminate, or the simulator will continue to run
|
||||||
|
simulator.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
33
src/experiments/erika-tester/experiment.hpp
Normal file
33
src/experiments/erika-tester/experiment.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef __ERIKA_TESTER_EXPERIMENT_HPP__
|
||||||
|
#define __ERIKA_TESTER_EXPERIMENT_HPP__
|
||||||
|
|
||||||
|
|
||||||
|
#include "sal/SALInst.hpp"
|
||||||
|
#include "efw/ExperimentFlow.hpp"
|
||||||
|
#include "efw/JobClient.hpp"
|
||||||
|
#include "util/Logger.hpp"
|
||||||
|
#include "util/ElfReader.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "util/llvmdisassembler/LLVMtoFailTranslator.hpp"
|
||||||
|
|
||||||
|
class ErikaTester : public fail::ExperimentFlow {
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
fail::Logger m_log;
|
||||||
|
fail::MemoryManager& m_mm;
|
||||||
|
fail::ElfReader m_elf;
|
||||||
|
fail::LLVMtoFailTranslator* m_ltof;
|
||||||
|
|
||||||
|
unsigned injectBitFlip(fail::address_t data_address, unsigned data_width, unsigned bitpos);
|
||||||
|
void redecodeCurrentInstruction();
|
||||||
|
const fail::ElfSymbol& getELFSymbol(const std::string name);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ErikaTester() : m_log("ErikaTester", false), m_mm(fail::simulator.getMemoryManager()) {}
|
||||||
|
|
||||||
|
bool run();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
20
src/experiments/erika-tester/main.cc
Normal file
20
src/experiments/erika-tester/main.cc
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#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]);
|
||||||
|
|
||||||
|
ErikaTesterCampaign c;
|
||||||
|
if (fail::campaignmanager.runCampaign(&c)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/experiments/erika-tracing/CMakeLists.txt
Normal file
18
src/experiments/erika-tracing/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
set(EXPERIMENT_NAME erika-tracing)
|
||||||
|
set(EXPERIMENT_TYPE ErikaTracing)
|
||||||
|
configure_file(../instantiate-experiment.ah.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
set(MY_CAMPAIGN_SRCS
|
||||||
|
experiment.hpp
|
||||||
|
experiment.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
## 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} fail-tracing fail-randomgenerator fail-checkpoint fail-comm)
|
||||||
12
src/experiments/erika-tracing/config.cmake
Normal file
12
src/experiments/erika-tracing/config.cmake
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
SET(bochs_configure_params --enable-a20-pin;--enable-x86-64;--enable-cpu-level=6;--enable-ne2000;--enable-acpi;--enable-pci;--enable-usb;--enable-trace-cache;--enable-fast-function-calls;--enable-host-specific-asms;--enable-readline;--enable-clgd54xx;--enable-fpu;--enable-vmx=2;--enable-monitor-mwait;--enable-cdrom;--enable-sb16=linux;--enable-gdb-stub;--with-nogui CACHE STRING "")
|
||||||
|
|
||||||
|
SET(BUILD_LLVM_DISASSEMBLER ON CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
SET(PLUGINS_ACTIVATED "tracing;checkpoint;randomgenerator" CACHE STRING "")
|
||||||
|
|
||||||
|
# Build the import and prune trace tools always
|
||||||
|
SET(BUILD_IMPORT_TRACE ON CACHE BOOL "" FORCE)
|
||||||
|
SET(BUILD_PRUNE_TRACE ON CACHE BOOL "" FORCE)
|
||||||
|
SET(BUILD_CONVERT_TRACE ON CACHE BOOL "" FORCE)
|
||||||
|
SET(BUILD_DUMP_TRACE ON CACHE BOOL "" FORCE)
|
||||||
|
SET(BUILD_LLVM_DISASSEMBLER ON CACHE BOOL "" FORCE)
|
||||||
291
src/experiments/erika-tracing/experiment.cc
Normal file
291
src/experiments/erika-tracing/experiment.cc
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "sal/SALInst.hpp"
|
||||||
|
#include "sal/Register.hpp"
|
||||||
|
#include "sal/Listener.hpp"
|
||||||
|
#include "experiment.hpp"
|
||||||
|
#include "util/CommandLine.hpp"
|
||||||
|
#include "util/gzstream/gzstream.h"
|
||||||
|
|
||||||
|
// required (enabled) plugins
|
||||||
|
#include "../plugins/tracing/TracingPlugin.hpp"
|
||||||
|
#include "../plugins/randomgenerator/RandomGenerator.hpp"
|
||||||
|
#include "../plugins/checkpoint/Checkpoint.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace fail;
|
||||||
|
|
||||||
|
void ErikaTracing::parseOptions() {
|
||||||
|
CommandLine &cmd = CommandLine::Inst();
|
||||||
|
cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] -Wf,[option] ... <BochsOptions...>\n\n");
|
||||||
|
CommandLine::option_handle HELP = cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit");
|
||||||
|
|
||||||
|
|
||||||
|
CommandLine::option_handle ELF_FILE = cmd.addOption("", "elf-file", Arg::Required,
|
||||||
|
"--elf-file \tELF Binary File (default: $FAIL_ELF_PATH)");
|
||||||
|
CommandLine::option_handle START_SYMBOL = cmd.addOption("s", "start-symbol", Arg::Required,
|
||||||
|
"-s,--start-symbol \tELF symbol to start tracing (default: main)");
|
||||||
|
CommandLine::option_handle STOP_SYMBOL = cmd.addOption("e", "end-symbol", Arg::Required,
|
||||||
|
"-e,--end-symbol \tELF symbol to end tracing");
|
||||||
|
CommandLine::option_handle SAVE_SYMBOL = cmd.addOption("S", "save-symbol", Arg::Required,
|
||||||
|
"-S,--save-symbol \tELF symbol to save the state of the machine (default: main)\n");
|
||||||
|
CommandLine::option_handle STATE_FILE = cmd.addOption("f", "state-file", Arg::Required,
|
||||||
|
"-f,--state-file \tFile/dir to save the state to (default: state)");
|
||||||
|
CommandLine::option_handle TRACE_FILE = cmd.addOption("t", "trace-file", Arg::Required,
|
||||||
|
"-t,--trace-file \tFile to save the execution trace to (default: trace.pb)\n");
|
||||||
|
|
||||||
|
CommandLine::option_handle FULL_TRACE = cmd.addOption("", "full-trace", Arg::None, "--full-trace \tDo a full trace (more data, default: off)");
|
||||||
|
CommandLine::option_handle MEM_SYMBOL = cmd.addOption("m", "memory-symbol", Arg::Required,
|
||||||
|
"-m,--memory-symbol \tELF symbol(s) to trace accesses (default: all mem read/writes are traced)");
|
||||||
|
CommandLine::option_handle MEM_REGION = cmd.addOption("M", "memory-region", Arg::Required,
|
||||||
|
"-M,--memory-region \trestrict memory region which is traced"
|
||||||
|
" (Possible formats: 0x<address>, 0x<address>:0x<address>, 0x<address>:<length>)");
|
||||||
|
|
||||||
|
if (!cmd.parse()) {
|
||||||
|
cerr << "Error parsing arguments." << endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd[HELP]) {
|
||||||
|
cmd.printUsage();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd[ELF_FILE].count() > 0)
|
||||||
|
elf_file = cmd[ELF_FILE].first()->arg;
|
||||||
|
else {
|
||||||
|
char * elfpath = getenv("FAIL_ELF_PATH");
|
||||||
|
if (elfpath == NULL) {
|
||||||
|
m_log << "FAIL_ELF_PATH not set :( (alternative: --elf-file) " << std::endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
elf_file = elfpath;
|
||||||
|
}
|
||||||
|
m_elf = new ElfReader(elf_file.c_str());
|
||||||
|
|
||||||
|
if (cmd[START_SYMBOL].count() > 0)
|
||||||
|
start_symbol = cmd[START_SYMBOL].first()->arg;
|
||||||
|
else
|
||||||
|
start_symbol = "main";
|
||||||
|
|
||||||
|
if (cmd[STOP_SYMBOL].count() > 0)
|
||||||
|
stop_symbol = std::string(cmd[STOP_SYMBOL].first()->arg);
|
||||||
|
else {
|
||||||
|
m_log << "You have to give an end symbol (-e,--end-symbol)!" << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd[SAVE_SYMBOL].count() > 0)
|
||||||
|
save_symbol = std::string(cmd[SAVE_SYMBOL].first()->arg);
|
||||||
|
else
|
||||||
|
save_symbol = "main";
|
||||||
|
|
||||||
|
if (cmd[STATE_FILE].count() > 0)
|
||||||
|
state_file = std::string(cmd[STATE_FILE].first()->arg);
|
||||||
|
else
|
||||||
|
state_file = "state";
|
||||||
|
|
||||||
|
if (cmd[TRACE_FILE].count() > 0)
|
||||||
|
trace_file = std::string(cmd[TRACE_FILE].first()->arg);
|
||||||
|
else
|
||||||
|
trace_file = "trace.pb";
|
||||||
|
|
||||||
|
use_memory_map = false;
|
||||||
|
|
||||||
|
if (cmd[MEM_SYMBOL].count() > 0) {
|
||||||
|
use_memory_map = true;
|
||||||
|
option::Option *opt = cmd[MEM_SYMBOL].first();
|
||||||
|
|
||||||
|
while (opt != 0) {
|
||||||
|
const ElfSymbol &symbol = m_elf->getSymbol(opt->arg);
|
||||||
|
assert(symbol.isValid());
|
||||||
|
|
||||||
|
m_log << "Adding '" << opt->arg << "' == 0x" << std::hex << symbol.getAddress()
|
||||||
|
<< "+" << std::dec << symbol.getSize() << " to trace map" << std::endl;
|
||||||
|
traced_memory_map.add(symbol.getAddress(), symbol.getSize());
|
||||||
|
|
||||||
|
opt = opt->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd[MEM_REGION].count() > 0) {
|
||||||
|
use_memory_map = true;
|
||||||
|
option::Option *opt = cmd[MEM_REGION].first();
|
||||||
|
|
||||||
|
while (opt != 0) {
|
||||||
|
char *endptr;
|
||||||
|
guest_address_t begin = strtol(opt->arg, &endptr, 16);
|
||||||
|
guest_address_t size;
|
||||||
|
if (endptr == opt->arg) {
|
||||||
|
m_log << "Couldn't parse " << opt->arg << std::endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char delim = *endptr;
|
||||||
|
if (delim == 0) {
|
||||||
|
size = 1;
|
||||||
|
} else if (delim == ':') {
|
||||||
|
char *p = endptr +1;
|
||||||
|
size = strtol(p, &endptr, 16) - begin;
|
||||||
|
if (p == endptr || *endptr != 0) {
|
||||||
|
m_log << "Couldn't parse " << opt->arg << std::endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
} else if (delim == '+') {
|
||||||
|
char *p = endptr +1;
|
||||||
|
size = strtol(p, &endptr, 10);
|
||||||
|
if (p == endptr || *endptr != 0) {
|
||||||
|
m_log << "Couldn't parse " << opt->arg << std::endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_log << "Couldn't parse " << opt->arg << std::endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
traced_memory_map.add(begin, size);
|
||||||
|
|
||||||
|
m_log << "Adding " << opt->arg << " 0x" << std::hex << begin
|
||||||
|
<< "+" << std::dec << size << " to trace map" << std::endl;
|
||||||
|
|
||||||
|
opt = opt->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd[FULL_TRACE]) {
|
||||||
|
this->full_trace = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(m_elf->getSymbol(start_symbol).isValid());
|
||||||
|
assert(m_elf->getSymbol(stop_symbol).isValid());
|
||||||
|
assert(m_elf->getSymbol(save_symbol).isValid());
|
||||||
|
|
||||||
|
m_log << "start symbol: " << start_symbol << " 0x" << std::hex << m_elf->getSymbol(start_symbol).getAddress() << std::endl;
|
||||||
|
m_log << "save symbol: " << save_symbol << " 0x" << std::hex << m_elf->getSymbol(save_symbol).getAddress() << std::endl;
|
||||||
|
m_log << "stop symbol: " << stop_symbol << " 0x" << std::hex << m_elf->getSymbol(stop_symbol).getAddress() << std::endl;
|
||||||
|
|
||||||
|
m_log << "state file: " << state_file << std::endl;
|
||||||
|
m_log << "trace file: " << trace_file << std::endl;
|
||||||
|
m_log << "full-trace: " << this->full_trace << std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ErikaTracing::run()
|
||||||
|
{
|
||||||
|
parseOptions();
|
||||||
|
|
||||||
|
BPSingleListener l_start_symbol(m_elf->getSymbol(start_symbol).getAddress());
|
||||||
|
BPSingleListener l_save_symbol (m_elf->getSymbol(save_symbol).getAddress());
|
||||||
|
BPSingleListener l_stop_symbol (m_elf->getSymbol(stop_symbol).getAddress());
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// STEP 1: run until interesting function starts, start the tracing
|
||||||
|
simulator.addListenerAndResume(&l_start_symbol);
|
||||||
|
m_log << start_symbol << " reached, start tracing" << std::endl;
|
||||||
|
|
||||||
|
// restrict memory access logging to injection target
|
||||||
|
TracingPlugin tp;
|
||||||
|
tp.setFullTrace(this->full_trace);
|
||||||
|
|
||||||
|
if (use_memory_map) {
|
||||||
|
m_log << "Use restricted memory map for tracing" << std::endl;
|
||||||
|
tp.restrictMemoryAddresses(&traced_memory_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
ogzstream of(trace_file.c_str());
|
||||||
|
if (of.bad()) {
|
||||||
|
m_log << "Couldn't open trace file: " << trace_file << std::endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
tp.setTraceFile(&of);
|
||||||
|
|
||||||
|
// this must be done *after* configuring the plugin:
|
||||||
|
simulator.addFlow(&tp);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// STEP 2: continue to the save point, and save state
|
||||||
|
if (start_symbol != save_symbol) {
|
||||||
|
simulator.addListenerAndResume(&l_save_symbol);
|
||||||
|
}
|
||||||
|
m_log << start_symbol << " reached, save state" << std::endl;
|
||||||
|
simulator.save(state_file);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// Step 3: add plugins
|
||||||
|
// symbol to trigger checkpoints
|
||||||
|
const ElfSymbol &s_fail_trace = m_elf->getSymbol("fail_trace");
|
||||||
|
const ElfSymbol &s_tos = m_elf->getSymbol("EE_x86_system_tos");
|
||||||
|
|
||||||
|
Checkpoint *cpoint;
|
||||||
|
if(s_fail_trace.isValid() && s_tos.isValid()) {
|
||||||
|
Checkpoint::range_vector check_ranges;
|
||||||
|
|
||||||
|
const ElfSymbol &s_os_stack = m_elf->getSymbol("os_stack");
|
||||||
|
assert (s_os_stack.isValid());
|
||||||
|
m_log << "found task stack: " << "os_stack" << std::endl;
|
||||||
|
|
||||||
|
Checkpoint::indirectable_address_t start = std::make_pair(s_tos.getAddress() + 0*4, true);
|
||||||
|
Checkpoint::indirectable_address_t end = std::make_pair(s_os_stack.getEnd(), false);
|
||||||
|
check_ranges.push_back(std::make_pair(start, end));
|
||||||
|
|
||||||
|
for(int i=1; i<255; i++) {
|
||||||
|
stringstream stackname;
|
||||||
|
stackname << "EE_x86_stack_";
|
||||||
|
stackname << i;
|
||||||
|
const ElfSymbol &s_stack = m_elf->getSymbol(stackname.str());
|
||||||
|
if(!s_stack.isValid()) {
|
||||||
|
m_log << "found " << std::dec << (i-1) << " task stacks" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log << "found task stack: " << stackname.str() << std::endl;
|
||||||
|
|
||||||
|
Checkpoint::indirectable_address_t start = std::make_pair(s_tos.getAddress() + i*4, true);
|
||||||
|
Checkpoint::indirectable_address_t end = std::make_pair(s_stack.getEnd(), false);
|
||||||
|
check_ranges.push_back(std::make_pair(start, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
cpoint = new Checkpoint(s_fail_trace, check_ranges, "checkpoint.trace");
|
||||||
|
simulator.addFlow(cpoint);
|
||||||
|
} else {
|
||||||
|
m_log << "Checkpoint plugin NOT added to simulation" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// symbol to read random values from
|
||||||
|
const ElfSymbol &s_random_source = m_elf->getSymbol("random_source");
|
||||||
|
RandomGenerator *rgen;
|
||||||
|
if (s_random_source.isValid()) {
|
||||||
|
const unsigned seed = 12342;
|
||||||
|
rgen = new RandomGenerator(s_random_source, seed);
|
||||||
|
simulator.addFlow(rgen);
|
||||||
|
} else {
|
||||||
|
m_log << "Randomgenerator plugin NOT added to simulation" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// Step 4: Continue to the stop point
|
||||||
|
simulator.addListener(&l_stop_symbol);
|
||||||
|
simulator.resume();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// Step 5: tear down the tracing
|
||||||
|
|
||||||
|
simulator.removeFlow(&tp);
|
||||||
|
// serialize trace to file
|
||||||
|
if (of.fail()) {
|
||||||
|
m_log << "failed to write " << trace_file << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
of.close();
|
||||||
|
|
||||||
|
simulator.clearListeners();
|
||||||
|
simulator.terminate();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
37
src/experiments/erika-tracing/experiment.hpp
Normal file
37
src/experiments/erika-tracing/experiment.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef __ERIKA_TRACING_HPP__
|
||||||
|
#define __ERIKA_TRACING_HPP__
|
||||||
|
|
||||||
|
#include "efw/ExperimentFlow.hpp"
|
||||||
|
#include "util/Logger.hpp"
|
||||||
|
#include "util/ElfReader.hpp"
|
||||||
|
#include "util/MemoryMap.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ErikaTracing : public fail::ExperimentFlow {
|
||||||
|
std::string start_symbol;
|
||||||
|
std::string stop_symbol;
|
||||||
|
std::string save_symbol;
|
||||||
|
|
||||||
|
std::string state_file;
|
||||||
|
std::string trace_file;
|
||||||
|
std::string elf_file;
|
||||||
|
|
||||||
|
bool use_memory_map;
|
||||||
|
fail::MemoryMap traced_memory_map;
|
||||||
|
|
||||||
|
bool full_trace;
|
||||||
|
|
||||||
|
fail::Logger m_log;
|
||||||
|
fail::ElfReader *m_elf;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void parseOptions();
|
||||||
|
bool run();
|
||||||
|
|
||||||
|
ErikaTracing() : full_trace(false), m_log("ErikaTracing", false) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __ERIKA_TRACING_HPP__
|
||||||
99
src/experiments/erika-tracing/fail_experiment.py
Executable file
99
src/experiments/erika-tracing/fail_experiment.py
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os,sys
|
||||||
|
import logging
|
||||||
|
import imp
|
||||||
|
import math
|
||||||
|
import collections
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
tmp_path = "%s/git/versuchung/src"% os.environ["HOME"]
|
||||||
|
if os.path.exists(tmp_path):
|
||||||
|
sys.path.append(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
from versuchung.experiment import Experiment
|
||||||
|
from versuchung.types import String, Bool,List
|
||||||
|
from versuchung.database import Database
|
||||||
|
from versuchung.files import File, Directory, Executable
|
||||||
|
from versuchung.archives import GitArchive
|
||||||
|
from versuchung.execute import shell
|
||||||
|
from versuchung.tex import DatarefDict
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
class FailTrace(Experiment):
|
||||||
|
inputs = {
|
||||||
|
"erika": GitArchive("gitosis@i4git.informatik.uni-erlangen.de:erika"),
|
||||||
|
"bochs-runner": Executable("/proj/i4danceos/tools/fail/bochs-experiment-runner.py"),
|
||||||
|
"erika-tracing": Executable("/proj/i4danceos/tools/fail/erika-tracing"),
|
||||||
|
}
|
||||||
|
outputs = {
|
||||||
|
"trace": Directory("trace"),
|
||||||
|
"elf": File("erika.elf"),
|
||||||
|
"iso": File("erika.iso"),
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
logging.info("Cloning ERIKA...")
|
||||||
|
|
||||||
|
with self.erika as erika_path:
|
||||||
|
shell("cd %s/examples/x86/coptermock-isorc; make", erika_path)
|
||||||
|
|
||||||
|
self.iso.copy_contents(os.path.join(erika_path, "examples/x86/coptermock-isorc/Debug/erika.iso"))
|
||||||
|
self.elf.copy_contents(os.path.join(erika_path, "examples/x86/coptermock-isorc/Debug/Debug/out.elf"))
|
||||||
|
|
||||||
|
shell(("cd %(resultdir)s; python %(bochs)s -F 50 -i %(iso)s -e %(elf)s -f %(fail)s"
|
||||||
|
+ " -m 8 -1 -- -Wf,--end-symbol=test_finish -Wf,--start-symbol=EE_oo_StartOS"
|
||||||
|
+ " -Wf,--trace-file=trace.pb -Wf,--save-symbol=EE_oo_StartOS") % {
|
||||||
|
"resultdir": self.trace.path,
|
||||||
|
"bochs": self.bochs_runner.path,
|
||||||
|
"iso": self.iso.path,
|
||||||
|
"elf": self.elf.path,
|
||||||
|
"fail": self.erika_tracing.path
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FailImport(Experiment):
|
||||||
|
inputs = {
|
||||||
|
"trace": FailTrace("FailTrace"),
|
||||||
|
"fail-tool-dir": Directory("/proj/i4danceos/tools/fail"),
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
variant = "erika/error-hook"
|
||||||
|
for (label, importer, importer_args) in [\
|
||||||
|
("mem", "MemoryImporter", []),
|
||||||
|
("regs", "RegisterImporter", []),
|
||||||
|
("ip", "RegisterImporter", ["--no-gp", "--ip"]),
|
||||||
|
("flags", "RegisterImporter", ["--no-gp", "--flags"]),
|
||||||
|
]:
|
||||||
|
benchmark = label
|
||||||
|
logging.info("Importing coredos/%s", benchmark)
|
||||||
|
cmdline = "%(path)s/import-trace -v %(variant)s -b %(benchmark)s -i %(importer)s "\
|
||||||
|
+ "-t %(trace)s -e %(elf)s %(args)s"
|
||||||
|
shell(cmdline %\
|
||||||
|
{"path": self.fail_tool_dir.path,
|
||||||
|
"variant": variant,
|
||||||
|
"benchmark": benchmark,
|
||||||
|
"importer": importer,
|
||||||
|
"trace": os.path.join(self.trace.trace.path, "trace.pb"),
|
||||||
|
"elf": self.trace.elf.path,
|
||||||
|
"args": " ".join(importer_args)})
|
||||||
|
shell("%s/prune-trace -v %s -b %% -p basic --overwrite",
|
||||||
|
self.fail_tool_dir.path, variant)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import shutil, sys
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
action = sys.argv[1]; del sys.argv[1]
|
||||||
|
actions = {"trace": FailTrace,
|
||||||
|
"import": FailImport}
|
||||||
|
|
||||||
|
if action in actions:
|
||||||
|
experiment = actions[action]()
|
||||||
|
dirname = experiment(sys.argv)
|
||||||
|
else:
|
||||||
|
print "No action to be taken, use: %s" % ", ".join(actions.keys())
|
||||||
|
|
||||||
Reference in New Issue
Block a user