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

View File

@ -6,7 +6,7 @@
namespace fail {
class EventManager;
class ListenerManager;
/**
* \class BufferCache
@ -14,19 +14,19 @@ class EventManager;
* \brief A simple dynamic array
*
* 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>
class BufferCache {
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.
*/
typedef std::list<T> cachelist_t;
/**
* 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.
* (This behaviour equals the STL iterator in C++.)
*/
@ -79,14 +79,14 @@ public:
*/
inline iterator end() { return m_Buffer.end(); }
/**
* Acts as a replacement for EventManager::makeActive, manipulating
* the buffer cache exclusively. EventManager::fireActiveEvents needs
* to be called to fire the active events (see there).
* This method is declared as a friend method in EventManager.
* @param idx the index of the event to trigger
* Acts as a replacement for ListenerManager::makeActive, manipulating
* the buffer cache exclusively. ListenerManager::triggerActiveListeners needs
* to be called to fire the active listeners (see there).
* This method is declared as a friend method in ListenerManager.
* @param idx the index of the listener to trigger
* @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

View File

@ -1,19 +1,19 @@
if(BUILD_BOCHS)
set(SRCS
BufferCache.cc
Event.cc
EventManager.cc
Listener.cc
ListenerManager.cc
Memory.cc
Register.cc
SimulatorController.cc
bochs/BochsController.cc
bochs/BochsEvents.cc
bochs/BochsListener.cc
)
else()
set(SRCS
BufferCache.cc
Event.cc
EventManager.cc
Listener.cc
ListenerManager.cc
Memory.cc
Register.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"
namespace fail {
bool TroubleEvent::isMatching(unsigned troubleNum) const
bool TroubleListener::isMatching(unsigned troubleNum) const
{
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
if (m_WatchNumbers[i] == troubleNum ||
@ -13,7 +13,7 @@ bool TroubleEvent::isMatching(unsigned troubleNum) const
return false;
}
bool TroubleEvent::removeWatchNumber(unsigned troubleNum)
bool TroubleListener::removeWatchNumber(unsigned troubleNum)
{
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
if (m_WatchNumbers[i] == troubleNum) {
@ -24,7 +24,7 @@ bool TroubleEvent::removeWatchNumber(unsigned troubleNum)
return false;
}
bool TroubleEvent::addWatchNumber(unsigned troubleNumber)
bool TroubleListener::addWatchNumber(unsigned troubleNumber)
{
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
if (m_WatchNumbers[i] == troubleNumber)
@ -34,7 +34,7 @@ bool TroubleEvent::addWatchNumber(unsigned troubleNumber)
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))
return false;
@ -44,20 +44,20 @@ bool MemAccessEvent::isMatching(address_t addr, accessType_t accesstype) const
return true;
}
bool BPEvent::aspaceIsMatching(address_t aspace) const
bool BPListener::aspaceIsMatching(address_t aspace) const
{
if (m_CR3 == ANY_ADDR || m_CR3 == aspace)
return true;
return false;
}
void BPRangeEvent::setWatchInstructionPointerRange(address_t start, address_t end)
void BPRangeListener::setWatchInstructionPointerRange(address_t start, address_t end)
{
m_WatchStartAddr = start;
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))
return false;
@ -67,7 +67,7 @@ bool BPRangeEvent::isMatching(address_t addr, address_t aspace) const
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 (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr)

View File

@ -1,5 +1,5 @@
#ifndef __EVENT_HPP__
#define __EVENT_HPP__
#ifndef __LISTENER_HPP__
#define __LISTENER_HPP__
#include <ctime>
#include <string>
@ -14,7 +14,7 @@ namespace fail {
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);
//! instruction wildcard
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);
/**
* \class BaseEvent
* This is the base class for all event types.
* \class BaseListener
* This is the base class for all listener types.
*/
class BaseEvent {
class BaseListener {
protected:
time_t m_tStamp; //!< time stamp of event
unsigned int m_OccCounter; //!< event fires when 0 is reached
time_t m_tStamp; //!< time stamp of listener
unsigned int m_OccCounter; //!< listener fires when 0 is reached
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:
BaseEvent() : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL)
BaseListener() : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL)
{ updateTime(); }
virtual ~BaseEvent() { }
virtual ~BaseListener() { }
/**
* This method is called when an experiment flow adds a new event by
* calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev).
* More specifically, the event will be added to the event-list first
* (buffer-list, to be precise) and then this event handler is called.
* This method is called when an experiment flow adds a new listener by
* calling \c simulator.addListener() or \c simulator.addListenerAndResume().
* More specifically, the listener will be added to the listener manager first
* (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
* the addition of the event \a pev, yielding an error in the
* experiment flow (i.e. \c INVALID_EVENT is returned).
* the addition of the listener \c this, yielding an error in the
* 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
* the event-management by calling \c removeEvent(prev), \c clearEvents()
* or by deleting a complete flow (\c removeFlow). More specifically, this
* event handler will be called *before* the event is actually deleted.
* This method is called when an experiment flow removes an listener from
* the listener-management by calling \c removeListener(), \c clearListeners()
* or by deleting a complete flow (\c removeFlow()). More specifically, this
* 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
* triggered by the simulator-backend. More specifically, this event handler
* will be called *before* the event is actually triggered, i.e. before the
* This method is called when an previously added listener is about to be
* triggered by the simulator-backend. More specifically, this listener handler
* will be called *before* the listener is actually triggered, i.e. before the
* 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
* the event gets created, id est the constructor is called. The meaning
* of this value depends on the actual event type.
* Retrieves the time stamp of this listener. The time stamp is set when
* the listener gets created, i.e. the constructor is called. The meaning
* of this value depends on the actual listener type.
* @return the time stamp
*/
std::time_t getTime() const { return (m_tStamp); }
/**
* Decreases the event counter by one. When this counter reaches zero, the
* event will be triggered.
* Decreases the listener counter by one. When this counter reaches zero, the
* listener will be triggered.
*/
void decreaseCounter() { --m_OccCounter; }
/**
* Sets the event counter to the specified value (default is one).
* @param val the new counter value
* Sets the listener counter to the specified value.
* @param val the new counter value (default is 1)
*/
void setCounter(unsigned int val = 1) { m_OccCounter = m_OccCounterInit = val; }
/**
@ -84,56 +85,56 @@ public:
*/
unsigned int getCounter() const { return (m_OccCounter); }
/**
* Resets the event counter to its default value, or the last
* value that was set through setCounter().
* Resets the listener counter to its default value, or the last
* value that was set through \c setCounter().
*/
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); }
/**
* Returns the parent experiment of this event (context).
* If the event was created temporarily or wasn't linked to a context,
* Returns the parent experiment of this listener (context).
* If the listener was created temporarily or wasn't linked to a context,
* the return value is \c NULL (unknown identity).
*/
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).
*/
void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; }
};
// ----------------------------------------------------------------------------
// Specialized events:
// Specialized listeners:
//
/**
* \class BEvent
* A Breakpoint event to observe instruction changes within a given address space.
* \class BListener
* A Breakpoint listener to observe instruction changes within a given address space.
*/
class BPEvent : virtual public BaseEvent {
class BPListener : virtual public BaseListener {
private:
address_t m_CR3;
address_t m_TriggerInstrPtr;
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.
* @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.
* ANY_ADDR can be used as a placeholder to allow debugging
* 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) { }
/**
* Returns the address space register of this event.
* Returns the address space register of this listener.
*/
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; }
/**
@ -145,96 +146,108 @@ public:
*/
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; }
/**
* 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.
*/
void setTriggerInstructionPointer(address_t iptr) { m_TriggerInstrPtr = iptr; }
};
/**
* \class BPSingleEvent
* A Breakpoint event to observe specific instruction pointers.
* \class BPSingleListener
* A Breakpoint listener to observe specific instruction pointers.
*/
class BPSingleEvent : virtual public BPEvent {
class BPSingleListener : virtual public BPListener {
private:
address_t m_WatchInstrPtr;
public:
/**
* Creates a new breakpoint event.
/**
* Creates a new breakpoint listener.
* @param ip the instruction pointer of the breakpoint. If the control
* flow reaches this address and its counter value is zero, the
* event will be triggered. \a eip can be set to the ANY_ADDR
* wildcard to allow arbitrary addresses.
* listener will be triggered. \a eip can be set to the ANY_ADDR
* wildcard to allow arbitrary addresses. Defaults to 0.
* @param address_space the address space to be oberserved, given as the
* content of a CR3 register. The event will not be triggered unless
* \a ip is part of the given address space.
* content of a CR3 register. The listener will not be triggered unless
* \a ip is part of the given address space. Defaults to \c ANY_ADDR.
* Here, too, ANY_ADDR is a placeholder to allow debugging
* in a random address space.
*/
BPSingleEvent(address_t ip = 0, address_t address_space = ANY_ADDR)
: BPEvent(address_space), m_WatchInstrPtr(ip) { }
BPSingleListener(address_t ip = 0, address_t address_space = ANY_ADDR)
: 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
{ 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)
{ m_WatchInstrPtr = iptr; }
/**
* 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;
};
/**
* \class BPRangeEvent
* A event type to observe ranges of instruction pointers.
* \class BPRangeListener
* A listener type to observe ranges of instruction pointers.
*/
class BPRangeEvent : virtual public BPEvent {
class BPRangeListener : virtual public BPListener {
private:
address_t m_WatchStartAddr;
address_t m_WatchEndAddr;
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.
* ANY_ADDR denotes the lower respectively the upper end of the address
* space.
*/
BPRangeEvent(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR)
: BPEvent(address_space), m_WatchStartAddr(start), m_WatchEndAddr(end)
BPRangeListener(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR)
: 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
{ return std::make_pair(m_WatchStartAddr, m_WatchEndAddr); }
/**
* Sets the instruction pointer watch range. Both ends of the range
* 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,
address_t end);
void setWatchInstructionPointerRange(address_t start, address_t end);
/**
* 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;
};
/**
* \class MemAccessEvent
* \class MemAccessListener
* Observes memory read/write accesses.
* 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)
*/
class MemAccessEvent : virtual public BaseEvent {
class MemAccessListener : virtual public BaseListener {
public:
enum accessType_t {
MEM_UNKNOWN = 0x0,
@ -250,7 +263,7 @@ private:
* (MEM_READ || MEM_WRITE || MEM_READWRITE).
*/
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;
//! Width of the memory access (# bytes).
size_t m_TriggerWidth;
@ -259,11 +272,11 @@ private:
//! Memory access type at m_TriggerAddr.
accessType_t m_AccessType;
public:
MemAccessEvent(accessType_t watchtype = MEM_READWRITE)
MemAccessListener(accessType_t watchtype = MEM_READWRITE)
: m_WatchAddr(ANY_ADDR), m_WatchType(watchtype),
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
m_AccessType(MEM_UNKNOWN) { }
MemAccessEvent(address_t addr,
MemAccessListener(address_t addr,
accessType_t watchtype = MEM_READWRITE)
: m_WatchAddr(addr), m_WatchType(watchtype),
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
@ -282,21 +295,21 @@ public:
bool isMatching(address_t addr, accessType_t accesstype) const;
/**
* 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.
*/
void setTriggerAddress(address_t addr) { m_TriggerAddr = addr; }
/**
* 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.
*/
void setTriggerWidth(size_t width) { m_TriggerWidth = width; }
@ -304,64 +317,62 @@ public:
* Returns the address of the instruction causing this memory
* access.
*/
address_t getTriggerInstructionPointer() const
{ return (m_TriggerIP); }
address_t getTriggerInstructionPointer() const { return m_TriggerIP; }
/**
* Sets the address of the instruction causing this memory
* access. Should not be used by experiment code.
*/
void setTriggerInstructionPointer(address_t addr)
{ m_TriggerIP = addr; }
void setTriggerInstructionPointer(address_t addr) { m_TriggerIP = addr; }
/**
* 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.
*/
void setTriggerAccessType(accessType_t type) { m_AccessType = type; }
/**
* 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.
*/
class MemReadEvent : virtual public MemAccessEvent {
class MemReadListener : virtual public MemAccessListener {
public:
MemReadEvent()
: MemAccessEvent(MEM_READ) { }
MemReadEvent(address_t addr)
: MemAccessEvent(addr, MEM_READ) { }
MemReadListener()
: MemAccessListener(MEM_READ) { }
MemReadListener(address_t addr)
: MemAccessListener(addr, MEM_READ) { }
};
/**
* \class MemWriteEvent
* \class MemWriteListener
* Observes memory write accesses.
*/
class MemWriteEvent : virtual public MemAccessEvent {
class MemWriteListener : virtual public MemAccessListener {
public:
MemWriteEvent()
: MemAccessEvent(MEM_READ) { }
MemWriteEvent(address_t addr)
: MemAccessEvent(addr, MEM_WRITE) { }
MemWriteListener()
: MemAccessListener(MEM_READ) { }
MemWriteListener(address_t addr)
: MemAccessListener(addr, MEM_WRITE) { }
};
/**
* \class TroubleEvent
* \class TroubleListener
* Observes interrupt/trap activties.
*/
class TroubleEvent : virtual public BaseEvent {
class TroubleListener : virtual public BaseListener {
private:
/**
* Specific guest system interrupt/trap number that actually
* trigger the event.
* trigger the listener.
*/
int m_TriggerNumber;
/**
@ -370,8 +381,8 @@ private:
*/
std::vector<unsigned> m_WatchNumbers;
public:
TroubleEvent() : m_TriggerNumber (-1) { }
TroubleEvent(unsigned troubleNumber)
TroubleListener() : m_TriggerNumber (-1) { }
TroubleListener(unsigned troubleNumber)
: m_TriggerNumber(-1)
{ addWatchNumber(troubleNumber); }
/**
@ -393,39 +404,39 @@ public:
* Returns the list of 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.
*/
bool isMatching(unsigned troubleNum) const;
/**
* 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)
{ m_TriggerNumber = troubleNum; }
/**
* 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.
*/
class InterruptEvent : virtual public TroubleEvent {
class InterruptListener : virtual public TroubleListener {
private:
bool m_IsNMI; //!< non maskable interrupt flag
public:
InterruptEvent() : m_IsNMI(false) { }
InterruptEvent(unsigned interrupt) : m_IsNMI(false)
InterruptListener() : m_IsNMI(false) { }
InterruptListener(unsigned interrupt) : m_IsNMI(false)
{ addWatchNumber(interrupt); }
/**
* 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).
*/
@ -433,29 +444,29 @@ public:
};
/**
* \class TrapEvent
* \class TrapListener
* Observes traps of the guest system.
*/
class TrapEvent : virtual public TroubleEvent {
class TrapListener : virtual public TroubleListener {
public:
TrapEvent() { }
TrapEvent(unsigned trap) { addWatchNumber(trap); }
TrapListener() { }
TrapListener(unsigned trap) { addWatchNumber(trap); }
};
/**
* \class GuestEvent
* \class GuestListener
* Used to receive data from the guest system.
*/
class GuestEvent : virtual public BaseEvent {
class GuestListener : virtual public BaseListener {
private:
char m_Data;
unsigned m_Port;
public:
GuestEvent() : m_Data(0), m_Port(0) { }
GuestListener() : m_Data(0), m_Port(0) { }
/**
* 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.
*/
@ -463,7 +474,7 @@ public:
/**
* 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.
*/
@ -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)
*/
class IOPortEvent : virtual public BaseEvent {
class IOPortListener : virtual public BaseListener {
private:
unsigned char m_Data;
unsigned m_Port;
bool m_Out;
public:
/**
* Initialises an IOPortEvent
* Initialises an IOPortListener
*
* @param port the port the event ist listening on
* @param out Defines the direction of the event.
* @param port the port the listener ist listening on
* @param out Defines the direction of the listener.
* \arg \c true Output 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
*/
unsigned char getData() const { return (m_Data); }
unsigned char getData() const { return m_Data; }
/**
* Sets the data which had been transmitted.
*/
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; }
/**
* 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
*/
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 false Input on the given port is captured.
*/
@ -524,36 +535,36 @@ public:
};
/**
* \class JumpEvent
* JumpEvents are used to observe conditional jumps (if...else if...else).
* \class JumpListener
* JumpListeners are used to observe conditional jumps (if...else if...else).
*/
class JumpEvent : virtual public BaseEvent {
class JumpListener : virtual public BaseListener {
private:
unsigned m_Opcode;
bool m_FlagTriggered;
public:
/**
* Constructs a new event object.
* Constructs a new listener object.
* @param parent the parent object
* @param opcode the opcode of the jump-instruction to be observed
* or ANY_INSTR to match all jump-instructions
*/
JumpEvent(unsigned opcode = ANY_INSTR)
JumpListener(unsigned opcode = ANY_INSTR)
: m_Opcode(opcode), m_FlagTriggered(false) { }
/**
* 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.
*/
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.
*/
bool isFlagTriggered() { return (m_FlagTriggered); }
bool isFlagTriggered() { return m_FlagTriggered; }
/**
* Sets the requestet jump-instruction opcode.
*/
@ -561,36 +572,35 @@ public:
/**
* Sets the trigger flag.
*/
void setFlagTriggered(bool flagTriggered)
{ m_FlagTriggered = flagTriggered; }
void setFlagTriggered(bool flagTriggered) { m_FlagTriggered = flagTriggered; }
};
/**
* \class GenericTimerEvent
* This event type is used to create timeouts within in an experiment.
* \class GenericTimerListener
* This listener type is used to create timeouts within in an experiment.
*
* Depending on your simulator backend, a concrete class needs to be derived
* from \c GenericTimerEvent. \c onEventAddition should be used to register and
* \c onEventDeletion to unregister the timer. \c onEventTrigger can be used to
* from \c GenericTimerListener. \c onListenerAddition should be used to register and
* \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-
* end doesn't support such timer types natively.
*/
class GenericTimerEvent : public BaseEvent {
class GenericTimerListener : public BaseListener {
protected:
unsigned m_Timeout; //!< timeout interval in milliseconds
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
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
* added to the simulator backend.
* @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) { }
~GenericTimerEvent() { }
~GenericTimerListener() { }
/**
* Retrieves the internal timer id. Maybe useful for debug output.
* @return the timer id
@ -610,4 +620,4 @@ public:
} // 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.
* @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.
* @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
* derived class this method has to be defined appropriately.
@ -81,7 +81,7 @@ public:
* Retrieves the register name.
* @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.
* 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
* @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
* creation the register isn't initially assigned.
* @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.
* @return the unique id
*/
unsigned int getId() const { return (m_Id); }
unsigned int getId() const { return m_Id; }
};
/**
@ -132,7 +132,7 @@ public:
* ^
* \endcode
*/
iterator begin() { return (m_Regs.begin()); }
iterator begin() { return m_Regs.begin(); }
/**
* Returns an iterator to the end of the interal data structure.
* \code
@ -140,7 +140,7 @@ public:
* ^
* \endcode
*/
iterator end() { return (m_Regs.end()); }
iterator end() { return m_Regs.end(); }
/**
* Constructs a new register set with type \a containerType.
* @param containerType the type of registers which should be stored
@ -152,12 +152,12 @@ public:
* Returns the type of this set.
* @return the type
*/
RegisterType getType() const { return (m_Type); }
RegisterType getType() const { return m_Type; }
/**
* Gets the number of registers of this set.
* @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.
* @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
* assertion is thrown)
*/
virtual Register* first() { return (getRegister(0)); }
virtual Register* first() { return getRegister(0); }
};
/**
@ -197,7 +197,7 @@ public:
* ^
* \endcode
*/
iterator begin() { return (m_Registers.begin()); }
iterator begin() { return m_Registers.begin(); }
/**
* Returns an iterator to the end of the interal data structure.
* \code
@ -205,7 +205,7 @@ public:
* ^
* \endcode
*/
iterator end() { return (m_Registers.end()); }
iterator end() { return m_Registers.end(); }
RegisterManager() { }
~RegisterManager() { clear(); }
@ -218,7 +218,7 @@ public:
* Retrieves the number of managed homogeneous register 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.
* @param i the index of the set to be returned

View File

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

View File

@ -7,9 +7,9 @@
#include <vector>
#include "efw/CoroutineManager.hpp"
#include "EventManager.hpp"
#include "ListenerManager.hpp"
#include "SALConfig.hpp"
#include "Event.hpp"
#include "Listener.hpp"
namespace fail {
@ -23,23 +23,22 @@ class MemoryManager;
*
* \brief The abstract interface for controlling simulators and
* 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
* simulator/debugger system. Experiments can enlist arbritrary events
* (Breakpoint, Memory access, Traps, etc.). The SimulatorController then
* simulator/debugger system. Experiments can enlist arbritrary listeners
* (Breakpoint, Memory access, Traps, etc.). The \c SimulatorController then
* activates the specific experiment There are further methods to read/write
* registers and memory, and control the SUT (save/restore/reset).
*/
class SimulatorController {
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
RegisterManager *m_Regs; //!< access to cpu register
MemoryManager *m_Mem; //!< access to memory pool
//! list of suppressed interrupts
std::vector<unsigned> m_SuppressedInterrupts;
friend class EventManager; //!< "outsources" the event management
std::vector<unsigned> m_SuppressedInterrupts; //!< list of suppressed interrupts
friend class ListenerManager; //!< "outsources" the listener management
public:
SimulatorController()
: m_Regs(NULL), m_Mem(NULL) { }
@ -60,17 +59,17 @@ public:
*/
void initExperiments();
/* ********************************************************************
* Standard Event Handler API
* Standard Listener Handler API
* ********************************************************************/
/**
* Breakpoint event handler. This routine needs to be called in the
* simulator specific backend each time a breakpoint event occurs.
* @param instrPtr the instruction pointer of the breakpoint event
* Breakpoint listener handler. This routine needs to be called in the
* simulator specific backend each time a breakpoint listener occurs.
* @param instrPtr the instruction pointer of the breakpoint listener
* @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 len the length of the accessed memory
* @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?
*/
void onMemoryAccessEvent(address_t addr, size_t len,
bool is_write, address_t instrPtr);
void onMemoryAccessListener(address_t addr, size_t len, bool is_write, address_t instrPtr);
/**
* Interrupt event handler.
* Interrupt listener handler.
* @param interruptNum the interrupt-type id
* @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
*/
void onTrapEvent(unsigned trapNum);
void onTrapListener(unsigned trapNum);
/**
* Guest system communication handler.
* @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.
* @param flagTriggered \c true if the jump was triggered due to a
* specific FLAG (zero/carry/sign/overflow/parity flag)
* @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:
* ********************************************************************/
@ -115,7 +113,7 @@ public:
virtual void save(const std::string& path) = 0;
/**
* Restore simulator state. Implicitly discards all previously
* registered events.
* registered listeners.
* @param path Location to previously saved state information
*/
virtual void restore(const std::string& path) = 0;
@ -144,20 +142,18 @@ public:
/**
* Remove a Interrupt from the list of suppressed.
* @param interruptNum the interrupt-type id
* @return \c true if sucessfully removed, \c false otherwise (not
* found)
* @return \c true if sucessfully removed, \c false otherwise (not found)
*/
bool removeSuppressedInterrupt(unsigned interruptNum);
/**
* Returns the (constant) initialized register manager.
* @return a reference to the register manager
*/
RegisterManager& getRegisterManager() { return (*m_Regs); }
const RegisterManager& getRegisterManager() const { return (*m_Regs); }
RegisterManager& getRegisterManager() { return *m_Regs; }
const RegisterManager& getRegisterManager() const { return *m_Regs; }
/**
* Sets the register manager.
* @param pReg the new register manager (or a concrete derived class of
* RegisterManager)
* @param pReg the new register manager (or a concrete derived class of \c RegisterManager)
*/
void setRegisterManager(RegisterManager* pReg) { m_Regs = pReg; }
/**
@ -165,74 +161,72 @@ public:
* @return a reference to the memory manager
*/
MemoryManager& getMemoryManager() { return (*m_Mem); }
const MemoryManager& getMemoryManager() const { return (*m_Mem); }
const MemoryManager& getMemoryManager() const { return *m_Mem; }
/**
* Sets the memory manager.
* @param pMem a new concrete memory manager
*/
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
* run it in.
* Adds the specified experiment or plugin and creates a coroutine to run it in.
* @param flow the experiment flow object to be added
*/
void addFlow(ExperimentFlow* flow);
/**
* 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
*/
void removeFlow(ExperimentFlow* flow);
/**
* Add event ev to the event management. This causes the event to be
* active.
* @param ev the event pointer to be added for the current flow
* @return \c true if the event has been added successfully, \c false otherwise
* Add listener \c li to the listener management. This causes the listener to be active.
* @param li the listener pointer to be added for the current flow
* @return \c true if the listener has been added successfully, \c false otherwise
*/
bool addEvent(BaseEvent* ev);
bool addListener(BaseListener* li);
/**
* Removes the event with the specified id.
* @param ev the pointer of the event-object to be removed; if \a ev is
* equal to \c NULL all events (for all experiments) will be
* removed
* Removes the listener with the specified pointer \c li.
* @param li the pointer of the listener-object to be removed; if \c li is
* equal to \c NULL, all listeners (for all experiments) will be removed
*/
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.
* @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
* one of those events occour, waitAny() will return the id of that event.
* @return the previously occurred event, or \c NULL if there are no
* events to wait for
* Switches the control flow to the simulator and waits on any listeners
* which have been added to the listener management. If one of those listeners
* occurs, resume() will return the pointer of that listener.
* @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
* \c addEvent() and \c waitAny()).
* @param ev the event pointer to be added
* @return the pointer of the occurred event (it is not guaranteed that
* this pointer will be equal to ev)
*
* FIXME: Rename to make clear this returns when *any* event occurs
* Add listener \a ev to the global buffer and continues the simulation
* (combines \c addListener() and \c resume()).
* @param li the listener pointer to be added
* @return the pointer of the occurred listener (it is not guaranteed that
* this pointer will be equal to li)
*/
BaseEvent* addEventAndWait(BaseEvent* ev);
BaseListener* addListenerAndResume(BaseListener* li);
/**
* Checks whether any experiment flow has events in the event-list.
* @return \c true if there are still events, or \c false otherwise
* Checks whether any experiment flow has listeners in the listener-list.
* @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.
* @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

View File

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

View File

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

View File

@ -1,34 +1,34 @@
#include "BochsEvents.hpp"
#include "BochsListener.hpp"
#include "../SALInst.hpp"
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));
if(getId() == -1)
return false; // unable to register the timer (error in Bochs' function call)
return true;
}
void TimerEvent::onEventDeletion()
void TimerListener::onDeletion()
{
// Unregister the time event:
// Unregister the time listener:
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>(
bx_pc_system.register_timer(pev, BochsController::onTimerTrigger, pev->getTimeout(),
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()));
return bx_pc_system.unregisterTimer(static_cast<unsigned>(pev->getId()));
}

View File

@ -1,74 +1,76 @@
#ifndef __BOCHS_EVENTS_HPP__
#define __BOCHS_EVENTS_HPP__
#include "../Event.hpp"
#include "../Listener.hpp"
#include "BochsController.hpp"
namespace fail {
/**
* \class TimerEvent
* Concrete TimerEvent implementation of GenericTimerEvent for the Bochs
* \class TimerListener
* Concrete TimerListener implementation of GenericTimerListener for the Bochs
* simulator backend.
*/
class TimerEvent : public GenericTimerEvent {
class TimerListener : public GenericTimerListener {
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
* (in Bochs) is limited to \c BX_MAX_TIMERS (defaults to 64 in v2.4.6).
* @param pev a pointer to the (experiment flow-) allocated TimerEvent object,
* providing all required information to start the time, e.g. the
* timeout value.
* @return \c The unique id of the timer recently created. This id is carried
* along with the TimerEvent, @see getId(). On errors, -1 is returned
* (e.g. because a timer with the same id is already existing)
* @param pli a pointer to the (experiment flow-) allocated TimerListener object,
* providing all required information to start the timer (e.g. the
* timeout value).
* @return The unique id of the timer recently created. This id is carried
* along with the TimerListener. On errors, -1 is returned (e.g. because
* 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.
* @param pev a pointer to the TimerEvent-object to be removed
* @return \c true if the timer with \a pev->getId() has been removed
* @param pli a pointer to the TimerListener-object to be removed
* @return \c true if the timer with \a pli->getId() has been removed
* successfully, \c false otherwise
*/
static bool m_unregisterTimer(TimerEvent* pev);
static bool m_unregisterTimer(TimerListener* pli);
public:
/**
* Creates a new timer event. This can be used to implement a timeout-
* mechanism in the experiment-flow. The timer starts automatically when
* added to FailBochs.
* @param timeout the time intervall in milliseconds (ms)
* @see SimulatorController::addEvent
* @see SimulatorController::addListener
*/
TimerEvent(unsigned timeout)
: GenericTimerEvent(timeout) { }
~TimerEvent() { onEventDeletion(); }
TimerListener(unsigned timeout)
: GenericTimerListener(timeout) { }
~TimerListener() { onDeletion(); }
/**
* 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
* (buffer-list, to be precise) and then this event handler is called.
* @return You should return \c true to continue and \c false to prevent
* the addition of the event \a pev, yielding an error in the
* experiment flow (i.e. -1 is returned).
*/
bool onEventAddition();
bool onAddition();
/**
* 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
* 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
* triggered by the simulator-backend. More specifically, this event handler
* will be called *before* the event is actually triggered, i.e. before the
* corresponding coroutine is toggled.
*/
void onEventTrigger() { onEventDeletion(); }
void onTrigger() { onDeletion(); }
// TODO/FIXME: bei neuer impl. anpassen
};
} // end-of-namespace: fail

View File

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

View File

@ -13,7 +13,7 @@
aspect Breakpoints {
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,
// BX_CPU(0) otherwise

View File

@ -19,12 +19,12 @@
aspect GuestSysCom {
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 rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
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 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(...)";
@ -30,7 +30,7 @@ aspect IOPortCom {
{
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
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 type = *(tjp->arg<1>());
if (type == BX_EXTERNAL_INTERRUPT)
fail::simulator.onInterruptEvent(vector, false);
fail::simulator.onInterruptListener(vector, false);
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()
{
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();
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
// (using pThis->set_XX(new_val))
// Step-2: Call tjp->proceed();
// Step-3: Eventually, unwind the changes of Step-1
// Step-3: Listenerually, unwind the changes of Step-1
//
// Example:
@ -109,7 +109,7 @@ aspect Jump {
advice execution (regJumpInstructions()) : around ()
{
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();

View File

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

View File

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

View File

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