Events -> Listeners, waitAny -> resume, addEventAndWait -> addListenerAndResume, ... (refactoring), updated experiments/plugin accordingly, coding-style fixed.

git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1448 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
adrian
2012-07-12 10:37:54 +00:00
parent d30e361ce5
commit a78911702a
26 changed files with 894 additions and 900 deletions

View File

@ -1,16 +1,15 @@
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include "BufferCache.hpp" #include "BufferCache.hpp"
#include "Event.hpp" #include "Listener.hpp"
#include "EventManager.hpp" #include "ListenerManager.hpp"
namespace fail { namespace fail {
template<class T> template<class T>
typename BufferCache<T>::iterator BufferCache<T>::makeActive(EventManager &ev_list, BufferCache<T>::iterator idx) typename BufferCache<T>::iterator BufferCache<T>::makeActive(ListenerManager &ev_list, BufferCache<T>::iterator idx)
{ {
assert(idx != end() && assert(idx != end() && "FATAL ERROR: Index larger than cache!");
"FATAL ERROR: Index larger than cache!");
T ev = *idx; T ev = *idx;
assert(ev && "FATAL ERROR: Object pointer cannot be NULL!"); assert(ev && "FATAL ERROR: Object pointer cannot be NULL!");
ev->decreaseCounter(); ev->decreaseCounter();
@ -20,14 +19,14 @@ typename BufferCache<T>::iterator BufferCache<T>::makeActive(EventManager &ev_li
ev->resetCounter(); ev->resetCounter();
// Note: This is the one and only situation in which remove() should NOT // Note: This is the one and only situation in which remove() should NOT
// store the removed item in the delete-list. // store the removed item in the delete-list.
EventManager::iterator it = std::find(ev_list.begin(), ev_list.end(), static_cast<BaseEvent*>(ev)); ListenerManager::iterator it = std::find(ev_list.begin(), ev_list.end(), static_cast<BaseListener*>(ev));
ev_list.m_remove(it, true); // remove event from buffer-list ev_list.m_remove(it, true); // remove listener from buffer-list
ev_list.m_FireList.push_back(ev); ev_list.m_FireList.push_back(ev);
return erase(idx); return erase(idx);
} }
// Declare here whatever instances of the template you are going to use: // Declare here whatever instances of the template you are going to use:
template class BufferCache<BPEvent*>; template class BufferCache<BPListener*>;
template class BufferCache<IOPortEvent*>; template class BufferCache<IOPortListener*>;
} // end-of-namespace: fail } // end-of-namespace: fail

View File

@ -6,7 +6,7 @@
namespace fail { namespace fail {
class EventManager; class ListenerManager;
/** /**
* \class BufferCache * \class BufferCache
@ -14,19 +14,19 @@ class EventManager;
* \brief A simple dynamic array * \brief A simple dynamic array
* *
* This class is intended to serve as a kind of cache for the * This class is intended to serve as a kind of cache for the
* untyped and therefore quite slow event handling mechanism of Fail*. * untyped and therefore quite slow listener handling mechanism of Fail*.
*/ */
template<class T> template<class T>
class BufferCache { class BufferCache {
public: public:
/** /**
* The list type inherent to this class. Like bufferlist_t in EventManager.hpp, * The list type inherent to this class. Like bufferlist_t in ListenerManager.hpp,
* but dynamically typed. * but dynamically typed.
*/ */
typedef std::list<T> cachelist_t; typedef std::list<T> cachelist_t;
/** /**
* The iterator of this class used to loop through the list of * The iterator of this class used to loop through the list of
* added events. To retrieve an iterator to the first element, call * added listeners. To retrieve an iterator to the first element, call
* begin(). end() returns the iterator, pointing after the last element. * begin(). end() returns the iterator, pointing after the last element.
* (This behaviour equals the STL iterator in C++.) * (This behaviour equals the STL iterator in C++.)
*/ */
@ -79,14 +79,14 @@ public:
*/ */
inline iterator end() { return m_Buffer.end(); } inline iterator end() { return m_Buffer.end(); }
/** /**
* Acts as a replacement for EventManager::makeActive, manipulating * Acts as a replacement for ListenerManager::makeActive, manipulating
* the buffer cache exclusively. EventManager::fireActiveEvents needs * the buffer cache exclusively. ListenerManager::triggerActiveListeners needs
* to be called to fire the active events (see there). * to be called to fire the active listeners (see there).
* This method is declared as a friend method in EventManager. * This method is declared as a friend method in ListenerManager.
* @param idx the index of the event to trigger * @param idx the index of the listener to trigger
* @returns an updated index which can be used to update a loop counter * @returns an updated index which can be used to update a loop counter
*/ */
iterator makeActive(EventManager &ev_list, iterator idx); iterator makeActive(ListenerManager &ev_list, iterator idx);
}; };
} // end-of-namespace: fail } // end-of-namespace: fail

View File

@ -1,19 +1,19 @@
if(BUILD_BOCHS) if(BUILD_BOCHS)
set(SRCS set(SRCS
BufferCache.cc BufferCache.cc
Event.cc Listener.cc
EventManager.cc ListenerManager.cc
Memory.cc Memory.cc
Register.cc Register.cc
SimulatorController.cc SimulatorController.cc
bochs/BochsController.cc bochs/BochsController.cc
bochs/BochsEvents.cc bochs/BochsListener.cc
) )
else() else()
set(SRCS set(SRCS
BufferCache.cc BufferCache.cc
Event.cc Listener.cc
EventManager.cc ListenerManager.cc
Memory.cc Memory.cc
Register.cc Register.cc
SimulatorController.cc SimulatorController.cc

View File

@ -1,196 +0,0 @@
#include <set>
#include "EventManager.hpp"
#include "SALInst.hpp"
namespace fail {
void EventManager::addToCaches(BaseEvent *ev)
{
BPEvent *bps_ev;
if ((bps_ev = dynamic_cast<BPEvent*>(ev)) != NULL)
m_Bp_cache.add(bps_ev);
IOPortEvent *io_ev;
if ((io_ev = dynamic_cast<IOPortEvent*>(ev)) != NULL)
m_Io_cache.add(io_ev);
}
void EventManager::removeFromCaches(BaseEvent *ev)
{
BPEvent *bpr_ev;
if ((bpr_ev = dynamic_cast<BPEvent*>(ev)) != NULL)
m_Bp_cache.remove(bpr_ev);
IOPortEvent *io_ev;
if ((io_ev = dynamic_cast<IOPortEvent*>(ev)) != NULL)
m_Io_cache.remove(io_ev);
}
void EventManager::clearCaches()
{
m_Bp_cache.clear();
m_Io_cache.clear();
}
void EventManager::add(BaseEvent* ev, ExperimentFlow* pExp)
{
assert(ev != NULL && "FATAL ERROR: Event (of base type BaseEvent*) cannot be NULL!");
// a zero counter does not make sense
assert(ev->getCounter() != 0);
ev->setParent(pExp); // event is linked to experiment flow
addToCaches(ev);
m_BufferList.push_back(ev);
}
void EventManager::remove(BaseEvent* ev)
{
// possible cases:
// - ev == 0 -> remove all events
// * clear m_BufferList
// * copy m_FireList to m_DeleteList
if (ev == 0) {
for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++)
(*it)->onEventDeletion();
for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++)
(*it)->onEventDeletion();
clearCaches();
m_BufferList.clear();
// all remaining active events must not fire anymore
m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end());
// - ev != 0 -> remove single event
// * find/remove ev in m_BufferList
// * if ev in m_FireList, copy to m_DeleteList
} else {
ev->onEventDeletion();
removeFromCaches(ev);
m_BufferList.remove(ev);
firelist_t::const_iterator it =
std::find(m_FireList.begin(), m_FireList.end(), ev);
if (it != m_FireList.end()) {
m_DeleteList.push_back(ev);
}
}
}
EventManager::iterator EventManager::remove(iterator it)
{
return m_remove(it, false);
}
EventManager::iterator EventManager::m_remove(iterator it, bool skip_deletelist)
{
if (!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 (onEventDeletion), if m_remove is called with the primary intention
// to *delete* (not "move") an event.
(*it)->onEventDeletion();
m_DeleteList.push_back(*it);
// Cached events have their own BufferCache<T>::makeActive() implementation, which
// calls this method and afterwards erase() in the cache class. This is why, when
// called from any kind of makeActive() method, it is unnecessary to call
// BufferCache<T>::remove() from m_remove().
// NOTE: in case the semantics of skip_deletelist change, please adapt the following lines
removeFromCaches(*it);
}
return m_BufferList.erase(it);
}
void EventManager::remove(ExperimentFlow* flow)
{
// all events?
if (flow == 0) {
for (bufferlist_t::iterator it = m_BufferList.begin();
it != m_BufferList.end(); it++)
(*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) {
(*it)->onEventDeletion();
it = m_BufferList.erase(it);
} else {
++it;
}
}
}
// events 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()) {
continue; // (already in the delete-list? -> skip!)
}
// ... 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) {
(*it)->onEventDeletion();
m_DeleteList.push_back(*it);
}
}
}
EventManager::~EventManager()
{
// nothing to do here yet
}
EventManager::iterator EventManager::makeActive(iterator it)
{
assert(it != m_BufferList.end() &&
"FATAL ERROR: Iterator has already reached the end!");
BaseEvent* ev = *it;
assert(ev && "FATAL ERROR: Event object pointer cannot be NULL!");
ev->decreaseCounter();
if (ev->getCounter() > 0) {
return ++it;
}
ev->resetCounter();
// Note: This is the one and only situation in which remove() should NOT
// 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;
}
void EventManager::fireActiveEvents()
{
for (firelist_t::iterator it = m_FireList.begin();
it != m_FireList.end(); it++) {
if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it)
== m_DeleteList.end()) { // not found in delete-list?
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):
m_pFired->onEventTrigger();
ExperimentFlow* pFlow = m_pFired->getParent();
assert(pFlow && "FATAL ERROR: The event has no parent experiment (owner)!");
simulator.m_Flows.toggle(pFlow);
}
}
m_FireList.clear();
m_DeleteList.clear();
// Note: Do NOT call any event handlers here!
}
size_t EventManager::getContextCount() const
{
std::set<ExperimentFlow*> uniqueFlows; // count unique ExperimentFlow-ptr
for (bufferlist_t::const_iterator it = m_BufferList.begin();
it != m_BufferList.end(); it++)
uniqueFlows.insert((*it)->getParent());
return uniqueFlows.size();
}
} // end-of-namespace: fail

View File

