diff --git a/src/core/efw/ExperimentFlow.hpp b/src/core/efw/ExperimentFlow.hpp index 5825bfe5..caadb756 100644 --- a/src/core/efw/ExperimentFlow.hpp +++ b/src/core/efw/ExperimentFlow.hpp @@ -26,9 +26,6 @@ public: { run(); simulator.clearEvents(this); // remove residual events - // FIXME: Consider removing this call (see EventList.cc, void remove(ExperimentFlow* flow)) - // a) with the advantage that we will potentially prevent serious segfaults but - // b) with the drawback that we cannot enforce any cleanups. } }; diff --git a/src/core/sal/CMakeLists.txt b/src/core/sal/CMakeLists.txt index fc2a2147..bd6f6770 100644 --- a/src/core/sal/CMakeLists.txt +++ b/src/core/sal/CMakeLists.txt @@ -7,6 +7,7 @@ if(BUILD_BOCHS) Register.cc SimulatorController.cc bochs/BochsController.cc + bochs/BochsEvents.cc ) else() set(SRCS diff --git a/src/core/sal/Event.cc b/src/core/sal/Event.cc index c4e0e6d4..a44c26ea 100644 --- a/src/core/sal/Event.cc +++ b/src/core/sal/Event.cc @@ -7,9 +7,8 @@ EventId BaseEvent::m_Counter = 0; bool TroubleEvent::isMatching(unsigned troubleNum) const { - for(unsigned i = 0; i < m_WatchNumbers.size(); i++) - { - if(m_WatchNumbers[i] == troubleNum || + for (unsigned i = 0; i < m_WatchNumbers.size(); i++) { + if (m_WatchNumbers[i] == troubleNum || m_WatchNumbers[i] == ANY_TRAP) return true; } @@ -18,10 +17,8 @@ bool TroubleEvent::isMatching(unsigned troubleNum) const bool TroubleEvent::removeWatchNumber(unsigned troubleNum) { - for(unsigned i = 0; i < m_WatchNumbers.size(); i++) - { - if(m_WatchNumbers[i] == troubleNum) - { + for (unsigned i = 0; i < m_WatchNumbers.size(); i++) { + if (m_WatchNumbers[i] == troubleNum) { m_WatchNumbers.erase(m_WatchNumbers.begin()+i); return true; } @@ -31,10 +28,8 @@ bool TroubleEvent::removeWatchNumber(unsigned troubleNum) bool TroubleEvent::addWatchNumber(unsigned troubleNumber) { - for(unsigned i = 0; i < m_WatchNumbers.size(); i++) - { - - if(m_WatchNumbers[i] == troubleNumber) + for (unsigned i = 0; i < m_WatchNumbers.size(); i++) { + if (m_WatchNumbers[i] == troubleNumber) return false; } m_WatchNumbers.push_back(troubleNumber); @@ -43,12 +38,12 @@ bool TroubleEvent::addWatchNumber(unsigned troubleNumber) bool MemAccessEvent::isMatching(address_t addr, accessType_t accesstype) const { - if(!(m_WatchType & accesstype)) - return (false); - else if(m_WatchAddr != addr && + if (!(m_WatchType & accesstype)) + return false; + else if (m_WatchAddr != addr && m_WatchAddr != ANY_ADDR) - return (false); - return (true); + return false; + return true; } bool BPEvent::aspaceIsMatching(address_t aspace) const @@ -77,9 +72,8 @@ bool BPRangeEvent::isMatching(address_t addr, address_t aspace) const bool BPSingleEvent::isMatching(address_t addr, address_t aspace) const { if (aspaceIsMatching(aspace)) { - if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr) { + if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr) return true; - } } return false; } diff --git a/src/core/sal/Event.hpp b/src/core/sal/Event.hpp index a45137f2..d8e96310 100644 --- a/src/core/sal/Event.hpp +++ b/src/core/sal/Event.hpp @@ -17,9 +17,9 @@ class ExperimentFlow; typedef unsigned long EventId; //!< type of event ids //! invalid event id (used as a return indicator) -const EventId INVALID_EVENT = (EventId)-1; +const EventId INVALID_EVENT = static_cast(-1); //! address wildcard (e.g. for BPEvent's) -const address_t ANY_ADDR = static_cast(-1); +const address_t ANY_ADDR = static_cast(-1); //! instruction wildcard const unsigned ANY_INSTR = static_cast(-1); //! trap wildcard @@ -45,6 +45,30 @@ public: BaseEvent() : m_Id(++m_Counter), m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL) { updateTime(); } virtual ~BaseEvent() { } + /** + * This method is called when an experiment flow adds a new event by + * calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev). + * More specifically, the event will be added to the event-list first + * (buffer-list, to be precise) and then this event handler is called. + * @return You should return \c true to continue and \c false to prevent + * the addition of the event \a pev, yielding an error in the + * experiment flow (i.e. \c INVALID_EVENT is returned). + */ + virtual bool onEventAddition() { return true; } + /** + * This method is called when an experiment flow removes an event from + * the event-management by calling \c removeEvent(prev), \c clearEvents() + * or by deleting a complete flow (\c removeFlow). More specifically, this + * event handler will be called *before* the event is actually deleted. + */ + virtual void onEventDeletion() { } + /** + * This method is called when an previously added event is about to be + * triggered by the simulator-backend. More specifically, this event handler + * will be called *before* the event is actually triggered, i.e. before the + * corresponding coroutine is toggled. + */ + virtual void onEventTrigger() { } /** * Retrieves the unique event id for this event. * @return the unique id @@ -116,8 +140,7 @@ public: * in a random address space. */ BPEvent(address_t address_space = ANY_ADDR) - : m_CR3(address_space), m_TriggerInstrPtr(ANY_ADDR) - {} + : m_CR3(address_space), m_TriggerInstrPtr(ANY_ADDR) { } /** * Returns the address space register of this event. */ @@ -539,7 +562,7 @@ public: */ unsigned getOpcode() const { return (m_Opcode); } /** - * Returns \c true, of the event was triggered due to specific register + * Returns \c true, if the event was triggered due to specific register * content, \c false otherwise. */ bool isRegisterTriggered() { return (!m_FlagTriggered); } @@ -560,26 +583,31 @@ public: }; /** - * \class TimerEvent - * This event type is used to create timeouts/timers within in an experiment. + * \class GenericTimerEvent + * This event type is used to create timeouts within in an experiment. + * + * Depending on your simulator backend, a concrete class needs to be derived + * from \c GenericTimerEvent. \c onEventAddition should be used to register and + * \c onEventDeletion to unregister the timer. \c onEventTrigger can be used to + * re-register the timer if a repetitive timer is requested and the back- + * end doesn't support such timer types natively. */ -class TimerEvent : public BaseEvent { -private: +class GenericTimerEvent : public BaseEvent { +protected: unsigned m_Timeout; //!< timeout interval in milliseconds timer_id_t m_Id; //!< internal timer id (sim-specific) - bool m_Once; //!< true, if the timer should be triggered only once + bool m_Once; //!< \c true, if the timer should be triggered only once, \c false otherwise public: /** * Creates a new timer event. This can be used to implement a timeout- * mechanism in the experiment-flow. The timer starts automatically when - * added to the simulator backend (@see SimulatorController::addEvent) + * added to the simulator backend. * @param timeout the time intervall in milliseconds (ms) - * @param once \c true, if the TimerEvent should be triggered once, - * \c false if it should occur regularly + * @see SimulatorController::addEvent */ - TimerEvent(unsigned timeout, bool once) - : m_Timeout(timeout), m_Id(-1), m_Once(once) { } - ~TimerEvent() { } + GenericTimerEvent(unsigned timeout) + : m_Timeout(timeout), m_Id(-1) { } + ~GenericTimerEvent() { } /** * Retrieves the internal timer id. Maybe useful for debug output. * @return the timer id @@ -595,11 +623,6 @@ public: * @return the timout in milliseconds */ unsigned getTimeout() const { return m_Timeout; } - /** - * Checks whether the timer occurs once or repetitive. - * @return \c true if timer triggers once, \c false if repetitive - */ - bool getOnceFlag() const { return m_Once; } }; } // end-of-namespace: fail diff --git a/src/core/sal/EventList.cc b/src/core/sal/EventList.cc index cf6b73cc..9fc6f041 100644 --- a/src/core/sal/EventList.cc +++ b/src/core/sal/EventList.cc @@ -8,26 +8,27 @@ namespace fail { void EventList::addToCaches(BaseEvent *ev) { BPEvent *bps_ev; - if((bps_ev = dynamic_cast(ev)) != NULL) + if ((bps_ev = dynamic_cast(ev)) != NULL) m_Bp_cache.add(bps_ev); IOPortEvent *io_ev; - if((io_ev = dynamic_cast(ev)) != NULL) + if ((io_ev = dynamic_cast(ev)) != NULL) m_Io_cache.add(io_ev); } void EventList::removeFromCaches(BaseEvent *ev) { BPEvent *bpr_ev; - if((bpr_ev = dynamic_cast(ev)) != NULL) + if ((bpr_ev = dynamic_cast(ev)) != NULL) m_Bp_cache.remove(bpr_ev); IOPortEvent *io_ev; - if((io_ev = dynamic_cast(ev)) != NULL) + if ((io_ev = dynamic_cast(ev)) != NULL) m_Io_cache.remove(io_ev); } -void EventList::clearCaches() { +void EventList::clearCaches() +{ m_Bp_cache.clear(); m_Io_cache.clear(); } @@ -41,7 +42,7 @@ EventId EventList::add(BaseEvent* ev, ExperimentFlow* pExp) addToCaches(ev); m_BufferList.push_back(ev); - return (ev->getId()); + return ev->getId(); } void EventList::remove(BaseEvent* ev) @@ -52,9 +53,9 @@ void EventList::remove(BaseEvent* ev) // * copy m_FireList to m_DeleteList if (ev == 0) { for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++) - simulator.onEventDeletion(*it); + (*it)->onEventDeletion(); for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++) - simulator.onEventDeletion(*it); + (*it)->onEventDeletion(); clearCaches(); m_BufferList.clear(); // all remaining active events must not fire anymore @@ -64,7 +65,7 @@ void EventList::remove(BaseEvent* ev) // * find/remove ev in m_BufferList // * if ev in m_FireList, copy to m_DeleteList } else { - simulator.onEventDeletion(ev); + ev->onEventDeletion(); removeFromCaches(ev); m_BufferList.remove(ev); @@ -78,7 +79,7 @@ void EventList::remove(BaseEvent* ev) EventList::iterator EventList::remove(iterator it) { - return (m_remove(it, false)); + return m_remove(it, false); } EventList::iterator EventList::m_remove(iterator it, bool skip_deletelist) @@ -87,9 +88,9 @@ EventList::iterator EventList::m_remove(iterator it, bool skip_deletelist) // If skip_deletelist = true, m_remove was called from makeActive. Accordingly, we // are not going to delete an event, instead we are "moving" an event object (= *it) // from the buffer list to the fire-list. Therefore we only need to call the simulator's - // event handler (m_onEventDeletion), if m_remove is called with the primary intention + // event handler (onEventDeletion), if m_remove is called with the primary intention // to *delete* (not "move") an event. - simulator.onEventDeletion(*it); + (*it)->onEventDeletion(); m_DeleteList.push_back(*it); // Cached events have their own BufferCache::makeActive() implementation, which @@ -98,35 +99,26 @@ EventList::iterator EventList::m_remove(iterator it, bool skip_deletelist) // BufferCache::remove() from m_remove(). // NOTE: in case the semantics of skip_deletelist change, please adapt the following lines - removeFromCaches((*it)); + removeFromCaches(*it); } - return (m_BufferList.erase(it)); + return m_BufferList.erase(it); } void EventList::remove(ExperimentFlow* flow) { - // WARNING: (*it) (= all elements in the lists) can be an invalid ptr because - // clearEvents will be called automatically when the allocating experiment (i.e. - // run()) has already ended. Accordingly, we cannot call - // simulator.onEventDeletion(*it) - // because a dynamic-cast of *it would cause a SEGFAULT. Therefor we require the - // experiment flow to remove all residual events by calling clearEvents() (with- - // in run()). As a consequence, we are now allowed to call the event-handler here. - // See ExperimentFlow.hpp for more details. - // all events? if (flow == 0) { for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++) - simulator.onEventDeletion(*it); // invoke event handler + (*it)->onEventDeletion(); // invoke event handler clearCaches(); m_BufferList.clear(); } else { // remove all events corresponding to a specific experiment ("flow"): for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); ) { if ((*it)->getParent() == flow) { - simulator.onEventDeletion(*it); + (*it)->onEventDeletion(); it = m_BufferList.erase(it); } else { ++it; @@ -143,7 +135,7 @@ void EventList::remove(ExperimentFlow* flow) // ... need to be pushed into m_DeleteList, as we're currently // iterating over m_FireList in fireActiveEvents() and cannot modify it if (flow == 0 || (*it)->getParent() == flow) { - simulator.onEventDeletion(*it); + (*it)->onEventDeletion(); m_DeleteList.push_back(*it); } } @@ -157,11 +149,11 @@ EventList::~EventList() BaseEvent* EventList::getEventFromId(EventId id) { // Loop through all events: - for(bufferlist_t::iterator it = m_BufferList.begin(); - it != m_BufferList.end(); it++) - if((*it)->getId() == id) - return (*it); - return (NULL); // Nothing found. + for (bufferlist_t::iterator it = m_BufferList.begin(); + it != m_BufferList.end(); it++) + if ((*it)->getId() == id) + return *it; + return NULL; // Nothing found. } EventList::iterator EventList::makeActive(iterator it) @@ -179,7 +171,7 @@ EventList::iterator EventList::makeActive(iterator it) // store the removed item in the delete-list. iterator it_next = m_remove(it, true); // remove event from buffer-list m_FireList.push_back(ev); - return (it_next); + return it_next; } void EventList::fireActiveEvents() @@ -191,7 +183,7 @@ void EventList::fireActiveEvents() m_pFired = *it; // Inform (call) the simulator's (internal) event handler that we are about // to trigger an event (*before* we actually toggle the experiment flow): - simulator.onEventTrigger(m_pFired); + m_pFired->onEventTrigger(); ExperimentFlow* pFlow = m_pFired->getParent(); assert(pFlow && "FATAL ERROR: The event has no parent experiment (owner)!"); simulator.m_Flows.toggle(pFlow); @@ -205,8 +197,8 @@ void EventList::fireActiveEvents() size_t EventList::getContextCount() const { std::set uniqueFlows; // count unique ExperimentFlow-ptr - for(bufferlist_t::const_iterator it = m_BufferList.begin(); - it != m_BufferList.end(); it++) + for (bufferlist_t::const_iterator it = m_BufferList.begin(); + it != m_BufferList.end(); it++) uniqueFlows.insert((*it)->getParent()); return uniqueFlows.size(); diff --git a/src/core/sal/EventList.hpp b/src/core/sal/EventList.hpp index 61288131..c0da7cfa 100644 --- a/src/core/sal/EventList.hpp +++ b/src/core/sal/EventList.hpp @@ -53,170 +53,169 @@ typedef io_cache_t::iterator io_iter_t; * details.) EventList is part of the SimulatorController and "outsources" * it's event management. */ -class EventList -{ - private: - // TODO: List separation of "critical types"? Hashing/sorted lists? (-> performance!) - bufferlist_t m_BufferList; //!< the storage for events added by exp. - firelist_t m_FireList; //!< the active events (used temporarily) - deletelist_t m_DeleteList; //!< the deleted events (used temporarily) - BaseEvent* m_pFired; //!< the recently fired Event-object - bp_cache_t m_Bp_cache; //!< the storage cache for breakpoint events - io_cache_t m_Io_cache; //!< the storage cache for port i/o events - friend bp_iter_t bp_cache_t::makeActive(EventList &ev_list, bp_iter_t idx); - friend io_iter_t io_cache_t::makeActive(EventList &ev_list, io_iter_t idx); - public: - /** - * The iterator of this class used to loop through the list of - * added events. To retrieve an iterator to the first element, call - * begin(). end() returns the iterator, pointing after the last element. - * (This behaviour equals the STL iterator in C++.) - */ - typedef bufferlist_t::iterator iterator; +class EventList { +private: + // TODO: List separation of "critical types"? Hashing/sorted lists? (-> performance!) + bufferlist_t m_BufferList; //!< the storage for events added by exp. + firelist_t m_FireList; //!< the active events (used temporarily) + deletelist_t m_DeleteList; //!< the deleted events (used temporarily) + BaseEvent* m_pFired; //!< the recently fired Event-object + bp_cache_t m_Bp_cache; //!< the storage cache for breakpoint events + io_cache_t m_Io_cache; //!< the storage cache for port i/o events + friend bp_iter_t bp_cache_t::makeActive(EventList &ev_list, bp_iter_t idx); + friend io_iter_t io_cache_t::makeActive(EventList &ev_list, io_iter_t idx); +public: + /** + * The iterator of this class used to loop through the list of + * added events. To retrieve an iterator to the first element, call + * begin(). end() returns the iterator, pointing after the last element. + * (This behaviour equals the STL iterator in C++.) + */ + typedef bufferlist_t::iterator iterator; - EventList() : m_pFired(NULL) { } - ~EventList(); - /** - * Adds the specified event object for the given ExperimentFlow to the - * list of events to be watched for. - * @param ev pointer to the event object to be added (cannot be \c NULL) - * @param pExp the event context (a pointer to the experiment object - * which is interested in such events, cannot be \c NULL) - * @return the id of the added event object, that is ev->getId() - */ - EventId add(BaseEvent* ev, ExperimentFlow* pExp); - /** - * Removes the event based upon the specified \a ev pointer (requires - * to loop through the whole buffer-list). - * @param ev the pointer of the event to be removed; if ev is set to - * \c NULL, all events (for \a all experiments) will be - * removed - */ - void remove(BaseEvent* ev); - /** - * Behaves like remove(BaseEvent) and additionally updates the provided - * iteration. - * @return the updated iterator which will point to the next element - */ - iterator remove(iterator it); - private: - /** - * Internal implementation of remove(iterator it) that allows - * to skip the delete-list. - * @return the updated iterator which will point to the next element - */ - iterator m_remove(iterator it, bool skip_deletelist); - public: - /** - * Returns an iterator to the beginning of the internal data structure. - * Don't forget to update the returned iterator when calling one of the - * modifying methods like makeActive() or remove(). Therefore you need - * to call the iterator-based variants of makeActive() and remove(). - * \code - * [X|1|2| ... |n] - * ^ - * \endcode - */ - iterator begin() { return (m_BufferList.begin()); } - /** - * Returns an iterator to the end of the interal data structure. - * Don't forget to update the returned iterator when calling one of the - * modifying methods like makeActive() or remove(). Therefore you need - * to call the iterator-based variants of makeActive() and remove(). - * \code - * [1|2| ... |n]X - * ^ - * \endcode - */ - iterator end() { return (m_BufferList.end()); } - /** - * Retrieves the event object for the given \a id. The internal data - * remains unchanged. - * @param id of event to be retrieved. - * @return pointer to event or \c NULL of \a id could not be found - */ - BaseEvent* getEventFromId(EventId id); - /** - * Removes all events for the specified experiment. - * @param flow pointer to experiment context (0 = all experiments) - */ - void remove(ExperimentFlow* flow); - /** - * Retrieves the number of experiments which currently have active - * events. This number is trivially equal to the (current) total - * number of ExperimentFlow-objects. - * @return number of experiments having active events - */ - size_t getContextCount() const; - /** - * Retrieves the total number of buffered events. This doesn't include - * the events in the fire- or delete-list. - * @return the total event count (for all flows) - */ - size_t getEventCount() const { return m_BufferList.size(); } - /** - * Retrieves the recently triggered event object. To map this object to - * it's context (id est, the related ExerimentFlow), use - * \c getLastFiredDest(). - * @return a pointer to the recent event or \c NULL if nothing has been - * triggered so far - */ - BaseEvent* getLastFired() { return (m_pFired); } - /** - * Retrieves the ExperimentFlow-object for the given BaseEvent (it's - * \a context). - * @param pEv the event object to be looked up - * @return a pointer to the context of \a pEv or \c NULL if the - * corresponding context could not be found - */ - ExperimentFlow* getExperimentOf(BaseEvent* pEv); - /** - * Moves the events from the (internal) buffer-list to the fire-list. - * To actually fire the events, call fireActiveEvents(). - * Returns an updated iterator which points to the next element. - * @param ev the event to trigger - * @return returns the updated iteration, 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); - /** - * Triggers the active events. Each event 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 event can be - * retrieved by calling \a getLastFired(). After all events have been - * triggered, the (internal) fire- and delete-list will be cleared. - * - * TODO: Improve naming (instead of "fireActiveEvents")? - */ - void fireActiveEvents(); - /** - * Retrieves the BPEvent buffer cache. - * @returns the buffer cache - */ - inline bp_cache_t &getBPBuffer() { return m_Bp_cache; } - /** - * Retrieves the IOPortEvent buffer cache. - * @returns the buffer cache - */ - inline io_cache_t &getIOBuffer() { return m_Io_cache; } - private: - /** - * Add an event to its appropriate cache. - * @param the event to add - */ - void addToCaches(BaseEvent *ev); - /** - * Remove an event from its cache. - * @param the event to remove - */ - void removeFromCaches(BaseEvent *ev); - /** - * Clear the event caches. - */ - void clearCaches(); + EventList() : m_pFired(NULL) { } + ~EventList(); + /** + * Adds the specified event object for the given ExperimentFlow to the + * list of events to be watched for. + * @param ev pointer to the event object to be added (cannot be \c NULL) + * @param pExp the event context (a pointer to the experiment object + * which is interested in such events, cannot be \c NULL) + * @return the id of the added event object, that is ev->getId() + */ + EventId add(BaseEvent* ev, ExperimentFlow* pExp); + /** + * Removes the event based upon the specified \a ev pointer (requires + * to loop through the whole buffer-list). + * @param ev the pointer of the event to be removed; if ev is set to + * \c NULL, all events (for \a all experiments) will be + * removed + */ + void remove(BaseEvent* ev); + /** + * Behaves like remove(BaseEvent) and additionally updates the provided + * iteration. + * @return the updated iterator which will point to the next element + */ + iterator remove(iterator it); +private: + /** + * Internal implementation of remove(iterator it) that allows + * to skip the delete-list. + * @return the updated iterator which will point to the next element + */ + iterator m_remove(iterator it, bool skip_deletelist); +public: + /** + * Returns an iterator to the beginning of the internal data structure. + * Don't forget to update the returned iterator when calling one of the + * modifying methods like makeActive() or remove(). Therefore you need + * to call the iterator-based variants of makeActive() and remove(). + * \code + * [X|1|2| ... |n] + * ^ + * \endcode + */ + iterator begin() { return (m_BufferList.begin()); } + /** + * Returns an iterator to the end of the interal data structure. + * Don't forget to update the returned iterator when calling one of the + * modifying methods like makeActive() or remove(). Therefore you need + * to call the iterator-based variants of makeActive() and remove(). + * \code + * [1|2| ... |n]X + * ^ + * \endcode + */ + iterator end() { return (m_BufferList.end()); } + /** + * Retrieves the event object for the given \a id. The internal data + * remains unchanged. + * @param id of event to be retrieved. + * @return pointer to event or \c NULL of \a id could not be found + */ + BaseEvent* getEventFromId(EventId id); + /** + * Removes all events for the specified experiment. + * @param flow pointer to experiment context (0 = all experiments) + */ + void remove(ExperimentFlow* flow); + /** + * Retrieves the number of experiments which currently have active + * events. This number is trivially equal to the (current) total + * number of ExperimentFlow-objects. + * @return number of experiments having active events + */ + size_t getContextCount() const; + /** + * Retrieves the total number of buffered events. This doesn't include + * the events in the fire- or delete-list. + * @return the total event count (for all flows) + */ + size_t getEventCount() const { return m_BufferList.size(); } + /** + * Retrieves the recently triggered event object. To map this object to + * it's context (id est, the related ExerimentFlow), use + * \c getLastFiredDest(). + * @return a pointer to the recent event or \c NULL if nothing has been + * triggered so far + */ + BaseEvent* getLastFired() { return (m_pFired); } + /** + * Retrieves the ExperimentFlow-object for the given BaseEvent (it's + * \a context). + * @param pEv the event object to be looked up + * @return a pointer to the context of \a pEv or \c NULL if the + * corresponding context could not be found + */ + ExperimentFlow* getExperimentOf(BaseEvent* pEv); + /** + * Moves the events from the (internal) buffer-list to the fire-list. + * To actually fire the events, call fireActiveEvents(). + * Returns an updated iterator which points to the next element. + * @param ev the event to trigger + * @return returns the updated iteration, 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); + /** + * Triggers the active events. Each event 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 event can be + * retrieved by calling \a getLastFired(). After all events have been + * triggered, the (internal) fire- and delete-list will be cleared. + * + * TODO: Improve naming (instead of "fireActiveEvents")? + */ + void fireActiveEvents(); + /** + * Retrieves the BPEvent buffer cache. + * @returns the buffer cache + */ + inline bp_cache_t &getBPBuffer() { return m_Bp_cache; } + /** + * Retrieves the IOPortEvent buffer cache. + * @returns the buffer cache + */ + inline io_cache_t &getIOBuffer() { return m_Io_cache; } +private: + /** + * Add an event to its appropriate cache. + * @param the event to add + */ + void addToCaches(BaseEvent *ev); + /** + * Remove an event from its cache. + * @param the event to remove + */ + void removeFromCaches(BaseEvent *ev); + /** + * Clear the event caches. + */ + void clearCaches(); }; } // end-of-namespace: fail diff --git a/src/core/sal/SimulatorController.cc b/src/core/sal/SimulatorController.cc index 66755185..fbca2022 100644 --- a/src/core/sal/SimulatorController.cc +++ b/src/core/sal/SimulatorController.cc @@ -11,7 +11,7 @@ EventId SimulatorController::addEvent(BaseEvent* ev) assert(ev != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!"); EventId ret = m_EvList.add(ev, m_Flows.getCurrent()); // Call the common postprocessing function: - if (!onEventAddition(ev)) { // If the return value signals "false"..., + if (!ev->onEventAddition()) { // If the return value signals "false"..., m_EvList.remove(ev); // ...skip the addition ret = INVALID_EVENT; } @@ -20,10 +20,12 @@ EventId SimulatorController::addEvent(BaseEvent* ev) BaseEvent* SimulatorController::waitAny(void) { + if (!hasEvents()) + return NULL; m_Flows.resume(); assert(m_EvList.getLastFired() != NULL && "FATAL ERROR: getLastFired() expected to be non-NULL!"); - return (m_EvList.getLastFired()); + return m_EvList.getLastFired(); } void SimulatorController::startup() @@ -45,26 +47,21 @@ void SimulatorController::onBreakpointEvent(address_t instrPtr, address_t addres { assert(false && "FIXME: SimulatorController::onBreakpointEvent() has not been tested before"); - - // FIXME: Improve performance + // FIXME: Improve performance! // Loop through all events of type BP*Event: EventList::iterator it = m_EvList.begin(); - while (it != m_EvList.end()) - { + while (it != m_EvList.end()) { BaseEvent* pev = *it; BPSingleEvent* pbp; BPRangeEvent* pbpr; - if((pbp = dynamic_cast(pev)) && pbp->isMatching(instrPtr, address_space)) - { - pbp->setTriggerInstructionPointer(instrPtr); + if ((pbp = dynamic_cast(pev)) && pbp->isMatching(instrPtr, address_space)) { + pbp->setTriggerInstructionPointer(instrPtr); it = m_EvList.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)) - { + } else if ((pbpr = dynamic_cast(pev)) && + pbpr->isMatching(instrPtr, address_space)) { pbpr->setTriggerInstructionPointer(instrPtr); it = m_EvList.makeActive(it); continue; // dito @@ -77,20 +74,17 @@ void SimulatorController::onBreakpointEvent(address_t instrPtr, address_t addres void SimulatorController::onMemoryAccessEvent(address_t addr, size_t len, bool is_write, address_t instrPtr) { - // FIXME: Improve performance - + // FIXME: Improve performance! MemAccessEvent::accessType_t accesstype = is_write ? MemAccessEvent::MEM_WRITE : MemAccessEvent::MEM_READ; EventList::iterator it = m_EvList.begin(); - while(it != m_EvList.end()) // check for active events - { + while (it != m_EvList.end()) { // check for active events BaseEvent* pev = *it; MemAccessEvent* ev = dynamic_cast(pev); // Is this a MemAccessEvent? Correct access type? - if(!ev || !ev->isMatching(addr, accesstype)) - { + if (!ev || !ev->isMatching(addr, accesstype)) { ++it; continue; // skip event activation } @@ -106,12 +100,10 @@ void SimulatorController::onMemoryAccessEvent(address_t addr, size_t len, void SimulatorController::onInterruptEvent(unsigned interruptNum, bool nmi) { EventList::iterator it = m_EvList.begin(); - while(it != m_EvList.end()) // check for active events - { + while (it != m_EvList.end()) { // check for active events BaseEvent* pev = *it; InterruptEvent* pie = dynamic_cast(pev); - if(!pie || !pie->isMatching(interruptNum)) - { + if (!pie || !pie->isMatching(interruptNum)) { ++it; continue; // skip event activation } @@ -124,55 +116,52 @@ void SimulatorController::onInterruptEvent(unsigned interruptNum, bool nmi) bool SimulatorController::isSuppressedInterrupt(unsigned interruptNum) { - for(size_t i = 0; i < m_SuppressedInterrupts.size(); i++) - if((m_SuppressedInterrupts[i] == interruptNum || - m_SuppressedInterrupts[i] == ANY_INTERRUPT) && interruptNum != (unsigned) interrupt_to_fire+32 ){ - if((int)interruptNum == interrupt_to_fire+32){ + for (size_t i = 0; i < m_SuppressedInterrupts.size(); i++) + if ((m_SuppressedInterrupts[i] == interruptNum || + m_SuppressedInterrupts[i] == ANY_INTERRUPT) && + interruptNum != (unsigned)interrupt_to_fire + 32) { + if ((int)interruptNum == interrupt_to_fire + 32) { interrupt_to_fire = -1; - return(true); + return true; } - return (true); + return true; } - return (false); + return false; } bool SimulatorController::addSuppressedInterrupt(unsigned interruptNum) { // Check if already existing: - if(isSuppressedInterrupt(interruptNum+32)) - return (false); // already added: nothing to do here + if (isSuppressedInterrupt(interruptNum+32)) + return false; // already added: nothing to do here - if(interruptNum == ANY_INTERRUPT){ + if (interruptNum == ANY_INTERRUPT) { m_SuppressedInterrupts.push_back(interruptNum); - return (true); - }else{ + return true; + } else { m_SuppressedInterrupts.push_back(interruptNum+32); - return (true); + return true; } } bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum) { - for(size_t i = 0; i < m_SuppressedInterrupts.size(); i++) - { - if(m_SuppressedInterrupts[i] == interruptNum+32 || m_SuppressedInterrupts[i] == ANY_INTERRUPT) - { + for (size_t i = 0; i < m_SuppressedInterrupts.size(); i++) { + if (m_SuppressedInterrupts[i] == interruptNum+32 || + m_SuppressedInterrupts[i] == ANY_INTERRUPT) m_SuppressedInterrupts.erase(m_SuppressedInterrupts.begin() + i); - return (true); - } + return true; } - return (false); + return false; } void SimulatorController::onTrapEvent(unsigned trapNum) { EventList::iterator it = m_EvList.begin(); - while(it != m_EvList.end()) // check for active events - { + while(it != m_EvList.end()) { // check for active events BaseEvent* pev = *it; TrapEvent* pte = dynamic_cast(pev); - if(!pte || !pte->isMatching(trapNum)) - { + if (!pte || !pte->isMatching(trapNum)) { ++it; continue; // skip event activation } @@ -185,12 +174,10 @@ void SimulatorController::onTrapEvent(unsigned trapNum) void SimulatorController::onGuestSystemEvent(char data, unsigned port) { EventList::iterator it = m_EvList.begin(); - while(it != m_EvList.end()) // check for active events - { + while (it != m_EvList.end()) { // check for active events BaseEvent* pev = *it; GuestEvent* pge = dynamic_cast(pev); - if(pge != NULL) - { + if (pge != NULL) { pge->setData(data); pge->setPort(port); it = m_EvList.makeActive(it); @@ -204,11 +191,9 @@ void SimulatorController::onGuestSystemEvent(char data, unsigned port) void SimulatorController::onJumpEvent(bool flagTriggered, unsigned opcode) { EventList::iterator it = m_EvList.begin(); - while(it != m_EvList.end()) // check for active events - { + while (it != m_EvList.end()) { // check for active events JumpEvent* pje = dynamic_cast(*it); - if(pje != NULL) - { + if (pje != NULL) { pje->setOpcode(opcode); pje->setFlagTriggered(flagTriggered); it = m_EvList.makeActive(it); @@ -238,7 +223,7 @@ void SimulatorController::removeFlow(ExperimentFlow* flow) BaseEvent* SimulatorController::addEventAndWait(BaseEvent* ev) { addEvent(ev); - return (waitAny()); + return waitAny(); } void SimulatorController::terminate(int exCode) diff --git a/src/core/sal/SimulatorController.hpp b/src/core/sal/SimulatorController.hpp index 51ce0ac5..82183538 100644 --- a/src/core/sal/SimulatorController.hpp +++ b/src/core/sal/SimulatorController.hpp @@ -105,33 +105,6 @@ public: * @param opcode the opcode of the conrecete jump instruction */ void onJumpEvent(bool flagTriggered, unsigned opcode); - /** - * This method is called when an experiment flow adds a new event by - * calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev). - * More specifically, the event will be added to the event-list first - * (buffer-list, to be precise) and then this event handler is called. - * @param pev the event which has been added - * @return You should return \c true to continue and \c false to prevent - * the addition of the event \a pev, yielding an error in the - * experiment flow (i.e. -1 is returned). - */ - virtual bool onEventAddition(BaseEvent* pev) { return true; } - /** - * This method is called when an experiment flow removes an event from - * the event-management by calling \c removeEvent(prev), \c clearEvents() - * or by deleting a complete flow (\c removeFlow). More specifically, this - * event handler will be called *before* the event is actually deleted. - * @param pev the event to be deleted when returning from the event handler - */ - virtual void onEventDeletion(BaseEvent* pev) { } - /** - * This method is called when an previously added event is about to be - * triggered by the simulator-backend. More specifically, this event handler - * will be called *before* the event is actually triggered, i.e. before the - * corresponding coroutine is toggled. - * @param pev the event to be triggered when returning from the event handler - */ - virtual void onEventTrigger(BaseEvent* pev) { } /* ******************************************************************** * Simulator Controller & Access API: * ********************************************************************/ @@ -236,10 +209,8 @@ public: /** * Waits on any events which have been added to the event management. If * one of those events occour, waitAny() will return the id of that event. - * @return the previously occurred event - * - * FIXME: Maybe this should return immediately if there are not events? - * (additional parameter flag?) + * @return the previously occurred event, or \c NULL if there are no + * events to wait for */ BaseEvent* waitAny(); /** diff --git a/src/core/sal/bochs/BochsController.cc b/src/core/sal/bochs/BochsController.cc index 13246e55..e0ed4a78 100644 --- a/src/core/sal/bochs/BochsController.cc +++ b/src/core/sal/bochs/BochsController.cc @@ -209,7 +209,7 @@ void BochsController::fireInterruptDone() m_Flows.toggle(m_CurrFlow); } -void BochsController::m_onTimerTrigger(void* thisPtr) +void BochsController::onTimerTrigger(void* thisPtr) { // FIXME: The timer logic can be modified to use only one timer in Bochs. // (For now, this suffices.) @@ -225,57 +225,6 @@ void BochsController::m_onTimerTrigger(void* thisPtr) simulator.m_EvList.fireActiveEvents(); } -timer_id_t BochsController::m_registerTimer(TimerEvent* pev) -{ - assert(pev != NULL); - return static_cast( - bx_pc_system.register_timer(pev, m_onTimerTrigger, pev->getTimeout(), !pev->getOnceFlag(), - 1/*start immediately*/, "Fail*: BochsController"/*name*/)); -} - -bool BochsController::m_unregisterTimer(TimerEvent* pev) -{ - assert(pev != NULL); - bx_pc_system.deactivate_timer(static_cast(pev->getId())); - return bx_pc_system.unregisterTimer(static_cast(pev->getId())); -} - -bool BochsController::onEventAddition(BaseEvent* pev) -{ - TimerEvent* tmev; - // Register the timer event in the Bochs simulator: - if ((tmev = dynamic_cast(pev)) != NULL) { - tmev->setId(m_registerTimer(tmev)); - if(tmev->getId() == -1) - return false; // unable to register the timer (error in Bochs' function call) - } - // Note: Maybe more stuff to do here for other event types. - return true; -} - -void BochsController::onEventDeletion(BaseEvent* pev) -{ - TimerEvent* tmev; - // Unregister the time event: - if ((tmev = dynamic_cast(pev)) != NULL) { - m_unregisterTimer(tmev); - } - // Note: Maybe more stuff to do here for other event types. -} - -void BochsController::onEventTrigger(BaseEvent* pev) -{ - TimerEvent* tmev; - // Unregister the time event, if once-flag is true: - if ((tmev = dynamic_cast(pev)) != NULL) { - if (tmev->getOnceFlag()) // deregister the timer (timer = single timeout) - m_unregisterTimer(tmev); - else // re-add the event (repetitive timer), tunneling the onEventAddition-handler - m_EvList.add(tmev, tmev->getParent()); - } - // Note: Maybe more stuff to do here for other event types. -} - const std::string& BochsController::getMnemonic() const { static std::string str; diff --git a/src/core/sal/bochs/BochsController.hpp b/src/core/sal/bochs/BochsController.hpp index 43384910..245e9592 100644 --- a/src/core/sal/bochs/BochsController.hpp +++ b/src/core/sal/bochs/BochsController.hpp @@ -8,6 +8,7 @@ #include #include "FailBochsGlobals.hpp" +#include "BochsEvents.hpp" #include "../SimulatorController.hpp" #include "../Event.hpp" @@ -34,40 +35,6 @@ private: unsigned m_Counter; //! current instr-ptr counter std::ostream* m_pDest; //! debug output object (defaults to \c std::cout) #endif - /** - * Static internal event handler for TimerEvents. This static function is - * called when a previously registered (Bochs) timer triggers. This function - * searches for the provided TimerEvent object within the EventList and - * fires such an event by calling \c fireActiveEvents(). - * @param thisPtr a pointer to the TimerEvent-object triggered - * - * FIXME: Due to Bochs internal timer and ips-configuration related stuff, - * the simulator sometimes panics with "keyboard error:21" (see line - * 1777 in bios/rombios.c, function keyboard_init()) if a TimerEvent - * is added *before* the bios has been loaded and initialized. To - * reproduce this error, try adding a TimerEvent as the initial step - * in your experiment code and wait for it (addEventAndWait()). - */ - static void m_onTimerTrigger(void *thisPtr); - /** - * Registers a timer in the Bochs simulator. This timer fires \a TimerEvents - * to inform the corresponding experiment-flow. Note that the number of timers - * (in Bochs) is limited to \c BX_MAX_TIMERS (defaults to 64 in v2.4.6). - * @param pev a pointer to the (experiment flow-) allocated TimerEvent object, - * providing all required information to start the time, e.g. the - * timeout value. - * @return \c The unique id of the timer recently created. This id is carried - * along with the TimerEvent, @see getId(). On error, -1 is returned - * (e.g. because a timer with the same id is already existing) - */ - timer_id_t m_registerTimer(TimerEvent* pev); - /** - * Deletes a timer. No further events will be triggered by the timer. - * @param pev a pointer to the TimerEvent-object to be removed - * @return \c true if the timer with \a pev->getId() has been removed - * successfully, \c false otherwise - */ - bool m_unregisterTimer(TimerEvent* pev); public: // Initialize the controller. BochsController(); @@ -81,7 +48,8 @@ public: * @param instrPtr the new instruction pointer * @param address_space the address space the CPU is currently in */ - void onInstrPtrChanged(address_t instrPtr, address_t address_space, BX_CPU_C *context, bxICacheEntry_c *cache_entry); + void onInstrPtrChanged(address_t instrPtr, address_t address_space, BX_CPU_C *context, + bxICacheEntry_c *cache_entry); /** * I/O port communication handler. This method is called (from * the IOPortCom aspect) every time when Bochs performs a port I/O operation. @@ -91,31 +59,20 @@ public: */ void onIOPortEvent(unsigned char data, unsigned port, bool out); /** - * This method is called when an experiment flow adds a new event by - * calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev). - * More specifically, the event will be added to the event-list first - * (buffer-list, to be precise) and then this event handler is called. - * @param pev the event which has been added - * @return You should return \c true to continue and \c false to prevent - * the addition of the event \a pev. + * Static internal event handler for TimerEvents. This static function is + * called when a previously registered (Bochs) timer triggers. This function + * searches for the provided TimerEvent object within the EventList and + * fires such an event by calling \c fireActiveEvents(). + * @param thisPtr a pointer to the TimerEvent-object triggered + * + * FIXME: Due to Bochs internal timer and ips-configuration related stuff, + * the simulator sometimes panics with "keyboard error:21" (see line + * 1777 in bios/rombios.c, function keyboard_init()) if a TimerEvent + * is added *before* the bios has been loaded and initialized. To + * reproduce this error, try adding a \c TimerEvent as the initial step + * in your experiment code and wait for it (\c addEventAndWait()). */ - bool onEventAddition(BaseEvent* pev); - /** - * This method is called when an experiment flow removes an event from - * the event-management by calling \c removeEvent(prev), \c clearEvents() - * or by deleting a complete flow (\c removeFlow). More specifically, - * this event handler will be called *before* the event is actually deleted. - * @param pev the event to be deleted when returning from the event handler - */ - void onEventDeletion(BaseEvent* pev); - /** - * This method is called when an previously added event is about to be - * triggered by the simulator-backend. More specifically, this event handler - * will be called *before* the event is actually triggered, i.e. before the - * corresponding coroutine is toggled. - * @param pev the event to be triggered when returning from the event handler - */ - void onEventTrigger(BaseEvent* pev); + static void onTimerTrigger(void *thisPtr); /* ******************************************************************** * Simulator Controller & Access API: * ********************************************************************/ diff --git a/src/core/sal/bochs/BochsEvents.cc b/src/core/sal/bochs/BochsEvents.cc new file mode 100644 index 00000000..8ca448de --- /dev/null +++ b/src/core/sal/bochs/BochsEvents.cc @@ -0,0 +1,36 @@ +#include "BochsEvents.hpp" +#include "../SALInst.hpp" + +namespace fail { + +bool TimerEvent::onEventAddition() +{ + // Register the timer event in the Bochs simulator: + setId(m_registerTimer(this)); + if(getId() == -1) + return false; // unable to register the timer (error in Bochs' function call) + return true; +} + +void TimerEvent::onEventDeletion() +{ + // Unregister the time event: + m_unregisterTimer(this); +} + +timer_id_t TimerEvent::m_registerTimer(TimerEvent* pev) +{ + assert(pev != NULL && "FATAL ERROR: TimerEvent object ptr cannot be NULL!"); + return static_cast( + bx_pc_system.register_timer(pev, BochsController::onTimerTrigger, pev->getTimeout(), + false, 1/*start immediately*/, "Fail*: BochsController"/*name*/)); +} + +bool TimerEvent::m_unregisterTimer(TimerEvent* pev) +{ + assert(pev != NULL && "FATAL ERROR: TimerEvent object ptr cannot be NULL!"); + bx_pc_system.deactivate_timer(static_cast(pev->getId())); + return bx_pc_system.unregisterTimer(static_cast(pev->getId())); +} + +} // end-of-namespace: fail diff --git a/src/core/sal/bochs/BochsEvents.hpp b/src/core/sal/bochs/BochsEvents.hpp new file mode 100644 index 00000000..7be3002d --- /dev/null +++ b/src/core/sal/bochs/BochsEvents.hpp @@ -0,0 +1,78 @@ +#ifndef __BOCHS_EVENTS_HPP__ + #define __BOCHS_EVENTS_HPP__ + +#include "../Event.hpp" + +#include "BochsController.hpp" + +namespace fail { + +/** + * \class TimerEvent + * Concrete TimerEvent implementation of GenericTimerEvent for the Bochs + * simulator backend. + */ +class TimerEvent : public GenericTimerEvent { +private: + /** + * Registers a timer in the Bochs simulator. This timer fires \a TimerEvents + * to inform the corresponding experiment-flow. Note that the number of timers + * (in Bochs) is limited to \c BX_MAX_TIMERS (defaults to 64 in v2.4.6). + * @param pev a pointer to the (experiment flow-) allocated TimerEvent object, + * providing all required information to start the time, e.g. the + * timeout value. + * @return \c The unique id of the timer recently created. This id is carried + * along with the TimerEvent, @see getId(). On errors, -1 is returned + * (e.g. because a timer with the same id is already existing) + */ + static timer_id_t m_registerTimer(TimerEvent* pev); + /** + * Deletes a timer. No further events will be triggered by the timer. + * @param pev a pointer to the TimerEvent-object to be removed + * @return \c true if the timer with \a pev->getId() has been removed + * successfully, \c false otherwise + */ + static bool m_unregisterTimer(TimerEvent* pev); +public: + /** + * Creates a new timer event. This can be used to implement a timeout- + * mechanism in the experiment-flow. The timer starts automatically when + * added to FailBochs. + * @param timeout the time intervall in milliseconds (ms) + * @param once \c true, if the TimerEvent should be triggered once, + * \c false if it should occur regularly + * @see SimulatorController::addEvent + */ + TimerEvent(unsigned timeout) + : GenericTimerEvent(timeout) { } + ~TimerEvent() { onEventDeletion(); } + + /** + * This method is called when an experiment flow adds a new event by + * calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev). + * More specifically, the event will be added to the event-list first + * (buffer-list, to be precise) and then this event handler is called. + * @return You should return \c true to continue and \c false to prevent + * the addition of the event \a pev, yielding an error in the + * experiment flow (i.e. -1 is returned). + */ + virtual bool onEventAddition(); + /** + * This method is called when an experiment flow removes an event from + * the event-management by calling \c removeEvent(prev), \c clearEvents() + * or by deleting a complete flow (\c removeFlow). More specifically, this + * event handler will be called *before* the event is actually deleted. + */ + virtual void onEventDeletion(); + /** + * This method is called when an previously added event is about to be + * triggered by the simulator-backend. More specifically, this event handler + * will be called *before* the event is actually triggered, i.e. before the + * corresponding coroutine is toggled. + */ + void onEventTrigger() { onEventDeletion(); } +}; + +} // end-of-namespace: fail + +#endif // __BOCHS_EVENTS_HPP__