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:
@ -1,16 +1,15 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "BufferCache.hpp"
|
#include "BufferCache.hpp"
|
||||||
#include "Event.hpp"
|
#include "Listener.hpp"
|
||||||
#include "EventManager.hpp"
|
#include "ListenerManager.hpp"
|
||||||
|
|
||||||
namespace fail {
|
namespace fail {
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
typename BufferCache<T>::iterator BufferCache<T>::makeActive(EventManager &ev_list, BufferCache<T>::iterator idx)
|
typename BufferCache<T>::iterator BufferCache<T>::makeActive(ListenerManager &ev_list, BufferCache<T>::iterator idx)
|
||||||
{
|
{
|
||||||
assert(idx != end() &&
|
assert(idx != end() && "FATAL ERROR: Index larger than cache!");
|
||||||
"FATAL ERROR: Index larger than cache!");
|
|
||||||
T ev = *idx;
|
T ev = *idx;
|
||||||
assert(ev && "FATAL ERROR: Object pointer cannot be NULL!");
|
assert(ev && "FATAL ERROR: Object pointer cannot be NULL!");
|
||||||
ev->decreaseCounter();
|
ev->decreaseCounter();
|
||||||
@ -20,14 +19,14 @@ typename BufferCache<T>::iterator BufferCache<T>::makeActive(EventManager &ev_li
|
|||||||
ev->resetCounter();
|
ev->resetCounter();
|
||||||
// Note: This is the one and only situation in which remove() should NOT
|
// Note: This is the one and only situation in which remove() should NOT
|
||||||
// store the removed item in the delete-list.
|
// store the removed item in the delete-list.
|
||||||
EventManager::iterator it = std::find(ev_list.begin(), ev_list.end(), static_cast<BaseEvent*>(ev));
|
ListenerManager::iterator it = std::find(ev_list.begin(), ev_list.end(), static_cast<BaseListener*>(ev));
|
||||||
ev_list.m_remove(it, true); // remove event from buffer-list
|
ev_list.m_remove(it, true); // remove listener from buffer-list
|
||||||
ev_list.m_FireList.push_back(ev);
|
ev_list.m_FireList.push_back(ev);
|
||||||
return erase(idx);
|
return erase(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Declare here whatever instances of the template you are going to use:
|
// Declare here whatever instances of the template you are going to use:
|
||||||
template class BufferCache<BPEvent*>;
|
template class BufferCache<BPListener*>;
|
||||||
template class BufferCache<IOPortEvent*>;
|
template class BufferCache<IOPortListener*>;
|
||||||
|
|
||||||
} // end-of-namespace: fail
|
} // end-of-namespace: fail
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace fail {
|
namespace fail {
|
||||||
|
|
||||||
class EventManager;
|
class ListenerManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class BufferCache
|
* \class BufferCache
|
||||||
@ -14,19 +14,19 @@ class EventManager;
|
|||||||
* \brief A simple dynamic array
|
* \brief A simple dynamic array
|
||||||
*
|
*
|
||||||
* This class is intended to serve as a kind of cache for the
|
* This class is intended to serve as a kind of cache for the
|
||||||
* untyped and therefore quite slow event handling mechanism of Fail*.
|
* untyped and therefore quite slow listener handling mechanism of Fail*.
|
||||||
*/
|
*/
|
||||||
template<class T>
|
template<class T>
|
||||||
class BufferCache {
|
class BufferCache {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* The list type inherent to this class. Like bufferlist_t in EventManager.hpp,
|
* The list type inherent to this class. Like bufferlist_t in ListenerManager.hpp,
|
||||||
* but dynamically typed.
|
* but dynamically typed.
|
||||||
*/
|
*/
|
||||||
typedef std::list<T> cachelist_t;
|
typedef std::list<T> cachelist_t;
|
||||||
/**
|
/**
|
||||||
* The iterator of this class used to loop through the list of
|
* The iterator of this class used to loop through the list of
|
||||||
* added events. To retrieve an iterator to the first element, call
|
* added listeners. To retrieve an iterator to the first element, call
|
||||||
* begin(). end() returns the iterator, pointing after the last element.
|
* begin(). end() returns the iterator, pointing after the last element.
|
||||||
* (This behaviour equals the STL iterator in C++.)
|
* (This behaviour equals the STL iterator in C++.)
|
||||||
*/
|
*/
|
||||||
@ -79,14 +79,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline iterator end() { return m_Buffer.end(); }
|
inline iterator end() { return m_Buffer.end(); }
|
||||||
/**
|
/**
|
||||||
* Acts as a replacement for EventManager::makeActive, manipulating
|
* Acts as a replacement for ListenerManager::makeActive, manipulating
|
||||||
* the buffer cache exclusively. EventManager::fireActiveEvents needs
|
* the buffer cache exclusively. ListenerManager::triggerActiveListeners needs
|
||||||
* to be called to fire the active events (see there).
|
* to be called to fire the active listeners (see there).
|
||||||
* This method is declared as a friend method in EventManager.
|
* This method is declared as a friend method in ListenerManager.
|
||||||
* @param idx the index of the event to trigger
|
* @param idx the index of the listener to trigger
|
||||||
* @returns an updated index which can be used to update a loop counter
|
* @returns an updated index which can be used to update a loop counter
|
||||||
*/
|
*/
|
||||||
iterator makeActive(EventManager &ev_list, iterator idx);
|
iterator makeActive(ListenerManager &ev_list, iterator idx);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end-of-namespace: fail
|
} // end-of-namespace: fail
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
if(BUILD_BOCHS)
|
if(BUILD_BOCHS)
|
||||||
set(SRCS
|
set(SRCS
|
||||||
BufferCache.cc
|
BufferCache.cc
|
||||||
Event.cc
|
Listener.cc
|
||||||
EventManager.cc
|
ListenerManager.cc
|
||||||
Memory.cc
|
Memory.cc
|
||||||
Register.cc
|
Register.cc
|
||||||
SimulatorController.cc
|
SimulatorController.cc
|
||||||
bochs/BochsController.cc
|
bochs/BochsController.cc
|
||||||
bochs/BochsEvents.cc
|
bochs/BochsListener.cc
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
set(SRCS
|
set(SRCS
|
||||||
BufferCache.cc
|
BufferCache.cc
|
||||||
Event.cc
|
Listener.cc
|
||||||
EventManager.cc
|
ListenerManager.cc
|
||||||
Memory.cc
|
Memory.cc
|
||||||
Register.cc
|
Register.cc
|
||||||
SimulatorController.cc
|
SimulatorController.cc
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -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__
|
|
||||||
@ -1,9 +1,9 @@
|
|||||||
#include "Event.hpp"
|
#include "Listener.hpp"
|
||||||
#include "SALInst.hpp"
|
#include "SALInst.hpp"
|
||||||
|
|
||||||
namespace fail {
|
namespace fail {
|
||||||
|
|
||||||
bool TroubleEvent::isMatching(unsigned troubleNum) const
|
bool TroubleListener::isMatching(unsigned troubleNum) const
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
|
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
|
||||||
if (m_WatchNumbers[i] == troubleNum ||
|
if (m_WatchNumbers[i] == troubleNum ||
|
||||||
@ -13,7 +13,7 @@ bool TroubleEvent::isMatching(unsigned troubleNum) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TroubleEvent::removeWatchNumber(unsigned troubleNum)
|
bool TroubleListener::removeWatchNumber(unsigned troubleNum)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
|
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
|
||||||
if (m_WatchNumbers[i] == troubleNum) {
|
if (m_WatchNumbers[i] == troubleNum) {
|
||||||
@ -24,7 +24,7 @@ bool TroubleEvent::removeWatchNumber(unsigned troubleNum)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TroubleEvent::addWatchNumber(unsigned troubleNumber)
|
bool TroubleListener::addWatchNumber(unsigned troubleNumber)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
|
for (unsigned i = 0; i < m_WatchNumbers.size(); i++) {
|
||||||
if (m_WatchNumbers[i] == troubleNumber)
|
if (m_WatchNumbers[i] == troubleNumber)
|
||||||
@ -34,7 +34,7 @@ bool TroubleEvent::addWatchNumber(unsigned troubleNumber)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MemAccessEvent::isMatching(address_t addr, accessType_t accesstype) const
|
bool MemAccessListener::isMatching(address_t addr, accessType_t accesstype) const
|
||||||
{
|
{
|
||||||
if (!(m_WatchType & accesstype))
|
if (!(m_WatchType & accesstype))
|
||||||
return false;
|
return false;
|
||||||
@ -44,20 +44,20 @@ bool MemAccessEvent::isMatching(address_t addr, accessType_t accesstype) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BPEvent::aspaceIsMatching(address_t aspace) const
|
bool BPListener::aspaceIsMatching(address_t aspace) const
|
||||||
{
|
{
|
||||||
if (m_CR3 == ANY_ADDR || m_CR3 == aspace)
|
if (m_CR3 == ANY_ADDR || m_CR3 == aspace)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BPRangeEvent::setWatchInstructionPointerRange(address_t start, address_t end)
|
void BPRangeListener::setWatchInstructionPointerRange(address_t start, address_t end)
|
||||||
{
|
{
|
||||||
m_WatchStartAddr = start;
|
m_WatchStartAddr = start;
|
||||||
m_WatchEndAddr = end;
|
m_WatchEndAddr = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BPRangeEvent::isMatching(address_t addr, address_t aspace) const
|
bool BPRangeListener::isMatching(address_t addr, address_t aspace) const
|
||||||
{
|
{
|
||||||
if (!aspaceIsMatching(aspace))
|
if (!aspaceIsMatching(aspace))
|
||||||
return false;
|
return false;
|
||||||
@ -67,7 +67,7 @@ bool BPRangeEvent::isMatching(address_t addr, address_t aspace) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BPSingleEvent::isMatching(address_t addr, address_t aspace) const
|
bool BPSingleListener::isMatching(address_t addr, address_t aspace) const
|
||||||
{
|
{
|
||||||
if (aspaceIsMatching(aspace)) {
|
if (aspaceIsMatching(aspace)) {
|
||||||
if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr)
|
if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef __EVENT_HPP__
|
#ifndef __LISTENER_HPP__
|
||||||
#define __EVENT_HPP__
|
#define __LISTENER_HPP__
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -14,7 +14,7 @@ namespace fail {
|
|||||||
|
|
||||||
class ExperimentFlow;
|
class ExperimentFlow;
|
||||||
|
|
||||||
//! address wildcard (e.g. for BPEvent's)
|
//! address wildcard (e.g. for BPListener's)
|
||||||
const address_t ANY_ADDR = static_cast<address_t>(-1);
|
const address_t ANY_ADDR = static_cast<address_t>(-1);
|
||||||
//! instruction wildcard
|
//! instruction wildcard
|
||||||
const unsigned ANY_INSTR = static_cast<unsigned>(-1);
|
const unsigned ANY_INSTR = static_cast<unsigned>(-1);
|
||||||
@ -24,58 +24,59 @@ const unsigned ANY_TRAP = static_cast<unsigned>(-1);
|
|||||||
const unsigned ANY_INTERRUPT = static_cast<unsigned>(-1);
|
const unsigned ANY_INTERRUPT = static_cast<unsigned>(-1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class BaseEvent
|
* \class BaseListener
|
||||||
* This is the base class for all event types.
|
* This is the base class for all listener types.
|
||||||
*/
|
*/
|
||||||
class BaseEvent {
|
class BaseListener {
|
||||||
protected:
|
protected:
|
||||||
time_t m_tStamp; //!< time stamp of event
|
time_t m_tStamp; //!< time stamp of listener
|
||||||
unsigned int m_OccCounter; //!< event fires when 0 is reached
|
unsigned int m_OccCounter; //!< listener fires when 0 is reached
|
||||||
unsigned int m_OccCounterInit; //!< initial value for m_OccCounter
|
unsigned int m_OccCounterInit; //!< initial value for m_OccCounter
|
||||||
ExperimentFlow* m_Parent; //!< this event belongs to experiment m_Parent
|
ExperimentFlow* m_Parent; //!< this listener belongs to experiment m_Parent
|
||||||
public:
|
public:
|
||||||
BaseEvent() : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL)
|
BaseListener() : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL)
|
||||||
{ updateTime(); }
|
{ updateTime(); }
|
||||||
virtual ~BaseEvent() { }
|
virtual ~BaseListener() { }
|
||||||
/**
|
/**
|
||||||
* This method is called when an experiment flow adds a new event by
|
* This method is called when an experiment flow adds a new listener by
|
||||||
* calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev).
|
* calling \c simulator.addListener() or \c simulator.addListenerAndResume().
|
||||||
* More specifically, the event will be added to the event-list first
|
* More specifically, the listener will be added to the listener manager first
|
||||||
* (buffer-list, to be precise) and then this event handler is called.
|
* (buffer-list, to be precise) and then this listener handler is called.
|
||||||
* @return You should return \c true to continue and \c false to prevent
|
* @return You should return \c true to continue and \c false to prevent
|
||||||
* the addition of the event \a pev, yielding an error in the
|
* the addition of the listener \c this, yielding an error in the
|
||||||
* experiment flow (i.e. \c INVALID_EVENT is returned).
|
* experiment flow (i.e., \c false is returned).
|
||||||
*/
|
*/
|
||||||
virtual bool onEventAddition() { return true; }
|
virtual bool onAddition() { return true; }
|
||||||
/**
|
/**
|
||||||
* This method is called when an experiment flow removes an event from
|
* This method is called when an experiment flow removes an listener from
|
||||||
* the event-management by calling \c removeEvent(prev), \c clearEvents()
|
* the listener-management by calling \c removeListener(), \c clearListeners()
|
||||||
* or by deleting a complete flow (\c removeFlow). More specifically, this
|
* or by deleting a complete flow (\c removeFlow()). More specifically, this
|
||||||
* event handler will be called *before* the event is actually deleted.
|
* listener handler will be called *before* the listener is actually deleted.
|
||||||
*/
|
*/
|
||||||
virtual void onEventDeletion() { }
|
virtual void onDeletion() { }
|
||||||
/**
|
/**
|
||||||
* This method is called when an previously added event is about to be
|
* This method is called when an previously added listener is about to be
|
||||||
* triggered by the simulator-backend. More specifically, this event handler
|
* triggered by the simulator-backend. More specifically, this listener handler
|
||||||
* will be called *before* the event is actually triggered, i.e. before the
|
* will be called *before* the listener is actually triggered, i.e. before the
|
||||||
* corresponding coroutine is toggled.
|
* corresponding coroutine is toggled.
|
||||||
*/
|
*/
|
||||||
virtual void onEventTrigger() { }
|
virtual void onTrigger() { }
|
||||||
|
// TODO: Hier noch ne neue Methode oder reicht es, die Semantik von onTrigger umzudef.?
|
||||||
/**
|
/**
|
||||||
* Retrieves the time stamp of this event. The time stamp is set when
|
* Retrieves the time stamp of this listener. The time stamp is set when
|
||||||
* the event gets created, id est the constructor is called. The meaning
|
* the listener gets created, i.e. the constructor is called. The meaning
|
||||||
* of this value depends on the actual event type.
|
* of this value depends on the actual listener type.
|
||||||
* @return the time stamp
|
* @return the time stamp
|
||||||
*/
|
*/
|
||||||
std::time_t getTime() const { return (m_tStamp); }
|
std::time_t getTime() const { return (m_tStamp); }
|
||||||
/**
|
/**
|
||||||
* Decreases the event counter by one. When this counter reaches zero, the
|
* Decreases the listener counter by one. When this counter reaches zero, the
|
||||||
* event will be triggered.
|
* listener will be triggered.
|
||||||
*/
|
*/
|
||||||
void decreaseCounter() { --m_OccCounter; }
|
void decreaseCounter() { --m_OccCounter; }
|
||||||
/**
|
/**
|
||||||
* Sets the event counter to the specified value (default is one).
|
* Sets the listener counter to the specified value.
|
||||||
* @param val the new counter value
|
* @param val the new counter value (default is 1)
|
||||||
*/
|
*/
|
||||||
void setCounter(unsigned int val = 1) { m_OccCounter = m_OccCounterInit = val; }
|
void setCounter(unsigned int val = 1) { m_OccCounter = m_OccCounterInit = val; }
|
||||||
/**
|
/**
|
||||||
@ -84,56 +85,56 @@ public:
|
|||||||
*/
|
*/
|
||||||
unsigned int getCounter() const { return (m_OccCounter); }
|
unsigned int getCounter() const { return (m_OccCounter); }
|
||||||
/**
|
/**
|
||||||
* Resets the event counter to its default value, or the last
|
* Resets the listener counter to its default value, or the last
|
||||||
* value that was set through setCounter().
|
* value that was set through \c setCounter().
|
||||||
*/
|
*/
|
||||||
void resetCounter() { m_OccCounter = m_OccCounterInit; }
|
void resetCounter() { m_OccCounter = m_OccCounterInit; }
|
||||||
/**
|
/**
|
||||||
* Sets the time stamp for this event to the current system time.
|
* Sets the time stamp for this listener to the current system time.
|
||||||
*/
|
*/
|
||||||
void updateTime() { time(&m_tStamp); }
|
void updateTime() { time(&m_tStamp); }
|
||||||
/**
|
/**
|
||||||
* Returns the parent experiment of this event (context).
|
* Returns the parent experiment of this listener (context).
|
||||||
* If the event was created temporarily or wasn't linked to a context,
|
* If the listener was created temporarily or wasn't linked to a context,
|
||||||
* the return value is \c NULL (unknown identity).
|
* the return value is \c NULL (unknown identity).
|
||||||
*/
|
*/
|
||||||
ExperimentFlow* getParent() const { return (m_Parent); }
|
ExperimentFlow* getParent() const { return (m_Parent); }
|
||||||
/**
|
/**
|
||||||
* Sets the parent (context) of this event. The default context
|
* Sets the parent (context) of this listener. The default context
|
||||||
* is \c NULL (unknown identity).
|
* is \c NULL (unknown identity).
|
||||||
*/
|
*/
|
||||||
void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; }
|
void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; }
|
||||||
};
|
};
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Specialized events:
|
// Specialized listeners:
|
||||||
//
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class BEvent
|
* \class BListener
|
||||||
* A Breakpoint event to observe instruction changes within a given address space.
|
* A Breakpoint listener to observe instruction changes within a given address space.
|
||||||
*/
|
*/
|
||||||
class BPEvent : virtual public BaseEvent {
|
class BPListener : virtual public BaseListener {
|
||||||
private:
|
private:
|
||||||
address_t m_CR3;
|
address_t m_CR3;
|
||||||
address_t m_TriggerInstrPtr;
|
address_t m_TriggerInstrPtr;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Creates a new breakpoint event. The range information is specific to
|
* Creates a new breakpoint listener. The range information is specific to
|
||||||
* the subclasses.
|
* the subclasses.
|
||||||
* @param address_space the address space to be oberserved, given as the
|
* @param address_space the address space to be oberserved, given as the
|
||||||
* content of a CR3 register. The event will not be triggered unless
|
* content of a CR3 register. The listener will not be triggered unless
|
||||||
* \a ip is part of the given address space.
|
* \a ip is part of the given address space.
|
||||||
* ANY_ADDR can be used as a placeholder to allow debugging
|
* ANY_ADDR can be used as a placeholder to allow debugging
|
||||||
* in a random address space.
|
* in a random address space.
|
||||||
*/
|
*/
|
||||||
BPEvent(address_t address_space = ANY_ADDR)
|
BPListener(address_t address_space = ANY_ADDR)
|
||||||
: m_CR3(address_space), m_TriggerInstrPtr(ANY_ADDR) { }
|
: m_CR3(address_space), m_TriggerInstrPtr(ANY_ADDR) { }
|
||||||
/**
|
/**
|
||||||
* Returns the address space register of this event.
|
* Returns the address space register of this listener.
|
||||||
*/
|
*/
|
||||||
address_t getCR3() const { return m_CR3; }
|
address_t getCR3() const { return m_CR3; }
|
||||||
/**
|
/**
|
||||||
* Sets the address space register for this event.
|
* Sets the address space register for this listener.
|
||||||
*/
|
*/
|
||||||
void setCR3(address_t iptr) { m_CR3 = iptr; }
|
void setCR3(address_t iptr) { m_CR3 = iptr; }
|
||||||
/**
|
/**
|
||||||
@ -145,96 +146,108 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool isMatching(address_t addr = 0, address_t address_space = ANY_ADDR) const = 0;
|
virtual bool isMatching(address_t addr = 0, address_t address_space = ANY_ADDR) const = 0;
|
||||||
/**
|
/**
|
||||||
* Returns the instruction pointer that triggered this event.
|
* Returns the instruction pointer that triggered this listener.
|
||||||
*/
|
*/
|
||||||
address_t getTriggerInstructionPointer() const { return m_TriggerInstrPtr; }
|
address_t getTriggerInstructionPointer() const { return m_TriggerInstrPtr; }
|
||||||
/**
|
/**
|
||||||
* Sets the instruction pointer that triggered this event. Should not
|
* Sets the instruction pointer that triggered this listener. Should not
|
||||||
* be used by experiment code.
|
* be used by experiment code.
|
||||||
*/
|
*/
|
||||||
void setTriggerInstructionPointer(address_t iptr) { m_TriggerInstrPtr = iptr; }
|
void setTriggerInstructionPointer(address_t iptr) { m_TriggerInstrPtr = iptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class BPSingleEvent
|
* \class BPSingleListener
|
||||||
* A Breakpoint event to observe specific instruction pointers.
|
* A Breakpoint listener to observe specific instruction pointers.
|
||||||
*/
|
*/
|
||||||
class BPSingleEvent : virtual public BPEvent {
|
class BPSingleListener : virtual public BPListener {
|
||||||
private:
|
private:
|
||||||
address_t m_WatchInstrPtr;
|
address_t m_WatchInstrPtr;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Creates a new breakpoint event.
|
* Creates a new breakpoint listener.
|
||||||
* @param ip the instruction pointer of the breakpoint. If the control
|
* @param ip the instruction pointer of the breakpoint. If the control
|
||||||
* flow reaches this address and its counter value is zero, the
|
* flow reaches this address and its counter value is zero, the
|
||||||
* event will be triggered. \a eip can be set to the ANY_ADDR
|
* listener will be triggered. \a eip can be set to the ANY_ADDR
|
||||||
* wildcard to allow arbitrary addresses.
|
* wildcard to allow arbitrary addresses. Defaults to 0.
|
||||||
* @param address_space the address space to be oberserved, given as the
|
* @param address_space the address space to be oberserved, given as the
|
||||||
* content of a CR3 register. The event will not be triggered unless
|
* content of a CR3 register. The listener will not be triggered unless
|
||||||
* \a ip is part of the given address space.
|
* \a ip is part of the given address space. Defaults to \c ANY_ADDR.
|
||||||
* Here, too, ANY_ADDR is a placeholder to allow debugging
|
* Here, too, ANY_ADDR is a placeholder to allow debugging
|
||||||
* in a random address space.
|
* in a random address space.
|
||||||
*/
|
*/
|
||||||
BPSingleEvent(address_t ip = 0, address_t address_space = ANY_ADDR)
|
BPSingleListener(address_t ip = 0, address_t address_space = ANY_ADDR)
|
||||||
: BPEvent(address_space), m_WatchInstrPtr(ip) { }
|
: BPListener(address_space), m_WatchInstrPtr(ip) { }
|
||||||
/**
|
/**
|
||||||
* Returns the instruction pointer this event waits for.
|
* Returns the instruction pointer this listener waits for.
|
||||||
|
* @return the instruction pointer specified in the constructor or by
|
||||||
|
* calling \c setWatchInstructionPointer()
|
||||||
*/
|
*/
|
||||||
address_t getWatchInstructionPointer() const
|
address_t getWatchInstructionPointer() const
|
||||||
{ return m_WatchInstrPtr; }
|
{ return m_WatchInstrPtr; }
|
||||||
/**
|
/**
|
||||||
* Sets the instruction pointer this event waits for.
|
* Sets the instruction pointer this listener waits for.
|
||||||
|
* @param iptr the new instruction ptr to wait for
|
||||||
*/
|
*/
|
||||||
void setWatchInstructionPointer(address_t iptr)
|
void setWatchInstructionPointer(address_t iptr)
|
||||||
{ m_WatchInstrPtr = iptr; }
|
{ m_WatchInstrPtr = iptr; }
|
||||||
/**
|
/**
|
||||||
* Checks whether a given address is matching.
|
* Checks whether a given address is matching.
|
||||||
|
* @param addr address to check
|
||||||
|
* @param address_space address space information
|
||||||
|
* @return \c true if address is within the range, \c false otherwise
|
||||||
*/
|
*/
|
||||||
bool isMatching(address_t addr, address_t address_space) const;
|
bool isMatching(address_t addr, address_t address_space) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class BPRangeEvent
|
* \class BPRangeListener
|
||||||
* A event type to observe ranges of instruction pointers.
|
* A listener type to observe ranges of instruction pointers.
|
||||||
*/
|
*/
|
||||||
class BPRangeEvent : virtual public BPEvent {
|
class BPRangeListener : virtual public BPListener {
|
||||||
private:
|
private:
|
||||||
address_t m_WatchStartAddr;
|
address_t m_WatchStartAddr;
|
||||||
address_t m_WatchEndAddr;
|
address_t m_WatchEndAddr;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Creates a new breakpoint-range event. The range's ends are both
|
* Creates a new breakpoint-range listener. The range's ends are both
|
||||||
* inclusive, i.e. an address matches if start <= addr <= end.
|
* inclusive, i.e. an address matches if start <= addr <= end.
|
||||||
* ANY_ADDR denotes the lower respectively the upper end of the address
|
* ANY_ADDR denotes the lower respectively the upper end of the address
|
||||||
* space.
|
* space.
|
||||||
*/
|
*/
|
||||||
BPRangeEvent(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR)
|
BPRangeListener(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR)
|
||||||
: BPEvent(address_space), m_WatchStartAddr(start), m_WatchEndAddr(end)
|
: BPListener(address_space), m_WatchStartAddr(start), m_WatchEndAddr(end)
|
||||||
{ }
|
{ }
|
||||||
/**
|
/**
|
||||||
* Returns the instruction pointer watch range of this event.
|
* Returns the instruction pointer watch range of this listener.
|
||||||
|
* @return the listerner's range
|
||||||
*/
|
*/
|
||||||
std::pair<address_t, address_t> getWatchInstructionPointerRange() const
|
std::pair<address_t, address_t> getWatchInstructionPointerRange() const
|
||||||
{ return std::make_pair(m_WatchStartAddr, m_WatchEndAddr); }
|
{ return std::make_pair(m_WatchStartAddr, m_WatchEndAddr); }
|
||||||
/**
|
/**
|
||||||
* Sets the instruction pointer watch range. Both ends of the range
|
* Sets the instruction pointer watch range. Both ends of the range
|
||||||
* may be ANY_ADDR (cf. constructor).
|
* may be ANY_ADDR (cf. constructor).
|
||||||
|
* @param start the (inclusive) lower bound, or \c ANY_ADDR
|
||||||
|
* @param end the (inclusive) upper bound, or \c ANY_ADDR
|
||||||
*/
|
*/
|
||||||
void setWatchInstructionPointerRange(address_t start,
|
void setWatchInstructionPointerRange(address_t start, address_t end);
|
||||||
address_t end);
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a given address is within the range.
|
* Checks whether a given address is within the range.
|
||||||
|
* @param addr address to check
|
||||||
|
* @param address_space address space information
|
||||||
|
* @return \c true if address is within the range (represented by
|
||||||
|
* \c this), \c false otherwise
|
||||||
*/
|
*/
|
||||||
bool isMatching(address_t addr, address_t address_space) const;
|
bool isMatching(address_t addr, address_t address_space) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class MemAccessEvent
|
* \class MemAccessListener
|
||||||
* Observes memory read/write accesses.
|
* Observes memory read/write accesses.
|
||||||
* FIXME? currently >8-bit accesses only match if their lowest address is being watched
|
* FIXME? currently >8-bit accesses only match if their lowest address is being watched
|
||||||
* (e.g., a 32-bit write to 0x4 also accesses 0x7, but this cannot be matched)
|
* (e.g., a 32-bit write to 0x4 also accesses 0x7, but this cannot be matched)
|
||||||
*/
|
*/
|
||||||
class MemAccessEvent : virtual public BaseEvent {
|
class MemAccessListener : virtual public BaseListener {
|
||||||
public:
|
public:
|
||||||
enum accessType_t {
|
enum accessType_t {
|
||||||
MEM_UNKNOWN = 0x0,
|
MEM_UNKNOWN = 0x0,
|
||||||
@ -250,7 +263,7 @@ private:
|
|||||||
* (MEM_READ || MEM_WRITE || MEM_READWRITE).
|
* (MEM_READ || MEM_WRITE || MEM_READWRITE).
|
||||||
*/
|
*/
|
||||||
accessType_t m_WatchType;
|
accessType_t m_WatchType;
|
||||||
//! Specific guest system address that actually triggered the event.
|
//! Specific guest system address that actually triggered the listener.
|
||||||
address_t m_TriggerAddr;
|
address_t m_TriggerAddr;
|
||||||
//! Width of the memory access (# bytes).
|
//! Width of the memory access (# bytes).
|
||||||
size_t m_TriggerWidth;
|
size_t m_TriggerWidth;
|
||||||
@ -259,11 +272,11 @@ private:
|
|||||||
//! Memory access type at m_TriggerAddr.
|
//! Memory access type at m_TriggerAddr.
|
||||||
accessType_t m_AccessType;
|
accessType_t m_AccessType;
|
||||||
public:
|
public:
|
||||||
MemAccessEvent(accessType_t watchtype = MEM_READWRITE)
|
MemAccessListener(accessType_t watchtype = MEM_READWRITE)
|
||||||
: m_WatchAddr(ANY_ADDR), m_WatchType(watchtype),
|
: m_WatchAddr(ANY_ADDR), m_WatchType(watchtype),
|
||||||
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
||||||
m_AccessType(MEM_UNKNOWN) { }
|
m_AccessType(MEM_UNKNOWN) { }
|
||||||
MemAccessEvent(address_t addr,
|
MemAccessListener(address_t addr,
|
||||||
accessType_t watchtype = MEM_READWRITE)
|
accessType_t watchtype = MEM_READWRITE)
|
||||||
: m_WatchAddr(addr), m_WatchType(watchtype),
|
: m_WatchAddr(addr), m_WatchType(watchtype),
|
||||||
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
||||||
@ -282,21 +295,21 @@ public:
|
|||||||
bool isMatching(address_t addr, accessType_t accesstype) const;
|
bool isMatching(address_t addr, accessType_t accesstype) const;
|
||||||
/**
|
/**
|
||||||
* Returns the specific memory address that actually triggered the
|
* Returns the specific memory address that actually triggered the
|
||||||
* event.
|
* listener.
|
||||||
*/
|
*/
|
||||||
address_t getTriggerAddress() const { return (m_TriggerAddr); }
|
address_t getTriggerAddress() const { return m_TriggerAddr; }
|
||||||
/**
|
/**
|
||||||
* Sets the specific memory address that actually triggered the event.
|
* Sets the specific memory address that actually triggered the listener.
|
||||||
* Should not be used by experiment code.
|
* Should not be used by experiment code.
|
||||||
*/
|
*/
|
||||||
void setTriggerAddress(address_t addr) { m_TriggerAddr = addr; }
|
void setTriggerAddress(address_t addr) { m_TriggerAddr = addr; }
|
||||||
/**
|
/**
|
||||||
* Returns the specific memory address that actually triggered the
|
* Returns the specific memory address that actually triggered the
|
||||||
* event.
|
* listener.
|
||||||
*/
|
*/
|
||||||
size_t getTriggerWidth() const { return (m_TriggerWidth); }
|
size_t getTriggerWidth() const { return m_TriggerWidth; }
|
||||||
/**
|
/**
|
||||||
* Sets the specific memory address that actually triggered the event.
|
* Sets the specific memory address that actually triggered the listener.
|
||||||
* Should not be used by experiment code.
|
* Should not be used by experiment code.
|
||||||
*/
|
*/
|
||||||
void setTriggerWidth(size_t width) { m_TriggerWidth = width; }
|
void setTriggerWidth(size_t width) { m_TriggerWidth = width; }
|
||||||
@ -304,64 +317,62 @@ public:
|
|||||||
* Returns the address of the instruction causing this memory
|
* Returns the address of the instruction causing this memory
|
||||||
* access.
|
* access.
|
||||||
*/
|
*/
|
||||||
address_t getTriggerInstructionPointer() const
|
address_t getTriggerInstructionPointer() const { return m_TriggerIP; }
|
||||||
{ return (m_TriggerIP); }
|
|
||||||
/**
|
/**
|
||||||
* Sets the address of the instruction causing this memory
|
* Sets the address of the instruction causing this memory
|
||||||
* access. Should not be used by experiment code.
|
* access. Should not be used by experiment code.
|
||||||
*/
|
*/
|
||||||
void setTriggerInstructionPointer(address_t addr)
|
void setTriggerInstructionPointer(address_t addr) { m_TriggerIP = addr; }
|
||||||
{ m_TriggerIP = addr; }
|
|
||||||
/**
|
/**
|
||||||
* Returns type (MEM_READ || MEM_WRITE) of the memory access that
|
* Returns type (MEM_READ || MEM_WRITE) of the memory access that
|
||||||
* triggered this event.
|
* triggered this listener.
|
||||||
*/
|
*/
|
||||||
accessType_t getTriggerAccessType() const { return (m_AccessType); }
|
accessType_t getTriggerAccessType() const { return m_AccessType; }
|
||||||
/**
|
/**
|
||||||
* Sets type of the memory access that triggered this event. Should
|
* Sets type of the memory access that triggered this listener. Should
|
||||||
* not be used by experiment code.
|
* not be used by experiment code.
|
||||||
*/
|
*/
|
||||||
void setTriggerAccessType(accessType_t type) { m_AccessType = type; }
|
void setTriggerAccessType(accessType_t type) { m_AccessType = type; }
|
||||||
/**
|
/**
|
||||||
* Returns memory access types (MEM_READ || MEM_WRITE || MEM_READWRITE)
|
* Returns memory access types (MEM_READ || MEM_WRITE || MEM_READWRITE)
|
||||||
* this event watches. Should not be used by experiment code.
|
* this listener watches. Should not be used by experiment code.
|
||||||
*/
|
*/
|
||||||
accessType_t getWatchAccessType() const { return (m_WatchType); }
|
accessType_t getWatchAccessType() const { return m_WatchType; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class MemReadEvent
|
* \class MemReadListener
|
||||||
* Observes memory read accesses.
|
* Observes memory read accesses.
|
||||||
*/
|
*/
|
||||||
class MemReadEvent : virtual public MemAccessEvent {
|
class MemReadListener : virtual public MemAccessListener {
|
||||||
public:
|
public:
|
||||||
MemReadEvent()
|
MemReadListener()
|
||||||
: MemAccessEvent(MEM_READ) { }
|
: MemAccessListener(MEM_READ) { }
|
||||||
MemReadEvent(address_t addr)
|
MemReadListener(address_t addr)
|
||||||
: MemAccessEvent(addr, MEM_READ) { }
|
: MemAccessListener(addr, MEM_READ) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class MemWriteEvent
|
* \class MemWriteListener
|
||||||
* Observes memory write accesses.
|
* Observes memory write accesses.
|
||||||
*/
|
*/
|
||||||
class MemWriteEvent : virtual public MemAccessEvent {
|
class MemWriteListener : virtual public MemAccessListener {
|
||||||
public:
|
public:
|
||||||
MemWriteEvent()
|
MemWriteListener()
|
||||||
: MemAccessEvent(MEM_READ) { }
|
: MemAccessListener(MEM_READ) { }
|
||||||
MemWriteEvent(address_t addr)
|
MemWriteListener(address_t addr)
|
||||||
: MemAccessEvent(addr, MEM_WRITE) { }
|
: MemAccessListener(addr, MEM_WRITE) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class TroubleEvent
|
* \class TroubleListener
|
||||||
* Observes interrupt/trap activties.
|
* Observes interrupt/trap activties.
|
||||||
*/
|
*/
|
||||||
class TroubleEvent : virtual public BaseEvent {
|
class TroubleListener : virtual public BaseListener {
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Specific guest system interrupt/trap number that actually
|
* Specific guest system interrupt/trap number that actually
|
||||||
* trigger the event.
|
* trigger the listener.
|
||||||
*/
|
*/
|
||||||
int m_TriggerNumber;
|
int m_TriggerNumber;
|
||||||
/**
|
/**
|
||||||
@ -370,8 +381,8 @@ private:
|
|||||||
*/
|
*/
|
||||||
std::vector<unsigned> m_WatchNumbers;
|
std::vector<unsigned> m_WatchNumbers;
|
||||||
public:
|
public:
|
||||||
TroubleEvent() : m_TriggerNumber (-1) { }
|
TroubleListener() : m_TriggerNumber (-1) { }
|
||||||
TroubleEvent(unsigned troubleNumber)
|
TroubleListener(unsigned troubleNumber)
|
||||||
: m_TriggerNumber(-1)
|
: m_TriggerNumber(-1)
|
||||||
{ addWatchNumber(troubleNumber); }
|
{ addWatchNumber(troubleNumber); }
|
||||||
/**
|
/**
|
||||||
@ -393,39 +404,39 @@ public:
|
|||||||
* Returns the list of observed numbers
|
* Returns the list of observed numbers
|
||||||
* @return a copy of the list which contains all observed numbers
|
* @return a copy of the list which contains all observed numbers
|
||||||
*/
|
*/
|
||||||
std::vector<unsigned> getWatchNumbers() { return (m_WatchNumbers); }
|
std::vector<unsigned> getWatchNumbers() { return m_WatchNumbers; }
|
||||||
/**
|
/**
|
||||||
* Checks whether a given interrupt-/trap-number is matching.
|
* Checks whether a given interrupt-/trap-number is matching.
|
||||||
*/
|
*/
|
||||||
bool isMatching(unsigned troubleNum) const;
|
bool isMatching(unsigned troubleNum) const;
|
||||||
/**
|
/**
|
||||||
* Sets the specific interrupt-/trap-number that actually triggered
|
* Sets the specific interrupt-/trap-number that actually triggered
|
||||||
* the event. Should not be used by experiment code.
|
* the listener. Should not be used by experiment code.
|
||||||
*/
|
*/
|
||||||
void setTriggerNumber(unsigned troubleNum)
|
void setTriggerNumber(unsigned troubleNum)
|
||||||
{ m_TriggerNumber = troubleNum; }
|
{ m_TriggerNumber = troubleNum; }
|
||||||
/**
|
/**
|
||||||
* Returns the specific interrupt-/trap-number that actually triggered
|
* Returns the specific interrupt-/trap-number that actually triggered
|
||||||
* the event.
|
* the listener.
|
||||||
*/
|
*/
|
||||||
unsigned getTriggerNumber() { return (m_TriggerNumber); }
|
unsigned getTriggerNumber() { return m_TriggerNumber; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class InterruptEvent
|
* \class InterruptListener
|
||||||
* Observes interrupts of the guest system.
|
* Observes interrupts of the guest system.
|
||||||
*/
|
*/
|
||||||
class InterruptEvent : virtual public TroubleEvent {
|
class InterruptListener : virtual public TroubleListener {
|
||||||
private:
|
private:
|
||||||
bool m_IsNMI; //!< non maskable interrupt flag
|
bool m_IsNMI; //!< non maskable interrupt flag
|
||||||
public:
|
public:
|
||||||
InterruptEvent() : m_IsNMI(false) { }
|
InterruptListener() : m_IsNMI(false) { }
|
||||||
InterruptEvent(unsigned interrupt) : m_IsNMI(false)
|
InterruptListener(unsigned interrupt) : m_IsNMI(false)
|
||||||
{ addWatchNumber(interrupt); }
|
{ addWatchNumber(interrupt); }
|
||||||
/**
|
/**
|
||||||
* Returns \c true if the interrupt is non maskable, \c false otherwise.
|
* Returns \c true if the interrupt is non maskable, \c false otherwise.
|
||||||
*/
|
*/
|
||||||
bool isNMI() { return (m_IsNMI); }
|
bool isNMI() { return m_IsNMI; }
|
||||||
/**
|
/**
|
||||||
* Sets the interrupt type (non maskable or not).
|
* Sets the interrupt type (non maskable or not).
|
||||||
*/
|
*/
|
||||||
@ -433,29 +444,29 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class TrapEvent
|
* \class TrapListener
|
||||||
* Observes traps of the guest system.
|
* Observes traps of the guest system.
|
||||||
*/
|
*/
|
||||||
class TrapEvent : virtual public TroubleEvent {
|
class TrapListener : virtual public TroubleListener {
|
||||||
public:
|
public:
|
||||||
TrapEvent() { }
|
TrapListener() { }
|
||||||
TrapEvent(unsigned trap) { addWatchNumber(trap); }
|
TrapListener(unsigned trap) { addWatchNumber(trap); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class GuestEvent
|
* \class GuestListener
|
||||||
* Used to receive data from the guest system.
|
* Used to receive data from the guest system.
|
||||||
*/
|
*/
|
||||||
class GuestEvent : virtual public BaseEvent {
|
class GuestListener : virtual public BaseListener {
|
||||||
private:
|
private:
|
||||||
char m_Data;
|
char m_Data;
|
||||||
unsigned m_Port;
|
unsigned m_Port;
|
||||||
public:
|
public:
|
||||||
GuestEvent() : m_Data(0), m_Port(0) { }
|
GuestListener() : m_Data(0), m_Port(0) { }
|
||||||
/**
|
/**
|
||||||
* Returns the data, transmitted by the guest system.
|
* Returns the data, transmitted by the guest system.
|
||||||
*/
|
*/
|
||||||
char getData() const { return (m_Data); }
|
char getData() const { return m_Data; }
|
||||||
/**
|
/**
|
||||||
* Sets the data which had been transmitted by the guest system.
|
* Sets the data which had been transmitted by the guest system.
|
||||||
*/
|
*/
|
||||||
@ -463,7 +474,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Returns the data length, transmitted by the guest system.
|
* Returns the data length, transmitted by the guest system.
|
||||||
*/
|
*/
|
||||||
unsigned getPort() const { return (m_Port); }
|
unsigned getPort() const { return m_Port; }
|
||||||
/**
|
/**
|
||||||
* Sets the data length which had been transmitted by the guest system.
|
* Sets the data length which had been transmitted by the guest system.
|
||||||
*/
|
*/
|
||||||
@ -471,52 +482,52 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class IOPortEvent
|
* \class IOPortListener
|
||||||
* Observes I/O access on architectures with a separate I/O access mechanism (e.g. IA-32)
|
* Observes I/O access on architectures with a separate I/O access mechanism (e.g. IA-32)
|
||||||
*/
|
*/
|
||||||
class IOPortEvent : virtual public BaseEvent {
|
class IOPortListener : virtual public BaseListener {
|
||||||
private:
|
private:
|
||||||
unsigned char m_Data;
|
unsigned char m_Data;
|
||||||
unsigned m_Port;
|
unsigned m_Port;
|
||||||
bool m_Out;
|
bool m_Out;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Initialises an IOPortEvent
|
* Initialises an IOPortListener
|
||||||
*
|
*
|
||||||
* @param port the port the event ist listening on
|
* @param port the port the listener ist listening on
|
||||||
* @param out Defines the direction of the event.
|
* @param out Defines the direction of the listener.
|
||||||
* \arg \c true Output on the given port is captured.
|
* \arg \c true Output on the given port is captured.
|
||||||
* \arg \c false Input on the given port is captured.
|
* \arg \c false Input on the given port is captured.
|
||||||
*/
|
*/
|
||||||
IOPortEvent(unsigned port, bool out) : m_Data(0), m_Port(port), m_Out(out) { }
|
IOPortListener(unsigned port, bool out) : m_Data(0), m_Port(port), m_Out(out) { }
|
||||||
/**
|
/**
|
||||||
* Returns the data sent to the specified port
|
* Returns the data sent to the specified port
|
||||||
*/
|
*/
|
||||||
unsigned char getData() const { return (m_Data); }
|
unsigned char getData() const { return m_Data; }
|
||||||
/**
|
/**
|
||||||
* Sets the data which had been transmitted.
|
* Sets the data which had been transmitted.
|
||||||
*/
|
*/
|
||||||
void setData(unsigned char data) { m_Data = data; }
|
void setData(unsigned char data) { m_Data = data; }
|
||||||
/**
|
/**
|
||||||
* Retrieves the port which this event is bound to.
|
* Retrieves the port which this listener is bound to.
|
||||||
*/
|
*/
|
||||||
unsigned getPort() const { return (m_Port); }
|
unsigned getPort() const { return m_Port; }
|
||||||
/**
|
/**
|
||||||
* Sets the port which this event is bound to.
|
* Sets the port which this listener is bound to.
|
||||||
*/
|
*/
|
||||||
void setPort(unsigned port) { m_Port = port; }
|
void setPort(unsigned port) { m_Port = port; }
|
||||||
/**
|
/**
|
||||||
* Checks whether a given port number is matching.
|
* Checks whether a given port number is matching.
|
||||||
* @param p The port number an I/O event occured on
|
* @param p The port number an I/O listener occured on
|
||||||
* @param out True if the communication was outbound, false otherwise
|
* @param out True if the communication was outbound, false otherwise
|
||||||
*/
|
*/
|
||||||
bool isMatching(unsigned p, bool out) const { return ( out = isOutEvent() && p == getPort()); }
|
bool isMatching(unsigned p, bool out) const { return ( out = isOutListener() && p == getPort()); }
|
||||||
/**
|
/**
|
||||||
* Tells you if this event is capturing outbound communication (inbound if false)
|
* Tells you if this listener is capturing outbound communication (inbound if false)
|
||||||
*/
|
*/
|
||||||
bool isOutEvent() const { return m_Out; }
|
bool isOutListener() const { return m_Out; }
|
||||||
/**
|
/**
|
||||||
* Change the event direction.
|
* Change the listener direction.
|
||||||
* \arg \c true Output on the given port is captured.
|
* \arg \c true Output on the given port is captured.
|
||||||
* \arg \c false Input on the given port is captured.
|
* \arg \c false Input on the given port is captured.
|
||||||
*/
|
*/
|
||||||
@ -524,36 +535,36 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class JumpEvent
|
* \class JumpListener
|
||||||
* JumpEvents are used to observe conditional jumps (if...else if...else).
|
* JumpListeners are used to observe conditional jumps (if...else if...else).
|
||||||
*/
|
*/
|
||||||
class JumpEvent : virtual public BaseEvent {
|
class JumpListener : virtual public BaseListener {
|
||||||
private:
|
private:
|
||||||
unsigned m_Opcode;
|
unsigned m_Opcode;
|
||||||
bool m_FlagTriggered;
|
bool m_FlagTriggered;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructs a new event object.
|
* Constructs a new listener object.
|
||||||
* @param parent the parent object
|
* @param parent the parent object
|
||||||
* @param opcode the opcode of the jump-instruction to be observed
|
* @param opcode the opcode of the jump-instruction to be observed
|
||||||
* or ANY_INSTR to match all jump-instructions
|
* or ANY_INSTR to match all jump-instructions
|
||||||
*/
|
*/
|
||||||
JumpEvent(unsigned opcode = ANY_INSTR)
|
JumpListener(unsigned opcode = ANY_INSTR)
|
||||||
: m_Opcode(opcode), m_FlagTriggered(false) { }
|
: m_Opcode(opcode), m_FlagTriggered(false) { }
|
||||||
/**
|
/**
|
||||||
* Retrieves the opcode of the jump-instruction.
|
* Retrieves the opcode of the jump-instruction.
|
||||||
*/
|
*/
|
||||||
unsigned getOpcode() const { return (m_Opcode); }
|
unsigned getOpcode() const { return m_Opcode; }
|
||||||
/**
|
/**
|
||||||
* Returns \c true, if the event was triggered due to specific register
|
* Returns \c true, if the listener was triggered due to specific register
|
||||||
* content, \c false otherwise.
|
* content, \c false otherwise.
|
||||||
*/
|
*/
|
||||||
bool isRegisterTriggered() { return (!m_FlagTriggered); }
|
bool isRegisterTriggered() { return !m_FlagTriggered; }
|
||||||
/**
|
/**
|
||||||
* Returns \c true, of the event was triggered due to specific FLAG
|
* Returns \c true, of the listener was triggered due to specific FLAG
|
||||||
* state, \c false otherwise. This is the common case.
|
* state, \c false otherwise. This is the common case.
|
||||||
*/
|
*/
|
||||||
bool isFlagTriggered() { return (m_FlagTriggered); }
|
bool isFlagTriggered() { return m_FlagTriggered; }
|
||||||
/**
|
/**
|
||||||
* Sets the requestet jump-instruction opcode.
|
* Sets the requestet jump-instruction opcode.
|
||||||
*/
|
*/
|
||||||
@ -561,36 +572,35 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Sets the trigger flag.
|
* Sets the trigger flag.
|
||||||
*/
|
*/
|
||||||
void setFlagTriggered(bool flagTriggered)
|
void setFlagTriggered(bool flagTriggered) { m_FlagTriggered = flagTriggered; }
|
||||||
{ m_FlagTriggered = flagTriggered; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class GenericTimerEvent
|
* \class GenericTimerListener
|
||||||
* This event type is used to create timeouts within in an experiment.
|
* This listener type is used to create timeouts within in an experiment.
|
||||||
*
|
*
|
||||||
* Depending on your simulator backend, a concrete class needs to be derived
|
* Depending on your simulator backend, a concrete class needs to be derived
|
||||||
* from \c GenericTimerEvent. \c onEventAddition should be used to register and
|
* from \c GenericTimerListener. \c onListenerAddition should be used to register and
|
||||||
* \c onEventDeletion to unregister the timer. \c onEventTrigger can be used to
|
* \c onListenerDeletion to unregister the timer. \c onListenerTrigger can be used to
|
||||||
* re-register the timer if a repetitive timer is requested and the back-
|
* re-register the timer if a repetitive timer is requested and the back-
|
||||||
* end doesn't support such timer types natively.
|
* end doesn't support such timer types natively.
|
||||||
*/
|
*/
|
||||||
class GenericTimerEvent : public BaseEvent {
|
class GenericTimerListener : public BaseListener {
|
||||||
protected:
|
protected:
|
||||||
unsigned m_Timeout; //!< timeout interval in milliseconds
|
unsigned m_Timeout; //!< timeout interval in milliseconds
|
||||||
timer_id_t m_Id; //!< internal timer id (sim-specific)
|
timer_id_t m_Id; //!< internal timer id (sim-specific)
|
||||||
bool m_Once; //!< \c true, if the timer should be triggered only once, \c false otherwise
|
bool m_Once; //!< \c true, if the timer should be triggered only once, \c false otherwise
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Creates a new timer event. This can be used to implement a timeout-
|
* Creates a new timer listener. This can be used to implement a timeout-
|
||||||
* mechanism in the experiment-flow. The timer starts automatically when
|
* mechanism in the experiment-flow. The timer starts automatically when
|
||||||
* added to the simulator backend.
|
* added to the simulator backend.
|
||||||
* @param timeout the time intervall in milliseconds (ms)
|
* @param timeout the time intervall in milliseconds (ms)
|
||||||
* @see SimulatorController::addEvent
|
* @see SimulatorController::addListener
|
||||||
*/
|
*/
|
||||||
GenericTimerEvent(unsigned timeout)
|
GenericTimerListener(unsigned timeout)
|
||||||
: m_Timeout(timeout), m_Id(-1) { }
|
: m_Timeout(timeout), m_Id(-1) { }
|
||||||
~GenericTimerEvent() { }
|
~GenericTimerListener() { }
|
||||||
/**
|
/**
|
||||||
* Retrieves the internal timer id. Maybe useful for debug output.
|
* Retrieves the internal timer id. Maybe useful for debug output.
|
||||||
* @return the timer id
|
* @return the timer id
|
||||||
@ -610,4 +620,4 @@ public:
|
|||||||
|
|
||||||
} // end-of-namespace: fail
|
} // end-of-namespace: fail
|
||||||
|
|
||||||
#endif // __EVENT_HPP__
|
#endif // __LISTENER_HPP__
|
||||||
191
src/core/sal/ListenerManager.cc
Normal file
191
src/core/sal/ListenerManager.cc
Normal 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
|
||||||
219
src/core/sal/ListenerManager.hpp
Normal file
219
src/core/sal/ListenerManager.hpp
Normal 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__
|
||||||
@ -55,12 +55,12 @@ public:
|
|||||||
* Returns the (fixed) type of this register.
|
* Returns the (fixed) type of this register.
|
||||||
* @return the type of this register
|
* @return the type of this register
|
||||||
*/
|
*/
|
||||||
RegisterType getType() const { return (m_Type); }
|
RegisterType getType() const { return m_Type; }
|
||||||
/**
|
/**
|
||||||
* Returns the (fixed) width of this register.
|
* Returns the (fixed) width of this register.
|
||||||
* @return the width in bits
|
* @return the width in bits
|
||||||
*/
|
*/
|
||||||
regwidth_t getWidth() const { return (m_Width); }
|
regwidth_t getWidth() const { return m_Width; }
|
||||||
/**
|
/**
|
||||||
* Returns the data referenced by this register. In a concrete
|
* Returns the data referenced by this register. In a concrete
|
||||||
* derived class this method has to be defined appropriately.
|
* derived class this method has to be defined appropriately.
|
||||||
@ -81,7 +81,7 @@ public:
|
|||||||
* Retrieves the register name.
|
* Retrieves the register name.
|
||||||
* @return the textual register description
|
* @return the textual register description
|
||||||
*/
|
*/
|
||||||
const std::string& getName() const { return (m_Name); }
|
const std::string& getName() const { return m_Name; }
|
||||||
/**
|
/**
|
||||||
* Retrieves the unique index within it's assigned register set.
|
* Retrieves the unique index within it's assigned register set.
|
||||||
* If the register has not been assigned, \c (size_t)-1 will be
|
* If the register has not been assigned, \c (size_t)-1 will be
|
||||||
@ -89,18 +89,18 @@ public:
|
|||||||
* @return the register index or -1 if not assigned
|
* @return the register index or -1 if not assigned
|
||||||
* @see isAssigned()
|
* @see isAssigned()
|
||||||
*/
|
*/
|
||||||
size_t getIndex() const { return (m_Index); }
|
size_t getIndex() const { return m_Index; }
|
||||||
/**
|
/**
|
||||||
* Checks whether this register has already been assigned. On
|
* Checks whether this register has already been assigned. On
|
||||||
* creation the register isn't initially assigned.
|
* creation the register isn't initially assigned.
|
||||||
* @return \c true if assigned, \c false otherwise
|
* @return \c true if assigned, \c false otherwise
|
||||||
*/
|
*/
|
||||||
bool isAssigned() const { return (m_Assigned); }
|
bool isAssigned() const { return m_Assigned; }
|
||||||
/**
|
/**
|
||||||
* Returns the unique id of this register.
|
* Returns the unique id of this register.
|
||||||
* @return the unique id
|
* @return the unique id
|
||||||
*/
|
*/
|
||||||
unsigned int getId() const { return (m_Id); }
|
unsigned int getId() const { return m_Id; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,7 +132,7 @@ public:
|
|||||||
* ^
|
* ^
|
||||||
* \endcode
|
* \endcode
|
||||||
*/
|
*/
|
||||||
iterator begin() { return (m_Regs.begin()); }
|
iterator begin() { return m_Regs.begin(); }
|
||||||
/**
|
/**
|
||||||
* Returns an iterator to the end of the interal data structure.
|
* Returns an iterator to the end of the interal data structure.
|
||||||
* \code
|
* \code
|
||||||
@ -140,7 +140,7 @@ public:
|
|||||||
* ^
|
* ^
|
||||||
* \endcode
|
* \endcode
|
||||||
*/
|
*/
|
||||||
iterator end() { return (m_Regs.end()); }
|
iterator end() { return m_Regs.end(); }
|
||||||
/**
|
/**
|
||||||
* Constructs a new register set with type \a containerType.
|
* Constructs a new register set with type \a containerType.
|
||||||
* @param containerType the type of registers which should be stored
|
* @param containerType the type of registers which should be stored
|
||||||
@ -152,12 +152,12 @@ public:
|
|||||||
* Returns the type of this set.
|
* Returns the type of this set.
|
||||||
* @return the type
|
* @return the type
|
||||||
*/
|
*/
|
||||||
RegisterType getType() const { return (m_Type); }
|
RegisterType getType() const { return m_Type; }
|
||||||
/**
|
/**
|
||||||
* Gets the number of registers of this set.
|
* Gets the number of registers of this set.
|
||||||
* @return the number of registers
|
* @return the number of registers
|
||||||
*/
|
*/
|
||||||
size_t count() const { return (m_Regs.size()); }
|
size_t count() const { return m_Regs.size(); }
|
||||||
/**
|
/**
|
||||||
* Retrieves the \a i-th register within this set.
|
* Retrieves the \a i-th register within this set.
|
||||||
* @return a pointer to the \a i-th register; if \a i is invalid, an
|
* @return a pointer to the \a i-th register; if \a i is invalid, an
|
||||||
@ -169,7 +169,7 @@ public:
|
|||||||
* @return a pointer to the first register (if existing -- otherwise an
|
* @return a pointer to the first register (if existing -- otherwise an
|
||||||
* assertion is thrown)
|
* assertion is thrown)
|
||||||
*/
|
*/
|
||||||
virtual Register* first() { return (getRegister(0)); }
|
virtual Register* first() { return getRegister(0); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,7 +197,7 @@ public:
|
|||||||
* ^
|
* ^
|
||||||
* \endcode
|
* \endcode
|
||||||
*/
|
*/
|
||||||
iterator begin() { return (m_Registers.begin()); }
|
iterator begin() { return m_Registers.begin(); }
|
||||||
/**
|
/**
|
||||||
* Returns an iterator to the end of the interal data structure.
|
* Returns an iterator to the end of the interal data structure.
|
||||||
* \code
|
* \code
|
||||||
@ -205,7 +205,7 @@ public:
|
|||||||
* ^
|
* ^
|
||||||
* \endcode
|
* \endcode
|
||||||
*/
|
*/
|
||||||
iterator end() { return (m_Registers.end()); }
|
iterator end() { return m_Registers.end(); }
|
||||||
|
|
||||||
RegisterManager() { }
|
RegisterManager() { }
|
||||||
~RegisterManager() { clear(); }
|
~RegisterManager() { clear(); }
|
||||||
@ -218,7 +218,7 @@ public:
|
|||||||
* Retrieves the number of managed homogeneous register sets.
|
* Retrieves the number of managed homogeneous register sets.
|
||||||
* @return the number of sets
|
* @return the number of sets
|
||||||
*/
|
*/
|
||||||
virtual size_t subsetCount() const { return (m_Subsets.size()); }
|
virtual size_t subsetCount() const { return m_Subsets.size(); }
|
||||||
/**
|
/**
|
||||||
* Gets the \a i-th register set.
|
* Gets the \a i-th register set.
|
||||||
* @param i the index of the set to be returned
|
* @param i the index of the set to be returned
|
||||||
|
|||||||
@ -6,26 +6,26 @@ namespace fail {
|
|||||||
// External reference declared in SALInst.hpp
|
// External reference declared in SALInst.hpp
|
||||||
ConcreteSimulatorController simulator;
|
ConcreteSimulatorController simulator;
|
||||||
|
|
||||||
bool SimulatorController::addEvent(BaseEvent* ev)
|
bool SimulatorController::addListener(BaseListener* li)
|
||||||
{
|
{
|
||||||
assert(ev != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
|
assert(li != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
|
||||||
m_EvList.add(ev, m_Flows.getCurrent());
|
m_LstList.add(li, m_Flows.getCurrent());
|
||||||
// Call the common postprocessing function:
|
// Call the common postprocessing function:
|
||||||
if (!ev->onEventAddition()) { // If the return value signals "false"...,
|
if (!li->onAddition()) { // If the return value signals "false"...,
|
||||||
m_EvList.remove(ev); // ...skip the addition
|
m_LstList.remove(li); // ...skip the addition
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseEvent* SimulatorController::waitAny(void)
|
BaseListener* SimulatorController::resume(void)
|
||||||
{
|
{
|
||||||
if (!hasEvents())
|
if (!hasListeners())
|
||||||
return NULL;
|
return NULL;
|
||||||
m_Flows.resume();
|
m_Flows.resume();
|
||||||
assert(m_EvList.getLastFired() != NULL &&
|
assert(m_LstList.getLastFired() != NULL &&
|
||||||
"FATAL ERROR: getLastFired() expected to be non-NULL!");
|
"FATAL ERROR: getLastFired() expected to be non-NULL!");
|
||||||
return m_EvList.getLastFired();
|
return m_LstList.getLastFired();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::startup()
|
void SimulatorController::startup()
|
||||||
@ -43,47 +43,47 @@ void SimulatorController::initExperiments()
|
|||||||
/* empty. */
|
/* empty. */
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::onBreakpointEvent(address_t instrPtr, address_t address_space)
|
void SimulatorController::onBreakpointListener(address_t instrPtr, address_t address_space)
|
||||||
{
|
{
|
||||||
assert(false &&
|
assert(false &&
|
||||||
"FIXME: SimulatorController::onBreakpointEvent() has not been tested before");
|
"FIXME: SimulatorController::onBreakpointListener() has not been tested before");
|
||||||
// FIXME: Improve performance!
|
// FIXME: Improve performance!
|
||||||
|
|
||||||
// Loop through all events of type BP*Event:
|
// Loop through all events of type BP*Listener:
|
||||||
EventManager::iterator it = m_EvList.begin();
|
ListenerManager::iterator it = m_LstList.begin();
|
||||||
while (it != m_EvList.end()) {
|
while (it != m_LstList.end()) {
|
||||||
BaseEvent* pev = *it;
|
BaseListener* pev = *it;
|
||||||
BPSingleEvent* pbp; BPRangeEvent* pbpr;
|
BPSingleListener* pbp; BPRangeListener* pbpr;
|
||||||
if ((pbp = dynamic_cast<BPSingleEvent*>(pev)) && pbp->isMatching(instrPtr, address_space)) {
|
if ((pbp = dynamic_cast<BPSingleListener*>(pev)) && pbp->isMatching(instrPtr, address_space)) {
|
||||||
pbp->setTriggerInstructionPointer(instrPtr);
|
pbp->setTriggerInstructionPointer(instrPtr);
|
||||||
it = m_EvList.makeActive(it);
|
it = m_LstList.makeActive(it);
|
||||||
// "it" has already been set to the next element (by calling
|
// "it" has already been set to the next element (by calling
|
||||||
// makeActive()):
|
// makeActive()):
|
||||||
continue; // -> skip iterator increment
|
continue; // -> skip iterator increment
|
||||||
} else if ((pbpr = dynamic_cast<BPRangeEvent*>(pev)) &&
|
} else if ((pbpr = dynamic_cast<BPRangeListener*>(pev)) &&
|
||||||
pbpr->isMatching(instrPtr, address_space)) {
|
pbpr->isMatching(instrPtr, address_space)) {
|
||||||
pbpr->setTriggerInstructionPointer(instrPtr);
|
pbpr->setTriggerInstructionPointer(instrPtr);
|
||||||
it = m_EvList.makeActive(it);
|
it = m_LstList.makeActive(it);
|
||||||
continue; // dito
|
continue; // dito
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
m_EvList.fireActiveEvents();
|
m_LstList.triggerActiveListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::onMemoryAccessEvent(address_t addr, size_t len,
|
void SimulatorController::onMemoryAccessListener(address_t addr, size_t len,
|
||||||
bool is_write, address_t instrPtr)
|
bool is_write, address_t instrPtr)
|
||||||
{
|
{
|
||||||
// FIXME: Improve performance!
|
// FIXME: Improve performance!
|
||||||
MemAccessEvent::accessType_t accesstype =
|
MemAccessListener::accessType_t accesstype =
|
||||||
is_write ? MemAccessEvent::MEM_WRITE
|
is_write ? MemAccessListener::MEM_WRITE
|
||||||
: MemAccessEvent::MEM_READ;
|
: MemAccessListener::MEM_READ;
|
||||||
|
|
||||||
EventManager::iterator it = m_EvList.begin();
|
ListenerManager::iterator it = m_LstList.begin();
|
||||||
while (it != m_EvList.end()) { // check for active events
|
while (it != m_LstList.end()) { // check for active events
|
||||||
BaseEvent* pev = *it;
|
BaseListener* pev = *it;
|
||||||
MemAccessEvent* ev = dynamic_cast<MemAccessEvent*>(pev);
|
MemAccessListener* ev = dynamic_cast<MemAccessListener*>(pev);
|
||||||
// Is this a MemAccessEvent? Correct access type?
|
// Is this a MemAccessListener? Correct access type?
|
||||||
if (!ev || !ev->isMatching(addr, accesstype)) {
|
if (!ev || !ev->isMatching(addr, accesstype)) {
|
||||||
++it;
|
++it;
|
||||||
continue; // skip event activation
|
continue; // skip event activation
|
||||||
@ -92,26 +92,26 @@ void SimulatorController::onMemoryAccessEvent(address_t addr, size_t len,
|
|||||||
ev->setTriggerWidth(len);
|
ev->setTriggerWidth(len);
|
||||||
ev->setTriggerInstructionPointer(instrPtr);
|
ev->setTriggerInstructionPointer(instrPtr);
|
||||||
ev->setTriggerAccessType(accesstype);
|
ev->setTriggerAccessType(accesstype);
|
||||||
it = m_EvList.makeActive(it);
|
it = m_LstList.makeActive(it);
|
||||||
}
|
}
|
||||||
m_EvList.fireActiveEvents();
|
m_LstList.triggerActiveListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::onInterruptEvent(unsigned interruptNum, bool nmi)
|
void SimulatorController::onInterruptListener(unsigned interruptNum, bool nmi)
|
||||||
{
|
{
|
||||||
EventManager::iterator it = m_EvList.begin();
|
ListenerManager::iterator it = m_LstList.begin();
|
||||||
while (it != m_EvList.end()) { // check for active events
|
while (it != m_LstList.end()) { // check for active events
|
||||||
BaseEvent* pev = *it;
|
BaseListener* pev = *it;
|
||||||
InterruptEvent* pie = dynamic_cast<InterruptEvent*>(pev);
|
InterruptListener* pie = dynamic_cast<InterruptListener*>(pev);
|
||||||
if (!pie || !pie->isMatching(interruptNum)) {
|
if (!pie || !pie->isMatching(interruptNum)) {
|
||||||
++it;
|
++it;
|
||||||
continue; // skip event activation
|
continue; // skip event activation
|
||||||
}
|
}
|
||||||
pie->setTriggerNumber(interruptNum);
|
pie->setTriggerNumber(interruptNum);
|
||||||
pie->setNMI(nmi);
|
pie->setNMI(nmi);
|
||||||
it = m_EvList.makeActive(it);
|
it = m_LstList.makeActive(it);
|
||||||
}
|
}
|
||||||
m_EvList.fireActiveEvents();
|
m_LstList.triggerActiveListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimulatorController::isSuppressedInterrupt(unsigned interruptNum)
|
bool SimulatorController::isSuppressedInterrupt(unsigned interruptNum)
|
||||||
@ -155,53 +155,53 @@ bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::onTrapEvent(unsigned trapNum)
|
void SimulatorController::onTrapListener(unsigned trapNum)
|
||||||
{
|
{
|
||||||
EventManager::iterator it = m_EvList.begin();
|
ListenerManager::iterator it = m_LstList.begin();
|
||||||
while (it != m_EvList.end()) { // check for active events
|
while (it != m_LstList.end()) { // check for active events
|
||||||
BaseEvent* pev = *it;
|
BaseListener* pev = *it;
|
||||||
TrapEvent* pte = dynamic_cast<TrapEvent*>(pev);
|
TrapListener* pte = dynamic_cast<TrapListener*>(pev);
|
||||||
if (!pte || !pte->isMatching(trapNum)) {
|
if (!pte || !pte->isMatching(trapNum)) {
|
||||||
++it;
|
++it;
|
||||||
continue; // skip event activation
|
continue; // skip event activation
|
||||||
}
|
}
|
||||||
pte->setTriggerNumber(trapNum);
|
pte->setTriggerNumber(trapNum);
|
||||||
it = m_EvList.makeActive(it);
|
it = m_LstList.makeActive(it);
|
||||||
}
|
}
|
||||||
m_EvList.fireActiveEvents();
|
m_LstList.triggerActiveListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::onGuestSystemEvent(char data, unsigned port)
|
void SimulatorController::onGuestSystemListener(char data, unsigned port)
|
||||||
{
|
{
|
||||||
EventManager::iterator it = m_EvList.begin();
|
ListenerManager::iterator it = m_LstList.begin();
|
||||||
while (it != m_EvList.end()) { // check for active events
|
while (it != m_LstList.end()) { // check for active events
|
||||||
BaseEvent* pev = *it;
|
BaseListener* pev = *it;
|
||||||
GuestEvent* pge = dynamic_cast<GuestEvent*>(pev);
|
GuestListener* pge = dynamic_cast<GuestListener*>(pev);
|
||||||
if (pge != NULL) {
|
if (pge != NULL) {
|
||||||
pge->setData(data);
|
pge->setData(data);
|
||||||
pge->setPort(port);
|
pge->setPort(port);
|
||||||
it = m_EvList.makeActive(it);
|
it = m_LstList.makeActive(it);
|
||||||
continue; // dito.
|
continue; // dito.
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
m_EvList.fireActiveEvents();
|
m_LstList.triggerActiveListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::onJumpEvent(bool flagTriggered, unsigned opcode)
|
void SimulatorController::onJumpListener(bool flagTriggered, unsigned opcode)
|
||||||
{
|
{
|
||||||
EventManager::iterator it = m_EvList.begin();
|
ListenerManager::iterator it = m_LstList.begin();
|
||||||
while (it != m_EvList.end()) { // check for active events
|
while (it != m_LstList.end()) { // check for active events
|
||||||
JumpEvent* pje = dynamic_cast<JumpEvent*>(*it);
|
JumpListener* pje = dynamic_cast<JumpListener*>(*it);
|
||||||
if (pje != NULL) {
|
if (pje != NULL) {
|
||||||
pje->setOpcode(opcode);
|
pje->setOpcode(opcode);
|
||||||
pje->setFlagTriggered(flagTriggered);
|
pje->setFlagTriggered(flagTriggered);
|
||||||
it = m_EvList.makeActive(it);
|
it = m_LstList.makeActive(it);
|
||||||
continue; // dito.
|
continue; // dito.
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
m_EvList.fireActiveEvents();
|
m_LstList.triggerActiveListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::addFlow(ExperimentFlow* flow)
|
void SimulatorController::addFlow(ExperimentFlow* flow)
|
||||||
@ -215,15 +215,15 @@ void SimulatorController::addFlow(ExperimentFlow* flow)
|
|||||||
void SimulatorController::removeFlow(ExperimentFlow* flow)
|
void SimulatorController::removeFlow(ExperimentFlow* flow)
|
||||||
{
|
{
|
||||||
// remove all remaining events of this flow
|
// remove all remaining events of this flow
|
||||||
clearEvents(flow);
|
clearListeners(flow);
|
||||||
// remove coroutine
|
// remove coroutine
|
||||||
m_Flows.remove(flow);
|
m_Flows.remove(flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseEvent* SimulatorController::addEventAndWait(BaseEvent* ev)
|
BaseListener* SimulatorController::addListenerAndResume(BaseListener* li)
|
||||||
{
|
{
|
||||||
addEvent(ev);
|
addListener(li);
|
||||||
return waitAny();
|
return resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorController::terminate(int exCode)
|
void SimulatorController::terminate(int exCode)
|
||||||
|
|||||||
@ -7,9 +7,9 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "efw/CoroutineManager.hpp"
|
#include "efw/CoroutineManager.hpp"
|
||||||
#include "EventManager.hpp"
|
#include "ListenerManager.hpp"
|
||||||
#include "SALConfig.hpp"
|
#include "SALConfig.hpp"
|
||||||
#include "Event.hpp"
|
#include "Listener.hpp"
|
||||||
|
|
||||||
namespace fail {
|
namespace fail {
|
||||||
|
|
||||||
@ -23,23 +23,22 @@ class MemoryManager;
|
|||||||
*
|
*
|
||||||
* \brief The abstract interface for controlling simulators and
|
* \brief The abstract interface for controlling simulators and
|
||||||
* accessing experiment data/flows (as part of the "Simulator
|
* accessing experiment data/flows (as part of the "Simulator
|
||||||
* Abstraction Layer".
|
* Abstraction Layer", SAL for short).
|
||||||
*
|
*
|
||||||
* This class manages (1..N) experiments and provides access to the underlying
|
* This class manages (1..N) experiments and provides access to the underlying
|
||||||
* simulator/debugger system. Experiments can enlist arbritrary events
|
* simulator/debugger system. Experiments can enlist arbritrary listeners
|
||||||
* (Breakpoint, Memory access, Traps, etc.). The SimulatorController then
|
* (Breakpoint, Memory access, Traps, etc.). The \c SimulatorController then
|
||||||
* activates the specific experiment There are further methods to read/write
|
* activates the specific experiment There are further methods to read/write
|
||||||
* registers and memory, and control the SUT (save/restore/reset).
|
* registers and memory, and control the SUT (save/restore/reset).
|
||||||
*/
|
*/
|
||||||
class SimulatorController {
|
class SimulatorController {
|
||||||
protected:
|
protected:
|
||||||
EventManager m_EvList; //!< storage where events are being buffered
|
ListenerManager m_LstList; //!< storage where listeners are being buffered
|
||||||
CoroutineManager m_Flows; //!< managed experiment flows
|
CoroutineManager m_Flows; //!< managed experiment flows
|
||||||
RegisterManager *m_Regs; //!< access to cpu register
|
RegisterManager *m_Regs; //!< access to cpu register
|
||||||
MemoryManager *m_Mem; //!< access to memory pool
|
MemoryManager *m_Mem; //!< access to memory pool
|
||||||
//! list of suppressed interrupts
|
std::vector<unsigned> m_SuppressedInterrupts; //!< list of suppressed interrupts
|
||||||
std::vector<unsigned> m_SuppressedInterrupts;
|
friend class ListenerManager; //!< "outsources" the listener management
|
||||||
friend class EventManager; //!< "outsources" the event management
|
|
||||||
public:
|
public:
|
||||||
SimulatorController()
|
SimulatorController()
|
||||||
: m_Regs(NULL), m_Mem(NULL) { }
|
: m_Regs(NULL), m_Mem(NULL) { }
|
||||||
@ -60,17 +59,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
void initExperiments();
|
void initExperiments();
|
||||||
/* ********************************************************************
|
/* ********************************************************************
|
||||||
* Standard Event Handler API
|
* Standard Listener Handler API
|
||||||
* ********************************************************************/
|
* ********************************************************************/
|
||||||
/**
|
/**
|
||||||
* Breakpoint event handler. This routine needs to be called in the
|
* Breakpoint listener handler. This routine needs to be called in the
|
||||||
* simulator specific backend each time a breakpoint event occurs.
|
* simulator specific backend each time a breakpoint listener occurs.
|
||||||
* @param instrPtr the instruction pointer of the breakpoint event
|
* @param instrPtr the instruction pointer of the breakpoint listener
|
||||||
* @param address_space the address space it should occur in
|
* @param address_space the address space it should occur in
|
||||||
*/
|
*/
|
||||||
void onBreakpointEvent(address_t instrPtr, address_t address_space);
|
void onBreakpointListener(address_t instrPtr, address_t address_space);
|
||||||
/**
|
/**
|
||||||
* Memory access event handler (read/write).
|
* Memory access listener handler (read/write).
|
||||||
* @param addr the accessed memory address
|
* @param addr the accessed memory address
|
||||||
* @param len the length of the accessed memory
|
* @param len the length of the accessed memory
|
||||||
* @param is_write \c true if memory is written, \c false if read
|
* @param is_write \c true if memory is written, \c false if read
|
||||||
@ -79,32 +78,31 @@ public:
|
|||||||
*
|
*
|
||||||
* FIXME: should instrPtr be part of this interface?
|
* FIXME: should instrPtr be part of this interface?
|
||||||
*/
|
*/
|
||||||
void onMemoryAccessEvent(address_t addr, size_t len,
|
void onMemoryAccessListener(address_t addr, size_t len, bool is_write, address_t instrPtr);
|
||||||
bool is_write, address_t instrPtr);
|
|
||||||
/**
|
/**
|
||||||
* Interrupt event handler.
|
* Interrupt listener handler.
|
||||||
* @param interruptNum the interrupt-type id
|
* @param interruptNum the interrupt-type id
|
||||||
* @param nmi nmi-value from guest-system
|
* @param nmi nmi-value from guest-system
|
||||||
*/
|
*/
|
||||||
void onInterruptEvent(unsigned interruptNum, bool nmi);
|
void onInterruptListener(unsigned interruptNum, bool nmi);
|
||||||
/**
|
/**
|
||||||
* Trap event handler.
|
* Trap listener handler.
|
||||||
* @param trapNum the trap-type id
|
* @param trapNum the trap-type id
|
||||||
*/
|
*/
|
||||||
void onTrapEvent(unsigned trapNum);
|
void onTrapListener(unsigned trapNum);
|
||||||
/**
|
/**
|
||||||
* Guest system communication handler.
|
* Guest system communication handler.
|
||||||
* @param data the "message" from the guest system
|
* @param data the "message" from the guest system
|
||||||
* @param port the port of the event
|
* @param port the port of the listener
|
||||||
*/
|
*/
|
||||||
void onGuestSystemEvent(char data, unsigned port);
|
void onGuestSystemListener(char data, unsigned port);
|
||||||
/**
|
/**
|
||||||
* (Conditional) Jump-instruction handler.
|
* (Conditional) Jump-instruction handler.
|
||||||
* @param flagTriggered \c true if the jump was triggered due to a
|
* @param flagTriggered \c true if the jump was triggered due to a
|
||||||
* specific FLAG (zero/carry/sign/overflow/parity flag)
|
* specific FLAG (zero/carry/sign/overflow/parity flag)
|
||||||
* @param opcode the opcode of the conrecete jump instruction
|
* @param opcode the opcode of the conrecete jump instruction
|
||||||
*/
|
*/
|
||||||
void onJumpEvent(bool flagTriggered, unsigned opcode);
|
void onJumpListener(bool flagTriggered, unsigned opcode);
|
||||||
/* ********************************************************************
|
/* ********************************************************************
|
||||||
* Simulator Controller & Access API:
|
* Simulator Controller & Access API:
|
||||||
* ********************************************************************/
|
* ********************************************************************/
|
||||||
@ -115,7 +113,7 @@ public:
|
|||||||
virtual void save(const std::string& path) = 0;
|
virtual void save(const std::string& path) = 0;
|
||||||
/**
|
/**
|
||||||
* Restore simulator state. Implicitly discards all previously
|
* Restore simulator state. Implicitly discards all previously
|
||||||
* registered events.
|
* registered listeners.
|
||||||
* @param path Location to previously saved state information
|
* @param path Location to previously saved state information
|
||||||
*/
|
*/
|
||||||
virtual void restore(const std::string& path) = 0;
|
virtual void restore(const std::string& path) = 0;
|
||||||
@ -144,20 +142,18 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Remove a Interrupt from the list of suppressed.
|
* Remove a Interrupt from the list of suppressed.
|
||||||
* @param interruptNum the interrupt-type id
|
* @param interruptNum the interrupt-type id
|
||||||
* @return \c true if sucessfully removed, \c false otherwise (not
|
* @return \c true if sucessfully removed, \c false otherwise (not found)
|
||||||
* found)
|
|
||||||
*/
|
*/
|
||||||
bool removeSuppressedInterrupt(unsigned interruptNum);
|
bool removeSuppressedInterrupt(unsigned interruptNum);
|
||||||
/**
|
/**
|
||||||
* Returns the (constant) initialized register manager.
|
* Returns the (constant) initialized register manager.
|
||||||
* @return a reference to the register manager
|
* @return a reference to the register manager
|
||||||
*/
|
*/
|
||||||
RegisterManager& getRegisterManager() { return (*m_Regs); }
|
RegisterManager& getRegisterManager() { return *m_Regs; }
|
||||||
const RegisterManager& getRegisterManager() const { return (*m_Regs); }
|
const RegisterManager& getRegisterManager() const { return *m_Regs; }
|
||||||
/**
|
/**
|
||||||
* Sets the register manager.
|
* Sets the register manager.
|
||||||
* @param pReg the new register manager (or a concrete derived class of
|
* @param pReg the new register manager (or a concrete derived class of \c RegisterManager)
|
||||||
* RegisterManager)
|
|
||||||
*/
|
*/
|
||||||
void setRegisterManager(RegisterManager* pReg) { m_Regs = pReg; }
|
void setRegisterManager(RegisterManager* pReg) { m_Regs = pReg; }
|
||||||
/**
|
/**
|
||||||
@ -165,74 +161,72 @@ public:
|
|||||||
* @return a reference to the memory manager
|
* @return a reference to the memory manager
|
||||||
*/
|
*/
|
||||||
MemoryManager& getMemoryManager() { return (*m_Mem); }
|
MemoryManager& getMemoryManager() { return (*m_Mem); }
|
||||||
const MemoryManager& getMemoryManager() const { return (*m_Mem); }
|
const MemoryManager& getMemoryManager() const { return *m_Mem; }
|
||||||
/**
|
/**
|
||||||
* Sets the memory manager.
|
* Sets the memory manager.
|
||||||
* @param pMem a new concrete memory manager
|
* @param pMem a new concrete memory manager
|
||||||
*/
|
*/
|
||||||
void setMemoryManager(MemoryManager* pMem) { m_Mem = pMem; }
|
void setMemoryManager(MemoryManager* pMem) { m_Mem = pMem; }
|
||||||
/* ********************************************************************
|
/* ********************************************************************
|
||||||
* Experiment-Flow & Event Management API:
|
* Experiment-Flow & Listener Management API:
|
||||||
* ********************************************************************/
|
* ********************************************************************/
|
||||||
/**
|
/**
|
||||||
* Adds the specified experiment or plugin and creates a coroutine to
|
* Adds the specified experiment or plugin and creates a coroutine to run it in.
|
||||||
* run it in.
|
|
||||||
* @param flow the experiment flow object to be added
|
* @param flow the experiment flow object to be added
|
||||||
*/
|
*/
|
||||||
void addFlow(ExperimentFlow* flow);
|
void addFlow(ExperimentFlow* flow);
|
||||||
/**
|
/**
|
||||||
* Removes the specified experiment or plugin and destroys its coroutine
|
* Removes the specified experiment or plugin and destroys its coroutine
|
||||||
* and all associated events.
|
* and all associated listeners.
|
||||||
* @param flow the experiment flow object to be removed
|
* @param flow the experiment flow object to be removed
|
||||||
*/
|
*/
|
||||||
void removeFlow(ExperimentFlow* flow);
|
void removeFlow(ExperimentFlow* flow);
|
||||||
/**
|
/**
|
||||||
* Add event ev to the event management. This causes the event to be
|
* Add listener \c li to the listener management. This causes the listener to be active.
|
||||||
* active.
|
* @param li the listener pointer to be added for the current flow
|
||||||
* @param ev the event pointer to be added for the current flow
|
* @return \c true if the listener has been added successfully, \c false otherwise
|
||||||
* @return \c true if the event has been added successfully, \c false otherwise
|
|
||||||
*/
|
*/
|
||||||
bool addEvent(BaseEvent* ev);
|
bool addListener(BaseListener* li);
|
||||||
/**
|
/**
|
||||||
* Removes the event with the specified id.
|
* Removes the listener with the specified pointer \c li.
|
||||||
* @param ev the pointer of the event-object to be removed; if \a ev is
|
* @param li the pointer of the listener-object to be removed; if \c li is
|
||||||
* equal to \c NULL all events (for all experiments) will be
|
* equal to \c NULL, all listeners (for all experiments) will be removed
|
||||||
* removed
|
|
||||||
*/
|
*/
|
||||||
void removeEvent(BaseEvent* ev) { m_EvList.remove(ev); }
|
void removeListener(BaseListener* li) { m_LstList.remove(li); }
|
||||||
/**
|
/**
|
||||||
* Removes all previously added events for all experiments. To
|
* Removes all previously added listeners for all experiments. To
|
||||||
* restrict this to a specific experiment flow, pass a pointer to it.
|
* restrict this to a specific experiment flow, pass a pointer to it.
|
||||||
|
* @param flow a ptr to a specific experiment flow whose listeners should
|
||||||
|
* be removed; if \c flow is \c NULL, all listeners will be removed
|
||||||
*/
|
*/
|
||||||
void clearEvents(ExperimentFlow *flow = 0) { m_EvList.remove(flow); }
|
void clearListeners(ExperimentFlow *flow = 0) { m_LstList.remove(flow); }
|
||||||
/**
|
/**
|
||||||
* Waits on any events which have been added to the event management. If
|
* Switches the control flow to the simulator and waits on any listeners
|
||||||
* one of those events occour, waitAny() will return the id of that event.
|
* which have been added to the listener management. If one of those listeners
|
||||||
* @return the previously occurred event, or \c NULL if there are no
|
* occurs, resume() will return the pointer of that listener.
|
||||||
* events to wait for
|
* @return the previously occurred listener, or \c NULL if there are no
|
||||||
|
* listeners to wait for
|
||||||
*/
|
*/
|
||||||
BaseEvent* waitAny();
|
BaseListener* resume();
|
||||||
/**
|
/**
|
||||||
* Add event \a ev to the global buffer and wait for it (combines
|
* Add listener \a ev to the global buffer and continues the simulation
|
||||||
* \c addEvent() and \c waitAny()).
|
* (combines \c addListener() and \c resume()).
|
||||||
* @param ev the event pointer to be added
|
* @param li the listener pointer to be added
|
||||||
* @return the pointer of the occurred event (it is not guaranteed that
|
* @return the pointer of the occurred listener (it is not guaranteed that
|
||||||
* this pointer will be equal to ev)
|
* this pointer will be equal to li)
|
||||||
*
|
|
||||||
* FIXME: Rename to make clear this returns when *any* event occurs
|
|
||||||
*/
|
*/
|
||||||
BaseEvent* addEventAndWait(BaseEvent* ev);
|
BaseListener* addListenerAndResume(BaseListener* li);
|
||||||
/**
|
/**
|
||||||
* Checks whether any experiment flow has events in the event-list.
|
* Checks whether any experiment flow has listeners in the listener-list.
|
||||||
* @return \c true if there are still events, or \c false otherwise
|
* @return \c true if there are still listeners, or \c false otherwise
|
||||||
*/
|
*/
|
||||||
bool hasEvents() const { return getEventCount() > 0; }
|
bool hasListeners() const { return getListenerCount() > 0; }
|
||||||
/**
|
/**
|
||||||
* Determines the number of (stored) events in the event-list which have
|
* Determines the number of (stored) listeners in the listener-list which have
|
||||||
* not been triggered so far.
|
* not been triggered so far.
|
||||||
* @return the actual number of events
|
* @return the actual number of listeners
|
||||||
*/
|
*/
|
||||||
unsigned getEventCount() const { return m_EvList.getEventCount(); }
|
unsigned getListenerCount() const { return m_LstList.getListenerCount(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end-of-namespace: fail
|
} // end-of-namespace: fail
|
||||||
|
|||||||
@ -101,13 +101,13 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp
|
|||||||
m_CacheEntry = cache_entry;
|
m_CacheEntry = cache_entry;
|
||||||
bool do_fire = false;
|
bool do_fire = false;
|
||||||
// Check for active breakpoint-events:
|
// Check for active breakpoint-events:
|
||||||
bp_cache_t &buffer_cache = m_EvList.getBPBuffer();
|
bp_cache_t &buffer_cache = m_LstList.getBPBuffer();
|
||||||
bp_cache_t::iterator it = buffer_cache.begin();
|
bp_cache_t::iterator it = buffer_cache.begin();
|
||||||
while (it != buffer_cache.end()) {
|
while (it != buffer_cache.end()) {
|
||||||
BPEvent* pEvBreakpt = *it;
|
BPListener* pEvBreakpt = *it;
|
||||||
if (pEvBreakpt->isMatching(instrPtr, address_space)) {
|
if (pEvBreakpt->isMatching(instrPtr, address_space)) {
|
||||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||||
it = buffer_cache.makeActive(m_EvList, it);
|
it = buffer_cache.makeActive(m_LstList, it);
|
||||||
do_fire = true;
|
do_fire = true;
|
||||||
// "it" has already been set to the next element (by calling
|
// "it" has already been set to the next element (by calling
|
||||||
// makeActive()):
|
// makeActive()):
|
||||||
@ -116,28 +116,28 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp
|
|||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
if (do_fire)
|
if (do_fire)
|
||||||
m_EvList.fireActiveEvents();
|
m_LstList.triggerActiveListeners();
|
||||||
// Note: SimulatorController::onBreakpointEvent will not be invoked in this
|
// Note: SimulatorController::onBreakpointListener will not be invoked in this
|
||||||
// implementation.
|
// implementation.
|
||||||
}
|
}
|
||||||
|
|
||||||
void BochsController::onIOPortEvent(unsigned char data, unsigned port, bool out) {
|
void BochsController::onIOPortListener(unsigned char data, unsigned port, bool out) {
|
||||||
// Check for active breakpoint-events:
|
// Check for active breakpoint-events:
|
||||||
io_cache_t &buffer_cache = m_EvList.getIOBuffer();
|
io_cache_t &buffer_cache = m_LstList.getIOBuffer();
|
||||||
io_cache_t::iterator it = buffer_cache.begin();
|
io_cache_t::iterator it = buffer_cache.begin();
|
||||||
while (it != buffer_cache.end()) {
|
while (it != buffer_cache.end()) {
|
||||||
IOPortEvent* pIOPt = (*it);
|
IOPortListener* pIOPt = (*it);
|
||||||
if (pIOPt->isMatching(port, out)) {
|
if (pIOPt->isMatching(port, out)) {
|
||||||
pIOPt->setData(data);
|
pIOPt->setData(data);
|
||||||
it = buffer_cache.makeActive(m_EvList, it);
|
it = buffer_cache.makeActive(m_LstList, it);
|
||||||
// "it" has already been set to the next element (by calling
|
// "it" has already been set to the next element (by calling
|
||||||
// makeActive()):
|
// makeActive()):
|
||||||
continue; // -> skip iterator increment
|
continue; // -> skip iterator increment
|
||||||
}
|
}
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
m_EvList.fireActiveEvents();
|
m_LstList.triggerActiveListeners();
|
||||||
// Note: SimulatorController::onBreakpointEvent will not be invoked in this
|
// Note: SimulatorController::onBreakpointListener will not be invoked in this
|
||||||
// implementation.
|
// implementation.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ void BochsController::saveDone()
|
|||||||
|
|
||||||
void BochsController::restore(const std::string& path)
|
void BochsController::restore(const std::string& path)
|
||||||
{
|
{
|
||||||
clearEvents();
|
clearListeners();
|
||||||
restore_bochs_request = true;
|
restore_bochs_request = true;
|
||||||
BX_CPU(0)->async_event |= 1;
|
BX_CPU(0)->async_event |= 1;
|
||||||
sr_path = path;
|
sr_path = path;
|
||||||
@ -181,7 +181,7 @@ void BochsController::restoreDone()
|
|||||||
|
|
||||||
void BochsController::reboot()
|
void BochsController::reboot()
|
||||||
{
|
{
|
||||||
clearEvents();
|
clearListeners();
|
||||||
reboot_bochs_request = true;
|
reboot_bochs_request = true;
|
||||||
BX_CPU(0)->async_event |= 1;
|
BX_CPU(0)->async_event |= 1;
|
||||||
m_CurrFlow = m_Flows.getCurrent();
|
m_CurrFlow = m_Flows.getCurrent();
|
||||||
@ -213,16 +213,16 @@ void BochsController::onTimerTrigger(void* thisPtr)
|
|||||||
{
|
{
|
||||||
// FIXME: The timer logic can be modified to use only one timer in Bochs.
|
// FIXME: The timer logic can be modified to use only one timer in Bochs.
|
||||||
// (For now, this suffices.)
|
// (For now, this suffices.)
|
||||||
TimerEvent* pTmEv = static_cast<TimerEvent*>(thisPtr);
|
TimerListener* pli = static_cast<TimerListener*>(thisPtr);
|
||||||
// Check for a matching TimerEvent. (In fact, we are only
|
// Check for a matching TimerListener. (In fact, we are only
|
||||||
// interessted in the iterator pointing at pTmEv.):
|
// interessted in the iterator pointing at pli.)
|
||||||
EventManager::iterator it = std::find(simulator.m_EvList.begin(),
|
ListenerManager::iterator it = std::find(simulator.m_LstList.begin(),
|
||||||
simulator.m_EvList.end(), pTmEv);
|
simulator.m_LstList.end(), pli);
|
||||||
// TODO: This has O(|m_EvList|) time complexity. We can further improve this
|
// TODO: This has O(|m_LstList|) time complexity. We can further improve this
|
||||||
// by creating a method such that makeActive(pTmEv) works as well,
|
// by creating a method such that makeActive(pli) works as well,
|
||||||
// reducing the time complexity to O(1).
|
// reducing the time complexity to O(1).
|
||||||
simulator.m_EvList.makeActive(it);
|
simulator.m_LstList.makeActive(it);
|
||||||
simulator.m_EvList.fireActiveEvents();
|
simulator.m_LstList.triggerActiveListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& BochsController::getMnemonic() const
|
const std::string& BochsController::getMnemonic() const
|
||||||
|
|||||||
@ -8,10 +8,10 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "FailBochsGlobals.hpp"
|
#include "FailBochsGlobals.hpp"
|
||||||
#include "BochsEvents.hpp"
|
#include "BochsListener.hpp"
|
||||||
|
|
||||||
#include "../SimulatorController.hpp"
|
#include "../SimulatorController.hpp"
|
||||||
#include "../Event.hpp"
|
#include "../Listener.hpp"
|
||||||
|
|
||||||
#include "bochs.h"
|
#include "bochs.h"
|
||||||
#include "cpu/cpu.h"
|
#include "cpu/cpu.h"
|
||||||
@ -40,7 +40,7 @@ public:
|
|||||||
BochsController();
|
BochsController();
|
||||||
~BochsController();
|
~BochsController();
|
||||||
/* ********************************************************************
|
/* ********************************************************************
|
||||||
* Standard Event Handler API:
|
* Standard Listener Handler API:
|
||||||
* ********************************************************************/
|
* ********************************************************************/
|
||||||
/**
|
/**
|
||||||
* Instruction pointer modification handler. This method is called (from
|
* Instruction pointer modification handler. This method is called (from
|
||||||
@ -57,20 +57,20 @@ public:
|
|||||||
* @param port the port it was transmitted on
|
* @param port the port it was transmitted on
|
||||||
* @param out true if the I/O traffic has been outbound, false otherwise
|
* @param out true if the I/O traffic has been outbound, false otherwise
|
||||||
*/
|
*/
|
||||||
void onIOPortEvent(unsigned char data, unsigned port, bool out);
|
void onIOPortListener(unsigned char data, unsigned port, bool out);
|
||||||
/**
|
/**
|
||||||
* Static internal event handler for TimerEvents. This static function is
|
* Static internal handler for TimerListeners. This static function is
|
||||||
* called when a previously registered (Bochs) timer triggers. This function
|
* called when a previously registered (Bochs) timer triggers. This function
|
||||||
* searches for the provided TimerEvent object within the EventManager and
|
* searches for the provided TimerListener object within the ListenerManager and
|
||||||
* fires such an event by calling \c fireActiveEvents().
|
* fires such an event by calling \c triggerActiveListeners().
|
||||||
* @param thisPtr a pointer to the TimerEvent-object triggered
|
* @param thisPtr a pointer to the TimerListener-object triggered
|
||||||
*
|
*
|
||||||
* FIXME: Due to Bochs internal timer and ips-configuration related stuff,
|
* FIXME: Due to Bochs internal timer and ips-configuration related stuff,
|
||||||
* the simulator sometimes panics with "keyboard error:21" (see line
|
* the simulator sometimes panics with "keyboard error:21" (see line
|
||||||
* 1777 in bios/rombios.c, function keyboard_init()) if a TimerEvent
|
* 1777 in bios/rombios.c, function keyboard_init()) if a TimerListener
|
||||||
* is added *before* the bios has been loaded and initialized. To
|
* is added *before* the bios has been loaded and initialized. To
|
||||||
* reproduce this error, try adding a \c TimerEvent as the initial step
|
* reproduce this error, try adding a \c TimerListener as the initial step
|
||||||
* in your experiment code and wait for it (\c addEventAndWait()).
|
* in your experiment code and wait for it (\c addListenerAndResume()).
|
||||||
*/
|
*/
|
||||||
static void onTimerTrigger(void *thisPtr);
|
static void onTimerTrigger(void *thisPtr);
|
||||||
/* ********************************************************************
|
/* ********************************************************************
|
||||||
@ -86,7 +86,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void saveDone();
|
void saveDone();
|
||||||
/**
|
/**
|
||||||
* Restore simulator state. Clears all Events.
|
* Restore simulator state. Clears all Listeners.
|
||||||
* @param path Location to previously saved state information
|
* @param path Location to previously saved state information
|
||||||
*/
|
*/
|
||||||
void restore(const std::string& path);
|
void restore(const std::string& path);
|
||||||
@ -95,7 +95,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void restoreDone();
|
void restoreDone();
|
||||||
/**
|
/**
|
||||||
* Reboot simulator. Clears all Events.
|
* Reboot simulator. Clears all Listeners.
|
||||||
*/
|
*/
|
||||||
void reboot();
|
void reboot();
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,34 +1,34 @@
|
|||||||
#include "BochsEvents.hpp"
|
#include "BochsListener.hpp"
|
||||||
#include "../SALInst.hpp"
|
#include "../SALInst.hpp"
|
||||||
|
|
||||||
namespace fail {
|
namespace fail {
|
||||||
|
|
||||||
bool TimerEvent::onEventAddition()
|
bool TimerListener::onAddition()
|
||||||
{
|
{
|
||||||
// Register the timer event in the Bochs simulator:
|
// Register the timer listener in the Bochs simulator:
|
||||||
setId(m_registerTimer(this));
|
setId(m_registerTimer(this));
|
||||||
if(getId() == -1)
|
if(getId() == -1)
|
||||||
return false; // unable to register the timer (error in Bochs' function call)
|
return false; // unable to register the timer (error in Bochs' function call)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimerEvent::onEventDeletion()
|
void TimerListener::onDeletion()
|
||||||
{
|
{
|
||||||
// Unregister the time event:
|
// Unregister the time listener:
|
||||||
m_unregisterTimer(this);
|
m_unregisterTimer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
timer_id_t TimerEvent::m_registerTimer(TimerEvent* pev)
|
timer_id_t TimerListener::m_registerTimer(TimerListener* pev)
|
||||||
{
|
{
|
||||||
assert(pev != NULL && "FATAL ERROR: TimerEvent object ptr cannot be NULL!");
|
assert(pev != NULL && "FATAL ERROR: TimerListener object ptr cannot be NULL!");
|
||||||
return static_cast<timer_id_t>(
|
return static_cast<timer_id_t>(
|
||||||
bx_pc_system.register_timer(pev, BochsController::onTimerTrigger, pev->getTimeout(),
|
bx_pc_system.register_timer(pev, BochsController::onTimerTrigger, pev->getTimeout(),
|
||||||
false, 1/*start immediately*/, "Fail*: BochsController"/*name*/));
|
false, 1/*start immediately*/, "Fail*: BochsController"/*name*/));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TimerEvent::m_unregisterTimer(TimerEvent* pev)
|
bool TimerListener::m_unregisterTimer(TimerListener* pev)
|
||||||
{
|
{
|
||||||
assert(pev != NULL && "FATAL ERROR: TimerEvent object ptr cannot be NULL!");
|
assert(pev != NULL && "FATAL ERROR: TimerListener object ptr cannot be NULL!");
|
||||||
bx_pc_system.deactivate_timer(static_cast<unsigned>(pev->getId()));
|
bx_pc_system.deactivate_timer(static_cast<unsigned>(pev->getId()));
|
||||||
return bx_pc_system.unregisterTimer(static_cast<unsigned>(pev->getId()));
|
return bx_pc_system.unregisterTimer(static_cast<unsigned>(pev->getId()));
|
||||||
}
|
}
|
||||||
@ -1,74 +1,76 @@
|
|||||||
#ifndef __BOCHS_EVENTS_HPP__
|
#ifndef __BOCHS_EVENTS_HPP__
|
||||||
#define __BOCHS_EVENTS_HPP__
|
#define __BOCHS_EVENTS_HPP__
|
||||||
|
|
||||||
#include "../Event.hpp"
|
#include "../Listener.hpp"
|
||||||
|
|
||||||
#include "BochsController.hpp"
|
#include "BochsController.hpp"
|
||||||
|
|
||||||
namespace fail {
|
namespace fail {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class TimerEvent
|
* \class TimerListener
|
||||||
* Concrete TimerEvent implementation of GenericTimerEvent for the Bochs
|
* Concrete TimerListener implementation of GenericTimerListener for the Bochs
|
||||||
* simulator backend.
|
* simulator backend.
|
||||||
*/
|
*/
|
||||||
class TimerEvent : public GenericTimerEvent {
|
class TimerListener : public GenericTimerListener {
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Registers a timer in the Bochs simulator. This timer fires \a TimerEvents
|
* Registers a timer in the Bochs simulator. This timer triggers \a TimerListeners
|
||||||
* to inform the corresponding experiment-flow. Note that the number of timers
|
* to inform the corresponding experiment-flow. Note that the number of timers
|
||||||
* (in Bochs) is limited to \c BX_MAX_TIMERS (defaults to 64 in v2.4.6).
|
* (in Bochs) is limited to \c BX_MAX_TIMERS (defaults to 64 in v2.4.6).
|
||||||
* @param pev a pointer to the (experiment flow-) allocated TimerEvent object,
|
* @param pli a pointer to the (experiment flow-) allocated TimerListener object,
|
||||||
* providing all required information to start the time, e.g. the
|
* providing all required information to start the timer (e.g. the
|
||||||
* timeout value.
|
* timeout value).
|
||||||
* @return \c The unique id of the timer recently created. This id is carried
|
* @return The unique id of the timer recently created. This id is carried
|
||||||
* along with the TimerEvent, @see getId(). On errors, -1 is returned
|
* along with the TimerListener. On errors, -1 is returned (e.g. because
|
||||||
* (e.g. because a timer with the same id is already existing)
|
* a timer with the same id is already existing)
|
||||||
|
* @see TimerListener::getId()
|
||||||
*/
|
*/
|
||||||
static timer_id_t m_registerTimer(TimerEvent* pev);
|
static timer_id_t m_registerTimer(TimerListener* pli);
|
||||||
/**
|
/**
|
||||||
* Deletes a timer. No further events will be triggered by the timer.
|
* Deletes a timer. No further events will be triggered by the timer.
|
||||||
* @param pev a pointer to the TimerEvent-object to be removed
|
* @param pli a pointer to the TimerListener-object to be removed
|
||||||
* @return \c true if the timer with \a pev->getId() has been removed
|
* @return \c true if the timer with \a pli->getId() has been removed
|
||||||
* successfully, \c false otherwise
|
* successfully, \c false otherwise
|
||||||
*/
|
*/
|
||||||
static bool m_unregisterTimer(TimerEvent* pev);
|
static bool m_unregisterTimer(TimerListener* pli);
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Creates a new timer event. This can be used to implement a timeout-
|
* Creates a new timer event. This can be used to implement a timeout-
|
||||||
* mechanism in the experiment-flow. The timer starts automatically when
|
* mechanism in the experiment-flow. The timer starts automatically when
|
||||||
* added to FailBochs.
|
* added to FailBochs.
|
||||||
* @param timeout the time intervall in milliseconds (ms)
|
* @param timeout the time intervall in milliseconds (ms)
|
||||||
* @see SimulatorController::addEvent
|
* @see SimulatorController::addListener
|
||||||
*/
|
*/
|
||||||
TimerEvent(unsigned timeout)
|
TimerListener(unsigned timeout)
|
||||||
: GenericTimerEvent(timeout) { }
|
: GenericTimerListener(timeout) { }
|
||||||
~TimerEvent() { onEventDeletion(); }
|
~TimerListener() { onDeletion(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called when an experiment flow adds a new event by
|
* This method is called when an experiment flow adds a new event by
|
||||||
* calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev).
|
* calling \c simulator.addListener() or \c simulator.addListenerAndResume().
|
||||||
* More specifically, the event will be added to the event-list first
|
* More specifically, the event will be added to the event-list first
|
||||||
* (buffer-list, to be precise) and then this event handler is called.
|
* (buffer-list, to be precise) and then this event handler is called.
|
||||||
* @return You should return \c true to continue and \c false to prevent
|
* @return You should return \c true to continue and \c false to prevent
|
||||||
* the addition of the event \a pev, yielding an error in the
|
* the addition of the event \a pev, yielding an error in the
|
||||||
* experiment flow (i.e. -1 is returned).
|
* experiment flow (i.e. -1 is returned).
|
||||||
*/
|
*/
|
||||||
bool onEventAddition();
|
bool onAddition();
|
||||||
/**
|
/**
|
||||||
* This method is called when an experiment flow removes an event from
|
* This method is called when an experiment flow removes an event from
|
||||||
* the event-management by calling \c removeEvent(prev), \c clearEvents()
|
* the event-management by calling \c removeListener(prev), \c clearListeners()
|
||||||
* or by deleting a complete flow (\c removeFlow). More specifically, this
|
* or by deleting a complete flow (\c removeFlow). More specifically, this
|
||||||
* event handler will be called *before* the event is actually deleted.
|
* event handler will be called *before* the event is actually deleted.
|
||||||
*/
|
*/
|
||||||
void onEventDeletion();
|
void onDeletion();
|
||||||
/**
|
/**
|
||||||
* This method is called when an previously added event is about to be
|
* This method is called when an previously added event is about to be
|
||||||
* triggered by the simulator-backend. More specifically, this event handler
|
* triggered by the simulator-backend. More specifically, this event handler
|
||||||
* will be called *before* the event is actually triggered, i.e. before the
|
* will be called *before* the event is actually triggered, i.e. before the
|
||||||
* corresponding coroutine is toggled.
|
* corresponding coroutine is toggled.
|
||||||
*/
|
*/
|
||||||
void onEventTrigger() { onEventDeletion(); }
|
void onTrigger() { onDeletion(); }
|
||||||
|
// TODO/FIXME: bei neuer impl. anpassen
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end-of-namespace: fail
|
} // end-of-namespace: fail
|
||||||
@ -32,7 +32,7 @@ public:
|
|||||||
* Retrieves the data of the register.
|
* Retrieves the data of the register.
|
||||||
* @return the current register data
|
* @return the current register data
|
||||||
*/
|
*/
|
||||||
regdata_t getData() { return (*m_pData); }
|
regdata_t getData() { return *m_pData; }
|
||||||
/**
|
/**
|
||||||
* Sets the content of the register.
|
* Sets the content of the register.
|
||||||
* @param data the new register data to be written
|
* @param data the new register data to be written
|
||||||
@ -136,44 +136,42 @@ public:
|
|||||||
* Returns \c true if the corresponding flag is set, or \c false
|
* Returns \c true if the corresponding flag is set, or \c false
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
bool getCarryFlag() const { return (BX_CPU(0)->get_CF()); }
|
bool getCarryFlag() const { return BX_CPU(0)->get_CF(); }
|
||||||
bool getParityFlag() const { return (BX_CPU(0)->get_PF()); }
|
bool getParityFlag() const { return BX_CPU(0)->get_PF(); }
|
||||||
bool getZeroFlag() const { return (BX_CPU(0)->get_ZF()); }
|
bool getZeroFlag() const { return BX_CPU(0)->get_ZF(); }
|
||||||
bool getSignFlag() const { return (BX_CPU(0)->get_SF()); }
|
bool getSignFlag() const { return BX_CPU(0)->get_SF(); }
|
||||||
bool getOverflowFlag() const { return (BX_CPU(0)->get_OF()); }
|
bool getOverflowFlag() const { return BX_CPU(0)->get_OF(); }
|
||||||
|
bool getTrapFlag() const { return BX_CPU(0)->get_TF(); }
|
||||||
bool getTrapFlag() const { return (BX_CPU(0)->get_TF()); }
|
bool getInterruptFlag() const { return BX_CPU(0)->get_IF(); }
|
||||||
bool getInterruptFlag() const { return (BX_CPU(0)->get_IF()); }
|
bool getDirectionFlag() const { return BX_CPU(0)->get_DF(); }
|
||||||
bool getDirectionFlag() const { return (BX_CPU(0)->get_DF()); }
|
unsigned getIOPrivilegeLevel() const { return BX_CPU(0)->get_IOPL(); }
|
||||||
unsigned getIOPrivilegeLevel() const { return (BX_CPU(0)->get_IOPL()); }
|
bool getNestedTaskFlag() const { return BX_CPU(0)->get_NT(); }
|
||||||
bool getNestedTaskFlag() const { return (BX_CPU(0)->get_NT()); }
|
bool getResumeFlag() const { return BX_CPU(0)->get_RF(); }
|
||||||
bool getResumeFlag() const { return (BX_CPU(0)->get_RF()); }
|
bool getVMFlag() const { return BX_CPU(0)->get_VM(); }
|
||||||
bool getVMFlag() const { return (BX_CPU(0)->get_VM()); }
|
bool getAlignmentCheckFlag() const { return BX_CPU(0)->get_AC(); }
|
||||||
bool getAlignmentCheckFlag() const { return (BX_CPU(0)->get_AC()); }
|
bool getVInterruptFlag() const { return BX_CPU(0)->get_VIF(); }
|
||||||
bool getVInterruptFlag() const { return (BX_CPU(0)->get_VIF()); }
|
bool getVInterruptPendingFlag() const { return BX_CPU(0)->get_VIP(); }
|
||||||
bool getVInterruptPendingFlag() const { return (BX_CPU(0)->get_VIP()); }
|
bool getIdentificationFlag() const { return BX_CPU(0)->get_ID(); }
|
||||||
bool getIdentificationFlag() const { return (BX_CPU(0)->get_ID()); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets/resets various status FLAGS.
|
* Sets/resets various status FLAGS.
|
||||||
*/
|
*/
|
||||||
void setCarryFlag(bool bit) { BX_CPU(0)->set_CF(bit); }
|
void setCarryFlag(bool bit) { BX_CPU(0)->set_CF(bit); }
|
||||||
void setParityFlag(bool bit) { BX_CPU(0)->set_PF(bit); }
|
void setParityFlag(bool bit) { BX_CPU(0)->set_PF(bit); }
|
||||||
void setZeroFlag(bool bit) { BX_CPU(0)->set_ZF(bit); }
|
void setZeroFlag(bool bit) { BX_CPU(0)->set_ZF(bit); }
|
||||||
void setSignFlag(bool bit) { BX_CPU(0)->set_SF(bit); }
|
void setSignFlag(bool bit) { BX_CPU(0)->set_SF(bit); }
|
||||||
void setOverflowFlag(bool bit) { BX_CPU(0)->set_OF(bit); }
|
void setOverflowFlag(bool bit) { BX_CPU(0)->set_OF(bit); }
|
||||||
|
void setTrapFlag(bool bit) { BX_CPU(0)->set_TF(bit); }
|
||||||
void setTrapFlag(bool bit) { BX_CPU(0)->set_TF(bit); }
|
void setInterruptFlag(bool bit) { BX_CPU(0)->set_IF(bit); }
|
||||||
void setInterruptFlag(bool bit) { BX_CPU(0)->set_IF(bit); }
|
void setDirectionFlag(bool bit) { BX_CPU(0)->set_DF(bit); }
|
||||||
void setDirectionFlag(bool bit) { BX_CPU(0)->set_DF(bit); }
|
void setIOPrivilegeLevel(unsigned lvl) { BX_CPU(0)->set_IOPL(lvl); }
|
||||||
void setIOPrivilegeLevel(unsigned lvl) { BX_CPU(0)->set_IOPL(lvl); }
|
void setNestedTaskFlag(bool bit) { BX_CPU(0)->set_NT(bit); }
|
||||||
void setNestedTaskFlag(bool bit) { BX_CPU(0)->set_NT(bit); }
|
void setResumeFlag(bool bit) { BX_CPU(0)->set_RF(bit); }
|
||||||
void setResumeFlag(bool bit) { BX_CPU(0)->set_RF(bit); }
|
void setVMFlag(bool bit) { BX_CPU(0)->set_VM(bit); }
|
||||||
void setVMFlag(bool bit) { BX_CPU(0)->set_VM(bit); }
|
void setAlignmentCheckFlag(bool bit) { BX_CPU(0)->set_AC(bit); }
|
||||||
void setAlignmentCheckFlag(bool bit) { BX_CPU(0)->set_AC(bit); }
|
void setVInterruptFlag(bool bit) { BX_CPU(0)->set_VIF(bit); }
|
||||||
void setVInterruptFlag(bool bit) { BX_CPU(0)->set_VIF(bit); }
|
void setVInterruptPendingFlag(bool bit) { BX_CPU(0)->set_VIP(bit); }
|
||||||
void setVInterruptPendingFlag(bool bit) { BX_CPU(0)->set_VIP(bit); }
|
void setIdentificationFlag(bool bit) { BX_CPU(0)->set_ID(bit); }
|
||||||
void setIdentificationFlag(bool bit) { BX_CPU(0)->set_ID(bit); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the content of the status register.
|
* Sets the content of the status register.
|
||||||
@ -184,8 +182,7 @@ public:
|
|||||||
{
|
{
|
||||||
#ifdef BX_SUPPORT_X86_64
|
#ifdef BX_SUPPORT_X86_64
|
||||||
// We are in 64 bit mode: Just assign the lower 32 bits!
|
// We are in 64 bit mode: Just assign the lower 32 bits!
|
||||||
(*m_pData) = ((*m_pData) & 0xFFFFFFFF00000000ULL) |
|
(*m_pData) = ((*m_pData) & 0xFFFFFFFF00000000ULL) | (data & 0xFFFFFFFFULL);
|
||||||
(data & 0xFFFFFFFFULL);
|
|
||||||
#else
|
#else
|
||||||
*m_pData = data;
|
*m_pData = data;
|
||||||
#endif
|
#endif
|
||||||
@ -204,7 +201,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
address_t getInstructionPointer()
|
address_t getInstructionPointer()
|
||||||
{
|
{
|
||||||
return (static_cast<address_t>(getSetOfType(RT_PC)->first()->getData()));
|
return static_cast<address_t>(getSetOfType(RT_PC)->first()->getData());
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Retruns the top address of the stack.
|
* Retruns the top address of the stack.
|
||||||
@ -213,9 +210,9 @@ public:
|
|||||||
address_t getStackPointer()
|
address_t getStackPointer()
|
||||||
{
|
{
|
||||||
#if BX_SUPPORT_X86_64
|
#if BX_SUPPORT_X86_64
|
||||||
return (static_cast<address_t>(getRegister(RID_RSP)->getData()));
|
return static_cast<address_t>(getRegister(RID_RSP)->getData());
|
||||||
#else
|
#else
|
||||||
return (static_cast<address_t>(getRegister(RID_ESP)->getData()));
|
return static_cast<address_t>(getRegister(RID_ESP)->getData());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -226,9 +223,9 @@ public:
|
|||||||
address_t getBasePointer()
|
address_t getBasePointer()
|
||||||
{
|
{
|
||||||
#if BX_SUPPORT_X86_64
|
#if BX_SUPPORT_X86_64
|
||||||
return (static_cast<address_t>(getRegister(RID_RBP)->getData()));
|
return static_cast<address_t>(getRegister(RID_RBP)->getData());
|
||||||
#else
|
#else
|
||||||
return (static_cast<address_t>(getRegister(RID_EBP)->getData()));
|
return static_cast<address_t>(getRegister(RID_EBP)->getData());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
aspect Breakpoints {
|
aspect Breakpoints {
|
||||||
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
||||||
|
|
||||||
advice execution (cpuLoop()) : after () // Event source: "instruction pointer"
|
advice execution (cpuLoop()) : after () // event source: "instruction pointer"
|
||||||
{
|
{
|
||||||
// Points to the cpu class: "this" if BX_USE_CPU_SMF == 0,
|
// Points to the cpu class: "this" if BX_USE_CPU_SMF == 0,
|
||||||
// BX_CPU(0) otherwise
|
// BX_CPU(0) otherwise
|
||||||
|
|||||||
@ -19,12 +19,12 @@
|
|||||||
aspect GuestSysCom {
|
aspect GuestSysCom {
|
||||||
pointcut outInstructions() = "% ...::bx_cpu_c::OUT_DX%(...)";
|
pointcut outInstructions() = "% ...::bx_cpu_c::OUT_DX%(...)";
|
||||||
|
|
||||||
advice execution (outInstructions()) : after () // Event source: "guest system"
|
advice execution (outInstructions()) : after () // Listener source: "guest system"
|
||||||
{
|
{
|
||||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||||
unsigned rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
unsigned rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||||
if (rDX == BOCHS_COM_PORT)
|
if (rDX == BOCHS_COM_PORT)
|
||||||
fail::simulator.onGuestSystemEvent((char)rAL, rDX);
|
fail::simulator.onGuestSystemListener((char)rAL, rDX);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ aspect IOPortCom {
|
|||||||
{
|
{
|
||||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||||
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||||
fail::simulator.onIOPortEvent(rAL, rDX, true);
|
fail::simulator.onIOPortListener(rAL, rDX, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX(...)";
|
pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX(...)";
|
||||||
@ -30,7 +30,7 @@ aspect IOPortCom {
|
|||||||
{
|
{
|
||||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||||
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||||
fail::simulator.onIOPortEvent(rAL, rDX, false);
|
fail::simulator.onIOPortListener(rAL, rDX, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -28,9 +28,9 @@ aspect Interrupt {
|
|||||||
unsigned vector = *(tjp->arg<0>());
|
unsigned vector = *(tjp->arg<0>());
|
||||||
unsigned type = *(tjp->arg<1>());
|
unsigned type = *(tjp->arg<1>());
|
||||||
if (type == BX_EXTERNAL_INTERRUPT)
|
if (type == BX_EXTERNAL_INTERRUPT)
|
||||||
fail::simulator.onInterruptEvent(vector, false);
|
fail::simulator.onInterruptListener(vector, false);
|
||||||
else if (type == BX_NMI)
|
else if (type == BX_NMI)
|
||||||
fail::simulator.onInterruptEvent(vector, true);
|
fail::simulator.onInterruptListener(vector, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -60,7 +60,7 @@ aspect Jump {
|
|||||||
advice execution (defJumpInstructions()) : around()
|
advice execution (defJumpInstructions()) : around()
|
||||||
{
|
{
|
||||||
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
|
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
|
||||||
fail::simulator.onJumpEvent(true, pInstr->getIaOpcode());
|
fail::simulator.onJumpListener(true, pInstr->getIaOpcode());
|
||||||
/*
|
/*
|
||||||
JoinPoint::That* pThis = tjp->that();
|
JoinPoint::That* pThis = tjp->that();
|
||||||
if(pThis == NULL)
|
if(pThis == NULL)
|
||||||
@ -77,7 +77,7 @@ aspect Jump {
|
|||||||
// Step-1: Modify one or more of the fxxxFlag according to the error you want to inject
|
// Step-1: Modify one or more of the fxxxFlag according to the error you want to inject
|
||||||
// (using pThis->set_XX(new_val))
|
// (using pThis->set_XX(new_val))
|
||||||
// Step-2: Call tjp->proceed();
|
// Step-2: Call tjp->proceed();
|
||||||
// Step-3: Eventually, unwind the changes of Step-1
|
// Step-3: Listenerually, unwind the changes of Step-1
|
||||||
//
|
//
|
||||||
|
|
||||||
// Example:
|
// Example:
|
||||||
@ -109,7 +109,7 @@ aspect Jump {
|
|||||||
advice execution (regJumpInstructions()) : around ()
|
advice execution (regJumpInstructions()) : around ()
|
||||||
{
|
{
|
||||||
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
|
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
|
||||||
fail::simulator.onJumpEvent(false, pInstr->getIaOpcode());
|
fail::simulator.onJumpListener(false, pInstr->getIaOpcode());
|
||||||
/*
|
/*
|
||||||
JoinPoint::That* pThis = tjp->that();
|
JoinPoint::That* pThis = tjp->that();
|
||||||
|
|
||||||
|
|||||||
@ -56,19 +56,19 @@ aspect MemAccess {
|
|||||||
// Fire a memory-write-event each time the guest system requests
|
// Fire a memory-write-event each time the guest system requests
|
||||||
// to write data to RAM:
|
// to write data to RAM:
|
||||||
//
|
//
|
||||||
// Event source: "memory write access"
|
// Listener source: "memory write access"
|
||||||
//
|
//
|
||||||
#ifdef CONFIG_EVENT_MEMWRITE
|
#ifdef CONFIG_EVENT_MEMWRITE
|
||||||
advice execution (write_methods()) : after ()
|
advice execution (write_methods()) : after ()
|
||||||
{
|
{
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
*(tjp->arg<1>()), sizeof(*(tjp->arg<2>())), true,
|
*(tjp->arg<1>()), sizeof(*(tjp->arg<2>())), true,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
}
|
}
|
||||||
|
|
||||||
advice execution (write_methods_RMW()) : after ()
|
advice execution (write_methods_RMW()) : after ()
|
||||||
{
|
{
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
rmw_address, sizeof(*(tjp->arg<0>())), true,
|
rmw_address, sizeof(*(tjp->arg<0>())), true,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ aspect MemAccess {
|
|||||||
{
|
{
|
||||||
std::cerr << "WOOOOOT write_methods_new_stack" << std::endl;
|
std::cerr << "WOOOOOT write_methods_new_stack" << std::endl;
|
||||||
// TODO: Log-level?
|
// TODO: Log-level?
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
*(tjp->arg<1>()), sizeof(*(tjp->arg<3>())), true,
|
*(tjp->arg<1>()), sizeof(*(tjp->arg<3>())), true,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ aspect MemAccess {
|
|||||||
{
|
{
|
||||||
std::cerr << "WOOOOOT write_methods_new_stack_64" << std::endl;
|
std::cerr << "WOOOOOT write_methods_new_stack_64" << std::endl;
|
||||||
// TODO: Log-level?
|
// TODO: Log-level?
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
*(tjp->arg<0>()), sizeof(*(tjp->arg<2>())), true,
|
*(tjp->arg<0>()), sizeof(*(tjp->arg<2>())), true,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ aspect MemAccess {
|
|||||||
// memory (e.g., to read vectors from the interrupt vector
|
// memory (e.g., to read vectors from the interrupt vector
|
||||||
// table).
|
// table).
|
||||||
/*
|
/*
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
*(tjp->arg<0>()), sizeof(*(tjp->arg<1>())), true,
|
*(tjp->arg<0>()), sizeof(*(tjp->arg<1>())), true,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
*/
|
*/
|
||||||
@ -109,19 +109,19 @@ aspect MemAccess {
|
|||||||
// Fire a memory-read-event each time the guest system requests
|
// Fire a memory-read-event each time the guest system requests
|
||||||
// to read data in RAM:
|
// to read data in RAM:
|
||||||
//
|
//
|
||||||
// Event source: "memory read access"
|
// Listener source: "memory read access"
|
||||||
//
|
//
|
||||||
#ifdef CONFIG_EVENT_MEMREAD
|
#ifdef CONFIG_EVENT_MEMREAD
|
||||||
advice execution (read_methods()) : before ()
|
advice execution (read_methods()) : before ()
|
||||||
{
|
{
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
*(tjp->arg<1>()), sizeof(*(tjp->result())), false,
|
*(tjp->arg<1>()), sizeof(*(tjp->result())), false,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
}
|
}
|
||||||
|
|
||||||
advice execution (read_methods_dqword()) : before ()
|
advice execution (read_methods_dqword()) : before ()
|
||||||
{
|
{
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
*(tjp->arg<1>()), 16, false,
|
*(tjp->arg<1>()), 16, false,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
}
|
}
|
||||||
@ -131,7 +131,7 @@ aspect MemAccess {
|
|||||||
{
|
{
|
||||||
rmw_address = *(tjp->arg<1>());
|
rmw_address = *(tjp->arg<1>());
|
||||||
#ifdef CONFIG_EVENT_MEMREAD
|
#ifdef CONFIG_EVENT_MEMREAD
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
*(tjp->arg<1>()), sizeof(*(tjp->result())), false,
|
*(tjp->arg<1>()), sizeof(*(tjp->result())), false,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
#endif
|
#endif
|
||||||
@ -145,7 +145,7 @@ aspect MemAccess {
|
|||||||
// memory (e.g., to read vectors from the interrupt vector
|
// memory (e.g., to read vectors from the interrupt vector
|
||||||
// table).
|
// table).
|
||||||
/*
|
/*
|
||||||
fail::simulator.onMemoryAccessEvent(
|
fail::simulator.onMemoryAccessListener(
|
||||||
*(tjp->arg<0>()), sizeof(*(tjp->result())), false,
|
*(tjp->arg<0>()), sizeof(*(tjp->result())), false,
|
||||||
getCPU(tjp->that())->prev_rip);
|
getCPU(tjp->that())->prev_rip);
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -15,9 +15,9 @@ aspect Trap {
|
|||||||
|
|
||||||
advice execution (exception_method()) : before ()
|
advice execution (exception_method()) : before ()
|
||||||
{
|
{
|
||||||
fail::simulator.onTrapEvent(*(tjp->arg<0>()));
|
fail::simulator.onTrapListener(*(tjp->arg<0>()));
|
||||||
// TODO: There are some different types of exceptions at cpu.h (line 265-281)
|
// TODO: There are some different types of exceptions at cpu.h (line 265-281)
|
||||||
// Which kind of a trap are these types?
|
// Which kind of traps are these types?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -84,12 +84,12 @@ void OVPController::onInstrPtrChanged(address_t instrPtr)
|
|||||||
// cerr << "instrPtr: 0x" << hex << instrPtr << " SP: 0x" << hex << m_Regs->getStackPointer() \
|
// cerr << "instrPtr: 0x" << hex << instrPtr << " SP: 0x" << hex << m_Regs->getStackPointer() \
|
||||||
// << " R0: 0x" << hex << r0 << " ST: 0x" << hex << st << endl;
|
// << " R0: 0x" << hex << r0 << " ST: 0x" << hex << st << endl;
|
||||||
|
|
||||||
// Check for active breakpoint-events:
|
// Check for active breakpoint-Listeners:
|
||||||
EventManager::iterator it = m_EvList.begin();
|
ListenerManager::iterator it = m_EvList.begin();
|
||||||
while (it != m_EvList.end()) {
|
while (it != m_EvList.end()) {
|
||||||
// FIXME: Performance verbessern (dazu muss entsprechend auch die Speicherung
|
// FIXME: Performance verbessern (dazu muss entsprechend auch die Speicherung
|
||||||
// in EventManager(.cc|.hpp) angepasst bzw. verbessert werden).
|
// in ListenerManager(.cc|.hpp) angepasst bzw. verbessert werden).
|
||||||
BPSingleEvent* pEvBreakpt = dynamic_cast<BPSingleEvent*>(*it);
|
BPSingleListener* pEvBreakpt = dynamic_cast<BPSingleListener*>(*it);
|
||||||
if (pEvBreakpt && (instrPtr == pEvBreakpt->getWatchInstructionPointer() ||
|
if (pEvBreakpt && (instrPtr == pEvBreakpt->getWatchInstructionPointer() ||
|
||||||
pEvBreakpt->getWatchInstructionPointer() == ANY_ADDR)) {
|
pEvBreakpt->getWatchInstructionPointer() == ANY_ADDR)) {
|
||||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||||
@ -98,7 +98,7 @@ void OVPController::onInstrPtrChanged(address_t instrPtr)
|
|||||||
// makeActive()):
|
// makeActive()):
|
||||||
continue; // -> skip iterator increment
|
continue; // -> skip iterator increment
|
||||||
}
|
}
|
||||||
BPRangeEvent* pEvRange = dynamic_cast<BPRangeEvent*>(*it);
|
BPRangeListener* pEvRange = dynamic_cast<BPRangeListener*>(*it);
|
||||||
if (pEvRange && pEvRange->isMatching(instrPtr)) {
|
if (pEvRange && pEvRange->isMatching(instrPtr)) {
|
||||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||||
it = m_EvList.makeActive(it);
|
it = m_EvList.makeActive(it);
|
||||||
@ -106,7 +106,7 @@ void OVPController::onInstrPtrChanged(address_t instrPtr)
|
|||||||
}
|
}
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
m_EvList.fireActiveEvents();
|
m_EvList.fireActiveListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OVPController::save(const string& path)
|
void OVPController::save(const string& path)
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "../SimulatorController.hpp"
|
#include "../SimulatorController.hpp"
|
||||||
#include "../Event.hpp"
|
#include "../Listener.hpp"
|
||||||
#include "../Register.hpp"
|
#include "../Register.hpp"
|
||||||
#include "ovp/OVPPlatform.hpp"
|
#include "ovp/OVPPlatform.hpp"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user