@ -1,222 +0,0 @@
#ifndef __EVENT_MANAGER_HPP__
#define __EVENT_MANAGER_HPP__
#include <cassert>
#include <list>
#include <vector>
#include <algorithm>
#include "Event.hpp"
#include "BufferCache.hpp"
namespace fail {
class ExperimentFlow;
/**
* Buffer-list for a specific experiment; acts as a simple storage container
* for events to watch for:
*/
typedef std::list<BaseEvent*> bufferlist_t;
/**
* List of events that match the current simulator event; these events will
* be triggered next (the list is used temporarily).
*/
typedef std::vector<BaseEvent*> firelist_t;
/**
* List of events that have been deleted during a toggled experiment flow while
* triggering the events in the fire-list. This list is used to skip already
* deleted events (therefore it is used temporarily either).
*/
typedef std::vector<BaseEvent*> deletelist_t;
/**
* Cache classes for the most commonly used types of events, utilising static typing.
* Apart from that, they work like bufferlist_t.
*/
typedef BufferCache<BPEvent*> bp_cache_t;
typedef bp_cache_t::iterator bp_iter_t;
typedef BufferCache<IOPortEvent*> io_cache_t;
typedef io_cache_t::iterator io_iter_t;
/**
* \class EventManager
*
* \brief This class manages the events of the Fail* implementation.
*
* If a event is triggered, the internal data structure will be updated (id est,
* the event will be removed from the so called buffer-list and added to the
* fire-list). Additionally, if an experiment-flow deletes an "active" event
* which is currently stored in the fire-list, the event (to be removed) will
* be added to a -so called- delete-list. This ensures to prevent triggering
* "active" events which have already been deleted by a previous experiment
* flow. (See makeActive() and fireActiveEvent() for implementation specific
* details.) EventManager is part of the SimulatorController and "outsources"
* it's event management.
*/
class EventManager {
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(EventManager &ev_list, bp_iter_t idx);
friend io_iter_t io_cache_t::makeActive(EventManager &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;
EventManager() : m_pFired(NULL) { }
~EventManager();
/**
* 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()
*/
void 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
* iterator.
* @param it the iterator pointing to the Event object to be removed
* @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.
* @param it the iterator pointing to the Event object to be removed
* @param skip_deletelist \c true to skip the deletion of the Event object
* or \false to behave like \c remove(iterator)
* @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
* @return iterator to the beginning
*/
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
* @return iterator to the end
*/
iterator end() { return (m_BufferList.end()); }
/**
* 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
#endif // __EVENT_MANAGER_HPP__

View File

@ -1,9 +1,9 @@
#include "Event.hpp" #include "Listener.hpp"
#include "SALInst.hpp" #include "SALInst.hpp"
namespace fail { namespace fail {
bool TroubleEvent::isMatching(unsigned troubleNum) const bool TroubleListener::isMatching(unsigned troubleNum) const
{ {
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) { for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
if (m_WatchNumbers[i] == troubleNum || if (m_WatchNumbers[i] == troubleNum ||
@ -13,7 +13,7 @@ bool TroubleEvent::isMatching(unsigned troubleNum) const
return false; return false;
} }
bool TroubleEvent::removeWatchNumber(unsigned troubleNum) bool TroubleListener::removeWatchNumber(unsigned troubleNum)
{ {
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) { for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
if (m_WatchNumbers[i] == troubleNum) { if (m_WatchNumbers[i] == troubleNum) {
@ -24,7 +24,7 @@ bool TroubleEvent::removeWatchNumber(unsigned troubleNum)
return false; return false;
} }
bool TroubleEvent::addWatchNumber(unsigned troubleNumber) bool TroubleListener::addWatchNumber(unsigned troubleNumber)
{ {
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) { for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
if (m_WatchNumbers[i] == troubleNumber) if (m_WatchNumbers[i] == troubleNumber)
@ -34,7 +34,7 @@ bool TroubleEvent::addWatchNumber(unsigned troubleNumber)
return true; return true;
} }
bool MemAccessEvent::isMatching(address_t addr, accessType_t accesstype) const bool MemAccessListener::isMatching(address_t addr, accessType_t accesstype) const
{ {
if (!(m_WatchType & accesstype)) if (!(m_WatchType & accesstype))
return false; return false;
@ -44,20 +44,20 @@ bool MemAccessEvent::isMatching(address_t addr, accessType_t accesstype) const
return true; return true;
} }
bool BPEvent::aspaceIsMatching(address_t aspace) const bool BPListener::aspaceIsMatching(address_t aspace) const
{ {
if (m_CR3 == ANY_ADDR || m_CR3 == aspace) if (m_CR3 == ANY_ADDR || m_CR3 == aspace)
return true; return true;
return false; return false;
} }
void BPRangeEvent::setWatchInstructionPointerRange(address_t start, address_t end) void BPRangeListener::setWatchInstructionPointerRange(address_t start, address_t end)
{ {
m_WatchStartAddr = start; m_WatchStartAddr = start;
m_WatchEndAddr = end; m_WatchEndAddr = end;
} }
bool BPRangeEvent::isMatching(address_t addr, address_t aspace) const bool BPRangeListener::isMatching(address_t addr, address_t aspace) const
{ {
if (!aspaceIsMatching(aspace)) if (!aspaceIsMatching(aspace))
return false; return false;
@ -67,7 +67,7 @@ bool BPRangeEvent::isMatching(address_t addr, address_t aspace) const
return true; return true;
} }
bool BPSingleEvent::isMatching(address_t addr, address_t aspace) const bool BPSingleListener::isMatching(address_t addr, address_t aspace) const
{ {
if (aspaceIsMatching(aspace)) { if (aspaceIsMatching(aspace)) {
if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr) if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr)

View File

@ -1,5 +1,5 @@
#ifndef __EVENT_HPP__ #ifndef __LISTENER_HPP__
#define __EVENT_HPP__ #define __LISTENER_HPP__
#include <ctime> #include <ctime>
#include <string> #include <string>
@ -14,7 +14,7 @@ namespace fail {
class ExperimentFlow; class ExperimentFlow;
//! address wildcard (e.g. for BPEvent's) //! address wildcard (e.g. for BPListener's)
const address_t ANY_ADDR = static_cast<address_t>(-1); const address_t ANY_ADDR = static_cast<address_t>(-1);
//! instruction wildcard //! instruction wildcard
const unsigned ANY_INSTR = static_cast<unsigned>(-1); const unsigned ANY_INSTR = static_cast<unsigned>(-1);
@ -24,58 +24,59 @@ const unsigned ANY_TRAP = static_cast<unsigned>(-1);
const unsigned ANY_INTERRUPT = static_cast<unsigned>(-1); const unsigned ANY_INTERRUPT = static_cast<unsigned>(-1);
/** /**
* \class BaseEvent * \class BaseListener
* This is the base class for all event types. * This is the base class for all listener types.
*/ */
class BaseEvent { class BaseListener {
protected: protected:
time_t m_tStamp; //!< time stamp of event time_t m_tStamp; //!< time stamp of listener
unsigned int m_OccCounter; //!< event fires when 0 is reached unsigned int m_OccCounter; //!< listener fires when 0 is reached
unsigned int m_OccCounterInit; //!< initial value for m_OccCounter unsigned int m_OccCounterInit; //!< initial value for m_OccCounter
ExperimentFlow* m_Parent; //!< this event belongs to experiment m_Parent ExperimentFlow* m_Parent; //!< this listener belongs to experiment m_Parent
public: public:
BaseEvent() : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL) BaseListener() : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL)
{ updateTime(); } { updateTime(); }
virtual ~BaseEvent() { } virtual ~BaseListener() { }
/** /**
* This method is called when an experiment flow adds a new event by * This method is called when an experiment flow adds a new listener by
* calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev). * calling \c simulator.addListener() or \c simulator.addListenerAndResume().
* More specifically, the event will be added to the event-list first * More specifically, the listener will be added to the listener manager first
* (buffer-list, to be precise) and then this event handler is called. * (buffer-list, to be precise) and then this listener handler is called.
* @return You should return \c true to continue and \c false to prevent * @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 * the addition of the listener \c this, yielding an error in the
* experiment flow (i.e. \c INVALID_EVENT is returned). * experiment flow (i.e., \c false is returned).
*/ */
virtual bool onEventAddition() { return true; } virtual bool onAddition() { return true; }
/** /**
* This method is called when an experiment flow removes an event from * This method is called when an experiment flow removes an listener from
* the event-management by calling \c removeEvent(prev), \c clearEvents() * the listener-management by calling \c removeListener(), \c clearListeners()
* or by deleting a complete flow (\c removeFlow). More specifically, this * or by deleting a complete flow (\c removeFlow()). More specifically, this
* event handler will be called *before* the event is actually deleted. * listener handler will be called *before* the listener is actually deleted.
*/ */
virtual void onEventDeletion() { } virtual void onDeletion() { }
/** /**
* This method is called when an previously added event is about to be * This method is called when an previously added listener is about to be
* triggered by the simulator-backend. More specifically, this event handler * triggered by the simulator-backend. More specifically, this listener handler
* will be called *before* the event is actually triggered, i.e. before the * will be called *before* the listener is actually triggered, i.e. before the
* corresponding coroutine is toggled. * corresponding coroutine is toggled.
*/ */
virtual void onEventTrigger() { } virtual void onTrigger() { }
// TODO: Hier noch ne neue Methode oder reicht es, die Semantik von onTrigger umzudef.?
/** /**
* Retrieves the time stamp of this event. The time stamp is set when * Retrieves the time stamp of this listener. The time stamp is set when
* the event gets created, id est the constructor is called. The meaning * the listener gets created, i.e. the constructor is called. The meaning
* of this value depends on the actual event type. * of this value depends on the actual listener type.
* @return the time stamp * @return the time stamp
*/ */
std::time_t getTime() const { return (m_tStamp); } std::time_t getTime() const { return (m_tStamp); }
/** /**
* Decreases the event counter by one. When this counter reaches zero, the * Decreases the listener counter by one. When this counter reaches zero, the
* event will be triggered. * listener will be triggered.
*/ */
void decreaseCounter() { --m_OccCounter; } void decreaseCounter() { --m_OccCounter; }
/** /**
* Sets the event counter to the specified value (default is one). * Sets the listener counter to the specified value.
* @param val the new counter value * @param val the new counter value (default is 1)
*/ */
void setCounter(unsigned int val = 1) { m_OccCounter = m_OccCounterInit = val; } void setCounter(unsigned int val = 1) { m_OccCounter = m_OccCounterInit = val; }
/** /**
@ -84,56 +85,56 @@ public:
*/ */
unsigned int getCounter() const { return (m_OccCounter); } unsigned int getCounter() const { return (m_OccCounter); }
/** /**
* Resets the event counter to its default value, or the last * Resets the listener counter to its default value, or the last
* value that was set through setCounter(). * value that was set through \c setCounter().
*/ */
void resetCounter() { m_OccCounter = m_OccCounterInit; } void resetCounter() { m_OccCounter = m_OccCounterInit; }
/** /**
* Sets the time stamp for this event to the current system time. * Sets the time stamp for this listener to the current system time.
*/ */
void updateTime() { time(&m_tStamp); } void updateTime() { time(&m_tStamp); }
/** /**
* Returns the parent experiment of this event (context). * Returns the parent experiment of this listener (context).
* If the event was created temporarily or wasn't linked to a context, * If the listener was created temporarily or wasn't linked to a context,
* the return value is \c NULL (unknown identity). * the return value is \c NULL (unknown identity).
*/ */
ExperimentFlow* getParent() const { return (m_Parent); } ExperimentFlow* getParent() const { return (m_Parent); }
/** /**
* Sets the parent (context) of this event. The default context * Sets the parent (context) of this listener. The default context
* is \c NULL (unknown identity). * is \c NULL (unknown identity).
*/ */
void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; } void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; }
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Specialized events: // Specialized listeners:
// //
/** /**
* \class BEvent * \class BListener
* A Breakpoint event to observe instruction changes within a given address space. * A Breakpoint listener to observe instruction changes within a given address space.
*/ */
class BPEvent : virtual public BaseEvent { class BPListener : virtual public BaseListener {
private: private:
address_t m_CR3; address_t m_CR3;
address_t m_TriggerInstrPtr; address_t m_TriggerInstrPtr;
public: public:
/** /**
* Creates a new breakpoint event. The range information is specific to * Creates a new breakpoint listener. The range information is specific to
* the subclasses. * the subclasses.
* @param address_space the address space to be oberserved, given as the * @param address_space the address space to be oberserved, given as the
* content of a CR3 register. The event will not be triggered unless * content of a CR3 register. The listener will not be triggered unless
* \a ip is part of the given address space. * \a ip is part of the given address space.
* ANY_ADDR can be used as a placeholder to allow debugging * ANY_ADDR can be used as a placeholder to allow debugging
* in a random address space. * in a random address space.
*/ */
BPEvent(address_t address_space = ANY_ADDR) BPListener(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. * Returns the address space register of this listener.
*/ */
address_t getCR3() const { return m_CR3; } address_t getCR3() const { return m_CR3; }
/** /**
* Sets the address space register for this event. * Sets the address space register for this listener.
*/ */
void setCR3(address_t iptr) { m_CR3 = iptr; } void setCR3(address_t iptr) { m_CR3 = iptr; }
/** /**
@ -145,96 +146,108 @@ public:
*/ */
virtual bool isMatching(address_t addr = 0, address_t address_space = ANY_ADDR) const = 0; virtual bool isMatching(address_t addr = 0, address_t address_space = ANY_ADDR) const = 0;
/** /**
* Returns the instruction pointer that triggered this event. * Returns the instruction pointer that triggered this listener.
*/ */
address_t getTriggerInstructionPointer() const { return m_TriggerInstrPtr; } address_t getTriggerInstructionPointer() const { return m_TriggerInstrPtr; }
/** /**
* Sets the instruction pointer that triggered this event. Should not * Sets the instruction pointer that triggered this listener. Should not
* be used by experiment code. * be used by experiment code.
*/ */
void setTriggerInstructionPointer(address_t iptr) { m_TriggerInstrPtr = iptr; } void setTriggerInstructionPointer(address_t iptr) { m_TriggerInstrPtr = iptr; }
}; };
/** /**
* \class BPSingleEvent * \class BPSingleListener
* A Breakpoint event to observe specific instruction pointers. * A Breakpoint listener to observe specific instruction pointers.
*/ */
class BPSingleEvent : virtual public BPEvent { class BPSingleListener : virtual public BPListener {
private: private:
address_t m_WatchInstrPtr; address_t m_WatchInstrPtr;
public: public:
/** /**
* Creates a new breakpoint event. * Creates a new breakpoint listener.
* @param ip the instruction pointer of the breakpoint. If the control * @param ip the instruction pointer of the breakpoint. If the control
* flow reaches this address and its counter value is zero, the * flow reaches this address and its counter value is zero, the
* event will be triggered. \a eip can be set to the ANY_ADDR * listener will be triggered. \a eip can be set to the ANY_ADDR
* wildcard to allow arbitrary addresses. * wildcard to allow arbitrary addresses. Defaults to 0.
* @param address_space the address space to be oberserved, given as the * @param address_space the address space to be oberserved, given as the
* content of a CR3 register. The event will not be triggered unless * content of a CR3 register. The listener will not be triggered unless
* \a ip is part of the given address space. * \a ip is part of the given address space. Defaults to \c ANY_ADDR.
* Here, too, ANY_ADDR is a placeholder to allow debugging * Here, too, ANY_ADDR is a placeholder to allow debugging
* in a random address space. * in a random address space.
*/ */
BPSingleEvent(address_t ip = 0, address_t address_space = ANY_ADDR) BPSingleListener(address_t ip = 0, address_t address_space = ANY_ADDR)
: BPEvent(address_space), m_WatchInstrPtr(ip) { } : BPListener(address_space), m_WatchInstrPtr(ip) { }
/** /**
* Returns the instruction pointer this event waits for. * Returns the instruction pointer this listener waits for.
* @return the instruction pointer specified in the constructor or by
* calling \c setWatchInstructionPointer()
*/ */
address_t getWatchInstructionPointer() const address_t getWatchInstructionPointer() const
{ return m_WatchInstrPtr; } { return m_WatchInstrPtr; }
/** /**
* Sets the instruction pointer this event waits for. * Sets the instruction pointer this listener waits for.
* @param iptr the new instruction ptr to wait for
*/ */
void setWatchInstructionPointer(address_t iptr) void setWatchInstructionPointer(address_t iptr)
{ m_WatchInstrPtr = iptr; } { m_WatchInstrPtr = iptr; }
/** /**
* Checks whether a given address is matching. * Checks whether a given address is matching.
* @param addr address to check
* @param address_space 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(address_t addr, address_t address_space) const;
}; };
/** /**
* \class BPRangeEvent * \class BPRangeListener
* A event type to observe ranges of instruction pointers. * A listener type to observe ranges of instruction pointers.
*/ */
class BPRangeEvent : virtual public BPEvent { class BPRangeListener : virtual public BPListener {
private: private:
address_t m_WatchStartAddr; address_t m_WatchStartAddr;
address_t m_WatchEndAddr; address_t m_WatchEndAddr;
public: public:
/** /**
* Creates a new breakpoint-range event. The range's ends are both * Creates a new breakpoint-range listener. The range's ends are both
* inclusive, i.e. an address matches if start <= addr <= end. * inclusive, i.e. an address matches if start <= addr <= end.
* ANY_ADDR denotes the lower respectively the upper end of the address * ANY_ADDR denotes the lower respectively the upper end of the address
* space. * space.
*/ */
BPRangeEvent(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR) BPRangeListener(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR)
: BPEvent(address_space), m_WatchStartAddr(start), m_WatchEndAddr(end) : BPListener(address_space), m_WatchStartAddr(start), m_WatchEndAddr(end)
{ } { }
/** /**
* Returns the instruction pointer watch range of this event. * Returns the instruction pointer watch range of this listener.
* @return the listerner's range
*/ */
std::pair<address_t, address_t> getWatchInstructionPointerRange() const std::pair<address_t, address_t> getWatchInstructionPointerRange() const
{ return std::make_pair(m_WatchStartAddr, m_WatchEndAddr); } { return std::make_pair(m_WatchStartAddr, m_WatchEndAddr); }
/** /**
* Sets the instruction pointer watch range. Both ends of the range * Sets the instruction pointer watch range. Both ends of the range
* may be ANY_ADDR (cf. constructor). * may be ANY_ADDR (cf. constructor).
* @param start the (inclusive) lower bound, or \c ANY_ADDR
* @param end the (inclusive) upper bound, or \c ANY_ADDR
*/ */
void setWatchInstructionPointerRange(address_t start, void setWatchInstructionPointerRange(address_t start, address_t end);
address_t end);
/** /**
* Checks whether a given address is within the range. * Checks whether a given address is within the range.
* @param addr address to check
* @param address_space address space information
* @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(address_t addr, address_t address_space) const;
}; };
/** /**
* \class MemAccessEvent * \class MemAccessListener
* Observes memory read/write accesses. * Observes memory read/write accesses.
* FIXME? currently >8-bit accesses only match if their lowest address is being watched * FIXME? currently >8-bit accesses only match if their lowest address is being watched
* (e.g., a 32-bit write to 0x4 also accesses 0x7, but this cannot be matched) * (e.g., a 32-bit write to 0x4 also accesses 0x7, but this cannot be matched)
*/ */
class MemAccessEvent : virtual public BaseEvent { class MemAccessListener : virtual public BaseListener {
public: public:
enum accessType_t { enum accessType_t {
MEM_UNKNOWN = 0x0, MEM_UNKNOWN = 0x0,
@ -250,7 +263,7 @@ private:
* (MEM_READ || MEM_WRITE || MEM_READWRITE). * (MEM_READ || MEM_WRITE || MEM_READWRITE).
*/ */
accessType_t m_WatchType; accessType_t m_WatchType;
//! Specific guest system address that actually triggered the event. //! Specific guest system address that actually triggered the listener.
address_t m_TriggerAddr; address_t m_TriggerAddr;
//! Width of the memory access (# bytes). //! Width of the memory access (# bytes).
size_t m_TriggerWidth; size_t m_TriggerWidth;
@ -259,11 +272,11 @@ private:
//! Memory access type at m_TriggerAddr. //! Memory access type at m_TriggerAddr.
accessType_t m_AccessType; accessType_t m_AccessType;
public: public:
MemAccessEvent(accessType_t watchtype = MEM_READWRITE) MemAccessListener(accessType_t watchtype = MEM_READWRITE)
: m_WatchAddr(ANY_ADDR), m_WatchType(watchtype), : m_WatchAddr(ANY_ADDR), m_WatchType(watchtype),
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR), m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
m_AccessType(MEM_UNKNOWN) { } m_AccessType(MEM_UNKNOWN) { }
MemAccessEvent(address_t addr, MemAccessListener(address_t addr,
accessType_t watchtype = MEM_READWRITE) accessType_t watchtype = MEM_READWRITE)
: m_WatchAddr(addr), m_WatchType(watchtype), : m_WatchAddr(addr), m_WatchType(watchtype),
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR), m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
@ -282,21 +295,21 @@ public:
bool isMatching(address_t addr, accessType_t accesstype) const; bool isMatching(address_t addr, accessType_t accesstype) const;
/** /**
* Returns the specific memory address that actually triggered the * Returns the specific memory address that actually triggered the
* event. * listener.
*/ */
address_t getTriggerAddress() const { return (m_TriggerAddr); } address_t getTriggerAddress() const { return m_TriggerAddr; }
/** /**
* Sets the specific memory address that actually triggered the event. * Sets the specific memory address that actually triggered the listener.
* Should not be used by experiment code. * Should not be used by experiment code.
*/ */
void setTriggerAddress(address_t addr) { m_TriggerAddr = addr; } void setTriggerAddress(address_t addr) { m_TriggerAddr = addr; }
/** /**
* Returns the specific memory address that actually triggered the * Returns the specific memory address that actually triggered the
* event. * listener.
*/ */
size_t getTriggerWidth() const { return (m_TriggerWidth); } size_t getTriggerWidth() const { return m_TriggerWidth; }
/** /**
* Sets the specific memory address that actually triggered the event. * Sets the specific memory address that actually triggered the listener.
* Should not be used by experiment code. * Should not be used by experiment code.
*/ */
void setTriggerWidth(size_t width) { m_TriggerWidth = width; } void setTriggerWidth(size_t width) { m_TriggerWidth = width; }
@ -304,64 +317,62 @@ public:
* Returns the address of the instruction causing this memory * Returns the address of the instruction causing this memory
* access. * access.
*/ */
address_t getTriggerInstructionPointer() const address_t getTriggerInstructionPointer() const { return m_TriggerIP; }
{ return (m_TriggerIP); }
/** /**
* Sets the address of the instruction causing this memory * Sets the address of the instruction causing this memory
* access. Should not be used by experiment code. * access. Should not be used by experiment code.
*/ */
void setTriggerInstructionPointer(address_t addr) void setTriggerInstructionPointer(address_t addr) { m_TriggerIP = addr; }
{ m_TriggerIP = addr; }
/** /**
* Returns type (MEM_READ || MEM_WRITE) of the memory access that * Returns type (MEM_READ || MEM_WRITE) of the memory access that
* triggered this event. * triggered this listener.
*/ */
accessType_t getTriggerAccessType() const { return (m_AccessType); } accessType_t getTriggerAccessType() const { return m_AccessType; }
/** /**
* Sets type of the memory access that triggered this event. Should * Sets type of the memory access that triggered this listener. Should
* not be used by experiment code. * not be used by experiment code.
*/ */
void setTriggerAccessType(accessType_t type) { m_AccessType = type; } void setTriggerAccessType(accessType_t type) { m_AccessType = type; }
/** /**
* Returns memory access types (MEM_READ || MEM_WRITE || MEM_READWRITE) * Returns memory access types (MEM_READ || MEM_WRITE || MEM_READWRITE)
* this event watches. Should not be used by experiment code. * this listener watches. Should not be used by experiment code.
*/ */
accessType_t getWatchAccessType() const { return (m_WatchType); } accessType_t getWatchAccessType() const { return m_WatchType; }
}; };
/** /**
* \class MemReadEvent * \class MemReadListener
* Observes memory read accesses. * Observes memory read accesses.
*/ */
class MemReadEvent : virtual public MemAccessEvent { class MemReadListener : virtual public MemAccessListener {
public: public:
MemReadEvent() MemReadListener()
: MemAccessEvent(MEM_READ) { } : MemAccessListener(MEM_READ) { }
MemReadEvent(address_t addr) MemReadListener(address_t addr)
: MemAccessEvent(addr, MEM_READ) { } : MemAccessListener(addr, MEM_READ) { }
}; };
/** /**
* \class MemWriteEvent * \class MemWriteListener
* Observes memory write accesses. * Observes memory write accesses.
*/ */
class MemWriteEvent : virtual public MemAccessEvent { class MemWriteListener : virtual public MemAccessListener {
public: public:
MemWriteEvent() MemWriteListener()
: MemAccessEvent(MEM_READ) { } : MemAccessListener(MEM_READ) { }
MemWriteEvent(address_t addr) MemWriteListener(address_t addr)
: MemAccessEvent(addr, MEM_WRITE) { } : MemAccessListener(addr, MEM_WRITE) { }
}; };
/** /**
* \class TroubleEvent * \class TroubleListener
* Observes interrupt/trap activties. * Observes interrupt/trap activties.
*/ */
class TroubleEvent : virtual public BaseEvent { class TroubleListener : virtual public BaseListener {
private: private:
/** /**
* Specific guest system interrupt/trap number that actually * Specific guest system interrupt/trap number that actually
* trigger the event. * trigger the listener.
*/ */
int m_TriggerNumber; int m_TriggerNumber;
/** /**
@ -370,8 +381,8 @@ private:
*/ */
std::vector<unsigned> m_WatchNumbers; std::vector<unsigned> m_WatchNumbers;
public: public:
TroubleEvent() : m_TriggerNumber (-1) { } TroubleListener() : m_TriggerNumber (-1) { }
TroubleEvent(unsigned troubleNumber) TroubleListener(unsigned troubleNumber)
: m_TriggerNumber(-1) : m_TriggerNumber(-1)
{ addWatchNumber(troubleNumber); } { addWatchNumber(troubleNumber); }
/** /**
@ -393,39 +404,39 @@ public:
* Returns the list of observed numbers * Returns the list of observed numbers
* @return a copy of the list which contains all observed numbers * @return a copy of the list which contains all observed numbers
*/ */
std::vector<unsigned> getWatchNumbers() { return (m_WatchNumbers); } std::vector<unsigned> getWatchNumbers() { return m_WatchNumbers; }
/** /**
* Checks whether a given interrupt-/trap-number is matching. * Checks whether a given interrupt-/trap-number is matching.
*/ */
bool isMatching(unsigned troubleNum) const; bool isMatching(unsigned troubleNum) const;
/** /**
* Sets the specific interrupt-/trap-number that actually triggered * Sets the specific interrupt-/trap-number that actually triggered
* the event. Should not be used by experiment code. * the listener. Should not be used by experiment code.
*/ */
void setTriggerNumber(unsigned troubleNum) void setTriggerNumber(unsigned troubleNum)
{ m_TriggerNumber = troubleNum; } { m_TriggerNumber = troubleNum; }
/** /**
* Returns the specific interrupt-/trap-number that actually triggered * Returns the specific interrupt-/trap-number that actually triggered
* the event. * the listener.
*/ */
unsigned getTriggerNumber() { return (m_TriggerNumber); } unsigned getTriggerNumber() { return m_TriggerNumber; }
}; };
/** /**
* \class InterruptEvent * \class InterruptListener
* Observes interrupts of the guest system. * Observes interrupts of the guest system.
*/ */
class InterruptEvent : virtual public TroubleEvent { class InterruptListener : virtual public TroubleListener {
private: private:
bool m_IsNMI; //!< non maskable interrupt flag bool m_IsNMI; //!< non maskable interrupt flag
public: public:
InterruptEvent() : m_IsNMI(false) { } InterruptListener() : m_IsNMI(false) { }
InterruptEvent(unsigned interrupt) : m_IsNMI(false) InterruptListener(unsigned interrupt) : m_IsNMI(false)
{ addWatchNumber(interrupt); } { addWatchNumber(interrupt); }
/** /**
* Returns \c true if the interrupt is non maskable, \c false otherwise. * Returns \c true if the interrupt is non maskable, \c false otherwise.
*/ */
bool isNMI() { return (m_IsNMI); } bool isNMI() { return m_IsNMI; }
/** /**
* Sets the interrupt type (non maskable or not). * Sets the interrupt type (non maskable or not).
*/ */
@ -433,29 +444,29 @@ public:
}; };
/** /**
* \class TrapEvent * \class TrapListener
* Observes traps of the guest system. * Observes traps of the guest system.
*/ */
class TrapEvent : virtual public TroubleEvent { class TrapListener : virtual public TroubleListener {
public: public:
TrapEvent() { } TrapListener() { }
TrapEvent(unsigned trap) { addWatchNumber(trap); } TrapListener(unsigned trap) { addWatchNumber(trap); }
}; };
/** /**
* \class GuestEvent * \class GuestListener
* Used to receive data from the guest system. * Used to receive data from the guest system.
*/ */
class GuestEvent : virtual public BaseEvent { class GuestListener : virtual public BaseListener {
private: private:
char m_Data; char m_Data;
unsigned m_Port; unsigned m_Port;
public: public:
GuestEvent() : m_Data(0), m_Port(0) { } GuestListener() : m_Data(0), m_Port(0) { }
/** /**
* Returns the data, transmitted by the guest system. * Returns the data, transmitted by the guest system.
*/ */
char getData() const { return (m_Data); } char getData() const { return m_Data; }
/** /**
* Sets the data which had been transmitted by the guest system. * Sets the data which had been transmitted by the guest system.
*/ */
@ -463,7 +474,7 @@ public:
/** /**
* Returns the data length, transmitted by the guest system. * Returns the data length, transmitted by the guest system.
*/ */
unsigned getPort() const { return (m_Port); } unsigned getPort() const { return m_Port; }
/** /**
* Sets the data length which had been transmitted by the guest system. * Sets the data length which had been transmitted by the guest system.
*/ */
@ -471,52 +482,52 @@ public:
}; };
/** /**
* \class IOPortEvent * \class IOPortListener
* Observes I/O access on architectures with a separate I/O access mechanism (e.g. IA-32) * Observes I/O access on architectures with a separate I/O access mechanism (e.g. IA-32)
*/ */
class IOPortEvent : virtual public BaseEvent { class IOPortListener : virtual public BaseListener {
private: private:
unsigned char m_Data; unsigned char m_Data;
unsigned m_Port; unsigned m_Port;
bool m_Out; bool m_Out;
public: public:
/** /**
* Initialises an IOPortEvent * Initialises an IOPortListener
* *
* @param port the port the event ist listening on * @param port the port the listener ist listening on
* @param out Defines the direction of the event. * @param out Defines the direction of the listener.
* \arg \c true Output on the given port is captured. * \arg \c true Output on the given port is captured.
* \arg \c false Input on the given port is captured. * \arg \c false Input on the given port is captured.
*/ */
IOPortEvent(unsigned port, bool out) : m_Data(0), m_Port(port), m_Out(out) { } IOPortListener(unsigned port, bool out) : m_Data(0), m_Port(port), m_Out(out) { }
/** /**
* Returns the data sent to the specified port * Returns the data sent to the specified port
*/ */
unsigned char getData() const { return (m_Data); } unsigned char getData() const { return m_Data; }
/** /**
* Sets the data which had been transmitted. * Sets the data which had been transmitted.
*/ */
void setData(unsigned char data) { m_Data = data; } void setData(unsigned char data) { m_Data = data; }
/** /**
* Retrieves the port which this event is bound to. * Retrieves the port which this listener is bound to.
*/ */
unsigned getPort() const { return (m_Port); } unsigned getPort() const { return m_Port; }
/** /**
* Sets the port which this event is bound to. * Sets the port which this listener is bound to.
*/ */
void setPort(unsigned port) { m_Port = port; } void setPort(unsigned port) { m_Port = port; }
/** /**
* Checks whether a given port number is matching. * Checks whether a given port number is matching.
* @param p The port number an I/O event occured on * @param p The port number an I/O listener occured on
* @param out True if the communication was outbound, false otherwise * @param out True if the communication was outbound, false otherwise
*/ */
bool isMatching(unsigned p, bool out) const { return ( out = isOutEvent() && p == getPort()); } bool isMatching(unsigned p, bool out) const { return ( out = isOutListener() && p == getPort()); }
/** /**
* Tells you if this event is capturing outbound communication (inbound if false) * Tells you if this listener is capturing outbound communication (inbound if false)
*/ */
bool isOutEvent() const { return m_Out; } bool isOutListener() const { return m_Out; }
/** /**
* Change the event direction. * Change the listener direction.
* \arg \c true Output on the given port is captured. * \arg \c true Output on the given port is captured.
* \arg \c false Input on the given port is captured. * \arg \c false Input on the given port is captured.
*/ */
@ -524,36 +535,36 @@ public:
}; };
/** /**
* \class JumpEvent * \class JumpListener
* JumpEvents are used to observe conditional jumps (if...else if...else). * JumpListeners are used to observe conditional jumps (if...else if...else).
*/ */
class JumpEvent : virtual public BaseEvent { class JumpListener : virtual public BaseListener {
private: private:
unsigned m_Opcode; unsigned m_Opcode;
bool m_FlagTriggered; bool m_FlagTriggered;
public: public:
/** /**
* Constructs a new event object. * Constructs a new listener object.
* @param parent the parent object * @param parent the parent object
* @param opcode the opcode of the jump-instruction to be observed * @param opcode the opcode of the jump-instruction to be observed
* or ANY_INSTR to match all jump-instructions * or ANY_INSTR to match all jump-instructions
*/ */
JumpEvent(unsigned opcode = ANY_INSTR) JumpListener(unsigned opcode = ANY_INSTR)
: m_Opcode(opcode), m_FlagTriggered(false) { } : m_Opcode(opcode), m_FlagTriggered(false) { }
/** /**
* Retrieves the opcode of the jump-instruction. * Retrieves the opcode of the jump-instruction.
*/ */
unsigned getOpcode() const { return (m_Opcode); } unsigned getOpcode() const { return m_Opcode; }
/** /**
* Returns \c true, if the event was triggered due to specific register * Returns \c true, if the listener was triggered due to specific register
* content, \c false otherwise. * content, \c false otherwise.
*/ */
bool isRegisterTriggered() { return (!m_FlagTriggered); } bool isRegisterTriggered() { return !m_FlagTriggered; }
/** /**
* Returns \c true, of the event was triggered due to specific FLAG * Returns \c true, of the listener was triggered due to specific FLAG
* state, \c false otherwise. This is the common case. * state, \c false otherwise. This is the common case.
*/ */
bool isFlagTriggered() { return (m_FlagTriggered); } bool isFlagTriggered() { return m_FlagTriggered; }
/** /**
* Sets the requestet jump-instruction opcode. * Sets the requestet jump-instruction opcode.
*/ */
@ -561,36 +572,35 @@ public:
/** /**
* Sets the trigger flag. * Sets the trigger flag.
*/ */
void setFlagTriggered(bool flagTriggered) void setFlagTriggered(bool flagTriggered) { m_FlagTriggered = flagTriggered; }
{ m_FlagTriggered = flagTriggered; }
}; };
/** /**
* \class GenericTimerEvent * \class GenericTimerListener
* This event type is used to create timeouts within in an experiment. * This listener type is used to create timeouts within in an experiment.
* *
* Depending on your simulator backend, a concrete class needs to be derived * Depending on your simulator backend, a concrete class needs to be derived
* from \c GenericTimerEvent. \c onEventAddition should be used to register and * from \c GenericTimerListener. \c onListenerAddition should be used to register and
* \c onEventDeletion to unregister the timer. \c onEventTrigger can be used to * \c onListenerDeletion to unregister the timer. \c onListenerTrigger can be used to
* re-register the timer if a repetitive timer is requested and the back- * re-register the timer if a repetitive timer is requested and the back-
* end doesn't support such timer types natively. * end doesn't support such timer types natively.
*/ */
class GenericTimerEvent : public BaseEvent { class GenericTimerListener : public BaseListener {
protected: protected:
unsigned m_Timeout; //!< timeout interval in milliseconds unsigned m_Timeout; //!< timeout interval in milliseconds
timer_id_t m_Id; //!< internal timer id (sim-specific) timer_id_t m_Id; //!< internal timer id (sim-specific)
bool m_Once; //!< \c true, if the timer should be triggered only once, \c false otherwise bool m_Once; //!< \c true, if the timer should be triggered only once, \c false otherwise
public: public:
/** /**
* Creates a new timer event. This can be used to implement a timeout- * Creates a new timer listener. This can be used to implement a timeout-
* mechanism in the experiment-flow. The timer starts automatically when * mechanism in the experiment-flow. The timer starts automatically when
* added to the simulator backend. * added to the simulator backend.
* @param timeout the time intervall in milliseconds (ms) * @param timeout the time intervall in milliseconds (ms)
* @see SimulatorController::addEvent * @see SimulatorController::addListener
*/ */
GenericTimerEvent(unsigned timeout) GenericTimerListener(unsigned timeout)
: m_Timeout(timeout), m_Id(-1) { } : m_Timeout(timeout), m_Id(-1) { }
~GenericTimerEvent() { } ~GenericTimerListener() { }
/** /**
* Retrieves the internal timer id. Maybe useful for debug output. * Retrieves the internal timer id. Maybe useful for debug output.
* @return the timer id * @return the timer id
@ -610,4 +620,4 @@ public:
} // end-of-namespace: fail } // end-of-namespace: fail
#endif // __EVENT_HPP__ #endif // __LISTENER_HPP__

View File

@ -0,0 +1,191 @@
#include <set>
#include "ListenerManager.hpp"
#include "SALInst.hpp"
namespace fail {
void ListenerManager::addToCaches(BaseListener *li)
{
BPListener *bps_li;
if ((bps_li = dynamic_cast<BPListener*>(li)) != NULL)
m_Bp_cache.add(bps_li);
IOPortListener *io_li;
if ((io_li = dynamic_cast<IOPortListener*>(li)) != NULL)
m_Io_cache.add(io_li);
}
void ListenerManager::removeFromCaches(BaseListener *li)
{
BPListener *bpr_li;
if ((bpr_li = dynamic_cast<BPListener*>(li)) != NULL)
m_Bp_cache.remove(bpr_li);
IOPortListener *io_li;
if ((io_li = dynamic_cast<IOPortListener*>(li)) != NULL)
m_Io_cache.remove(io_li);
}
void ListenerManager::clearCaches()
{
m_Bp_cache.clear();
m_Io_cache.clear();
}
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);
li->setParent(pExp); // listener is linked to experiment flow
addToCaches(li);
m_BufferList.push_back(li);
}
void ListenerManager::remove(BaseListener* li)
{
// possible cases:
// - li == 0 -> remove all listeners
// * clear m_BufferList
// * 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
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->onDeletion();
removeFromCaches(li);
m_BufferList.remove(li);
firelist_t::const_iterator it =
std::find(m_FireList.begin(), m_FireList.end(), li);
if (it != m_FireList.end()) {
m_DeleteList.push_back(li);
}
}
}
ListenerManager::iterator ListenerManager::m_remove(iterator it, bool skip_deletelist)
{
if (!skip_deletelist) {
// If skip_deletelist = true, m_remove was called from makeActive. Accordingly, we
// are not going to delete an listener, instead we are "moving" a listener object (= *it)
// from the buffer list to the fire-list. Therefore we only need to call the simulator's
// listener handler (onDeletion), if m_remove is called with the primary intention
// to *delete* (not "move") a listener.
(*it)->onDeletion();
m_DeleteList.push_back(*it);
// Cached listeners have their own BufferCache<T>::makeActive() implementation, which
// calls this method and afterwards erase() in the cache class. This is why, when
// called from any kind of makeActive() method, it is unnecessary to call
// BufferCache<T>::remove() from m_remove().
// NOTE: in case the semantics of skip_deletelist change, please adapt the following lines
removeFromCaches(*it);
}
return m_BufferList.erase(it);
}
void ListenerManager::remove(ExperimentFlow* flow)
{
// all listeners?
if (flow == 0) {
for (bufferlist_t::iterator it = m_BufferList.begin();
it != m_BufferList.end(); it++)
(*it)->onDeletion(); // invoke listener handler
clearCaches();
m_BufferList.clear();
} 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);
} else {
++it;
}
}
}
// 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()) {
continue; // (already in the delete-list? -> skip!)
}
// ... need to be pushed into m_DeleteList, as we're currently
// iterating over m_FireList in triggerActiveListeners() and cannot modify it
if (flow == 0 || (*it)->getParent() == flow) {
(*it)->onDeletion();
m_DeleteList.push_back(*it);
}
}
}
ListenerManager::~ListenerManager()
{
// nothing to do here yet
}
ListenerManager::iterator ListenerManager::makeActive(iterator it)
{
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();
if (li->getCounter() > 0) {
return ++it;
}
li->resetCounter();
// Note: This is the one and only situation in which remove() should NOT
// store the removed item in the delete-list.
iterator it_next = m_remove(it, true); // remove listener from buffer-list
m_FireList.push_back(li);
return it_next;
}
void ListenerManager::triggerActiveListeners()
{
for (firelist_t::iterator it = m_FireList.begin();
it != m_FireList.end(); it++) {
if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it)
== m_DeleteList.end()) { // not found in delete-list?
m_pFired = *it;
// Inform (call) the simulator's (internal) listener handler that we are about
// to trigger an listener (*before* we actually toggle the experiment flow):
m_pFired->onTrigger();
ExperimentFlow* pFlow = m_pFired->getParent();
assert(pFlow && "FATAL ERROR: The listener has no parent experiment (owner)!");
simulator.m_Flows.toggle(pFlow);
}
}
m_FireList.clear();
m_DeleteList.clear();
// Note: Do NOT call any listener handlers here!
}
size_t ListenerManager::getContextCount() const
{
std::set<ExperimentFlow*> uniqueFlows; // count unique ExperimentFlow-ptr
for (bufferlist_t::const_iterator it = m_BufferList.begin();
it != m_BufferList.end(); it++)
uniqueFlows.insert((*it)->getParent());
return uniqueFlows.size();
}
} // end-of-namespace: fail

View File

@ -0,0 +1,219 @@
#ifndef __LISTENER_MANAGER_HPP__
#define __LISTENER_MANAGER_HPP__
#include <cassert>
#include <list>
#include <vector>
#include <algorithm>
#include "Listener.hpp"
#include "BufferCache.hpp"
namespace fail {
class ExperimentFlow;
/**
* Buffer-list for a specific experiment; acts as a simple storage container
* for listeners to watch for:
*/
typedef std::list<BaseListener*> bufferlist_t;
/**
* List of listeners that match the current simulator listener; these listeners will
* be triggered next (the list is used temporarily).
*/
typedef std::vector<BaseListener*> firelist_t;
/**
* List of listeners that have been deleted during a toggled experiment flow while
* triggering the listeners in the fire-list. This list is used to skip already
* deleted listeners (therefore it is used temporarily either).
*/
typedef std::vector<BaseListener*> deletelist_t;
/**
* Cache classes for the most commonly used types of listeners, utilising static typing.
* Apart from that, they work like bufferlist_t.
*/
typedef BufferCache<BPListener*> bp_cache_t;
typedef bp_cache_t::iterator bp_iter_t;
typedef BufferCache<IOPortListener*> io_cache_t;
typedef io_cache_t::iterator io_iter_t;
/**
* \class ListenerManager
*
* \brief This class manages the listeners of the Fail* implementation.
*
* If a listener is triggered, the internal data structure will be updated (i.e.,
* the listener will be removed from the so called buffer-list and added to the
* fire-list). Additionally, if an experiment-flow deletes an "active" listener
* which is currently stored in the fire-list, the listener (to be removed) will
* be added to a -so called- delete-list. This ensures to prevent triggering
* "active" listeners which have already been deleted by a previous experiment
* flow. (See makeActive() and fireActiveListener() for implementation specific
* details.) ListenerManager is part of the SimulatorController and "outsources"
* it's listener management.
*/
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)
BaseListener* m_pFired; //!< the recently fired Listener-object
bp_cache_t m_Bp_cache; //!< the storage cache for breakpoint listeners
io_cache_t m_Io_cache; //!< the storage cache for port i/o listeners
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:
/**
* 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().
* \c end() returns the iterator, pointing after the last element.
* (This behaviour equals the STL iterator in C++.)
*/
typedef bufferlist_t::iterator iterator;
ListenerManager() : m_pFired(NULL) { }
~ListenerManager();
/**
* Adds the specified listener object for the given ExperimentFlow to the
* list of listeners to be watched for.
* @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()
*/
void add(BaseListener* li, ExperimentFlow* flow);
/**
* Removes the listener based upon the specified \a ev 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
* removed
*/
void remove(BaseListener* li);
/**
* Behaves like remove(BaseListener*) and additionally updates the provided
* iterator.
* @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);
private:
/**
* Internal implementation of remove(iterator it) that allows
* to skip the delete-list.
* @param it the iterator pointing to the Listener object to be removed
* @param skip_deletelist \c true to skip the deletion of the Listener object
* or \false to behave like \c remove(iterator)
* @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
* @return iterator to the beginning
*/
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
* @return iterator to the end
*/
iterator end() { return (m_BufferList.end()); }
/**
* Removes all listeners 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
* listeners. This number is trivially equal to the (current) total
* number of ExperimentFlow-objects.
* @return number of experiments having active listeners
*/
size_t getContextCount() const;
/**
* Retrieves the total number of buffered listeners. This doesn't include
* the listeners in the fire- or delete-list.
* @return the total listener count (for all flows)
*/
size_t getListenerCount() const { return m_BufferList.size(); }
/**
* Retrieves the recently triggered listener object. To map this object to it's
* context (i.e., the related \c ExerimentFlow), use \c getLastFiredDest().
* @return a pointer to the recent listener or \c NULL if nothing has been
* triggered so far
*/
BaseListener* getLastFired() { return (m_pFired); }
/**
* Retrieves the ExperimentFlow-object for the given BaseListener (it's
* \a context).
* @param li the listener 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(BaseListener* li);
/**
* 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.
* @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
* iterator should be used to continue the iteration
*
* TODO: Improve naming (instead of "makeActive")?
*/
iterator makeActive(iterator it);
/**
* 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
* triggered, the (internal) fire- and delete-list will be cleared.
*/
void triggerActiveListeners();
/**
* Retrieves the BPListener buffer cache.
* @returns the buffer cache
*/
inline bp_cache_t &getBPBuffer() { return m_Bp_cache; }
/**
* Retrieves the IOPortListener buffer cache.
* @returns the buffer cache
*/
inline io_cache_t &getIOBuffer() { return m_Io_cache; }
private:
/**
* Add an listener to its appropriate cache.
* @param li the listener to add
*/
void addToCaches(BaseListener* li);
/**
* Remove an listener from its cache.
* @param li the listener to remove
*/
void removeFromCaches(BaseListener* li);
/**
* Clear the listener caches.
*/
void clearCaches();
};
} // end-of-namespace: fail
#endif // __LISTENER_MANAGER_HPP__

View File

@ -55,12 +55,12 @@ public:
* Returns the (fixed) type of this register. * Returns the (fixed) type of this register.
* @return the type of this register * @return the type of this register
*/ */
RegisterType getType() const { return (m_Type); } RegisterType getType() const { return m_Type; }
/** /**
* Returns the (fixed) width of this register. * Returns the (fixed) width of this register.
* @return the width in bits * @return the width in bits
*/ */
regwidth_t getWidth() const { return (m_Width); } regwidth_t getWidth() const { return m_Width; }
/** /**
* Returns the data referenced by this register. In a concrete * Returns the data referenced by this register. In a concrete
* derived class this method has to be defined appropriately. * derived class this method has to be defined appropriately.
@ -81,7 +81,7 @@ public:
* Retrieves the register name. * Retrieves the register name.
* @return the textual register description * @return the textual register description
*/ */
const std::string& getName() const { return (m_Name); } const std::string& getName() const { return m_Name; }
/** /**
* Retrieves the unique index within it's assigned register set. * Retrieves the unique index within it's assigned register set.
* If the register has not been assigned, \c (size_t)-1 will be * If the register has not been assigned, \c (size_t)-1 will be
@ -89,18 +89,18 @@ public:
* @return the register index or -1 if not assigned * @return the register index or -1 if not assigned
* @see isAssigned() * @see isAssigned()
*/ */
size_t getIndex() const { return (m_Index); } size_t getIndex() const { return m_Index; }
/** /**
* Checks whether this register has already been assigned. On * Checks whether this register has already been assigned. On
* creation the register isn't initially assigned. * creation the register isn't initially assigned.
* @return \c true if assigned, \c false otherwise * @return \c true if assigned, \c false otherwise
*/ */
bool isAssigned() const { return (m_Assigned); } bool isAssigned() const { return m_Assigned; }
/** /**
* Returns the unique id of this register. * Returns the unique id of this register.
* @return the unique id * @return the unique id
*/ */
unsigned int getId() const { return (m_Id); } unsigned int getId() const { return m_Id; }
}; };
/** /**
@ -132,7 +132,7 @@ public:
* ^ * ^
* \endcode * \endcode
*/ */
iterator begin() { return (m_Regs.begin()); } iterator begin() { return m_Regs.begin(); }
/** /**
* Returns an iterator to the end of the interal data structure. * Returns an iterator to the end of the interal data structure.
* \code * \code
@ -140,7 +140,7 @@ public:
* ^ * ^
* \endcode * \endcode
*/ */
iterator end() { return (m_Regs.end()); } iterator end() { return m_Regs.end(); }
/** /**
* Constructs a new register set with type \a containerType. * Constructs a new register set with type \a containerType.
* @param containerType the type of registers which should be stored * @param containerType the type of registers which should be stored
@ -152,12 +152,12 @@ public:
* Returns the type of this set. * Returns the type of this set.
* @return the type * @return the type
*/ */
RegisterType getType() const { return (m_Type); } RegisterType getType() const { return m_Type; }
/** /**
* Gets the number of registers of this set. * Gets the number of registers of this set.
* @return the number of registers * @return the number of registers
*/ */
size_t count() const { return (m_Regs.size()); } size_t count() const { return m_Regs.size(); }
/** /**
* Retrieves the \a i-th register within this set. * Retrieves the \a i-th register within this set.
* @return a pointer to the \a i-th register; if \a i is invalid, an * @return a pointer to the \a i-th register; if \a i is invalid, an
@ -169,7 +169,7 @@ public:
* @return a pointer to the first register (if existing -- otherwise an * @return a pointer to the first register (if existing -- otherwise an
* assertion is thrown) * assertion is thrown)
*/ */
virtual Register* first() { return (getRegister(0)); } virtual Register* first() { return getRegister(0); }
}; };
/** /**
@ -197,7 +197,7 @@ public:
* ^ * ^
* \endcode * \endcode
*/ */
iterator begin() { return (m_Registers.begin()); } iterator begin() { return m_Registers.begin(); }
/** /**
* Returns an iterator to the end of the interal data structure. * Returns an iterator to the end of the interal data structure.
* \code * \code
@ -205,7 +205,7 @@ public:
* ^ * ^
* \endcode * \endcode
*/ */
iterator end() { return (m_Registers.end()); } iterator end() { return m_Registers.end(); }
RegisterManager() { } RegisterManager() { }
~RegisterManager() { clear(); } ~RegisterManager() { clear(); }
@ -218,7 +218,7 @@ public:
* Retrieves the number of managed homogeneous register sets. * Retrieves the number of managed homogeneous register sets.
* @return the number of sets * @return the number of sets
*/ */
virtual size_t subsetCount() const { return (m_Subsets.size()); } virtual size_t subsetCount() const { return m_Subsets.size(); }
/** /**
* Gets the \a i-th register set. * Gets the \a i-th register set.
* @param i the index of the set to be returned * @param i the index of the set to be returned

View File

@ -6,26 +6,26 @@ namespace fail {
// External reference declared in SALInst.hpp // External reference declared in SALInst.hpp
ConcreteSimulatorController simulator; ConcreteSimulatorController simulator;
bool SimulatorController::addEvent(BaseEvent* ev) bool SimulatorController::addListener(BaseListener* li)
{ {
assert(ev != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!"); assert(li != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
m_EvList.add(ev, m_Flows.getCurrent()); m_LstList.add(li, m_Flows.getCurrent());
// Call the common postprocessing function: // Call the common postprocessing function:
if (!ev->onEventAddition()) { // If the return value signals "false"..., if (!li->onAddition()) { // If the return value signals "false"...,
m_EvList.remove(ev); // ...skip the addition m_LstList.remove(li); // ...skip the addition
return false; return false;
} }
return true; return true;
} }
BaseEvent* SimulatorController::waitAny(void) BaseListener* SimulatorController::resume(void)
{ {
if (!hasEvents()) if (!hasListeners())
return NULL; return NULL;
m_Flows.resume(); m_Flows.resume();
assert(m_EvList.getLastFired() != NULL && assert(m_LstList.getLastFired() != NULL &&
"FATAL ERROR: getLastFired() expected to be non-NULL!"); "FATAL ERROR: getLastFired() expected to be non-NULL!");
return m_EvList.getLastFired(); return m_LstList.getLastFired();
} }
void SimulatorController::startup() void SimulatorController::startup()
@ -43,47 +43,47 @@ void SimulatorController::initExperiments()
/* empty. */ /* empty. */
} }
void SimulatorController::onBreakpointEvent(address_t instrPtr, address_t address_space) void SimulatorController::onBreakpointListener(address_t instrPtr, address_t address_space)
{ {
assert(false && assert(false &&
"FIXME: SimulatorController::onBreakpointEvent() has not been tested before"); "FIXME: SimulatorController::onBreakpointListener() has not been tested before");
// FIXME: Improve performance! // FIXME: Improve performance!
// Loop through all events of type BP*Event: // Loop through all events of type BP*Listener:
EventManager::iterator it = m_EvList.begin(); ListenerManager::iterator it = m_LstList.begin();
while (it != m_EvList.end()) { while (it != m_LstList.end()) {
BaseEvent* pev = *it; BaseListener* pev = *it;
BPSingleEvent* pbp; BPRangeEvent* pbpr; BPSingleListener* pbp; BPRangeListener* pbpr;
if ((pbp = dynamic_cast<BPSingleEvent*>(pev)) && pbp->isMatching(instrPtr, address_space)) { if ((pbp = dynamic_cast<BPSingleListener*>(pev)) && pbp->isMatching(instrPtr, address_space)) {
pbp->setTriggerInstructionPointer(instrPtr); pbp->setTriggerInstructionPointer(instrPtr);
it = m_EvList.makeActive(it); it = m_LstList.makeActive(it);
// "it" has already been set to the next element (by calling // "it" has already been set to the next element (by calling
// makeActive()): // makeActive()):
continue; // -> skip iterator increment continue; // -> skip iterator increment
} else if ((pbpr = dynamic_cast<BPRangeEvent*>(pev)) && } else if ((pbpr = dynamic_cast<BPRangeListener*>(pev)) &&
pbpr->isMatching(instrPtr, address_space)) { pbpr->isMatching(instrPtr, address_space)) {
pbpr->setTriggerInstructionPointer(instrPtr); pbpr->setTriggerInstructionPointer(instrPtr);
it = m_EvList.makeActive(it); it = m_LstList.makeActive(it);
continue; // dito continue; // dito
} }
++it; ++it;
} }
m_EvList.fireActiveEvents(); m_LstList.triggerActiveListeners();
} }
void SimulatorController::onMemoryAccessEvent(address_t addr, size_t len, void SimulatorController::onMemoryAccessListener(address_t addr, size_t len,
bool is_write, address_t instrPtr) bool is_write, address_t instrPtr)
{ {
// FIXME: Improve performance! // FIXME: Improve performance!
MemAccessEvent::accessType_t accesstype = MemAccessListener::accessType_t accesstype =
is_write ? MemAccessEvent::MEM_WRITE is_write ? MemAccessListener::MEM_WRITE
: MemAccessEvent::MEM_READ; : MemAccessListener::MEM_READ;
EventManager::iterator it = m_EvList.begin(); ListenerManager::iterator it = m_LstList.begin();
while (it != m_EvList.end()) { // check for active events while (it != m_LstList.end()) { // check for active events
BaseEvent* pev = *it; BaseListener* pev = *it;
MemAccessEvent* ev = dynamic_cast<MemAccessEvent*>(pev); MemAccessListener* ev = dynamic_cast<MemAccessListener*>(pev);
// Is this a MemAccessEvent? Correct access type? // Is this a MemAccessListener? Correct access type?
if (!ev || !ev->isMatching(addr, accesstype)) { if (!ev || !ev->isMatching(addr, accesstype)) {
++it; ++it;
continue; // skip event activation continue; // skip event activation
@ -92,26 +92,26 @@ void SimulatorController::onMemoryAccessEvent(address_t addr, size_t len,
ev->setTriggerWidth(len); ev->setTriggerWidth(len);
ev->setTriggerInstructionPointer(instrPtr); ev->setTriggerInstructionPointer(instrPtr);
ev->setTriggerAccessType(accesstype); ev->setTriggerAccessType(accesstype);
it = m_EvList.makeActive(it); it = m_LstList.makeActive(it);
} }
m_EvList.fireActiveEvents(); m_LstList.triggerActiveListeners();
} }
void SimulatorController::onInterruptEvent(unsigned interruptNum, bool nmi) void SimulatorController::onInterruptListener(unsigned interruptNum, bool nmi)
{ {
EventManager::iterator it = m_EvList.begin(); ListenerManager::iterator it = m_LstList.begin();
while (it != m_EvList.end()) { // check for active events while (it != m_LstList.end()) { // check for active events
BaseEvent* pev = *it; BaseListener* pev = *it;
InterruptEvent* pie = dynamic_cast<InterruptEvent*>(pev); InterruptListener* pie = dynamic_cast<InterruptListener*>(pev);
if (!pie || !pie->isMatching(interruptNum)) { if (!pie || !pie->isMatching(interruptNum)) {
++it; ++it;
continue; // skip event activation continue; // skip event activation
} }
pie->setTriggerNumber(interruptNum); pie->setTriggerNumber(interruptNum);
pie->setNMI(nmi); pie->setNMI(nmi);
it = m_EvList.makeActive(it); it = m_LstList.makeActive(it);
} }
m_EvList.fireActiveEvents(); m_LstList.triggerActiveListeners();
} }
bool SimulatorController::isSuppressedInterrupt(unsigned interruptNum) bool SimulatorController::isSuppressedInterrupt(unsigned interruptNum)
@ -155,53 +155,53 @@ bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum)
return false; return false;
} }
void SimulatorController::onTrapEvent(unsigned trapNum) void SimulatorController::onTrapListener(unsigned trapNum)
{ {
EventManager::iterator it = m_EvList.begin(); ListenerManager::iterator it = m_LstList.begin();
while (it != m_EvList.end()) { // check for active events while (it != m_LstList.end()) { // check for active events
BaseEvent* pev = *it; BaseListener* pev = *it;
TrapEvent* pte = dynamic_cast<TrapEvent*>(pev); TrapListener* pte = dynamic_cast<TrapListener*>(pev);
if (!pte || !pte->isMatching(trapNum)) { if (!pte || !pte->isMatching(trapNum)) {
++it; ++it;
continue; // skip event activation continue; // skip event activation
} }
pte->setTriggerNumber(trapNum); pte->setTriggerNumber(trapNum);
it = m_EvList.makeActive(it); it = m_LstList.makeActive(it);
} }
m_EvList.fireActiveEvents(); m_LstList.triggerActiveListeners();
} }
void SimulatorController::onGuestSystemEvent(char data, unsigned port) void SimulatorController::onGuestSystemListener(char data, unsigned port)
{ {
EventManager::iterator it = m_EvList.begin(); ListenerManager::iterator it = m_LstList.begin();
while (it != m_EvList.end()) { // check for active events while (it != m_LstList.end()) { // check for active events
BaseEvent* pev = *it; BaseListener* pev = *it;
GuestEvent* pge = dynamic_cast<GuestEvent*>(pev); GuestListener* pge = dynamic_cast<GuestListener*>(pev);
if (pge != NULL) { if (pge != NULL) {
pge->setData(data); pge->setData(data);
pge->setPort(port); pge->setPort(port);
it = m_EvList.makeActive(it); it = m_LstList.makeActive(it);
continue; // dito. continue; // dito.
} }
++it; ++it;
} }
m_EvList.fireActiveEvents(); m_LstList.triggerActiveListeners();
} }
void SimulatorController::onJumpEvent(bool flagTriggered, unsigned opcode) void SimulatorController::onJumpListener(bool flagTriggered, unsigned opcode)
{ {
EventManager::iterator it = m_EvList.begin(); ListenerManager::iterator it = m_LstList.begin();
while (it != m_EvList.end()) { // check for active events while (it != m_LstList.end()) { // check for active events
JumpEvent* pje = dynamic_cast<JumpEvent*>(*it); JumpListener* pje = dynamic_cast<JumpListener*>(*it);
if (pje != NULL) { if (pje != NULL) {
pje->setOpcode(opcode); pje->setOpcode(opcode);
pje->setFlagTriggered(flagTriggered); pje->setFlagTriggered(flagTriggered);
it = m_EvList.makeActive(it); it = m_LstList.makeActive(it);
continue; // dito. continue; // dito.
} }
++it; ++it;
} }
m_EvList.fireActiveEvents(); m_LstList.triggerActiveListeners();
} }
void SimulatorController::addFlow(ExperimentFlow* flow) void SimulatorController::addFlow(ExperimentFlow* flow)
@ -215,15 +215,15 @@ void SimulatorController::addFlow(ExperimentFlow* flow)
void SimulatorController::removeFlow(ExperimentFlow* flow) void SimulatorController::removeFlow(ExperimentFlow* flow)
{ {
// remove all remaining events of this flow // remove all remaining events of this flow
clearEvents(flow); clearListeners(flow);
// remove coroutine // remove coroutine
m_Flows.remove(flow); m_Flows.remove(flow);
} }
BaseEvent* SimulatorController::addEventAndWait(BaseEvent* ev) BaseListener* SimulatorController::addListenerAndResume(BaseListener* li)
{ {
addEvent(ev); addListener(li);
return waitAny(); return resume();
} }
void SimulatorController::terminate(int exCode) void SimulatorController::terminate(int exCode)

