diff --git a/core/SAL/bochs/BochsController.cc b/core/SAL/bochs/BochsController.cc index e22ad5bb..a03c6a6a 100644 --- a/core/SAL/bochs/BochsController.cc +++ b/core/SAL/bochs/BochsController.cc @@ -92,10 +92,14 @@ void BochsController::dbgEnableInstrPtrOutput(unsigned regularity, std::ostream* void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_space) { - #ifdef DEBUG +#if 0 + //the original code - performs magnitudes worse than + //the code below and is responsible for most (~87 per cent) + //of the slowdown of FailBochs +#ifdef DEBUG if(m_Regularity != 0 && ++m_Counter % m_Regularity == 0) (*m_pDest) << "0x" << std::hex << instrPtr; - #endif +#endif // Check for active breakpoint-events: fi::EventList::iterator it = m_EvList.begin(); while(it != m_EvList.end()) @@ -113,6 +117,37 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp it++; } m_EvList.fireActiveEvents(); +#endif + //this code is highly optimised for the average case, so it me appear a bit ugly + bool do_fire = false; + unsigned i = 0; + fi::BufferCache *buffer_cache = m_EvList.getBPBuffer(); + while(i < buffer_cache->get_count()) { + fi::BPEvent *pEvBreakpt = buffer_cache->get(i); + if(pEvBreakpt->isMatching(instrPtr, address_space)) { + pEvBreakpt->setTriggerInstructionPointer(instrPtr); + + //transition to STL: find the element we are working on in the Event List + fi::EventList::iterator it = std::find(m_EvList.begin(), m_EvList.end(), pEvBreakpt); + it = m_EvList.makeActive(it); + //find out how much elements need to be skipped to get in sync again + //(should be one or none, the loop is just to make sure) + for(unsigned j = i; j < buffer_cache->get_count(); j++) { + if(buffer_cache->get(j) == (*it)) { + i = j; + break; + } + } + // we now know we need to fire the active events - usually we do not have to + do_fire = true; + // "i" has already been set to the next element (by calling + // makeActive()): + continue; // -> skip loop increment + } + i++; + } + if(do_fire) + m_EvList.fireActiveEvents(); // Note: SimulatorController::onBreakpointEvent will not be invoked in this // implementation. } diff --git a/core/SAL/bochs/IOPortCom.ah b/core/SAL/bochs/IOPortCom.ah index 3c9e0296..ffeac6b5 100644 --- a/core/SAL/bochs/IOPortCom.ah +++ b/core/SAL/bochs/IOPortCom.ah @@ -14,7 +14,7 @@ aspect IOPortCom { // ATM only capturing bytewise output (most common, I suppose) - pointcut outInstruction() = "% ...::bx_cpu_c::OUT_DXAL%(...)"; + pointcut outInstruction() = "% ...::bx_cpu_c::OUT_DXAL(...)"; advice execution (outInstruction()) : after () { @@ -23,7 +23,7 @@ aspect IOPortCom sal::simulator.onIOPortEvent(rAL, rDX, true); } - pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX%(...)"; + pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX(...)"; advice execution (inInstruction()) : after () { diff --git a/core/controller/BufferCache.cc b/core/controller/BufferCache.cc new file mode 100644 index 00000000..75e63816 --- /dev/null +++ b/core/controller/BufferCache.cc @@ -0,0 +1,74 @@ +#include "BufferCache.hpp" +#include "Event.hpp" + +namespace fi { + +template int BufferCache::add(T val) { + size_t new_size = get_count() + 1; + size_t new_last_index = get_count(); + + int res = reallocate_buffer(new_size); + if (res == 0) { + set(new_last_index, val); + } + + return res; +} + +template int BufferCache::remove(T val) { + bool do_remove = false; + for (size_t i = 0; i < get_count(); i++) { + if (get(i) == val) { + do_remove = true; + } + if (do_remove) { + if (i > get_count() - 1) { + set(i, get(i + 1)); + } + } + } + + int res = 0; + if (do_remove) { + size_t new_size = get_count() - 1; + res = reallocate_buffer(new_size); + } + + return res; +} + +template void BufferCache::clear() { + set_count(0); + free(m_Buffer); + m_Buffer = NULL; +} + +template int BufferCache::erase(int idx) { + for (size_t i = idx; i < get_count() - 1; i++) { + set(i, get(i + 1)); + } + + size_t new_size = get_count() - 1; + if (reallocate_buffer(new_size) != 0) + return -1; + return idx; +} + +template int BufferCache::reallocate_buffer(size_t new_size) { + if (new_size == 0) { + clear(); + return 0; + } + void *new_buffer = realloc(m_Buffer, new_size * sizeof(T)); + if (new_buffer == NULL) + return 10; + m_Buffer = static_cast(new_buffer); + set_count(new_size); + return 0; +} + +//declare whatever instances of the template +//you are going to use here: +template class BufferCache; + +} /* namespace fi */ diff --git a/core/controller/BufferCache.hpp b/core/controller/BufferCache.hpp new file mode 100644 index 00000000..499b5e32 --- /dev/null +++ b/core/controller/BufferCache.hpp @@ -0,0 +1,82 @@ +#ifndef __BUFFERCACHE_HPP__ +#define __BUFFERCACHE_HPP__ + +#include + +namespace fi { + +/** + * \class BufferCache + * + * \brief A simple dynamic array + * + * This class is intended to serve as a kind of cache for the entirely STL-based, + * untyped and therefore quite slow event handling mechanism of Fail*. + * To keep the code easily readable, the buffer management methods + * are less performant than the could be (remove() and erase() have linear complexity). + */ +template class BufferCache { +public: + BufferCache() + : m_Buffer(NULL), m_Buffer_count(0) {} + ~BufferCache() {} + /** + * Add an element to the array. The object pointed to remains untouched. + * @param val the element to add + * @returns 0 if successful, an error code otherwise (ATM only 10 if malloc() fails) + */ + int add(T val); + /** + * Remove an element from the array. The object pointed to remains untouched. + * @param val the element to remove + * @returns 0 if successful, an error code otherwise (ATM only 10 if malloc() fails) + */ + int remove(T val); + /** + * Remove an element at a specific position. The object pointed to remains untouched. + * @param val the element to remove + * @returns a pointer to the given element's successor if successful, -1 otherwise + */ + int erase(int i); + /** + * Clears the array, removing all elements. The objects pointed to remain untouched. + */ + void clear(); + /** + * Retrieve an element from the array. Should be inlined. + * @param idx the position to retrieve the element from + * @returns the element at the given position + */ + inline T get(size_t idx) { return m_Buffer[idx]; } + /** + * Set an element at a given position. Should be inlined. + * @param idx the position to change an element at + * @param val the new value of the given element + */ + inline void set(size_t idx, T val) { m_Buffer[idx] = val; } + /** + * Retrieves the current length of the array. Should be inlined. + * @returns the array length + */ + inline size_t get_count() { return m_Buffer_count; } +protected: + /** + * Changes the current length of the array. Should be inlined. + * @param new_count the new array length + */ + inline void set_count(size_t new_count) { m_Buffer_count = new_count; } + /** + * Reallocates the buffer. This implementation is extremely primitive, + * but since the amount of entries is small, + * this will not be significant, hopefully. Should be inlined. + * @param new_size the new number of elements in the array + * @returns 0 if successful, an error code otherwise (ATM only 10 if malloc() fails) + */ + inline int reallocate_buffer(size_t new_size); +private: + T *m_Buffer; + size_t m_Buffer_count; +}; + +} /* namespace fi */ +#endif /* BUFFERCACHE_H_ */ diff --git a/core/controller/CMakeLists.txt b/core/controller/CMakeLists.txt index 0c1ef0c4..b48e86ce 100644 --- a/core/controller/CMakeLists.txt +++ b/core/controller/CMakeLists.txt @@ -1,4 +1,5 @@ set(SRCS + BufferCache.cc CampaignManager.cc CoroutineManager.cc Event.cc diff --git a/core/controller/EventList.cc b/core/controller/EventList.cc index f2ce2734..e2a996b8 100644 --- a/core/controller/EventList.cc +++ b/core/controller/EventList.cc @@ -12,6 +12,10 @@ EventId EventList::add(BaseEvent* ev, ExperimentFlow* pExp) // a zero counter does not make sense assert(ev->getCounter() != 0); ev->setParent(pExp); // event is linked to experiment flow + + BPEvent *bp_ev; + if((bp_ev = dynamic_cast(ev)) != NULL) + m_Bp_cache.add(bp_ev); m_BufferList.push_back(ev); return (ev->getId()); } @@ -27,6 +31,7 @@ void EventList::remove(BaseEvent* ev) sal::simulator.onEventDeletion(*it); for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++) sal::simulator.onEventDeletion(*it); + m_Bp_cache.clear(); m_BufferList.clear(); // all remaining active events must not fire anymore m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end()); @@ -36,6 +41,10 @@ void EventList::remove(BaseEvent* ev) // * if ev in m_FireList, copy to m_DeleteList } else { sal::simulator.onEventDeletion(ev); + + BPEvent *bp_ev; + if((bp_ev = dynamic_cast(ev)) != NULL) + m_Bp_cache.remove(bp_ev); m_BufferList.remove(ev); firelist_t::const_iterator it = std::find(m_FireList.begin(), m_FireList.end(), ev); @@ -61,6 +70,10 @@ EventList::iterator EventList::m_remove(iterator it, bool skip_deletelist) sal::simulator.onEventDeletion(*it); m_DeleteList.push_back(*it); } + + BPEvent *bp_ev; + if((bp_ev = dynamic_cast(*it)) != NULL) + m_Bp_cache.remove(bp_ev); return (m_BufferList.erase(it)); } @@ -80,6 +93,7 @@ void EventList::remove(ExperimentFlow* flow) for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++) sal::simulator.onEventDeletion(*it); // invoke event handler + m_Bp_cache.clear(); m_BufferList.clear(); } else { // remove all events corresponding to a specific experiment ("flow"): for (bufferlist_t::iterator it = m_BufferList.begin(); diff --git a/core/controller/EventList.hpp b/core/controller/EventList.hpp index 9e7f53c1..c13e1550 100644 --- a/core/controller/EventList.hpp +++ b/core/controller/EventList.hpp @@ -7,6 +7,7 @@ #include #include "Event.hpp" +#include "BufferCache.hpp" namespace fi { @@ -53,6 +54,7 @@ class EventList deletelist_t m_DeleteList; //!< the deleted events (used temporarily) // TODO: Hashing? BaseEvent* m_pFired; //!< the recently fired Event-object + BufferCache m_Bp_cache; public: /** * The iterator of this class used to loop through the list of @@ -180,6 +182,11 @@ class EventList * TODO: Improve naming (instead of "fireActiveEvents")? */ void fireActiveEvents(); + /** + * Retrieves the BPEvent buffer cache. + * @returns the buffer cache + */ + inline BufferCache *getBPBuffer() { return &m_Bp_cache; } }; }; // end-of-namespace: fi diff --git a/core/experiments/l4sys/campaign.cc b/core/experiments/l4sys/campaign.cc index 03379995..fcc231f8 100644 --- a/core/experiments/l4sys/campaign.cc +++ b/core/experiments/l4sys/campaign.cc @@ -33,7 +33,7 @@ bool L4SysCampaign::run() int count = 0; //iterate over one register for (int bit_offset = 0; bit_offset < 1; ++bit_offset) { - for (int instr_offset = 0; instr_offset < COOL_ECC_NUMINSTR; ++instr_offset) { + for (int instr_offset = 0; instr_offset < L4SYS_NUMINSTR; ++instr_offset) { L4SysExperimentData *d = new L4SysExperimentData; d->msg.set_instr_offset(instr_offset); d->msg.set_bit_offset(bit_offset); diff --git a/core/experiments/l4sys/experiment.cc b/core/experiments/l4sys/experiment.cc index 5c8ea9d9..977d94eb 100644 --- a/core/experiments/l4sys/experiment.cc +++ b/core/experiments/l4sys/experiment.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -33,7 +34,7 @@ using std::endl; char const * const state_folder = "l4sys.state"; char const * const instr_list_fn = "ip.list"; char const * const golden_run_fn = "golden.out"; -sal::address_t const aspace = 0x1fd44000; +sal::address_t const aspace = 0x01e00000; std::string output; std::vector instr_list; std::string golden_run; @@ -80,14 +81,12 @@ bool L4SysExperiment::run() { log << "startup" << endl; - //FIXME: this is a race condition: - //only one L4SysExperiment instance should execute this block - //at a time +#ifdef PREPARE_EXPERIMENT struct stat teststruct; // STEP 1: run until interesting function starts, and save state if (stat(state_folder, &teststruct) == -1) { - bp.setWatchInstructionPointer(COOL_ECC_FUNC_ENTRY); + bp.setWatchInstructionPointer(L4SYS_FUNC_ENTRY); sal::simulator.addEventAndWait(&bp); log << "test function entry reached, saving state" << endl; @@ -112,7 +111,7 @@ bool L4SysExperiment::run() { std::ofstream instr_list_file(instr_list_fn); instr_list_file << std::hex; bp.setWatchInstructionPointer(fi::ANY_ADDR); - while (bp.getTriggerInstructionPointer() != COOL_ECC_CALCDONE) { + while (bp.getTriggerInstructionPointer() != L4SYS_FUNC_EXIT) { sal::simulator.addEventAndWait(&bp); //short sanity check sal::address_t curr_instr = bp.getTriggerInstructionPointer(); @@ -146,7 +145,7 @@ bool L4SysExperiment::run() { sal::simulator.addSuppressedInterrupt(32); std::ofstream golden_run_file(golden_run_fn); - bp.setWatchInstructionPointer(COOL_ECC_CALCDONE); + bp.setWatchInstructionPointer(L4SYS_FUNC_EXIT); bp.setCounter(times_run); sal::simulator.addEvent(&bp); fi::BaseEvent* ev = waitIOOrOther(true); @@ -183,10 +182,10 @@ bool L4SysExperiment::run() { output.reserve(flen); } - //end of critical section +#endif // STEP 4: The actual experiment. - for (int i = 0; i < COOL_ECC_NUMINSTR; i++) { + for (int i = 0; i < 1/*L4SYS_NUMINSTR*/; i++) { log << "restoring state" << endl; sal::simulator.restore(state_folder); @@ -195,7 +194,7 @@ bool L4SysExperiment::run() { if (!m_jc.getParam(param)) { log << "Dying." << endl; // communicate that we were told to die - sal::simulator.terminate(1); // "return (false);" ? + sal::simulator.terminate(1); } int id = param.getWorkloadID(); int instr_offset = param.msg.instr_offset(); @@ -239,10 +238,10 @@ bool L4SysExperiment::run() { } // aftermath - fi::BPSingleEvent ev_done(COOL_ECC_CALCDONE, aspace); + fi::BPSingleEvent ev_done(L4SYS_FUNC_EXIT, aspace); ev_done.setCounter(times_run); sal::simulator.addEvent(&ev_done); - const unsigned instr_run = times_run * COOL_ECC_NUMINSTR; + const unsigned instr_run = times_run * L4SYS_NUMINSTR; fi::BPSingleEvent ev_timeout(fi::ANY_ADDR, aspace); ev_timeout.setCounter(instr_run + 3000); sal::simulator.addEvent(&ev_timeout); @@ -264,7 +263,7 @@ bool L4SysExperiment::run() { if (ev == &ev_done) { if (strcmp(output.c_str(), golden_run.c_str()) == 0) { log << std::dec << "Result DONE" << endl; - param.msg.set_resulttype(param.msg.CALCDONE); + param.msg.set_resulttype(param.msg.DONE); } else { log << std::dec << "Result WRONG" << endl; param.msg.set_resulttype(param.msg.WRONG); diff --git a/core/experiments/l4sys/experimentInfo.hpp b/core/experiments/l4sys/experimentInfo.hpp index ac012734..a1df6223 100644 --- a/core/experiments/l4sys/experimentInfo.hpp +++ b/core/experiments/l4sys/experimentInfo.hpp @@ -3,9 +3,10 @@ // FIXME autogenerate this -#define COOL_ECC_FUNC_ENTRY 0x1007cd0 -#define COOL_ECC_CALCDONE 0x1007d3a -#define COOL_ECC_NUMINSTR 3166 -#define HEADLESS_EXPERIMENT +#define L4SYS_FUNC_ENTRY 0x1007cd0 +#define L4SYS_FUNC_EXIT 0x1007d3a +#define L4SYS_NUMINSTR 3184 +//#define HEADLESS_EXPERIMENT +#define PREPARE_EXPERIMENT #endif diff --git a/core/experiments/l4sys/l4sys.proto b/core/experiments/l4sys/l4sys.proto index dc56f376..f8d50bbc 100644 --- a/core/experiments/l4sys/l4sys.proto +++ b/core/experiments/l4sys/l4sys.proto @@ -6,7 +6,7 @@ message L4SysProtoMsg { // results // make these optional to reduce overhead for server->client communication enum ResultType { - CALCDONE = 1; + DONE = 1; TIMEOUT = 2; TRAP = 3; INTR = 4; diff --git a/core/jobserver/JobServer.cc b/core/jobserver/JobServer.cc index af920065..6dd45d3f 100644 --- a/core/jobserver/JobServer.cc +++ b/core/jobserver/JobServer.cc @@ -45,7 +45,7 @@ ExperimentData *JobServer::getDone() && m_doneJobs.Size() == 0) { // FRICKEL workaround sleep(1); - ExperimentData *exp; + ExperimentData *exp = NULL; if (m_doneJobs.Dequeue_nb(exp)) { return exp; }