From 0cb6b3949030b8204cb76c4aa687770d4b4cefce Mon Sep 17 00:00:00 2001 From: adrian Date: Tue, 25 Sep 2012 10:10:02 +0000 Subject: [PATCH] Aspect-based implementation of fast breakpoints added (optional). git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1685 8c4709b5-6ec9-48aa-a5cd-a96041d1645a --- src/core/sal/CMakeLists.txt | 3 + src/core/sal/Event.hpp | 4 +- src/core/sal/Listener.cc | 26 +-- src/core/sal/Listener.hpp | 75 +++++-- src/core/sal/ListenerManager.cc | 201 ++++++++++++++---- src/core/sal/ListenerManager.hpp | 67 ++++-- src/core/sal/SimulatorController.cc | 14 +- src/core/sal/SimulatorController.hpp | 21 +- src/core/sal/bochs/BochsController.cc | 3 +- src/core/sal/bochs/BochsController.hpp | 4 +- src/core/sal/bochs/Breakpoints.ah | 2 +- src/core/sal/perf/BreakpointBuffer.cc | 23 ++ src/core/sal/perf/BreakpointBuffer.hpp | 157 ++++++++++++++ .../sal/perf/BreakpointControllerSlice.ah | 49 +++++ src/core/sal/perf/BreakpointManagerSlice.ah | 56 +++++ src/core/sal/perf/BufferInterface.hpp | 42 ++++ src/core/sal/perf/FastBreakpoints.ah | 85 ++++++++ 17 files changed, 725 insertions(+), 107 deletions(-) create mode 100644 src/core/sal/perf/BreakpointBuffer.cc create mode 100644 src/core/sal/perf/BreakpointBuffer.hpp create mode 100644 src/core/sal/perf/BreakpointControllerSlice.ah create mode 100644 src/core/sal/perf/BreakpointManagerSlice.ah create mode 100644 src/core/sal/perf/BufferInterface.hpp create mode 100644 src/core/sal/perf/FastBreakpoints.ah diff --git a/src/core/sal/CMakeLists.txt b/src/core/sal/CMakeLists.txt index 2221c11b..576e54d4 100644 --- a/src/core/sal/CMakeLists.txt +++ b/src/core/sal/CMakeLists.txt @@ -6,6 +6,7 @@ if(BUILD_BOCHS) SALConfig.cc Register.cc SimulatorController.cc + perf/BreakpointBuffer.cc bochs/BochsController.cc bochs/BochsListener.cc ) @@ -29,6 +30,7 @@ elseif(BUILD_OVP) SALConfig.cc Register.cc SimulatorController.cc + perf/BreakpointBuffer.cc ${VARIANT}/OVPController.cc ) elseif(BUILD_QEMU) @@ -38,6 +40,7 @@ elseif(BUILD_QEMU) ListenerManager.cc SALConfig.cc Register.cc + perf/BreakpointBuffer.cc SimulatorController.cc qemu/QEMUController.cc qemu/QEMUListener.cc diff --git a/src/core/sal/Event.hpp b/src/core/sal/Event.hpp index 449b6087..6ed9de60 100644 --- a/src/core/sal/Event.hpp +++ b/src/core/sal/Event.hpp @@ -201,8 +201,10 @@ public: /** * Creates a new \c InterruptEvent. * @param nmi the new NMI (non maskable interrupt) flag state + * @param triggerNum system and type specific number identifying the requestet + * "trouble-type" */ - InterruptEvent(bool nmi) : m_IsNMI(nmi) { } + InterruptEvent(bool nmi, int triggerNum) : TroubleEvent(triggerNum), m_IsNMI(nmi) { } /** * Returns \c true if the interrupt is non maskable, \c false otherwise. * @return \c true if NMI flag is set, \c false otherwise diff --git a/src/core/sal/Listener.cc b/src/core/sal/Listener.cc index dfbba2e8..8b4660f9 100644 --- a/src/core/sal/Listener.cc +++ b/src/core/sal/Listener.cc @@ -3,10 +3,10 @@ namespace fail { -bool TroubleListener::isMatching(unsigned troubleNum) const +bool TroubleListener::isMatching(const TroubleEvent* pEv) const { for (unsigned i = 0; i < m_WatchNumbers.size(); i++) { - if (m_WatchNumbers[i] == troubleNum || + if (m_WatchNumbers[i] == pEv->getTriggerNumber() || m_WatchNumbers[i] == ANY_TRAP) return true; } @@ -34,13 +34,13 @@ bool TroubleListener::addWatchNumber(unsigned troubleNumber) return true; } -bool MemAccessListener::isMatching(address_t addr, size_t width, - MemAccessEvent::access_type_t accesstype) const +bool MemAccessListener::isMatching(const MemAccessEvent* pEv) const { - if (!(m_WatchType & accesstype)) { + if (!(m_WatchType & pEv->getTriggerAccessType())) { return false; } else if (m_WatchAddr != ANY_ADDR - && (m_WatchAddr >= addr + width || m_WatchAddr + m_WatchWidth <= addr)) { + && (m_WatchAddr >= pEv->getTriggerAddress() + pEv->getTriggerWidth() + || m_WatchAddr + m_WatchWidth <= pEv->getTriggerAddress())) { return false; } return true; @@ -59,20 +59,20 @@ void BPRangeListener::setWatchInstructionPointerRange(address_t start, address_t m_WatchEndAddr = end; } -bool BPRangeListener::isMatching(address_t addr, address_t aspace) const +bool BPRangeListener::isMatching(const BPEvent* pEv) const { - if (!aspaceIsMatching(aspace)) + if (!aspaceIsMatching(pEv->getCR3())) return false; - if ((m_WatchStartAddr != ANY_ADDR && addr < m_WatchStartAddr) || - (m_WatchEndAddr != ANY_ADDR && addr > m_WatchEndAddr)) + if ((m_WatchStartAddr != ANY_ADDR && pEv->getTriggerInstructionPointer() < m_WatchStartAddr) || + (m_WatchEndAddr != ANY_ADDR && pEv->getTriggerInstructionPointer() > m_WatchEndAddr)) return false; return true; } -bool GenericBPSingleListener::isMatching(address_t addr, address_t aspace) const +bool GenericBPSingleListener::isMatching(const BPEvent* pEv) const { - if (aspaceIsMatching(aspace)) { - if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr) + if (aspaceIsMatching(pEv->getCR3())) { + if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == pEv->getTriggerInstructionPointer()) return true; } return false; diff --git a/src/core/sal/Listener.hpp b/src/core/sal/Listener.hpp index f538d077..6b0fcb1d 100644 --- a/src/core/sal/Listener.hpp +++ b/src/core/sal/Listener.hpp @@ -9,15 +9,12 @@ #include "SALConfig.hpp" #include "Event.hpp" +#include "perf/BufferInterface.hpp" namespace fail { class ExperimentFlow; -// FIXME(?): Maybe it suffices to provide a "setEventData" method in order to -// set the event data at once. (Consequently, all actual setters for -// attributes of the event-data objects could be removed.) - /** * \class BaseListener * This is the base class for all listener types. @@ -28,8 +25,11 @@ protected: unsigned int m_OccCounter; //!< listener fires when 0 is reached unsigned int m_OccCounterInit; //!< initial value for m_OccCounter ExperimentFlow* m_Parent; //!< this listener belongs to experiment m_Parent + index_t m_Loc; //!< location of this listener object within the buffer-list + PerfBufferBase* m_Home; //!< ptr to performance buffer-list impl. or NULL of not existing public: - BaseListener() : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL) + BaseListener() + : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL), m_Loc(INVALID_INDEX), m_Home(NULL) { } virtual ~BaseListener() { } // FIXME remove from queues /** @@ -56,7 +56,6 @@ public: * corresponding coroutine is toggled. */ virtual void onTrigger() { } - // TODO: Hier noch ne neue Methode oder reicht es, die Semantik von onTrigger umzudef.? /** * Decreases the listener counter by one. When this counter reaches zero, the * listener will be triggered. @@ -71,7 +70,7 @@ public: * Returns the current counter value. * @return the counter value */ - unsigned int getCounter() const { return (m_OccCounter); } + unsigned int getCounter() const { return m_OccCounter; } /** * Resets the listener counter to its default value, or the last * value that was set through \c setCounter(). @@ -81,13 +80,42 @@ public: * Returns the parent experiment of this listener (context). * If the listener was created temporarily or wasn't linked to a context, * the return value is \c NULL (unknown identity). + * @return the listener's parent ptr */ - ExperimentFlow* getParent() const { return (m_Parent); } + ExperimentFlow* getParent() const { return m_Parent; } /** * Sets the parent (context) of this listener. The default context * is \c NULL (unknown identity). + * @param pFlow the new parent ptr or \c NULL (= unknown identity) */ void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; } + /** + * Sets the location of this listener within the buffer-list (which itself + * part of the buffer-list). + * @param idx the new index or \c INVALID_INDEX if not managed by (= added to) + * the ListenerManager. + */ + void setLocation(index_t idx) { m_Loc = idx; } + /** + * Returns the current location of this listener in the buffer-list. See + * \c setLocation for further details. + * @return the location (index) within the buffer-list + */ + index_t getLocation() { return m_Loc; } + /** + * Sets the performance buffer(-list), this listener is stored in. Upon calling + * this method, the listener should already be stored in the buffer \c pHome. + * @param pHome a pointer to the (fast) performance buffer-list, specific to this + * event type + */ + void setPerformanceBuffer(PerfBufferBase* pHome) { m_Home = pHome; } + /** + * Retrieves the pointer to the performance buffer-list implementation for + * this listener. The returned value may be \c NULL in case of a missing + * performance implementation. + * @return the ptr to the performance buffer-list, or \c NULL if not existing + */ + PerfBufferBase* getPerformanceBuffer() { return m_Home; } }; // ---------------------------------------------------------------------------- // Specialized listeners: @@ -125,10 +153,6 @@ public: * Checks whether a given address space is matching. */ bool aspaceIsMatching(address_t address_space = ANY_ADDR) const; - /** - * Checks whether a given address is matching. - */ - virtual bool isMatching(address_t addr = 0, address_t address_space = ANY_ADDR) const = 0; /** * Returns the instruction pointer that triggered this listener. */ @@ -138,6 +162,12 @@ public: * be used by experiment code. */ void setTriggerInstructionPointer(address_t iptr) { m_Data.setTriggerInstructionPointer(iptr); } + /** + * Checks whether a given address is matching. + * @param pEv Breakpoint event data, retrieved by the simulator + * @return \c true if matching, \c false otherwise + */ + virtual bool isMatching(const BPEvent* pEv) const = 0; }; /** @@ -174,12 +204,11 @@ public: */ void setWatchInstructionPointer(address_t iptr) { m_WatchInstrPtr = iptr; } /** - * Checks whether a given address is matching. - * @param addr address to check - * @param address_space address space information + * Checks whether a given address (encapsulated in \c pEv) is matching. + * @param pEv address to check, including address space information * @return \c true if address is within the range, \c false otherwise */ - bool isMatching(address_t addr, address_t address_space) const; + bool isMatching(const BPEvent* pEv) const; }; /** @@ -219,7 +248,7 @@ public: * @return \c true if address is within the range (represented by * \c this), \c false otherwise */ - bool isMatching(address_t addr, address_t address_space) const; + bool isMatching(const BPEvent* pEv) const; }; /** @@ -260,10 +289,6 @@ public: * Sets the width of the memory area being watched (defaults to 1). */ void setWatchWidth(size_t width) { m_WatchWidth = width; } - /** - * Checks whether a given physical memory access is matching. - */ - bool isMatching(address_t addr, size_t width, MemAccessEvent::access_type_t accesstype) const; /** * Returns the specific physical memory address that actually triggered the * listener. @@ -310,6 +335,10 @@ public: * this listener watches. Should not be used by experiment code. */ MemAccessEvent::access_type_t getWatchAccessType() const { return m_WatchType; } + /** + * Checks whether a given physical memory access is matching. + */ + bool isMatching(const MemAccessEvent* pEv) const; }; /** @@ -375,7 +404,7 @@ public: /** * Checks whether a given interrupt-/trap-number is matching. */ - bool isMatching(unsigned troubleNum) const; + bool isMatching(const TroubleEvent* pEv) const; /** * Sets the specific interrupt-/trap-number that actually triggered * the listener. Should not be used by experiment code. @@ -488,7 +517,7 @@ public: * @param p The port number an I/O listener occured on * @param out True if the communication was outbound, false otherwise */ - bool isMatching(unsigned p, bool out) const { return out = isOutListener() && p == getPort(); } + bool isMatching(unsigned p, bool out) const { return out == isOutListener() && p == getPort(); } /** * Tells you if this listener is capturing outbound communication (inbound if false) */ diff --git a/src/core/sal/ListenerManager.cc b/src/core/sal/ListenerManager.cc index 9607206d..265a4699 100644 --- a/src/core/sal/ListenerManager.cc +++ b/src/core/sal/ListenerManager.cc @@ -5,7 +5,7 @@ namespace fail { -void ListenerManager::addToCaches(BaseListener *li) +void ListenerManager::addToCaches(BaseListener *li) // FIXME: REMOVE { BPListener *bps_li; if ((bps_li = dynamic_cast(li)) != NULL) @@ -16,7 +16,7 @@ void ListenerManager::addToCaches(BaseListener *li) m_Io_cache.add(io_li); } -void ListenerManager::removeFromCaches(BaseListener *li) +void ListenerManager::removeFromCaches(BaseListener *li) // FIXME: REMOVE { BPListener *bpr_li; if ((bpr_li = dynamic_cast(li)) != NULL) @@ -27,7 +27,7 @@ void ListenerManager::removeFromCaches(BaseListener *li) m_Io_cache.remove(io_li); } -void ListenerManager::clearCaches() +void ListenerManager::clearCaches() // FIXME: REMOVE { m_Bp_cache.clear(); m_Io_cache.clear(); @@ -36,38 +36,66 @@ void ListenerManager::clearCaches() void ListenerManager::add(BaseListener* li, ExperimentFlow* pExp) { assert(li != NULL && "FATAL ERROR: Listener (of base type BaseListener*) cannot be NULL!"); - // a zero counter does not make sense - assert(li->getCounter() != 0); + // A zero counter does not make sense + assert(li->getCounter() != 0 && "FATAL ERROR: Listener counter has already been zero!"); li->setParent(pExp); // listener is linked to experiment flow - - addToCaches(li); + // Ensure that the listener is not associated with any performance impl.: + li->setPerformanceBuffer(NULL); m_BufferList.push_back(li); + // Note: To keep the indices (within any perf-buffer-list) valid, we have to + // add new elements at the end of the vector. + index_t idx = m_BufferList.size()-1; + assert(m_BufferList[idx] == li && "FATAL ERROR: Invalid index after push_back() unexpected!"); + li->setLocation(idx); + addToCaches(li); // FIXME: REMOVE } void ListenerManager::remove(BaseListener* li) { - // possible cases: - // - li == 0 -> remove all listeners - // * clear m_BufferList - // * copy m_FireList to m_DeleteList + // Possible cases: + // - li == 0 -> remove all listeners for the current flow + // * Inform the listeners (call onDeletion) + // * Clear m_BufferList + // * Remove indices in corresponding perf. buffer-lists (if existing) + // * Copy m_FireList to m_DeleteList if (li == 0) { - for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++) - (*it)->onDeletion(); - for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++) - (*it)->onDeletion(); - clearCaches(); - m_BufferList.clear(); - // all remaining active listeners must not fire anymore + // We have to remove *all* indices in *all* (possibly added) performance implementations + // of matching listeners. + for (index_t i = 0; i < m_BufferList.size(); ) { + if (m_BufferList[i]->getParent() == simulator.m_Flows.getCurrent()) { + m_BufferList[i]->onDeletion(); + if(m_BufferList[i]->getPerformanceBuffer() != NULL) + m_BufferList[i]->getPerformanceBuffer()->remove(i); + m_remove(i); + // Inspect the element at m_BufferList[i] a 2nd time + // (this element has been updated by m_remove()). + } else { + ++i; + } + } + for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++) { + if ((*it)->getParent() == simulator.m_Flows.getCurrent()) { + (*it)->onDeletion(); + // Listeners in the fire-list have already been deleted in the buffer-list. + // Consequently, they must have been deleted in the perf. buffer-list, too. + // ==> no further processing required here + } + } + clearCaches(); // FIXME: REMOVE + // All remaining active listeners must not fire anymore m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end()); - // - li != 0 -> remove single listener - // * find/remove 'li' in 'm_BufferList' - // * if 'li' in 'm_FireList', copy to 'm_DeleteList' - } else { + // - li != 0 -> remove single listener (if added previously) + // * Inform the listeners (call onDeletion) + // * Remove the index in the perf. buffer-list (if existing) + // * Find/remove 'li' in 'm_BufferList' + // * If 'li' in 'm_FireList', copy to 'm_DeleteList' + } else if (li->getLocation() != INVALID_INDEX) { // Check if li hasn't been added previously (Q&D) li->onDeletion(); - - removeFromCaches(li); - m_BufferList.remove(li); + removeFromCaches(li); // FIXME: REMOVE + if (li->getPerformanceBuffer() != NULL) + li->getPerformanceBuffer()->remove(li->getLocation()); + m_remove(li->getLocation()); firelist_t::const_iterator it = std::find(m_FireList.begin(), m_FireList.end(), li); if (it != m_FireList.end()) { @@ -76,6 +104,48 @@ void ListenerManager::remove(BaseListener* li) } } +ExperimentFlow* ListenerManager::getExperimentOf(BaseListener* li) +{ + for (iterator it = begin(); it != end(); ++it) { + if (*it == li) + return (*it)->getParent(); + } + return NULL; +} + +void ListenerManager::m_remove(index_t idx) +{ + // Note: This operation has O(1) time complexity. It copies (aka "swaps") the + // trailing element "m_BufferList[m_BufferList.size()-1]" to the slot + // at "m_BufferList[idx]" and removes the last element (pop_back()). + if(idx == INVALID_INDEX) + ((BaseListener*)NULL)->setPerformanceBuffer(NULL); + + // Override the element to be deleted (= copy the last element to the slot + // of the element to be deleted) and update their attributes: + if (!m_BufferList.empty() && idx != INVALID_INDEX) { + m_BufferList[idx]->setPerformanceBuffer(NULL); + m_BufferList[idx]->setLocation(INVALID_INDEX); + // Do we have at least 2 elements (so that there *is* a trailing element + // to be used for swapping)? If idx == m_BufferList.size()-1, the last + // element should be removed so there is no need to swap: + if (m_BufferList.size() > 1 && idx != m_BufferList.size()-1) { + // Update the index of the element object itself: + m_BufferList[idx] = m_BufferList[m_BufferList.size()-1]; + index_t oldLoc = m_BufferList[idx]->getLocation(); + m_BufferList[idx]->setLocation(idx); + // Update the (unique) index within it's performance buffer-list (if existing): + PerfBufferBase* pBuf = m_BufferList[idx]->getPerformanceBuffer(); + if (pBuf != NULL) { + pBuf->remove(oldLoc); + pBuf->add(idx); + } + } + // (pop_back()'s behaviour is undefined if the vector is empty) + m_BufferList.pop_back(); // remove the last element + } +} + ListenerManager::iterator ListenerManager::m_remove(iterator it, bool skip_deletelist) { if (!skip_deletelist) { @@ -93,37 +163,68 @@ ListenerManager::iterator ListenerManager::m_remove(iterator it, bool skip_delet // BufferCache::remove() from m_remove(). // NOTE: in case the semantics of skip_deletelist change, please adapt the following lines - removeFromCaches(*it); + removeFromCaches(*it); // FIXME: REMOVE (incl. comments) } - return m_BufferList.erase(it); + // This has O(1) time complexity due to a underlying std::vector (-> random access iterator) + index_t dist = std::distance(begin(), it); + assert((*it)->getPerformanceBuffer() == NULL && + "FATAL ERROR: makeActive(iterator) cannot be called on listeners stored in a perf. \ + buff-list! Use makeActive(BaseListener*) instead!"); + // Remove the element from the buffer-list: + m_remove((*it)->getLocation()); + // Create a new iterator, pointing to the element after *it + // (the new iterator actually points to the same slot in the vector, but now + // this slot stores the element which has previously been stored in the last slot): + // This is required because the provided iterator "it" isn't valid anymore (due + // to the deletion of the last element within m_remove(index_t)). + + return begin() + dist; // O(1) + // Note: "begin() + dist" yields end() if dist is "large enough" (as computed above) } void ListenerManager::remove(ExperimentFlow* flow) { - // all listeners? + // All listeners (in all flows)? if (flow == 0) { - for (bufferlist_t::iterator it = m_BufferList.begin(); - it != m_BufferList.end(); it++) + // We have to remove *all* indices in *all* (possibly added) performance implementations. + // Therefore we collect the ptr to these impl. in order to call clear() for each of them. + std::set perfBufLists; + for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++) { (*it)->onDeletion(); // invoke listener handler - clearCaches(); + if((*it)->getPerformanceBuffer() != NULL) + perfBufLists.insert((*it)->getPerformanceBuffer()); + (*it)->setPerformanceBuffer(NULL); + (*it)->setLocation(INVALID_INDEX); + } + for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++) { + (*it)->onDeletion(); + // See remove(BaseListener*). + } m_BufferList.clear(); + // Remove the indices within each performance buffer-list (maybe empty): + for (std::set::iterator it = perfBufLists.begin(); + it != perfBufLists.end(); ++it) + (*it)->clear(); + clearCaches(); // FIXME: REMOVE + // All remaining active listeners must not fire anymore + m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end()); } else { // remove all listeners corresponding to a specific experiment ("flow"): - for (bufferlist_t::iterator it = m_BufferList.begin(); - it != m_BufferList.end(); ) { - if ((*it)->getParent() == flow) { - (*it)->onDeletion(); - it = m_BufferList.erase(it); + for (index_t i = 0; i < m_BufferList.size(); ) { + if (m_BufferList[i]->getParent() == flow) { + m_BufferList[i]->onDeletion(); + if (m_BufferList[i]->getPerformanceBuffer() != NULL) // perf. buffer-list existing? + m_BufferList[i]->getPerformanceBuffer()->remove(i); // delete it! + m_remove(i); } else { - ++it; + ++i; } } } // listeners that just fired / are about to fire ... for (firelist_t::const_iterator it = m_FireList.begin(); it != m_FireList.end(); it++) { - if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it) - != m_DeleteList.end()) { + if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it) != m_DeleteList.end()) { continue; // (already in the delete-list? -> skip!) } // ... need to be pushed into m_DeleteList, as we're currently @@ -142,8 +243,7 @@ ListenerManager::~ListenerManager() ListenerManager::iterator ListenerManager::makeActive(iterator it) { - assert(it != m_BufferList.end() && - "FATAL ERROR: Iterator has already reached the end!"); + assert(it != m_BufferList.end() && "FATAL ERROR: Iterator has already reached the end!"); BaseListener* li = *it; assert(li && "FATAL ERROR: Listener object pointer cannot be NULL!"); li->decreaseCounter(); @@ -158,6 +258,24 @@ ListenerManager::iterator ListenerManager::makeActive(iterator it) return it_next; } +void ListenerManager::makeActive(BaseListener* pLi) +{ + assert(pLi && "FATAL ERROR: Listener object pointer cannot be NULL!"); + // If there is no performance buffer-list implementation, skip the operation: + if (pLi->getPerformanceBuffer() == NULL) + return; + // Decrease and check the listener counter: + pLi->decreaseCounter(); + if (pLi->getCounter() > 0) + return; + pLi->resetCounter(); + // Remove the index of the listener from the performance buffer-list: + pLi->getPerformanceBuffer()->remove(pLi->getLocation()); + // Move the listener object from the buffer-list to the fire-list: + m_remove(pLi->getLocation()); // (updates the internals of the listener, too) + m_FireList.push_back(pLi); +} + void ListenerManager::triggerActiveListeners() { for (firelist_t::iterator it = m_FireList.begin(); @@ -180,6 +298,7 @@ void ListenerManager::triggerActiveListeners() size_t ListenerManager::getContextCount() const { + // Note: This works even if there are active performance buffer-list implementations. std::set uniqueFlows; // count unique ExperimentFlow-ptr for (bufferlist_t::const_iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++) diff --git a/src/core/sal/ListenerManager.hpp b/src/core/sal/ListenerManager.hpp index 15c1ff0f..2201e26b 100644 --- a/src/core/sal/ListenerManager.hpp +++ b/src/core/sal/ListenerManager.hpp @@ -15,9 +15,9 @@ class ExperimentFlow; /** * Buffer-list for a specific experiment; acts as a simple storage container - * for listeners to watch for: + * for listeners to watch for. */ -typedef std::list bufferlist_t; +typedef std::vector bufferlist_t; /** * List of listeners that match the current simulator listener; these listeners will * be triggered next (the list is used temporarily). @@ -55,7 +55,6 @@ typedef io_cache_t::iterator io_iter_t; */ class ListenerManager { private: - // TODO: List separation of "critical types"? Hashing/sorted lists? (-> performance!) bufferlist_t m_BufferList; //!< the storage for listeners added by exp. firelist_t m_FireList; //!< the active listeners (used temporarily) deletelist_t m_DeleteList; //!< the deleted listeners (used temporarily) @@ -65,6 +64,15 @@ private: friend bp_iter_t bp_cache_t::makeActive(ListenerManager &ev_list, bp_iter_t idx); friend io_iter_t io_cache_t::makeActive(ListenerManager &ev_list, io_iter_t idx); public: + /** + * Determines the pointer to the listener base type, stored at index \c idx. + * @param idx the index within the buffer-list of the listener to retrieve + * @return the pointer to the (up-casted) base type (if \c idx is invalid and debug + * mode is enabled, an assertion is thrown) + * @note This operation has O(1) time complexity (due to the underlying \c std::vector). + */ + inline BaseListener* dereference(index_t idx) { assert(idx < m_BufferList.size() && + "FATAL ERROR: Invalid index! Listener already deleted?"); return m_BufferList[idx]; } /** * The iterator of this class used to loop through the list of added * listeners. To retrieve an iterator to the first element, call \c begin(). @@ -81,15 +89,18 @@ public: * @param li pointer to the listener object to be added (cannot be \c NULL) * @param flow the listener context (a pointer to the experiment object * which is interested in such listeners (cannot be \c NULL) - * @return the id of the added listener object, that is ev->getId() + * @note You need to overload this method if you are adding a (type-specific) + * performance buffer-list implementation. */ void add(BaseListener* li, ExperimentFlow* flow); /** - * Removes the listener based upon the specified \a ev pointer (requires + * Removes the listener based upon the specified \c li pointer (requires * to loop through the whole buffer-list). - * @param li the pointer of the listener to be removed; if ev is set to - * \c NULL, all listeners (for \a all experiments) will be + * @param li the pointer of the listener to be removed; if \c li is set to + * \c NULL, all listeners (for the \a current experiment) will be * removed + * @note There is no need to re-implement this method in a performance- + * buffer-list implementation. */ void remove(BaseListener* li); /** @@ -98,7 +109,7 @@ public: * @param it the iterator pointing to the Listener object to be removed * @return the updated iterator which will point to the next element */ - iterator remove(iterator it); + iterator remove(iterator it) { return m_remove(it, false); } private: /** * Internal implementation of remove(iterator it) that allows @@ -109,6 +120,16 @@ private: * @return the updated iterator which will point to the next element */ iterator m_remove(iterator it, bool skip_deletelist); + /** + * Updates the buffer-list by "removing" the element located at index \c idx. + * This is done by replacing the element with the last element of the vector. + * @param idx the index of the element to be removed + * @warning The internals of the listener, stored at index \c idx wont be + * updated. + * @note This method should typically be used in a performance buffer-list + * implemenation. + */ + void m_remove(index_t idx); public: /** * Returns an iterator to the beginning of the internal data structure. @@ -170,20 +191,38 @@ public: /** * Moves the listeners from the (internal) buffer-list to the fire-list. * To actually fire the listeners, call triggerActiveListeners(). - * Returns an updated iterator which points to the next element. + * Returns an updated iterator which points to the next element. Additionally, + * \c makeActive() decreases the listener counter and performs it's operation if + * and only if the *decreased* listener counter is zero. * @param ev the listener to trigger - * @return returns the updated iteration, pointing to the next element - * after makeActive returns, "it" is invalid, so the returned + * @return returns the updated iterator, pointing to the next element + * after makeActive returns, "it" is *invalid*, so the *returned* * iterator should be used to continue the iteration * * TODO: Improve naming (instead of "makeActive")? */ iterator makeActive(iterator it); + /** + * Moves the listener \c pLi from the (internal "performance") buffer-list \c pSrc + * to the fire-list. This method should be called from a performance implemenation. + * It expects the member \c pLi->getLocation() to be valid, i.e. the element needs + * to be stored in the buffer-list previously. Additionally, \c makeActive() + * decreases the listener counter and performs it's operation if and only if the + * *decreased* listener counter is zero. Activated listener objects will be updated + * regarding their location and performance-buffer reference, i.e. their index and + * performance-buffer pointer will be invalidated (by setting \c INVALID_INDEX and + * \c NULL, respectively). + * To actually fire the listeners, call triggerActiveListeners(). + * @param pLi the listener object pointer to trigger; \c pLi will be removed in + * \c pLi->getPerformanceBuffer(). If the performance buffer-list ptr is + * \c NULL, nothing will be done. + */ + void makeActive(BaseListener* pLi); /** * Triggers the active listeners. Each listener is triggered if it has not - * recently been removed (id est: is not found in the delete-list). See - * makeActive() for more details. The recently triggered listener can be - * retrieved by calling \a getLastFired(). After all listeners have been + * recently been removed (i.e.: is not found in the delete-list). See + * \c makeActive() for more details. The recently triggered listener can be + * retrieved by calling \c getLastFired(). After all listeners have been * triggered, the (internal) fire- and delete-list will be cleared. */ void triggerActiveListeners(); diff --git a/src/core/sal/SimulatorController.cc b/src/core/sal/SimulatorController.cc index 2a672eea..fffcc36b 100644 --- a/src/core/sal/SimulatorController.cc +++ b/src/core/sal/SimulatorController.cc @@ -52,17 +52,18 @@ void SimulatorController::onBreakpoint(address_t instrPtr, address_t address_spa // Loop through all listeners of type BP*Listener: ListenerManager::iterator it = m_LstList.begin(); + BPEvent tmp(instrPtr, address_space); while (it != m_LstList.end()) { BaseListener* pev = *it; BPSingleListener* pbp; BPRangeListener* pbpr; - if ((pbp = dynamic_cast(pev)) && pbp->isMatching(instrPtr, address_space)) { + if ((pbp = dynamic_cast(pev)) && pbp->isMatching(&tmp)) { pbp->setTriggerInstructionPointer(instrPtr); it = m_LstList.makeActive(it); // "it" has already been set to the next element (by calling // makeActive()): continue; // -> skip iterator increment } else if ((pbpr = dynamic_cast(pev)) && - pbpr->isMatching(instrPtr, address_space)) { + pbpr->isMatching(&tmp)) { pbpr->setTriggerInstructionPointer(instrPtr); it = m_LstList.makeActive(it); continue; // dito @@ -80,12 +81,13 @@ void SimulatorController::onMemoryAccess(address_t addr, size_t len, is_write ? MemAccessEvent::MEM_WRITE : MemAccessEvent::MEM_READ; + MemAccessEvent tmp(addr, len, instrPtr, accesstype); ListenerManager::iterator it = m_LstList.begin(); while (it != m_LstList.end()) { // check for active listeners BaseListener* pev = *it; MemAccessListener* ev = dynamic_cast(pev); // Is this a MemAccessListener? Correct access type? - if (!ev || !ev->isMatching(addr, len, accesstype)) { + if (!ev || !ev->isMatching(&tmp)) { ++it; continue; // skip listener activation } @@ -101,10 +103,11 @@ void SimulatorController::onMemoryAccess(address_t addr, size_t len, void SimulatorController::onInterrupt(unsigned interruptNum, bool nmi) { ListenerManager::iterator it = m_LstList.begin(); + InterruptEvent tmp(nmi, interruptNum); while (it != m_LstList.end()) { // check for active listeners BaseListener* pev = *it; InterruptListener* pie = dynamic_cast(pev); - if (!pie || !pie->isMatching(interruptNum)) { + if (!pie || !pie->isMatching(&tmp)) { ++it; continue; // skip listener activation } @@ -156,11 +159,12 @@ bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum) void SimulatorController::onTrap(unsigned trapNum) { + TroubleEvent tmp(trapNum); ListenerManager::iterator it = m_LstList.begin(); while (it != m_LstList.end()) { // check for active listeners BaseListener* pev = *it; TrapListener* pte = dynamic_cast(pev); - if (!pte || !pte->isMatching(trapNum)) { + if (!pte || !pte->isMatching(&tmp)) { ++it; continue; // skip listener activation } diff --git a/src/core/sal/SimulatorController.hpp b/src/core/sal/SimulatorController.hpp index b0361fa3..edc8e8ae 100644 --- a/src/core/sal/SimulatorController.hpp +++ b/src/core/sal/SimulatorController.hpp @@ -182,15 +182,15 @@ public: */ void removeFlow(ExperimentFlow* flow); /** - * Add listener \c li to the listener management. This causes the listener to be active. + * Adds the listener \c li to the listener management. This causes the listener to be active. * @param li the listener pointer to be added for the current flow * @return \c true if the listener has been added successfully, \c false otherwise */ bool addListener(BaseListener* li); /** * Removes the listener with the specified pointer \c li. - * @param li the pointer of the listener-object to be removed; if \c li is - * equal to \c NULL, all listeners (for all experiments) will be removed + * @param li the pointer of the listener object to be removed; if \c li is + * equal to \c NULL, all listeners (for the \a current experiments) will be removed */ void removeListener(BaseListener* li) { m_LstList.remove(li); } /** @@ -209,15 +209,15 @@ public: */ BaseListener* resume(); /** - * Add listener \a ev to the global buffer and continues the simulation + * Add listener \c li to the global buffer and continues the simulation * (combines \c addListener() and \c resume()). * @param li the listener pointer to be added * @return the pointer of the occurred listener (it is not guaranteed that - * this pointer will be equal to li) + * this pointer will be equal to \c li) */ BaseListener* addListenerAndResume(BaseListener* li); /** - * Checks whether any experiment flow has listeners in the listener-list. + * Checks whether any experiment flow has listeners in the listener (buffer-)list. * @return \c true if there are still listeners, or \c false otherwise */ bool hasListeners() const { return getListenerCount() > 0; } @@ -227,6 +227,15 @@ public: * @return the actual number of listeners */ unsigned getListenerCount() const { return m_LstList.getListenerCount(); } + /** + * Determines the pointer to the listener base type, stored at index \c idx. + * @param idx the index within the buffer-list of the listener to retrieve + * @return the pointer to the (up-casted) base type (if \c idx is invalid and debug + * mode is enabled, an assertion is thrown) + * @note This operation has O(1) time complexity (due to the underlying \c std::vector). + * @see ListenerManager::dereference() + */ + inline BaseListener* dereference(index_t idx) { return m_LstList.dereference(idx); } }; // FIXME (see SimulatorController.cc): Weird, homeless global variable diff --git a/src/core/sal/bochs/BochsController.cc b/src/core/sal/bochs/BochsController.cc index 5e6db078..9d9bb9f9 100644 --- a/src/core/sal/bochs/BochsController.cc +++ b/src/core/sal/bochs/BochsController.cc @@ -97,9 +97,10 @@ void BochsController::onBreakpoint(address_t instrPtr, address_t address_space) // Check for active breakpoint-events: bp_cache_t &buffer_cache = m_LstList.getBPBuffer(); bp_cache_t::iterator it = buffer_cache.begin(); + BPEvent tmp(instrPtr, address_space); while (it != buffer_cache.end()) { BPListener* pEvBreakpt = *it; - if (pEvBreakpt->isMatching(instrPtr, address_space)) { + if (pEvBreakpt->isMatching(&tmp)) { pEvBreakpt->setTriggerInstructionPointer(instrPtr); it = buffer_cache.makeActive(m_LstList, it); do_fire = true; diff --git a/src/core/sal/bochs/BochsController.hpp b/src/core/sal/bochs/BochsController.hpp index f5f34e74..3937f58a 100644 --- a/src/core/sal/bochs/BochsController.hpp +++ b/src/core/sal/bochs/BochsController.hpp @@ -47,10 +47,10 @@ public: /** * Instruction pointer modification handler implementing the onBreakpoint * handler of the SimulatorController. This method is called (from - * the Breakpoints aspect) every time the Bochs-internal IP changes. + * the Breakpoints aspect) *every* time the Bochs-internal IP changes. * The handler itself evaluates if a breakpoint event needs to be triggered. * This handler needs to implement the breakpoint-mechanism in an indirect - * fashion because the Bochs simulator doesn't support breakpoints explicitly. + * fashion because the Bochs simulator doesn't support native breakpoints. * To match the interface specified by the simulator class, we need to provide * the two members \c m_CPUContext and \c m_CacheEntry. The elements are * being set before the handler is called (see \c updateBPEventInfo()). diff --git a/src/core/sal/bochs/Breakpoints.ah b/src/core/sal/bochs/Breakpoints.ah index 5c31324b..0c7e45d1 100644 --- a/src/core/sal/bochs/Breakpoints.ah +++ b/src/core/sal/bochs/Breakpoints.ah @@ -22,7 +22,7 @@ aspect Breakpoints { // Points to the *current* bxInstruction-object bxICacheEntry_c* pEntry = *(tjp->arg<1>()); - // report this event to the Bochs controller: + // Report this event to the Bochs controller: fail::simulator.updateBPEventInfo(pThis, pEntry); fail::simulator.onBreakpoint(pThis->get_instruction_pointer(), pThis->cr3); // Note: get_bx_opcode_name(pInstr->getIaOpcode()) retrieves the mnemonics. diff --git a/src/core/sal/perf/BreakpointBuffer.cc b/src/core/sal/perf/BreakpointBuffer.cc new file mode 100644 index 00000000..5d8ea26c --- /dev/null +++ b/src/core/sal/perf/BreakpointBuffer.cc @@ -0,0 +1,23 @@ +#include "BreakpointBuffer.hpp" +#include "../SALInst.hpp" + +namespace fail { + +// FIXME: not inlined +ResultSet& PerfVectorBreakpoints::gather(BPEvent* pData) +{ + static ResultSet res; + res.clear(); + // Search for all indices of matching listener objects: + for(std::vector::iterator it = m_BufList.begin(); it != m_BufList.end(); ++it) { + BPListener* pLi = static_cast(simulator.dereference(*it)); + if (pLi->isMatching(pData)) { + // Update trigger IPtr: + pLi->setTriggerInstructionPointer(pData->getTriggerInstructionPointer()); + res.add(*it); + } + } + return res; +} + +} // end-of-namespace: fail diff --git a/src/core/sal/perf/BreakpointBuffer.hpp b/src/core/sal/perf/BreakpointBuffer.hpp new file mode 100644 index 00000000..964561aa --- /dev/null +++ b/src/core/sal/perf/BreakpointBuffer.hpp @@ -0,0 +1,157 @@ +#ifndef __BREAKPOINT_BUFFER_HPP__ + #define __BREAKPOINT_BUFFER_HPP__ + +#include "BufferInterface.hpp" +#include "../Listener.hpp" +#include +#include + +// TODOs: +// - Make these implementations even faster (see below: continue PerfVecSortedSingleBP). +// - The implementation of gather() (see below) in BreakpointBuffer.cc (not inlined in +// .hpp) avoids an include cycle. Unfortunately, this may cause a bad performance +// because gather() won't be inlined anymore! (The method is called quite often.) + +namespace fail { + +class BPEvent; + +/** + * \class ResultSet + * + * Results (= indices of matching listeners) returned by the "gather"-method, + * see below. (This class can be seen as a "temporary fire-list".) + */ +class ResultSet { + std::vector m_Res; +public: + ResultSet() { } + bool hasMore() const { return !m_Res.empty(); } + index_t getNext() { index_t idx = m_Res.back(); m_Res.pop_back(); return idx; } + void add(index_t idx) { m_Res.push_back(idx); } + size_t size() const { return m_Res.size(); } + void clear() { m_Res.clear(); } +}; + +/** + * Concrete implementation of the PerfBufferBase class for \c std::vector + * and \c BPSingleListener. + */ +class PerfVectorBreakpoints : public PerfBufferBase { +protected: + std::vector m_BufList; +public: + void add(index_t idx) { m_BufList.push_back(idx); } + void remove(index_t idx) + { + for (std::vector::iterator it = m_BufList.begin(); + it != m_BufList.end(); ++it) { + if (*it == idx) { + m_BufList.erase(it); + break; + } + } + } + void clear() { m_BufList.clear(); } + size_t size() const { return m_BufList.size(); } + ResultSet& gather(BPEvent* pData); +}; + +/** + * \class PerfVecSortedSingleBP + * + * This class implements a faster mechanism to store BPSingleListener + * based on binary search on their corresponding instruction pointer. + */ +/* +class PerfVecSortedSingleBP : public PerfVectorBreakpoints { +private: + SimulatorController* m_Sim; +public: + PerfVecSortedSingleBP(SimulatorController* pSim) : m_Sim(pSim) { } + ** + * TODO. + * @warning The method expects that \c idx is a valid index within the main + * buffer-list. Therefore we are allowed to call \c SimulatorController::dereference(). + * Additionally, the indexed listener is epected to be of type \c BPListener*. + * + void add(index_t idx) + { + address_t watchIP = static_cast(m_Sim->dereference(idx))->getWatchInstructionPointer(); + // Keep the indices sorted in ascending order regarding their instr. ptr. + // Search for the slot to insert the element (skip lower IPs): + int pos = binarySearch(m_BufList, 0, m_BufList.size()-1, idx); + assert(pos < 0 && "FATAL ERROR: Element already inserted!"); + m_BufList.insert(it + (pos * (-1) + 1), idx); +// for (std::vector::iterator it = m_BufList.begin(); it != m_BufList.end(); ++it) { +// if (static_cast( +// m_Sim->dereference(*it))->getWatchInstructionPointer() > watchIP) { +// m_BufList.insert(it, idx); // Insert the element before "it" +// break; +// } + } + } + + static bool CompareInstrPtr(index_t arg1, index_t arg2, void* pStuff) + { + SimulatorController* pSim = static_cast(pStuff); + #define VAL(idx) \ + static_cast(pSim->dereference(idx))->getWatchInstructionPointer() + return (VAL(arg1) > VAL(arg2); + } + + ** + * Searches vec[first]...vec[last] for \c key. Returns the index of the matching + * element if it finds key, otherwise -(index where it could be inserted)-1. + * @param vec array of sorted (ascending) values. + * @param first lower subscript bound + * @param last upper subscript bound + * @param key value to search for + * @param pfnCmp a function pointer to a comparison function, returning \c true + * if the first argument is greater than the second, otherwise \c false. + * The last parameter equals the last parameter provided by calling this + * method. It can be used to pass additional data. + * @return index of key, or -insertion_position -1 if key is not in the array. + * This value can easily be transformed into the position to insert it. + * + int binarySearch(const std::vector& vec, index_t first, index_t last, index_t key, bool (*pfnCmp)(index_t, index_t, void*), void* pStuff = NULL) + { + + while (first <= last) { + int mid = (first + last) / 2; // compute mid point. + if (VAL(key) > VAL(vec[mid])) + first = mid + 1; // repeat search in top half. + else if (VAL(key) < VAL(vec[mid])) + last = mid - 1; // repeat search in bottom half. + else + return mid; // found it. return position + } + return -(first + 1); // failed to find key + } + void remove(index_t idx) + { + int pos = binarySearch(m_BufList, 0, m_BufList.size()-1, idx, CompareInstrPtr, m_Sim); + if (pos >= 0) + m_BufList.erase(m_BufList.begin() + pos); + } + ResultSet& gather(BPEvent* pData) + { + // TODO: Improve this by using binary search, too! + ResultSet res; + // Search for all indices of matching listener objects: + for(std::vector::iterator it = m_BufList.begin(); it != m_BufList.end(); ++it) { + BPListener* pLi = static_cast(simulator.dereference(*it)); + if (pLi->isMatching(pData)) { + // Update trigger IPtr: + pLi->setTriggerInstructionPointer(pData->getTriggerInstructionPointer()); + res.add(*it); + } + } + return res; + } +}; +*/ + +} // end-of-namespace: fail + +#endif // __BREAKPOINT_BUFFER_HPP__ diff --git a/src/core/sal/perf/BreakpointControllerSlice.ah b/src/core/sal/perf/BreakpointControllerSlice.ah new file mode 100644 index 00000000..06c9a3c0 --- /dev/null +++ b/src/core/sal/perf/BreakpointControllerSlice.ah @@ -0,0 +1,49 @@ +#ifndef __BREAKPOINT_CONTROLLER_SLICE_AH__ + #define __BREAKPOINT_CONTROLLER_SLICE_AH__ + +#include "config/FailConfig.hpp" + +#ifdef CONFIG_FAST_BREAKPOINTS + +#include +#include "../Listener.hpp" + +/** + * \class BreakpointControllerSlice + * + * The slice class definition to be used with FastBreakpoint.ah. + * The members of this class will be sliced into the \c SimulatorController class. + */ +slice class BreakpointControllerSlice { +public: + bool addListener(fail::BPSingleListener* sli) + { + assert(sli != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!"); + m_LstList.add(sli, m_Flows.getCurrent()); + // Call the common postprocessing function: + if (!sli->onAddition()) { // If the return value signals "false"..., + m_LstList.remove(sli); // ...skip the addition + return false; + } + return true; + } + fail::BaseListener* addListenerAndResume(fail::BPSingleListener* sli) + { addListener(sli); return resume(); } + bool addListener(fail::BPRangeListener* rli) + { + assert(rli != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!"); + m_LstList.add(rli, m_Flows.getCurrent()); + // Call the common postprocessing function: + if (!rli->onAddition()) { // If the return value signals "false"..., + m_LstList.remove(rli); // ...skip the addition + return false; + } + return true; + } + fail::BaseListener* addListenerAndResume(fail::BPRangeListener* rli) + { addListener(rli); return resume(); } +}; + +#endif // CONFIG_FAST_BREAKPOINTS + +#endif // __BREAKPOINT_CONTROLLER_SLICE_AH__ diff --git a/src/core/sal/perf/BreakpointManagerSlice.ah b/src/core/sal/perf/BreakpointManagerSlice.ah new file mode 100644 index 00000000..4d7266b9 --- /dev/null +++ b/src/core/sal/perf/BreakpointManagerSlice.ah @@ -0,0 +1,56 @@ +#ifndef __BREAKPOINT_MANAGER_SLICE_AH__ + #define __BREAKPOINT_MANAGER_SLICE_AH__ + +#include "config/FailConfig.hpp" + +#ifdef CONFIG_FAST_BREAKPOINTS + +#include "BreakpointBuffer.hpp" + +/** + * \class BreakpointManagerSlice + * + * The slice class definition to be used with FastBreakpoint.ah. + * The members of this class will be sliced into the \c ListenerManager class. + */ +slice class BreakpointManagerSlice { +private: + fail::PerfVectorBreakpoints m_SingleListeners; + fail::PerfVectorBreakpoints m_RangeListeners; +public: + fail::PerfVectorBreakpoints& getSingleListeners() { return m_SingleListeners; } + fail::PerfVectorBreakpoints& getRangeListeners() { return m_RangeListeners; } + + void add(fail::BPSingleListener* sli, fail::ExperimentFlow* flow) + { + assert(sli != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!"); + sli->setParent(flow); + // (1) Add sli to the main buffer-list, ... + m_BufferList.push_back(sli); + // (2) ... update it's location (index) / performance-buffer (ptr) and ... + fail::index_t idx = m_BufferList.size()-1; + assert(m_BufferList[idx] == sli && "FATAL ERROR: Invalid index after push_back() unexpected!"); + sli->setLocation(idx); + sli->setPerformanceBuffer(&m_SingleListeners); + // (3) ... add this index to the m_SingleListeners vector. + m_SingleListeners.add(idx); + } + void add(fail::BPRangeListener* rli, fail::ExperimentFlow* flow) + { + assert(rli != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!"); + rli->setParent(flow); + // (1) Add sli to the main buffer-list, ... + m_BufferList.push_back(rli); + // (2) ... update it's location (index) / performance-buffer (ptr) and ... + fail::index_t idx = m_BufferList.size()-1; + assert(m_BufferList[idx] == rli && "FATAL ERROR: Invalid index after push_back() unexpected!"); + rli->setLocation(idx); + rli->setPerformanceBuffer(&m_RangeListeners); + // (3) ... add this index to the m_RangeListeners vector. + m_RangeListeners.add(idx); + } +}; + +#endif // CONFIG_FAST_BREAKPOINTS + +#endif // __BREAKPOINT_MANAGER_SLICE_AH__ diff --git a/src/core/sal/perf/BufferInterface.hpp b/src/core/sal/perf/BufferInterface.hpp new file mode 100644 index 00000000..30b73ae5 --- /dev/null +++ b/src/core/sal/perf/BufferInterface.hpp @@ -0,0 +1,42 @@ +#ifndef __BUFFER_INTERFACE_HPP__ + #define __BUFFER_INTERFACE_HPP__ + +#include + +namespace fail { + +typedef std::size_t index_t; //!< the index type of elements, stored in the buffer-list +const index_t INVALID_INDEX = static_cast(-1); //!< a constant, representing an invalid idx + +/** + * \class PerfBufferBase + * + * Generic performance data structure for storing often used listener objects + * (This class can be seen as a "fast, type specific buffer-list".) + */ +class PerfBufferBase { +public: + /** + * Adds the index \c idx to the performance buffer-list. + * @param idx the element to be added + */ + virtual void add(index_t idx) = 0; + /** + * Removes the specified index \c idx. + * @param idx the element to be deleted + */ + virtual void remove(index_t idx) = 0; + /** + * Removes all elements from the perf. buffer-list. + */ + virtual void clear() = 0; + /** + * Retrieves the number of elements in this data structure. + * @return the number of elements in the perf. buffer-list + */ + virtual std::size_t size() const = 0; +}; + +} // end-of-namespace: fail + +#endif // __BUFFER_INTERFACE_HPP__ diff --git a/src/core/sal/perf/FastBreakpoints.ah b/src/core/sal/perf/FastBreakpoints.ah new file mode 100644 index 00000000..f45d38e0 --- /dev/null +++ b/src/core/sal/perf/FastBreakpoints.ah @@ -0,0 +1,85 @@ +#ifndef __FAST_BREAKPOINTS_AH__ + #define __FAST_BREAKPOINTS_AH__ + +#include "config/FailConfig.hpp" + +#ifdef CONFIG_FAST_BREAKPOINTS + +#ifndef CONFIG_EVENT_BREAKPOINTS +#error Breakpoint events are required for fast breakpoints! +#endif + +#include "BreakpointManagerSlice.ah" // slice class "BreakpointManagerSlice" +#include "BreakpointControllerSlice.ah" // slice class "BreakpointControllerSlice" +#include "../ListenerManager.hpp" +#include "../Listener.hpp" +#include "BreakpointBuffer.hpp" +#include "../SALInst.hpp" + +// TODOs: +// - Check if this solutation works, even if there are more than one +// active performance aspects. + +aspect FastBreakpoints { + + // Refer to slice classes: + advice "fail::ListenerManager" : slice BreakpointManagerSlice; + advice "fail::SimulatorController" : slice BreakpointControllerSlice; + + // These around-advices handle the (special) case where an experiment + // adds a listener object casted to the base type but actually has the + // BPSingle- or BPRangeListener type. + advice execution("bool fail::SimulatorController::addListener(fail::BaseListener*)") : around () + { + // Note: The logic to update the performance buffer-list is + // placed in the addListener() methods. + fail::BaseListener* pLi = *(tjp->arg<0>()); + fail::BPSingleListener* pSi = dynamic_cast(pLi); + fail::BPRangeListener* pRi = dynamic_cast(pLi); + if (pSi != NULL) + *(tjp->result()) = tjp->that()->addListener(pSi); + else if (pRi != NULL) + *(tjp->result()) = tjp->that()->addListener(pRi); + else + tjp->proceed(); + } + advice call("BaseListener* fail::SimulatorController::addListenerAndResume(fail::BaseListener*)") : around () + { + // Note: The logic to update the performance buffer-list is + // placed in the addListenerAndResume methods. + fail::BaseListener* pLi = *(tjp->arg<0>()); + fail::BPSingleListener* pSi = dynamic_cast(pLi); + fail::BPRangeListener* pRi = dynamic_cast(pLi); + if (pSi != NULL) + *(tjp->result()) = tjp->that()->addListenerAndResume(pSi); + else if (pRi != NULL) + *(tjp->result()) = tjp->that()->addListenerAndResume(pRi); + else + tjp->proceed(); + } + + // The event handler for employing breakpoint events. + advice execution("void fail::%Controller::onBreakpoint(...)") : around () + { + // Note: "BPListener" is an abstract class anyway. + + fail::ListenerManager& ref = tjp->target()->m_LstList; + fail::BPEvent tmp(*(tjp->arg<0>()), *(tjp->arg<1>())); + + // Check for matching BPSingleListeners: + fail::ResultSet& res1 = ref.getSingleListeners().gather(&tmp); + while (res1.hasMore()) + ref.makeActive(ref.dereference(res1.getNext())); + + // Check for matching BPRangeListeners: + fail::ResultSet& res2 = ref.getRangeListeners().gather(&tmp); + while (res2.hasMore()) + ref.makeActive(ref.dereference(res2.getNext())); + + ref.triggerActiveListeners(); + } +}; + +#endif // CONFIG_FAST_BREAKPOINTS // see above + +#endif // __FAST_BREAKPOINTS_AH__