View File

@ -7,9 +7,9 @@
#include <vector> #include <vector>
#include "efw/CoroutineManager.hpp" #include "efw/CoroutineManager.hpp"
#include "EventManager.hpp" #include "ListenerManager.hpp"
#include "SALConfig.hpp" #include "SALConfig.hpp"
#include "Event.hpp" #include "Listener.hpp"
namespace fail { namespace fail {
@ -23,23 +23,22 @@ class MemoryManager;
* *
* \brief The abstract interface for controlling simulators and * \brief The abstract interface for controlling simulators and
* accessing experiment data/flows (as part of the "Simulator * accessing experiment data/flows (as part of the "Simulator
* Abstraction Layer". * Abstraction Layer", SAL for short).
* *
* This class manages (1..N) experiments and provides access to the underlying * This class manages (1..N) experiments and provides access to the underlying
* simulator/debugger system. Experiments can enlist arbritrary events * simulator/debugger system. Experiments can enlist arbritrary listeners
* (Breakpoint, Memory access, Traps, etc.). The SimulatorController then * (Breakpoint, Memory access, Traps, etc.). The \c SimulatorController then
* activates the specific experiment There are further methods to read/write * activates the specific experiment There are further methods to read/write
* registers and memory, and control the SUT (save/restore/reset). * registers and memory, and control the SUT (save/restore/reset).
*/ */
class SimulatorController { class SimulatorController {
protected: protected:
EventManager m_EvList; //!< storage where events are being buffered ListenerManager m_LstList; //!< storage where listeners are being buffered
CoroutineManager m_Flows; //!< managed experiment flows CoroutineManager m_Flows; //!< managed experiment flows
RegisterManager *m_Regs; //!< access to cpu register RegisterManager *m_Regs; //!< access to cpu register
MemoryManager *m_Mem; //!< access to memory pool MemoryManager *m_Mem; //!< access to memory pool
//! list of suppressed interrupts std::vector<unsigned> m_SuppressedInterrupts; //!< list of suppressed interrupts
std::vector<unsigned> m_SuppressedInterrupts; friend class ListenerManager; //!< "outsources" the listener management
friend class EventManager; //!< "outsources" the event management
public: public:
SimulatorController() SimulatorController()
: m_Regs(NULL), m_Mem(NULL) { } : m_Regs(NULL), m_Mem(NULL) { }
@ -60,17 +59,17 @@ public:
*/ */
void initExperiments(); void initExperiments();
/* ******************************************************************** /* ********************************************************************
* Standard Event Handler API * Standard Listener Handler API
* ********************************************************************/ * ********************************************************************/
/** /**
* Breakpoint event handler. This routine needs to be called in the * Breakpoint listener handler. This routine needs to be called in the
* simulator specific backend each time a breakpoint event occurs. * simulator specific backend each time a breakpoint listener occurs.
* @param instrPtr the instruction pointer of the breakpoint event * @param instrPtr the instruction pointer of the breakpoint listener
* @param address_space the address space it should occur in * @param address_space the address space it should occur in
*/ */
void onBreakpointEvent(address_t instrPtr, address_t address_space); void onBreakpointListener(address_t instrPtr, address_t address_space);
/** /**
* Memory access event handler (read/write). * Memory access listener handler (read/write).
* @param addr the accessed memory address * @param addr the accessed memory address
* @param len the length of the accessed memory * @param len the length of the accessed memory
* @param is_write \c true if memory is written, \c false if read * @param is_write \c true if memory is written, \c false if read
@ -79,32 +78,31 @@ public:
* *
* FIXME: should instrPtr be part of this interface? * FIXME: should instrPtr be part of this interface?
*/ */
void onMemoryAccessEvent(address_t addr, size_t len, void onMemoryAccessListener(address_t addr, size_t len, bool is_write, address_t instrPtr);
bool is_write, address_t instrPtr);
/** /**
* Interrupt event handler. * Interrupt listener handler.
* @param interruptNum the interrupt-type id * @param interruptNum the interrupt-type id
* @param nmi nmi-value from guest-system * @param nmi nmi-value from guest-system
*/ */
void onInterruptEvent(unsigned interruptNum, bool nmi); void onInterruptListener(unsigned interruptNum, bool nmi);
/** /**
* Trap event handler. * Trap listener handler.
* @param trapNum the trap-type id * @param trapNum the trap-type id
*/ */
void onTrapEvent(unsigned trapNum); void onTrapListener(unsigned trapNum);
/** /**
* Guest system communication handler. * Guest system communication handler.
* @param data the "message" from the guest system * @param data the "message" from the guest system
* @param port the port of the event * @param port the port of the listener
*/ */
void onGuestSystemEvent(char data, unsigned port); void onGuestSystemListener(char data, unsigned port);
/** /**
* (Conditional) Jump-instruction handler. * (Conditional) Jump-instruction handler.
* @param flagTriggered \c true if the jump was triggered due to a * @param flagTriggered \c true if the jump was triggered due to a
* specific FLAG (zero/carry/sign/overflow/parity flag) * specific FLAG (zero/carry/sign/overflow/parity flag)
* @param opcode the opcode of the conrecete jump instruction * @param opcode the opcode of the conrecete jump instruction
*/ */
void onJumpEvent(bool flagTriggered, unsigned opcode); void onJumpListener(bool flagTriggered, unsigned opcode);
/* ******************************************************************** /* ********************************************************************
* Simulator Controller & Access API: * Simulator Controller & Access API:
* ********************************************************************/ * ********************************************************************/
@ -115,7 +113,7 @@ public:
virtual void save(const std::string& path) = 0; virtual void save(const std::string& path) = 0;
/** /**
* Restore simulator state. Implicitly discards all previously * Restore simulator state. Implicitly discards all previously
* registered events. * registered listeners.
* @param path Location to previously saved state information * @param path Location to previously saved state information
*/ */
virtual void restore(const std::string& path) = 0; virtual void restore(const std::string& path) = 0;
@ -144,20 +142,18 @@ public:
/** /**
* Remove a Interrupt from the list of suppressed. * Remove a Interrupt from the list of suppressed.
* @param interruptNum the interrupt-type id * @param interruptNum the interrupt-type id
* @return \c true if sucessfully removed, \c false otherwise (not * @return \c true if sucessfully removed, \c false otherwise (not found)
* found)
*/ */
bool removeSuppressedInterrupt(unsigned interruptNum); bool removeSuppressedInterrupt(unsigned interruptNum);
/** /**
* Returns the (constant) initialized register manager. * Returns the (constant) initialized register manager.
* @return a reference to the register manager * @return a reference to the register manager
*/ */
RegisterManager& getRegisterManager() { return (*m_Regs); } RegisterManager& getRegisterManager() { return *m_Regs; }
const RegisterManager& getRegisterManager() const { return (*m_Regs); } const RegisterManager& getRegisterManager() const { return *m_Regs; }
/** /**
* Sets the register manager. * Sets the register manager.
* @param pReg the new register manager (or a concrete derived class of * @param pReg the new register manager (or a concrete derived class of \c RegisterManager)
* RegisterManager)
*/ */
void setRegisterManager(RegisterManager* pReg) { m_Regs = pReg; } void setRegisterManager(RegisterManager* pReg) { m_Regs = pReg; }
/** /**
@ -165,74 +161,72 @@ public:
* @return a reference to the memory manager * @return a reference to the memory manager
*/ */
MemoryManager& getMemoryManager() { return (*m_Mem); } MemoryManager& getMemoryManager() { return (*m_Mem); }
const MemoryManager& getMemoryManager() const { return (*m_Mem); } const MemoryManager& getMemoryManager() const { return *m_Mem; }
/** /**
* Sets the memory manager. * Sets the memory manager.
* @param pMem a new concrete memory manager * @param pMem a new concrete memory manager
*/ */
void setMemoryManager(MemoryManager* pMem) { m_Mem = pMem; } void setMemoryManager(MemoryManager* pMem) { m_Mem = pMem; }
/* ******************************************************************** /* ********************************************************************
* Experiment-Flow & Event Management API: * Experiment-Flow & Listener Management API:
* ********************************************************************/ * ********************************************************************/
/** /**
* Adds the specified experiment or plugin and creates a coroutine to * Adds the specified experiment or plugin and creates a coroutine to run it in.
* run it in.
* @param flow the experiment flow object to be added * @param flow the experiment flow object to be added
*/ */
void addFlow(ExperimentFlow* flow); void addFlow(ExperimentFlow* flow);
/** /**
* Removes the specified experiment or plugin and destroys its coroutine * Removes the specified experiment or plugin and destroys its coroutine
* and all associated events. * and all associated listeners.
* @param flow the experiment flow object to be removed * @param flow the experiment flow object to be removed
*/ */
void removeFlow(ExperimentFlow* flow); void removeFlow(ExperimentFlow* flow);
/** /**
* Add event ev to the event management. This causes the event to be * Add listener \c li to the listener management. This causes the listener to be active.
* active. * @param li the listener pointer to be added for the current flow
* @param ev the event pointer to be added for the current flow * @return \c true if the listener has been added successfully, \c false otherwise
* @return \c true if the event has been added successfully, \c false otherwise
*/ */
bool addEvent(BaseEvent* ev); bool addListener(BaseListener* li);
/** /**
* Removes the event with the specified id. * Removes the listener with the specified pointer \c li.
* @param ev the pointer of the event-object to be removed; if \a ev is * @param li the pointer of the listener-object to be removed; if \c li is
* equal to \c NULL all events (for all experiments) will be * equal to \c NULL, all listeners (for all experiments) will be removed
* removed
*/ */
void removeEvent(BaseEvent* ev) { m_EvList.remove(ev); } void removeListener(BaseListener* li) { m_LstList.remove(li); }
/** /**
* Removes all previously added events for all experiments. To * Removes all previously added listeners for all experiments. To
* restrict this to a specific experiment flow, pass a pointer to it. * restrict this to a specific experiment flow, pass a pointer to it.
* @param flow a ptr to a specific experiment flow whose listeners should
* be removed; if \c flow is \c NULL, all listeners will be removed
*/ */
void clearEvents(ExperimentFlow *flow = 0) { m_EvList.remove(flow); } void clearListeners(ExperimentFlow *flow = 0) { m_LstList.remove(flow); }
/** /**
* Waits on any events which have been added to the event management. If * Switches the control flow to the simulator and waits on any listeners
* one of those events occour, waitAny() will return the id of that event. * which have been added to the listener management. If one of those listeners
* @return the previously occurred event, or \c NULL if there are no * occurs, resume() will return the pointer of that listener.
* events to wait for * @return the previously occurred listener, or \c NULL if there are no
* listeners to wait for
*/ */
BaseEvent* waitAny(); BaseListener* resume();
/** /**
* Add event \a ev to the global buffer and wait for it (combines * Add listener \a ev to the global buffer and continues the simulation
* \c addEvent() and \c waitAny()). * (combines \c addListener() and \c resume()).
* @param ev the event pointer to be added * @param li the listener pointer to be added
* @return the pointer of the occurred event (it is not guaranteed that * @return the pointer of the occurred listener (it is not guaranteed that
* this pointer will be equal to ev) * this pointer will be equal to li)
*
* FIXME: Rename to make clear this returns when *any* event occurs
*/ */
BaseEvent* addEventAndWait(BaseEvent* ev); BaseListener* addListenerAndResume(BaseListener* li);
/** /**
* Checks whether any experiment flow has events in the event-list. * Checks whether any experiment flow has listeners in the listener-list.
* @return \c true if there are still events, or \c false otherwise * @return \c true if there are still listeners, or \c false otherwise
*/ */
bool hasEvents() const { return getEventCount() > 0; } bool hasListeners() const { return getListenerCount() > 0; }
/** /**
* Determines the number of (stored) events in the event-list which have * Determines the number of (stored) listeners in the listener-list which have
* not been triggered so far. * not been triggered so far.
* @return the actual number of events * @return the actual number of listeners
*/ */
unsigned getEventCount() const { return m_EvList.getEventCount(); } unsigned getListenerCount() const { return m_LstList.getListenerCount(); }
}; };
} // end-of-namespace: fail } // end-of-namespace: fail

View File

@ -101,13 +101,13 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp
m_CacheEntry = cache_entry; m_CacheEntry = cache_entry;
bool do_fire = false; bool do_fire = false;
// Check for active breakpoint-events: // Check for active breakpoint-events:
bp_cache_t &buffer_cache = m_EvList.getBPBuffer(); bp_cache_t &buffer_cache = m_LstList.getBPBuffer();
bp_cache_t::iterator it = buffer_cache.begin(); bp_cache_t::iterator it = buffer_cache.begin();
while (it != buffer_cache.end()) { while (it != buffer_cache.end()) {
BPEvent* pEvBreakpt = *it; BPListener* pEvBreakpt = *it;
if (pEvBreakpt->isMatching(instrPtr, address_space)) { if (pEvBreakpt->isMatching(instrPtr, address_space)) {
pEvBreakpt->setTriggerInstructionPointer(instrPtr); pEvBreakpt->setTriggerInstructionPointer(instrPtr);
it = buffer_cache.makeActive(m_EvList, it); it = buffer_cache.makeActive(m_LstList, it);
do_fire = true; do_fire = true;
// "it" has already been set to the next element (by calling // "it" has already been set to the next element (by calling
// makeActive()): // makeActive()):
@ -116,28 +116,28 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp
it++; it++;
} }
if (do_fire) if (do_fire)
m_EvList.fireActiveEvents(); m_LstList.triggerActiveListeners();
// Note: SimulatorController::onBreakpointEvent will not be invoked in this // Note: SimulatorController::onBreakpointListener will not be invoked in this
// implementation. // implementation.
} }
void BochsController::onIOPortEvent(unsigned char data, unsigned port, bool out) { void BochsController::onIOPortListener(unsigned char data, unsigned port, bool out) {
// Check for active breakpoint-events: // Check for active breakpoint-events:
io_cache_t &buffer_cache = m_EvList.getIOBuffer(); io_cache_t &buffer_cache = m_LstList.getIOBuffer();
io_cache_t::iterator it = buffer_cache.begin(); io_cache_t::iterator it = buffer_cache.begin();
while (it != buffer_cache.end()) { while (it != buffer_cache.end()) {
IOPortEvent* pIOPt = (*it); IOPortListener* pIOPt = (*it);
if (pIOPt->isMatching(port, out)) { if (pIOPt->isMatching(port, out)) {
pIOPt->setData(data); pIOPt->setData(data);
it = buffer_cache.makeActive(m_EvList, it); it = buffer_cache.makeActive(m_LstList, it);
// "it" has already been set to the next element (by calling // "it" has already been set to the next element (by calling
// makeActive()): // makeActive()):
continue; // -> skip iterator increment continue; // -> skip iterator increment
} }
it++; it++;
} }
m_EvList.fireActiveEvents(); m_LstList.triggerActiveListeners();
// Note: SimulatorController::onBreakpointEvent will not be invoked in this // Note: SimulatorController::onBreakpointListener will not be invoked in this
// implementation. // implementation.
} }
@ -165,7 +165,7 @@ void BochsController::saveDone()
void BochsController::restore(const std::string& path) void BochsController::restore(const std::string& path)
{ {
clearEvents(); clearListeners();
restore_bochs_request = true; restore_bochs_request = true;
BX_CPU(0)->async_event |= 1; BX_CPU(0)->async_event |= 1;
sr_path = path; sr_path = path;
@ -181,7 +181,7 @@ void BochsController::restoreDone()
void BochsController::reboot() void BochsController::reboot()
{ {
clearEvents(); clearListeners();
reboot_bochs_request = true; reboot_bochs_request = true;
BX_CPU(0)->async_event |= 1; BX_CPU(0)->async_event |= 1;
m_CurrFlow = m_Flows.getCurrent(); m_CurrFlow = m_Flows.getCurrent();
@ -213,16 +213,16 @@ void BochsController::onTimerTrigger(void* thisPtr)
{ {
// FIXME: The timer logic can be modified to use only one timer in Bochs. // FIXME: The timer logic can be modified to use only one timer in Bochs.
// (For now, this suffices.) // (For now, this suffices.)
TimerEvent* pTmEv = static_cast<TimerEvent*>(thisPtr); TimerListener* pli = static_cast<TimerListener*>(thisPtr);
// Check for a matching TimerEvent. (In fact, we are only // Check for a matching TimerListener. (In fact, we are only
// interessted in the iterator pointing at pTmEv.): // interessted in the iterator pointing at pli.)
EventManager::iterator it = std::find(simulator.m_EvList.begin(), ListenerManager::iterator it = std::find(simulator.m_LstList.begin(),
simulator.m_EvList.end(), pTmEv); simulator.m_LstList.end(), pli);
// TODO: This has O(|m_EvList|) time complexity. We can further improve this // TODO: This has O(|m_LstList|) time complexity. We can further improve this
// by creating a method such that makeActive(pTmEv) works as well, // by creating a method such that makeActive(pli) works as well,
// reducing the time complexity to O(1). // reducing the time complexity to O(1).
simulator.m_EvList.makeActive(it); simulator.m_LstList.makeActive(it);
simulator.m_EvList.fireActiveEvents(); simulator.m_LstList.triggerActiveListeners();
} }
const std::string& BochsController::getMnemonic() const const std::string& BochsController::getMnemonic() const

View File

@ -8,10 +8,10 @@
#include <string.h> #include <string.h>
#include "FailBochsGlobals.hpp" #include "FailBochsGlobals.hpp"
#include "BochsEvents.hpp" #include "BochsListener.hpp"
#include "../SimulatorController.hpp" #include "../SimulatorController.hpp"
#include "../Event.hpp" #include "../Listener.hpp"
#include "bochs.h" #include "bochs.h"
#include "cpu/cpu.h" #include "cpu/cpu.h"
@ -40,7 +40,7 @@ public:
BochsController(); BochsController();
~BochsController(); ~BochsController();
/* ******************************************************************** /* ********************************************************************
* Standard Event Handler API: * Standard Listener Handler API:
* ********************************************************************/ * ********************************************************************/
/** /**
* Instruction pointer modification handler. This method is called (from * Instruction pointer modification handler. This method is called (from
@ -57,20 +57,20 @@ public:
* @param port the port it was transmitted on * @param port the port it was transmitted on
* @param out true if the I/O traffic has been outbound, false otherwise * @param out true if the I/O traffic has been outbound, false otherwise
*/ */
void onIOPortEvent(unsigned char data, unsigned port, bool out); void onIOPortListener(unsigned char data, unsigned port, bool out);
/** /**
* Static internal event handler for TimerEvents. This static function is * Static internal handler for TimerListeners. This static function is
* called when a previously registered (Bochs) timer triggers. This function * called when a previously registered (Bochs) timer triggers. This function
* searches for the provided TimerEvent object within the EventManager and * searches for the provided TimerListener object within the ListenerManager and
* fires such an event by calling \c fireActiveEvents(). * fires such an event by calling \c triggerActiveListeners().
* @param thisPtr a pointer to the TimerEvent-object triggered * @param thisPtr a pointer to the TimerListener-object triggered
* *
* FIXME: Due to Bochs internal timer and ips-configuration related stuff, * FIXME: Due to Bochs internal timer and ips-configuration related stuff,
* the simulator sometimes panics with "keyboard error:21" (see line * the simulator sometimes panics with "keyboard error:21" (see line
* 1777 in bios/rombios.c, function keyboard_init()) if a TimerEvent * 1777 in bios/rombios.c, function keyboard_init()) if a TimerListener
* is added *before* the bios has been loaded and initialized. To * is added *before* the bios has been loaded and initialized. To
* reproduce this error, try adding a \c TimerEvent as the initial step * reproduce this error, try adding a \c TimerListener as the initial step
* in your experiment code and wait for it (\c addEventAndWait()). * in your experiment code and wait for it (\c addListenerAndResume()).
*/ */
static void onTimerTrigger(void *thisPtr); static void onTimerTrigger(void *thisPtr);
/* ******************************************************************** /* ********************************************************************
@ -86,7 +86,7 @@ public:
*/ */
void saveDone(); void saveDone();
/** /**
* Restore simulator state. Clears all Events. * Restore simulator state. Clears all Listeners.
* @param path Location to previously saved state information * @param path Location to previously saved state information
*/ */
void restore(const std::string& path); void restore(const std::string& path);
@ -95,7 +95,7 @@ public:
*/ */
void restoreDone(); void restoreDone();
/** /**
* Reboot simulator. Clears all Events. * Reboot simulator. Clears all Listeners.
*/ */
void reboot(); void reboot();
/** /**

View File

@ -1,34 +1,34 @@
#include "BochsEvents.hpp" #include "BochsListener.hpp"
#include "../SALInst.hpp" #include "../SALInst.hpp"
namespace fail { namespace fail {
bool TimerEvent::onEventAddition() bool TimerListener::onAddition()
{ {
// Register the timer event in the Bochs simulator: // Register the timer listener in the Bochs simulator:
setId(m_registerTimer(this)); setId(m_registerTimer(this));
if(getId() == -1) if(getId() == -1)
return false; // unable to register the timer (error in Bochs' function call) return false; // unable to register the timer (error in Bochs' function call)
return true; return true;
} }
void TimerEvent::onEventDeletion() void TimerListener::onDeletion()
{ {
// Unregister the time event: // Unregister the time listener:
m_unregisterTimer(this); m_unregisterTimer(this);
} }
timer_id_t TimerEvent::m_registerTimer(TimerEvent* pev) timer_id_t TimerListener::m_registerTimer(TimerListener* pev)
{ {
assert(pev != NULL && "FATAL ERROR: TimerEvent object ptr cannot be NULL!"); assert(pev != NULL && "FATAL ERROR: TimerListener object ptr cannot be NULL!");
return static_cast<timer_id_t>( return static_cast<timer_id_t>(
bx_pc_system.register_timer(pev, BochsController::onTimerTrigger, pev->getTimeout(), bx_pc_system.register_timer(pev, BochsController::onTimerTrigger, pev->getTimeout(),
false, 1/*start immediately*/, "Fail*: BochsController"/*name*/)); false, 1/*start immediately*/, "Fail*: BochsController"/*name*/));
} }
bool TimerEvent::m_unregisterTimer(TimerEvent* pev) bool TimerListener::m_unregisterTimer(TimerListener* pev)
{ {
assert(pev != NULL && "FATAL ERROR: TimerEvent object ptr cannot be NULL!"); assert(pev != NULL && "FATAL ERROR: TimerListener object ptr cannot be NULL!");
bx_pc_system.deactivate_timer(static_cast<unsigned>(pev->getId())); bx_pc_system.deactivate_timer(static_cast<unsigned>(pev->getId()));
return bx_pc_system.unregisterTimer(static_cast<unsigned>(pev->getId())); return bx_pc_system.unregisterTimer(static_cast<unsigned>(pev->getId()));
} }

