L4Sys Experiment: more on address space tracing

* introduce L4SYS_ADDRESS_SPACE_TRACE to indicate that we want
  to trace instructions in a different AS from the one we are starting
  the experiment in
* add CR3Run() to determine address space ID

Change-Id: I7bdaf1e858a6dd369af5175bd56e1b4e2d5f05ef
This commit is contained in:
Bjoern Doebel
2013-10-21 14:50:28 +02:00
parent 523f4a465b
commit 77b2e208d0
3 changed files with 169 additions and 57 deletions

View File

@ -22,8 +22,7 @@
#include "util/ProtoStream.hpp"
#include "TracePlugin.pb.h"
#include "util/gzstream/gzstream.h"
#include "l4sys.pb.h"
#include <sal/bochs/BochsMemory.hpp>
using namespace std;
using namespace fail;
@ -204,8 +203,15 @@ void L4SysExperiment::terminate(int reason) {
simulator.terminate(reason);
}
void L4SysExperiment::terminateWithError(string details, int reason) {
L4SysProtoMsg_Result *result = param->msg.add_result();
void L4SysExperiment::terminateWithError(string details, int reason,
L4SysProtoMsg_Result *r = 0) {
L4SysProtoMsg_Result *result;
if (r)
result = r;
else
result = param->msg.add_result();
result->set_resulttype(param->msg.UNKNOWN);
result->set_resultdata(simulator.getCPU(0).getInstructionPointer());
result->set_output(sanitised(currentOutput.c_str()));
@ -230,21 +236,37 @@ void L4SysExperiment::startAndSaveInitState(fail::BPSingleListener* bp)
simulator.save(L4SYS_STATE_FOLDER);
delete bp;
}
void L4SysExperiment::CR3run(fail::BPSingleListener *bp)
{
bp->setWatchInstructionPointer(L4SYS_FUNC_ENTRY);
simulator.addListenerAndResume(bp);
log << "Reached entry point @ " << hex << bp->getTriggerInstructionPointer()
<< " == " << simulator.getCPU(0).getInstructionPointer()
<< endl;
log << "CR3 = " << hex << BX_CPU(0)->cr3 << endl;
}
void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener* bp)
{
fail::MemAccessListener ML(ANY_ADDR, MemAccessEvent::MEM_READWRITE);
ogzstream out("trace.pb");
ProtoOStream *os = new ProtoOStream(&out);
size_t count = 0, inst_accepted = 0, mem = 0, mem_valid = 0;
simtime_t prevtime = 0, currtime;
simtime_diff_t deltatime;
log << "restoring state" << endl;
simulator.restore(L4SYS_STATE_FOLDER);
currtime = simulator.getTimerTicks();
log << "EIP = " << hex
<< simulator.getCPU(0).getInstructionPointer()
<< endl;
#ifdef L4SYS_FILTER_INSTRUCTIONS
ofstream instr_list_file(L4SYS_INSTRUCTION_LIST, ios::binary);
RangeSetInstructionFilter filtering(L4SYS_FILTER);
bp->setWatchInstructionPointer(ANY_ADDR);
fail::MemAccessListener ML(ANY_ADDR, MemAccessEvent::MEM_READWRITE);
if (!simulator.addListener(&ML)) {
log << "did not add memory listener..." << std::endl;
exit(1);
@ -254,16 +276,14 @@ void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener* bp)
exit(1);
}
size_t count = 0, inst_accepted = 0, mem = 0, mem_valid = 0;
#if L4SYS_FILTER_INSTRUCTIONS
ofstream instr_list_file(L4SYS_INSTRUCTION_LIST, ios::binary);
RangeSetInstructionFilter filtering(L4SYS_FILTER);
bp->setWatchInstructionPointer(ANY_ADDR);
map<address_t, unsigned> times_called_map;
bool injecting = false;
ogzstream out("trace.pb");
ProtoOStream *os = new ProtoOStream(&out);
simtime_t prevtime = 0, currtime;
simtime_diff_t deltatime;
while (bp->getTriggerInstructionPointer() != L4SYS_FUNC_EXIT) {
fail::BaseListener *res = simulator.resume();
address_t curr_addr = 0;
@ -272,10 +292,10 @@ void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener* bp)
if (res == &ML) {
curr_addr = ML.getTriggerInstructionPointer();
simulator.addListener(&ML);
++mem;
if (BX_CPU(0)->cr3 != L4SYS_ADDRESS_SPACE) {
if ((L4SYS_ADDRESS_SPACE_TRACE != ANY_ADDR) && (BX_CPU(0)->cr3 != L4SYS_ADDRESS_SPACE_TRACE)) {
continue;
}
++mem;
} else if (res == bp) {
curr_addr = bp->getTriggerInstructionPointer();
assert(curr_addr == simulator.getCPU(0).getInstructionPointer());
@ -294,8 +314,15 @@ void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener* bp)
injecting = false;
}
// Only trace if:
// 1) we are between FILTER_ENTRY and FILTER_EXIT, and
// 2) we have a valid instruction according to filter rules, and
// 3) we are in the TRACE address space
if (!injecting or
!filtering.isValidInstr(curr_addr, reinterpret_cast<char const*>(calculateInstructionAddress()))) {
!filtering.isValidInstr(curr_addr, reinterpret_cast<char const*>(calculateInstructionAddress()))
or
(BX_CPU(0)->cr3 != L4SYS_ADDRESS_SPACE_TRACE)
) {
//log << "connt..." << std::endl;
continue;
}
@ -358,22 +385,62 @@ void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener* bp)
<< dec << count << " instructions; " << inst_accepted << " accepted" << endl;
log << "mem accesses: " << mem << ", valid: " << mem_valid << std::endl;
#else
int count = 0;
int ul = 0, kernel = 0;
bp.setWatchInstructionPointer(ANY_ADDR);
for (; bp.getTriggerInstructionPointer() != L4SYS_FUNC_EXIT; ++count) {
simulator.addListenerAndResume(&bp);
if (bp.getTriggerInstructionPointer() < 0xC0000000) {
ul++;
} else {
kernel++;
}
bp->setWatchInstructionPointer(ANY_ADDR);
while(bp->getTriggerInstructionPointer() != L4SYS_FUNC_EXIT)
{
fail::BaseListener *res = simulator.resume();
address_t curr_addr = 0;
// XXX: See the API problem below!
if (res == &ML) {
curr_addr = ML.getTriggerInstructionPointer();
simulator.addListener(&ML);
if ((L4SYS_ADDRESS_SPACE_TRACE != ANY_ADDR) && (BX_CPU(0)->cr3 != L4SYS_ADDRESS_SPACE_TRACE)) {
continue;
}
++mem;
} else if (res == bp) {
curr_addr = bp->getTriggerInstructionPointer();
assert(curr_addr == simulator.getCPU(0).getInstructionPointer());
simulator.addListener(bp);
++count;
}
if (curr_addr < 0xC0000000) // XXX filter for kernel-only experiment
continue;
currtime = simulator.getTimerTicks();
deltatime = currtime - prevtime;
if (res == &ML) {
#if 0
log << "Memory event IP " << std::hex << ML.getTriggerInstructionPointer()
<< " @ " << ML.getTriggerAddress() << "("
<< ML.getTriggerAccessType() << "," << ML.getTriggerWidth()
<< ")" << std::endl;
#endif
++mem_valid;
Trace_Event te;
if (deltatime != 0) { te.set_time_delta(deltatime); };
te.set_ip(curr_addr);
te.set_memaddr(ML.getTriggerAddress());
te.set_accesstype( (ML.getTriggerAccessType() & MemAccessEvent::MEM_READ) ? te.READ : te.WRITE );
te.set_width(ML.getTriggerWidth());
os->writeMessage(&te);
} else if (res == bp) {
Trace_Event e;
if (deltatime != 0) { e.set_time_delta(deltatime); };
e.set_ip(curr_addr);
os->writeMessage(&e);
} else {
printf("Unknown res? %p\n", res);
}
prevtime = currtime;
}
log << "EIP = " << hex
<< simulator.getCPU(0).getInstructionPointer() << endl;
log << "test function calculation position reached after "
<< dec << count << " instructions; "
<< "ul: " << ul << ", kernel: " << kernel << endl;
<< dec << count << " instructions; " << count << " accepted" << endl;
log << "mem accesses: " << mem << ", valid: " << mem_valid << std::endl;
#endif
delete bp;
}
@ -495,16 +562,16 @@ void L4SysExperiment::setupFilteredBreakpoint(fail::BPSingleListener* bp, int in
fail::BPSingleListener*
L4SysExperiment::prepareMemoryExperiment(int ip, int offset, int dataAddress)
{
fail::BPSingleListener *bp = new BPSingleListener(0, L4SYS_ADDRESS_SPACE);
fail::BPSingleListener *bp = new BPSingleListener(0, L4SYS_ADDRESS_SPACE_TRACE);
log << "\033[34;1mMemory fault injection\033[0m at instruction " << std::hex << offset
<< ", ip " << ip << ", address " << dataAddress << std::endl;
#ifdef L4SYS_FILTER_INSTRUCTIONS
setupFilteredBreakpoint(bp, offset);
#if L4SYS_FILTER_INSTRUCTIONS
setupFilteredBreakpoint(bp, offset-1);
assert(bp->getWatchInstructionPointer() == (address_t)(ip & 0xFFFFFFFF));
#else
bp->setWatchInstructionPointer(ANY_ADDR);
bp->setCounter(instr_offset);
bp->setCounter(offset);
#endif
return bp;
}
@ -513,7 +580,7 @@ L4SysExperiment::prepareMemoryExperiment(int ip, int offset, int dataAddress)
fail::BPSingleListener*
L4SysExperiment::prepareRegisterExperiment(int ip, int offset, int dataAddress)
{
fail::BPSingleListener *bp = new BPSingleListener(0, L4SYS_ADDRESS_SPACE);
fail::BPSingleListener *bp = new BPSingleListener(0, L4SYS_ADDRESS_SPACE_TRACE);
int reg, regOffset;
reg = ((dataAddress >> 4) & 0xF) + 1; // regs start at 1
@ -523,26 +590,36 @@ L4SysExperiment::prepareRegisterExperiment(int ip, int offset, int dataAddress)
<< " reg data (" << reg << ", "
<< regOffset << ")" << std::endl;
#ifdef L4SYS_FILTER_INSTRUCTIONS
setupFilteredBreakpoint(bp, offset);
#if L4SYS_FILTER_INSTRUCTIONS
setupFilteredBreakpoint(bp, offset-1);
log << bp->getWatchInstructionPointer() << std::endl;
log << ip << std::endl;
assert(bp->getWatchInstructionPointer() == (address_t)(ip & 0xFFFFFFFF));
log << bp->getCounter() << std::endl;
#else
log << "Exp offset: " << offset << std::endl;
bp->setWatchInstructionPointer(ANY_ADDR);
bp->setCounter(instr_offset);
bp->setCounter(offset);
#endif
return bp;
}
void L4SysExperiment::doMemoryInjection(int address, int bit)
bool L4SysExperiment::doMemoryInjection(int address, int bit)
{
MemoryManager& mm = simulator.getMemoryManager();
// XXX: evil, but I need to bail out if memory access is invalid
host_address_t addr = reinterpret_cast<BochsMemoryManager*>(&mm)->guestToHost(address);
if (addr == (host_address_t)ADDR_INV)
return false;
byte_t data = mm.getByte(address);
byte_t newdata = data ^ (1 << bit);
mm.setByte(address, newdata);
log << "[" << std::hex << address << "] " << (int)data
<< " -> " << (int)newdata << std::endl;
return true;
}
@ -571,15 +648,21 @@ bool L4SysExperiment::run()
#if PREPARATION_STEP == 1
// STEP 1: run until interesting function starts, and save state
// -> needs L4SYS_ADDRESS_SPACE, because it runs until L4SYS_FUNC_ENTRY
startAndSaveInitState(new BPSingleListener(0, L4SYS_ADDRESS_SPACE));
#elif PREPARATION_STEP == 2
// STEP 2: determine instructions executed
collectInstructionTrace(new BPSingleListener(0, L4SYS_ADDRESS_SPACE));
collectInstructionTrace(new BPSingleListener(0, ANY_ADDR));
#elif PREPARATION_STEP == 3
// STEP 3: determine the output of a "golden run"
// -> golden run needs L4SYS_ADDRESS_SPACE as it breaks on
// L4SYS_FUNC_EXIT
goldenRun(new BPSingleListener(0, L4SYS_ADDRESS_SPACE));
#elif PREPARATION_STEP == 4
CR3run(new BPSingleListener(0));
#elif PREPARATION_STEP == 0
// LAST STEP: The actual experiment.
validatePrerequisites();
@ -626,15 +709,25 @@ bool L4SysExperiment::run()
fail::BaseListener *go = waitIOOrOther(true);
assert(go == bp);
log << "Hit BP. Start time " << now << ", new time " << simulator.getTimerTicks()
log << "Hit BP @ " << hex << bp->getTriggerInstructionPointer() << " " << bp->getWatchInstructionPointer()
<< " Start time " << now << ", new time " << simulator.getTimerTicks()
<< ", diff = " << simulator.getTimerTicks() - now << std::endl;
#if L4SYS_FILTER_INSTRUCTIONS
assert(bp->getTriggerInstructionPointer() == bp->getWatchInstructionPointer());
#endif
result->set_injection_ip(bp->getTriggerInstructionPointer());
if (exp_type == param->msg.MEM) {
result->set_bit_offset(bit);
doMemoryInjection(param->msg.fsppilot().data_address(), bit);
log << "injection addr: "
<< std::hex << param->msg.fsppilot().data_address()
<< std::endl;
result->set_injection_address(param->msg.fsppilot().data_address());
if (!doMemoryInjection(param->msg.fsppilot().data_address(), bit))
{
terminateWithError("invalid mem access", 51, result);
}
} else if (exp_type == param->msg.GPRFLIP) {
int reg = (param->msg.fsppilot().data_address() >> 4) + 1;
result->set_register_offset(static_cast<L4SysProtoMsg_RegisterType>(reg));
@ -649,9 +742,18 @@ bool L4SysExperiment::run()
unsigned instr_left = L4SYS_TOTINSTR - instr_offset; // XXX offset is in NUMINSTR, TOTINSTR is higher
BPSingleListener ev_incomplete(ANY_ADDR, L4SYS_ADDRESS_SPACE);
ev_incomplete.setCounter(instr_left);
/*
* Use hard-coded value for incomplete counter. We are currently looking at short-running pieces
* of code. This means that in the error case, where a lot of data is still to be printed to serial
* line, the benchmark does not complete this within <short-time> * <1.x> cycles. Instead, we use
* a frame large enough to catch some more output even at the end of a run.
*/
ev_incomplete.setCounter(2000000);
simulator.addListener(&ev_incomplete);
/*
* This timeout will always be at least one second - see calculateTimeout()
*/
TimerListener ev_timeout(calculateTimeout(instr_left));
simulator.addListener(&ev_timeout);
log << "continue... (" << simulator.getListenerCount()

View File

@ -8,6 +8,8 @@
#include "util/Logger.hpp"
#include "sal/Listener.hpp"
#include "l4sys.pb.h"
class L4SysExperimentData;
/**
@ -107,12 +109,13 @@ private:
/**
* Send back the experiment parameter set with a description of the error.
*/
void terminateWithError(std::string details, int reason);
void terminateWithError(std::string details, int reason, L4SysProtoMsg_Result*);
/**
* Run until L4SYS_FUNC_ENTRY and save state (experiment preparation,
* phase 1)
*/
void startAndSaveInitState(fail::BPSingleListener* bp);
void CR3run(fail::BPSingleListener *bp);
/**
* Collect list of executed instructions, considering instruction
* filtering if configured (experiment preparation, phase 2).
@ -153,7 +156,7 @@ private:
/**
* Perform memory bit flip at (address, bit).
*/
void doMemoryInjection(int address, int bit);
bool doMemoryInjection(int address, int bit);
/**
* Perform register bit flip in the specified (register, bit)

View File

@ -5,17 +5,24 @@
#define MAX_INSTR_BYTES 15
// the bounds of the program (space, instructions and time)
#define L4SYS_ADDRESS_SPACE 0x1fd4c000
// client
#define L4SYS_ADDRESS_SPACE 0x1fd4c000
// master
//#define L4SYS_ADDRESS_SPACE_TRACE 0x1fd4c000
// vcpu task
#define L4SYS_ADDRESS_SPACE_TRACE 0x1fd5a000
//#define L4SYS_ADDRESS_SPACE_TRACE L4SYS_ADDRESS_SPACE
// FUNC_{ENTRY,EXIT} specifies the range that needs to
// be captured to log program output properly
#define L4SYS_FUNC_ENTRY 0x01000350
#define L4SYS_FUNC_EXIT 0x010004bd
#define L4SYS_FUNC_ENTRY 0x60002052
//#define L4SYS_FUNC_ENTRY 0x10000
#define L4SYS_FUNC_EXIT 0x600020ae
// FILTER_{ENTRY,EXIT} specifies the range that injections
// should be carried out on (should be a subset of the above)
// and only works with FILTER_INSTRUCTIONS turned on
#define L4SYS_FILTER_ENTRY 0x0100042e
#define L4SYS_FILTER_EXIT 0x01000434
#define L4SYS_FILTER_ENTRY 0x60002067
#define L4SYS_FILTER_EXIT 0x60002075
// select instruction filtering
// XXX: this should be always on and the code should be
@ -24,8 +31,8 @@
#define L4SYS_FILTER_INSTRUCTIONS 1
// kernel: 2377547, userland: 79405472
#define L4SYS_NUMINSTR 2988
#define L4SYS_TOTINSTR 67811
#define L4SYS_NUMINSTR 141
#define L4SYS_TOTINSTR 154205
#define L4SYS_BOCHS_IPS 5000000
// several file names used
@ -38,6 +45,6 @@
// flags
// 0 - preparation complete
// >0 - next step to execute
#define PREPARATION_STEP 2
#define PREPARATION_STEP 0
#endif // __L4SYS_EXPERIMENT_INFO_HPP__