View File

@ -1,74 +1,76 @@
#ifndef __BOCHS_EVENTS_HPP__ #ifndef __BOCHS_EVENTS_HPP__
#define __BOCHS_EVENTS_HPP__ #define __BOCHS_EVENTS_HPP__
#include "../Event.hpp" #include "../Listener.hpp"
#include "BochsController.hpp" #include "BochsController.hpp"
namespace fail { namespace fail {
/** /**
* \class TimerEvent * \class TimerListener
* Concrete TimerEvent implementation of GenericTimerEvent for the Bochs * Concrete TimerListener implementation of GenericTimerListener for the Bochs
* simulator backend. * simulator backend.
*/ */
class TimerEvent : public GenericTimerEvent { class TimerListener : public GenericTimerListener {
private: private:
/** /**
* Registers a timer in the Bochs simulator. This timer fires \a TimerEvents * Registers a timer in the Bochs simulator. This timer triggers \a TimerListeners
* to inform the corresponding experiment-flow. Note that the number of timers * 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). * (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, * @param pli a pointer to the (experiment flow-) allocated TimerListener object,
* providing all required information to start the time, e.g. the * providing all required information to start the timer (e.g. the
* timeout value. * timeout value).
* @return \c The unique id of the timer recently created. This id is carried * @return The unique id of the timer recently created. This id is carried
* along with the TimerEvent, @see getId(). On errors, -1 is returned * along with the TimerListener. On errors, -1 is returned (e.g. because
* (e.g. because a timer with the same id is already existing) * a timer with the same id is already existing)
* @see TimerListener::getId()
*/ */
static timer_id_t m_registerTimer(TimerEvent* pev); static timer_id_t m_registerTimer(TimerListener* pli);
/** /**
* Deletes a timer. No further events will be triggered by the timer. * Deletes a timer. No further events will be triggered by the timer.
* @param pev a pointer to the TimerEvent-object to be removed * @param pli a pointer to the TimerListener-object to be removed
* @return \c true if the timer with \a pev->getId() has been removed * @return \c true if the timer with \a pli->getId() has been removed
* successfully, \c false otherwise * successfully, \c false otherwise
*/ */
static bool m_unregisterTimer(TimerEvent* pev); static bool m_unregisterTimer(TimerListener* pli);
public: public:
/** /**
* Creates a new timer event. This can be used to implement a timeout- * Creates a new timer event. This can be used to implement a timeout-
* mechanism in the experiment-flow. The timer starts automatically when * mechanism in the experiment-flow. The timer starts automatically when
* added to FailBochs. * added to FailBochs.
* @param timeout the time intervall in milliseconds (ms) * @param timeout the time intervall in milliseconds (ms)
* @see SimulatorController::addEvent * @see SimulatorController::addListener
*/ */
TimerEvent(unsigned timeout) TimerListener(unsigned timeout)
: GenericTimerEvent(timeout) { } : GenericTimerListener(timeout) { }
~TimerEvent() { onEventDeletion(); } ~TimerListener() { onDeletion(); }
/** /**
* This method is called when an experiment flow adds a new event by * This method is called when an experiment flow adds a new event by
* calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev). * calling \c simulator.addListener() or \c simulator.addListenerAndResume().
* More specifically, the event will be added to the event-list first * More specifically, the event will be added to the event-list first
* (buffer-list, to be precise) and then this event handler is called. * (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 * @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 * the addition of the event \a pev, yielding an error in the
* experiment flow (i.e. -1 is returned). * experiment flow (i.e. -1 is returned).
*/ */
bool onEventAddition(); bool onAddition();
/** /**
* This method is called when an experiment flow removes an event from * This method is called when an experiment flow removes an event from
* the event-management by calling \c removeEvent(prev), \c clearEvents() * the event-management by calling \c removeListener(prev), \c clearListeners()
* or by deleting a complete flow (\c removeFlow). More specifically, this * or by deleting a complete flow (\c removeFlow). More specifically, this
* event handler will be called *before* the event is actually deleted. * event handler will be called *before* the event is actually deleted.
*/ */
void onEventDeletion(); void onDeletion();
/** /**
* This method is called when an previously added event is about to be * This method is called when an previously added event is about to be
* triggered by the simulator-backend. More specifically, this event handler * triggered by the simulator-backend. More specifically, this event handler
* will be called *before* the event is actually triggered, i.e. before the * will be called *before* the event is actually triggered, i.e. before the
* corresponding coroutine is toggled. * corresponding coroutine is toggled.
*/ */
void onEventTrigger() { onEventDeletion(); } void onTrigger() { onDeletion(); }
// TODO/FIXME: bei neuer impl. anpassen
}; };
} // end-of-namespace: fail } // end-of-namespace: fail

View File

@ -32,7 +32,7 @@ public:
* Retrieves the data of the register. * Retrieves the data of the register.
* @return the current register data * @return the current register data
*/ */
regdata_t getData() { return (*m_pData); } regdata_t getData() { return *m_pData; }
/** /**
* Sets the content of the register. * Sets the content of the register.
* @param data the new register data to be written * @param data the new register data to be written
@ -136,44 +136,42 @@ public:
* Returns \c true if the corresponding flag is set, or \c false * Returns \c true if the corresponding flag is set, or \c false
* otherwise. * otherwise.
*/ */
bool getCarryFlag() const { return (BX_CPU(0)->get_CF()); } bool getCarryFlag() const { return BX_CPU(0)->get_CF(); }
bool getParityFlag() const { return (BX_CPU(0)->get_PF()); } bool getParityFlag() const { return BX_CPU(0)->get_PF(); }
bool getZeroFlag() const { return (BX_CPU(0)->get_ZF()); } bool getZeroFlag() const { return BX_CPU(0)->get_ZF(); }
bool getSignFlag() const { return (BX_CPU(0)->get_SF()); } bool getSignFlag() const { return BX_CPU(0)->get_SF(); }
bool getOverflowFlag() const { return (BX_CPU(0)->get_OF()); } bool getOverflowFlag() const { return BX_CPU(0)->get_OF(); }
bool getTrapFlag() const { return BX_CPU(0)->get_TF(); }
bool getTrapFlag() const { return (BX_CPU(0)->get_TF()); } bool getInterruptFlag() const { return BX_CPU(0)->get_IF(); }
bool getInterruptFlag() const { return (BX_CPU(0)->get_IF()); } bool getDirectionFlag() const { return BX_CPU(0)->get_DF(); }
bool getDirectionFlag() const { return (BX_CPU(0)->get_DF()); } unsigned getIOPrivilegeLevel() const { return BX_CPU(0)->get_IOPL(); }
unsigned getIOPrivilegeLevel() const { return (BX_CPU(0)->get_IOPL()); } bool getNestedTaskFlag() const { return BX_CPU(0)->get_NT(); }
bool getNestedTaskFlag() const { return (BX_CPU(0)->get_NT()); } bool getResumeFlag() const { return BX_CPU(0)->get_RF(); }
bool getResumeFlag() const { return (BX_CPU(0)->get_RF()); } bool getVMFlag() const { return BX_CPU(0)->get_VM(); }
bool getVMFlag() const { return (BX_CPU(0)->get_VM()); } bool getAlignmentCheckFlag() const { return BX_CPU(0)->get_AC(); }
bool getAlignmentCheckFlag() const { return (BX_CPU(0)->get_AC()); } bool getVInterruptFlag() const { return BX_CPU(0)->get_VIF(); }
bool getVInterruptFlag() const { return (BX_CPU(0)->get_VIF()); } bool getVInterruptPendingFlag() const { return BX_CPU(0)->get_VIP(); }
bool getVInterruptPendingFlag() const { return (BX_CPU(0)->get_VIP()); } bool getIdentificationFlag() const { return BX_CPU(0)->get_ID(); }
bool getIdentificationFlag() const { return (BX_CPU(0)->get_ID()); }
/** /**
* Sets/resets various status FLAGS. * Sets/resets various status FLAGS.
*/ */
void setCarryFlag(bool bit) { BX_CPU(0)->set_CF(bit); } void setCarryFlag(bool bit) { BX_CPU(0)->set_CF(bit); }
void setParityFlag(bool bit) { BX_CPU(0)->set_PF(bit); } void setParityFlag(bool bit) { BX_CPU(0)->set_PF(bit); }
void setZeroFlag(bool bit) { BX_CPU(0)->set_ZF(bit); } void setZeroFlag(bool bit) { BX_CPU(0)->set_ZF(bit); }
void setSignFlag(bool bit) { BX_CPU(0)->set_SF(bit); } void setSignFlag(bool bit) { BX_CPU(0)->set_SF(bit); }
void setOverflowFlag(bool bit) { BX_CPU(0)->set_OF(bit); } void setOverflowFlag(bool bit) { BX_CPU(0)->set_OF(bit); }
void setTrapFlag(bool bit) { BX_CPU(0)->set_TF(bit); }
void setTrapFlag(bool bit) { BX_CPU(0)->set_TF(bit); } void setInterruptFlag(bool bit) { BX_CPU(0)->set_IF(bit); }
void setInterruptFlag(bool bit) { BX_CPU(0)->set_IF(bit); } void setDirectionFlag(bool bit) { BX_CPU(0)->set_DF(bit); }
void setDirectionFlag(bool bit) { BX_CPU(0)->set_DF(bit); } void setIOPrivilegeLevel(unsigned lvl) { BX_CPU(0)->set_IOPL(lvl); }
void setIOPrivilegeLevel(unsigned lvl) { BX_CPU(0)->set_IOPL(lvl); } void setNestedTaskFlag(bool bit) { BX_CPU(0)->set_NT(bit); }
void setNestedTaskFlag(bool bit) { BX_CPU(0)->set_NT(bit); } void setResumeFlag(bool bit) { BX_CPU(0)->set_RF(bit); }
void setResumeFlag(bool bit) { BX_CPU(0)->set_RF(bit); } void setVMFlag(bool bit) { BX_CPU(0)->set_VM(bit); }
void setVMFlag(bool bit) { BX_CPU(0)->set_VM(bit); } void setAlignmentCheckFlag(bool bit) { BX_CPU(0)->set_AC(bit); }
void setAlignmentCheckFlag(bool bit) { BX_CPU(0)->set_AC(bit); } void setVInterruptFlag(bool bit) { BX_CPU(0)->set_VIF(bit); }
void setVInterruptFlag(bool bit) { BX_CPU(0)->set_VIF(bit); } void setVInterruptPendingFlag(bool bit) { BX_CPU(0)->set_VIP(bit); }
void setVInterruptPendingFlag(bool bit) { BX_CPU(0)->set_VIP(bit); } void setIdentificationFlag(bool bit) { BX_CPU(0)->set_ID(bit); }
void setIdentificationFlag(bool bit) { BX_CPU(0)->set_ID(bit); }
/** /**
* Sets the content of the status register. * Sets the content of the status register.
@ -184,8 +182,7 @@ public:
{ {
#ifdef BX_SUPPORT_X86_64 #ifdef BX_SUPPORT_X86_64
// We are in 64 bit mode: Just assign the lower 32 bits! // We are in 64 bit mode: Just assign the lower 32 bits!
(*m_pData) = ((*m_pData) & 0xFFFFFFFF00000000ULL) | (*m_pData) = ((*m_pData) & 0xFFFFFFFF00000000ULL) | (data & 0xFFFFFFFFULL);
(data & 0xFFFFFFFFULL);
#else #else
*m_pData = data; *m_pData = data;
#endif #endif
@ -204,7 +201,7 @@ public:
*/ */
address_t getInstructionPointer() address_t getInstructionPointer()
{ {
return (static_cast<address_t>(getSetOfType(RT_PC)->first()->getData())); return static_cast<address_t>(getSetOfType(RT_PC)->first()->getData());
} }
/** /**
* Retruns the top address of the stack. * Retruns the top address of the stack.
@ -213,9 +210,9 @@ public:
address_t getStackPointer() address_t getStackPointer()
{ {
#if BX_SUPPORT_X86_64 #if BX_SUPPORT_X86_64
return (static_cast<address_t>(getRegister(RID_RSP)->getData())); return static_cast<address_t>(getRegister(RID_RSP)->getData());
#else #else
return (static_cast<address_t>(getRegister(RID_ESP)->getData())); return static_cast<address_t>(getRegister(RID_ESP)->getData());
#endif #endif
} }
/** /**
@ -226,9 +223,9 @@ public:
address_t getBasePointer() address_t getBasePointer()
{ {
#if BX_SUPPORT_X86_64 #if BX_SUPPORT_X86_64
return (static_cast<address_t>(getRegister(RID_RBP)->getData())); return static_cast<address_t>(getRegister(RID_RBP)->getData());
#else #else
return (static_cast<address_t>(getRegister(RID_EBP)->getData())); return static_cast<address_t>(getRegister(RID_EBP)->getData());
#endif #endif
} }
}; };

View File

@ -13,7 +13,7 @@
aspect Breakpoints { aspect Breakpoints {
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)"; pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
advice execution (cpuLoop()) : after () // Event source: "instruction pointer" advice execution (cpuLoop()) : after () // event source: "instruction pointer"
{ {
// Points to the cpu class: "this" if BX_USE_CPU_SMF == 0, // Points to the cpu class: "this" if BX_USE_CPU_SMF == 0,
// BX_CPU(0) otherwise // BX_CPU(0) otherwise

View File

@ -19,12 +19,12 @@
aspect GuestSysCom { aspect GuestSysCom {
pointcut outInstructions() = "% ...::bx_cpu_c::OUT_DX%(...)"; pointcut outInstructions() = "% ...::bx_cpu_c::OUT_DX%(...)";
advice execution (outInstructions()) : after () // Event source: "guest system" advice execution (outInstructions()) : after () // Listener source: "guest system"
{ {
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
unsigned rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data unsigned rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
if (rDX == BOCHS_COM_PORT) if (rDX == BOCHS_COM_PORT)
fail::simulator.onGuestSystemEvent((char)rAL, rDX); fail::simulator.onGuestSystemListener((char)rAL, rDX);
} }
}; };

View File

@ -21,7 +21,7 @@ aspect IOPortCom {
{ {
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
fail::simulator.onIOPortEvent(rAL, rDX, true); fail::simulator.onIOPortListener(rAL, rDX, true);
} }
pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX(...)"; pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX(...)";
@ -30,7 +30,7 @@ aspect IOPortCom {
{ {
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
fail::simulator.onIOPortEvent(rAL, rDX, false); fail::simulator.onIOPortListener(rAL, rDX, false);
} }
}; };

View File

@ -28,9 +28,9 @@ aspect Interrupt {
unsigned vector = *(tjp->arg<0>()); unsigned vector = *(tjp->arg<0>());
unsigned type = *(tjp->arg<1>()); unsigned type = *(tjp->arg<1>());
if (type == BX_EXTERNAL_INTERRUPT) if (type == BX_EXTERNAL_INTERRUPT)
fail::simulator.onInterruptEvent(vector, false); fail::simulator.onInterruptListener(vector, false);
else if (type == BX_NMI) else if (type == BX_NMI)
fail::simulator.onInterruptEvent(vector, true); fail::simulator.onInterruptListener(vector, true);
} }
}; };

View File

@ -60,7 +60,7 @@ aspect Jump {
advice execution (defJumpInstructions()) : around() advice execution (defJumpInstructions()) : around()
{ {
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
fail::simulator.onJumpEvent(true, pInstr->getIaOpcode()); fail::simulator.onJumpListener(true, pInstr->getIaOpcode());
/* /*
JoinPoint::That* pThis = tjp->that(); JoinPoint::That* pThis = tjp->that();
if(pThis == NULL) if(pThis == NULL)
@ -77,7 +77,7 @@ aspect Jump {
// Step-1: Modify one or more of the fxxxFlag according to the error you want to inject // Step-1: Modify one or more of the fxxxFlag according to the error you want to inject
// (using pThis->set_XX(new_val)) // (using pThis->set_XX(new_val))
// Step-2: Call tjp->proceed(); // Step-2: Call tjp->proceed();
// Step-3: Eventually, unwind the changes of Step-1 // Step-3: Listenerually, unwind the changes of Step-1
// //
// Example: // Example:
@ -109,7 +109,7 @@ aspect Jump {
advice execution (regJumpInstructions()) : around () advice execution (regJumpInstructions()) : around ()
{ {
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
fail::simulator.onJumpEvent(false, pInstr->getIaOpcode()); fail::simulator.onJumpListener(false, pInstr->getIaOpcode());
/* /*
JoinPoint::That* pThis = tjp->that(); JoinPoint::That* pThis = tjp->that();

View File

@ -56,19 +56,19 @@ aspect MemAccess {
// Fire a memory-write-event each time the guest system requests // Fire a memory-write-event each time the guest system requests
// to write data to RAM: // to write data to RAM:
// //
// Event source: "memory write access" // Listener source: "memory write access"
// //
#ifdef CONFIG_EVENT_MEMWRITE #ifdef CONFIG_EVENT_MEMWRITE
advice execution (write_methods()) : after () advice execution (write_methods()) : after ()
{ {
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
*(tjp->arg<1>()), sizeof(*(tjp->arg<2>())), true, *(tjp->arg<1>()), sizeof(*(tjp->arg<2>())), true,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
} }
advice execution (write_methods_RMW()) : after () advice execution (write_methods_RMW()) : after ()
{ {
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
rmw_address, sizeof(*(tjp->arg<0>())), true, rmw_address, sizeof(*(tjp->arg<0>())), true,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
} }
@ -77,7 +77,7 @@ aspect MemAccess {
{ {
std::cerr << "WOOOOOT write_methods_new_stack" << std::endl; std::cerr << "WOOOOOT write_methods_new_stack" << std::endl;
// TODO: Log-level? // TODO: Log-level?
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
*(tjp->arg<1>()), sizeof(*(tjp->arg<3>())), true, *(tjp->arg<1>()), sizeof(*(tjp->arg<3>())), true,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
} }
@ -86,7 +86,7 @@ aspect MemAccess {
{ {
std::cerr << "WOOOOOT write_methods_new_stack_64" << std::endl; std::cerr << "WOOOOOT write_methods_new_stack_64" << std::endl;
// TODO: Log-level? // TODO: Log-level?
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
*(tjp->arg<0>()), sizeof(*(tjp->arg<2>())), true, *(tjp->arg<0>()), sizeof(*(tjp->arg<2>())), true,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
} }
@ -98,7 +98,7 @@ aspect MemAccess {
// memory (e.g., to read vectors from the interrupt vector // memory (e.g., to read vectors from the interrupt vector
// table). // table).
/* /*
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
*(tjp->arg<0>()), sizeof(*(tjp->arg<1>())), true, *(tjp->arg<0>()), sizeof(*(tjp->arg<1>())), true,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
*/ */
@ -109,19 +109,19 @@ aspect MemAccess {
// Fire a memory-read-event each time the guest system requests // Fire a memory-read-event each time the guest system requests
// to read data in RAM: // to read data in RAM:
// //
// Event source: "memory read access" // Listener source: "memory read access"
// //
#ifdef CONFIG_EVENT_MEMREAD #ifdef CONFIG_EVENT_MEMREAD
advice execution (read_methods()) : before () advice execution (read_methods()) : before ()
{ {
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
*(tjp->arg<1>()), sizeof(*(tjp->result())), false, *(tjp->arg<1>()), sizeof(*(tjp->result())), false,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
} }
advice execution (read_methods_dqword()) : before () advice execution (read_methods_dqword()) : before ()
{ {
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
*(tjp->arg<1>()), 16, false, *(tjp->arg<1>()), 16, false,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
} }
@ -131,7 +131,7 @@ aspect MemAccess {
{ {
rmw_address = *(tjp->arg<1>()); rmw_address = *(tjp->arg<1>());
#ifdef CONFIG_EVENT_MEMREAD #ifdef CONFIG_EVENT_MEMREAD
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
*(tjp->arg<1>()), sizeof(*(tjp->result())), false, *(tjp->arg<1>()), sizeof(*(tjp->result())), false,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
#endif #endif
@ -145,7 +145,7 @@ aspect MemAccess {
// memory (e.g., to read vectors from the interrupt vector // memory (e.g., to read vectors from the interrupt vector
// table). // table).
/* /*
fail::simulator.onMemoryAccessEvent( fail::simulator.onMemoryAccessListener(
*(tjp->arg<0>()), sizeof(*(tjp->result())), false, *(tjp->arg<0>()), sizeof(*(tjp->result())), false,
getCPU(tjp->that())->prev_rip); getCPU(tjp->that())->prev_rip);
*/ */

View File

@ -15,9 +15,9 @@ aspect Trap {
advice execution (exception_method()) : before () advice execution (exception_method()) : before ()
{ {
fail::simulator.onTrapEvent(*(tjp->arg<0>())); fail::simulator.onTrapListener(*(tjp->arg<0>()));
// TODO: There are some different types of exceptions at cpu.h (line 265-281) // TODO: There are some different types of exceptions at cpu.h (line 265-281)
// Which kind of a trap are these types? // Which kind of traps are these types?
} }
}; };

View File

@ -84,12 +84,12 @@ void OVPController::onInstrPtrChanged(address_t instrPtr)
// cerr << "instrPtr: 0x" << hex << instrPtr << " SP: 0x" << hex << m_Regs->getStackPointer() \ // cerr << "instrPtr: 0x" << hex << instrPtr << " SP: 0x" << hex << m_Regs->getStackPointer() \
// << " R0: 0x" << hex << r0 << " ST: 0x" << hex << st << endl; // << " R0: 0x" << hex << r0 << " ST: 0x" << hex << st << endl;
// Check for active breakpoint-events: // Check for active breakpoint-Listeners:
EventManager::iterator it = m_EvList.begin(); ListenerManager::iterator it = m_EvList.begin();
while (it != m_EvList.end()) { while (it != m_EvList.end()) {
// FIXME: Performance verbessern (dazu muss entsprechend auch die Speicherung // FIXME: Performance verbessern (dazu muss entsprechend auch die Speicherung
// in EventManager(.cc|.hpp) angepasst bzw. verbessert werden). // in ListenerManager(.cc|.hpp) angepasst bzw. verbessert werden).
BPSingleEvent* pEvBreakpt = dynamic_cast<BPSingleEvent*>(*it); BPSingleListener* pEvBreakpt = dynamic_cast<BPSingleListener*>(*it);
if (pEvBreakpt && (instrPtr == pEvBreakpt->getWatchInstructionPointer() || if (pEvBreakpt && (instrPtr == pEvBreakpt->getWatchInstructionPointer() ||
pEvBreakpt->getWatchInstructionPointer() == ANY_ADDR)) { pEvBreakpt->getWatchInstructionPointer() == ANY_ADDR)) {
pEvBreakpt->setTriggerInstructionPointer(instrPtr); pEvBreakpt->setTriggerInstructionPointer(instrPtr);
@ -98,7 +98,7 @@ void OVPController::onInstrPtrChanged(address_t instrPtr)
// makeActive()): // makeActive()):
continue; // -> skip iterator increment continue; // -> skip iterator increment
} }
BPRangeEvent* pEvRange = dynamic_cast<BPRangeEvent*>(*it); BPRangeListener* pEvRange = dynamic_cast<BPRangeListener*>(*it);
if (pEvRange && pEvRange->isMatching(instrPtr)) { if (pEvRange && pEvRange->isMatching(instrPtr)) {
pEvBreakpt->setTriggerInstructionPointer(instrPtr); pEvBreakpt->setTriggerInstructionPointer(instrPtr);
it = m_EvList.makeActive(it); it = m_EvList.makeActive(it);
@ -106,7 +106,7 @@ void OVPController::onInstrPtrChanged(address_t instrPtr)
} }
it++; it++;
} }
m_EvList.fireActiveEvents(); m_EvList.fireActiveListeners();
} }
void OVPController::save(const string& path) void OVPController::save(const string& path)

View File

@ -7,7 +7,7 @@
#include <string> #include <string>
#include "../SimulatorController.hpp" #include "../SimulatorController.hpp"
#include "../Event.hpp" #include "../Listener.hpp"
#include "../Register.hpp" #include "../Register.hpp"
#include "ovp/OVPPlatform.hpp" #include "ovp/OVPPlatform.hpp"