Aspect-based implementation of fast breakpoints added (optional).

git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1685 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
adrian
2012-09-25 10:10:02 +00:00
parent 0783931bf6
commit 0cb6b39490
17 changed files with 725 additions and 107 deletions

View File

@ -6,6 +6,7 @@ if(BUILD_BOCHS)
SALConfig.cc SALConfig.cc
Register.cc Register.cc
SimulatorController.cc SimulatorController.cc
perf/BreakpointBuffer.cc
bochs/BochsController.cc bochs/BochsController.cc
bochs/BochsListener.cc bochs/BochsListener.cc
) )
@ -29,6 +30,7 @@ elseif(BUILD_OVP)
SALConfig.cc SALConfig.cc
Register.cc Register.cc
SimulatorController.cc SimulatorController.cc
perf/BreakpointBuffer.cc
${VARIANT}/OVPController.cc ${VARIANT}/OVPController.cc
) )
elseif(BUILD_QEMU) elseif(BUILD_QEMU)
@ -38,6 +40,7 @@ elseif(BUILD_QEMU)
ListenerManager.cc ListenerManager.cc
SALConfig.cc SALConfig.cc
Register.cc Register.cc
perf/BreakpointBuffer.cc
SimulatorController.cc SimulatorController.cc
qemu/QEMUController.cc qemu/QEMUController.cc
qemu/QEMUListener.cc qemu/QEMUListener.cc

View File

@ -201,8 +201,10 @@ public:
/** /**
* Creates a new \c InterruptEvent. * Creates a new \c InterruptEvent.
* @param nmi the new NMI (non maskable interrupt) flag state * @param nmi the new NMI (non maskable interrupt) flag state
* @param triggerNum system and type specific number identifying the requestet
* "trouble-type"
*/ */
InterruptEvent(bool nmi) : m_IsNMI(nmi) { } InterruptEvent(bool nmi, int triggerNum) : TroubleEvent(triggerNum), m_IsNMI(nmi) { }
/** /**
* Returns \c true if the interrupt is non maskable, \c false otherwise. * Returns \c true if the interrupt is non maskable, \c false otherwise.
* @return \c true if NMI flag is set, \c false otherwise * @return \c true if NMI flag is set, \c false otherwise

View File

@ -3,10 +3,10 @@
namespace fail { namespace fail {
bool TroubleListener::isMatching(unsigned troubleNum) const bool TroubleListener::isMatching(const TroubleEvent* pEv) 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] == pEv->getTriggerNumber() ||
m_WatchNumbers[i] == ANY_TRAP) m_WatchNumbers[i] == ANY_TRAP)
return true; return true;
} }
@ -34,13 +34,13 @@ bool TroubleListener::addWatchNumber(unsigned troubleNumber)
return true; return true;
} }
bool MemAccessListener::isMatching(address_t addr, size_t width, bool MemAccessListener::isMatching(const MemAccessEvent* pEv) const
MemAccessEvent::access_type_t accesstype) const
{ {
if (!(m_WatchType & accesstype)) { if (!(m_WatchType & pEv->getTriggerAccessType())) {
return false; return false;
} else if (m_WatchAddr != ANY_ADDR } else if (m_WatchAddr != ANY_ADDR
&& (m_WatchAddr >= addr + width || m_WatchAddr + m_WatchWidth <= addr)) { && (m_WatchAddr >= pEv->getTriggerAddress() + pEv->getTriggerWidth()
|| m_WatchAddr + m_WatchWidth <= pEv->getTriggerAddress())) {
return false; return false;
} }
return true; return true;
@ -59,20 +59,20 @@ void BPRangeListener::setWatchInstructionPointerRange(address_t start, address_t
m_WatchEndAddr = end; m_WatchEndAddr = end;
} }
bool BPRangeListener::isMatching(address_t addr, address_t aspace) const bool BPRangeListener::isMatching(const BPEvent* pEv) const
{ {
if (!aspaceIsMatching(aspace)) if (!aspaceIsMatching(pEv->getCR3()))
return false; return false;
if ((m_WatchStartAddr != ANY_ADDR && addr < m_WatchStartAddr) || if ((m_WatchStartAddr != ANY_ADDR && pEv->getTriggerInstructionPointer() < m_WatchStartAddr) ||
(m_WatchEndAddr != ANY_ADDR && addr > m_WatchEndAddr)) (m_WatchEndAddr != ANY_ADDR && pEv->getTriggerInstructionPointer() > m_WatchEndAddr))
return false; return false;
return true; return true;
} }
bool GenericBPSingleListener::isMatching(address_t addr, address_t aspace) const bool GenericBPSingleListener::isMatching(const BPEvent* pEv) const
{ {
if (aspaceIsMatching(aspace)) { if (aspaceIsMatching(pEv->getCR3())) {
if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr) if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == pEv->getTriggerInstructionPointer())
return true; return true;
} }
return false; return false;

View File

@ -9,15 +9,12 @@
#include "SALConfig.hpp" #include "SALConfig.hpp"
#include "Event.hpp" #include "Event.hpp"
#include "perf/BufferInterface.hpp"
namespace fail { namespace fail {
class ExperimentFlow; class ExperimentFlow;
// FIXME(?): Maybe it suffices to provide a "setEventData" method in order to
// set the event data at once. (Consequently, all actual setters for
// attributes of the event-data objects could be removed.)
/** /**
* \class BaseListener * \class BaseListener
* This is the base class for all listener types. * This is the base class for all listener types.
@ -28,8 +25,11 @@ protected:
unsigned int m_OccCounter; //!< listener fires when 0 is reached unsigned int m_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 listener belongs to experiment m_Parent ExperimentFlow* m_Parent; //!< this listener belongs to experiment m_Parent
index_t m_Loc; //!< location of this listener object within the buffer-list
PerfBufferBase* m_Home; //!< ptr to performance buffer-list impl. or NULL of not existing
public: public:
BaseListener() : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL) BaseListener()
: m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL), m_Loc(INVALID_INDEX), m_Home(NULL)
{ } { }
virtual ~BaseListener() { } // FIXME remove from queues virtual ~BaseListener() { } // FIXME remove from queues
/** /**
@ -56,7 +56,6 @@ public:
* corresponding coroutine is toggled. * corresponding coroutine is toggled.
*/ */
virtual void onTrigger() { } virtual void onTrigger() { }
// TODO: Hier noch ne neue Methode oder reicht es, die Semantik von onTrigger umzudef.?
/** /**
* Decreases the listener counter by one. When this counter reaches zero, the * Decreases the listener counter by one. When this counter reaches zero, the
* listener will be triggered. * listener will be triggered.
@ -71,7 +70,7 @@ public:
* Returns the current counter value. * Returns the current counter value.
* @return the counter value * @return the counter value
*/ */
unsigned int getCounter() const { return (m_OccCounter); } unsigned int getCounter() const { return m_OccCounter; }
/** /**
* Resets the listener counter to its default value, or the last * Resets the listener counter to its default value, or the last
* value that was set through \c setCounter(). * value that was set through \c setCounter().
@ -81,13 +80,42 @@ public:
* Returns the parent experiment of this listener (context). * Returns the parent experiment of this listener (context).
* If the listener 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).
* @return the listener's parent ptr
*/ */
ExperimentFlow* getParent() const { return (m_Parent); } ExperimentFlow* getParent() const { return m_Parent; }
/** /**
* Sets the parent (context) of this listener. The default context * Sets the parent (context) of this listener. The default context
* is \c NULL (unknown identity). * is \c NULL (unknown identity).
* @param pFlow the new parent ptr or \c NULL (= unknown identity)
*/ */
void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; } void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; }
/**
* Sets the location of this listener within the buffer-list (which itself
* part of the buffer-list).
* @param idx the new index or \c INVALID_INDEX if not managed by (= added to)
* the ListenerManager.
*/
void setLocation(index_t idx) { m_Loc = idx; }
/**
* Returns the current location of this listener in the buffer-list. See
* \c setLocation for further details.
* @return the location (index) within the buffer-list
*/
index_t getLocation() { return m_Loc; }
/**
* Sets the performance buffer(-list), this listener is stored in. Upon calling
* this method, the listener should already be stored in the buffer \c pHome.
* @param pHome a pointer to the (fast) performance buffer-list, specific to this
* event type
*/
void setPerformanceBuffer(PerfBufferBase* pHome) { m_Home = pHome; }
/**
* Retrieves the pointer to the performance buffer-list implementation for
* this listener. The returned value may be \c NULL in case of a missing
* performance implementation.
* @return the ptr to the performance buffer-list, or \c NULL if not existing
*/
PerfBufferBase* getPerformanceBuffer() { return m_Home; }
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Specialized listeners: // Specialized listeners:
@ -125,10 +153,6 @@ public:
* Checks whether a given address space is matching. * Checks whether a given address space is matching.
*/ */
bool aspaceIsMatching(address_t address_space = ANY_ADDR) const; bool aspaceIsMatching(address_t address_space = ANY_ADDR) const;
/**
* Checks whether a given address is matching.
*/
virtual bool isMatching(address_t addr = 0, address_t address_space = ANY_ADDR) const = 0;
/** /**
* Returns the instruction pointer that triggered this listener. * Returns the instruction pointer that triggered this listener.
*/ */
@ -138,6 +162,12 @@ public:
* be used by experiment code. * be used by experiment code.
*/ */
void setTriggerInstructionPointer(address_t iptr) { m_Data.setTriggerInstructionPointer(iptr); } void setTriggerInstructionPointer(address_t iptr) { m_Data.setTriggerInstructionPointer(iptr); }
/**
* Checks whether a given address is matching.
* @param pEv Breakpoint event data, retrieved by the simulator
* @return \c true if matching, \c false otherwise
*/
virtual bool isMatching(const BPEvent* pEv) const = 0;
}; };
/** /**
@ -174,12 +204,11 @@ public:
*/ */
void setWatchInstructionPointer(address_t iptr) { m_WatchInstrPtr = iptr; } void setWatchInstructionPointer(address_t iptr) { m_WatchInstrPtr = iptr; }
/** /**
* Checks whether a given address is matching. * Checks whether a given address (encapsulated in \c pEv) is matching.
* @param addr address to check * @param pEv address to check, including address space information
* @param address_space address space information
* @return \c true if address is within the range, \c false otherwise * @return \c true if address is within the range, \c false otherwise
*/ */
bool isMatching(address_t addr, address_t address_space) const; bool isMatching(const BPEvent* pEv) const;
}; };
/** /**
@ -219,7 +248,7 @@ public:
* @return \c true if address is within the range (represented by * @return \c true if address is within the range (represented by
* \c this), \c false otherwise * \c this), \c false otherwise
*/ */
bool isMatching(address_t addr, address_t address_space) const; bool isMatching(const BPEvent* pEv) const;
}; };
/** /**
@ -260,10 +289,6 @@ public:
* Sets the width of the memory area being watched (defaults to 1). * Sets the width of the memory area being watched (defaults to 1).
*/ */
void setWatchWidth(size_t width) { m_WatchWidth = width; } void setWatchWidth(size_t width) { m_WatchWidth = width; }
/**
* Checks whether a given physical memory access is matching.
*/
bool isMatching(address_t addr, size_t width, MemAccessEvent::access_type_t accesstype) const;
/** /**
* Returns the specific physical memory address that actually triggered the * Returns the specific physical memory address that actually triggered the
* listener. * listener.
@ -310,6 +335,10 @@ public:
* this listener watches. Should not be used by experiment code. * this listener watches. Should not be used by experiment code.
*/ */
MemAccessEvent::access_type_t getWatchAccessType() const { return m_WatchType; } MemAccessEvent::access_type_t getWatchAccessType() const { return m_WatchType; }
/**
* Checks whether a given physical memory access is matching.
*/
bool isMatching(const MemAccessEvent* pEv) const;
}; };
/** /**
@ -375,7 +404,7 @@ public:
/** /**
* Checks whether a given interrupt-/trap-number is matching. * Checks whether a given interrupt-/trap-number is matching.
*/ */
bool isMatching(unsigned troubleNum) const; bool isMatching(const TroubleEvent* pEv) const;
/** /**
* Sets the specific interrupt-/trap-number that actually triggered * Sets the specific interrupt-/trap-number that actually triggered
* the listener. Should not be used by experiment code. * the listener. Should not be used by experiment code.
@ -488,7 +517,7 @@ public:
* @param p The port number an I/O listener occured on * @param 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 = isOutListener() && p == getPort(); } bool isMatching(unsigned p, bool out) const { return out == isOutListener() && p == getPort(); }
/** /**
* Tells you if this listener is capturing outbound communication (inbound if false) * Tells you if this listener is capturing outbound communication (inbound if false)
*/ */

View File

@ -5,7 +5,7 @@
namespace fail { namespace fail {
void ListenerManager::addToCaches(BaseListener *li) void ListenerManager::addToCaches(BaseListener *li) // FIXME: REMOVE
{ {
BPListener *bps_li; BPListener *bps_li;
if ((bps_li = dynamic_cast<BPListener*>(li)) != NULL) if ((bps_li = dynamic_cast<BPListener*>(li)) != NULL)
@ -16,7 +16,7 @@ void ListenerManager::addToCaches(BaseListener *li)
m_Io_cache.add(io_li); m_Io_cache.add(io_li);
} }
void ListenerManager::removeFromCaches(BaseListener *li) void ListenerManager::removeFromCaches(BaseListener *li) // FIXME: REMOVE
{ {
BPListener *bpr_li; BPListener *bpr_li;
if ((bpr_li = dynamic_cast<BPListener*>(li)) != NULL) if ((bpr_li = dynamic_cast<BPListener*>(li)) != NULL)
@ -27,7 +27,7 @@ void ListenerManager::removeFromCaches(BaseListener *li)
m_Io_cache.remove(io_li); m_Io_cache.remove(io_li);
} }
void ListenerManager::clearCaches() void ListenerManager::clearCaches() // FIXME: REMOVE
{ {
m_Bp_cache.clear(); m_Bp_cache.clear();
m_Io_cache.clear(); m_Io_cache.clear();
@ -36,38 +36,66 @@ void ListenerManager::clearCaches()
void ListenerManager::add(BaseListener* li, ExperimentFlow* pExp) void ListenerManager::add(BaseListener* li, ExperimentFlow* pExp)
{ {
assert(li != NULL && "FATAL ERROR: Listener (of base type BaseListener*) cannot be NULL!"); assert(li != NULL && "FATAL ERROR: Listener (of base type BaseListener*) cannot be NULL!");
// a zero counter does not make sense // A zero counter does not make sense
assert(li->getCounter() != 0); assert(li->getCounter() != 0 && "FATAL ERROR: Listener counter has already been zero!");
li->setParent(pExp); // listener is linked to experiment flow li->setParent(pExp); // listener is linked to experiment flow
// Ensure that the listener is not associated with any performance impl.:
addToCaches(li); li->setPerformanceBuffer(NULL);
m_BufferList.push_back(li); m_BufferList.push_back(li);
// Note: To keep the indices (within any perf-buffer-list) valid, we have to
// add new elements at the end of the vector.
index_t idx = m_BufferList.size()-1;
assert(m_BufferList[idx] == li && "FATAL ERROR: Invalid index after push_back() unexpected!");
li->setLocation(idx);
addToCaches(li); // FIXME: REMOVE
} }
void ListenerManager::remove(BaseListener* li) void ListenerManager::remove(BaseListener* li)
{ {
// possible cases: // Possible cases:
// - li == 0 -> remove all listeners // - li == 0 -> remove all listeners for the current flow
// * clear m_BufferList // * Inform the listeners (call onDeletion)
// * copy m_FireList to m_DeleteList // * Clear m_BufferList
// * Remove indices in corresponding perf. buffer-lists (if existing)
// * Copy m_FireList to m_DeleteList
if (li == 0) { if (li == 0) {
for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++) // We have to remove *all* indices in *all* (possibly added) performance implementations
(*it)->onDeletion(); // of matching listeners.
for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++) for (index_t i = 0; i < m_BufferList.size(); ) {
(*it)->onDeletion(); if (m_BufferList[i]->getParent() == simulator.m_Flows.getCurrent()) {
clearCaches(); m_BufferList[i]->onDeletion();
m_BufferList.clear(); if(m_BufferList[i]->getPerformanceBuffer() != NULL)
// all remaining active listeners must not fire anymore m_BufferList[i]->getPerformanceBuffer()->remove(i);
m_remove(i);
// Inspect the element at m_BufferList[i] a 2nd time
// (this element has been updated by m_remove()).
} else {
++i;
}
}
for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++) {
if ((*it)->getParent() == simulator.m_Flows.getCurrent()) {
(*it)->onDeletion();
// Listeners in the fire-list have already been deleted in the buffer-list.
// Consequently, they must have been deleted in the perf. buffer-list, too.
// ==> no further processing required here
}
}
clearCaches(); // FIXME: REMOVE
// All remaining active listeners must not fire anymore
m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end()); m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end());
// - li != 0 -> remove single listener // - li != 0 -> remove single listener (if added previously)
// * find/remove 'li' in 'm_BufferList' // * Inform the listeners (call onDeletion)
// * if 'li' in 'm_FireList', copy to 'm_DeleteList' // * Remove the index in the perf. buffer-list (if existing)
} else { // * Find/remove 'li' in 'm_BufferList'
// * If 'li' in 'm_FireList', copy to 'm_DeleteList'
} else if (li->getLocation() != INVALID_INDEX) { // Check if li hasn't been added previously (Q&D)
li->onDeletion(); li->onDeletion();
removeFromCaches(li); // FIXME: REMOVE
removeFromCaches(li); if (li->getPerformanceBuffer() != NULL)
m_BufferList.remove(li); li->getPerformanceBuffer()->remove(li->getLocation());
m_remove(li->getLocation());
firelist_t::const_iterator it = firelist_t::const_iterator it =
std::find(m_FireList.begin(), m_FireList.end(), li); std::find(m_FireList.begin(), m_FireList.end(), li);
if (it != m_FireList.end()) { if (it != m_FireList.end()) {
@ -76,6 +104,48 @@ void ListenerManager::remove(BaseListener* li)
} }
} }
ExperimentFlow* ListenerManager::getExperimentOf(BaseListener* li)
{
for (iterator it = begin(); it != end(); ++it) {
if (*it == li)
return (*it)->getParent();
}
return NULL;
}
void ListenerManager::m_remove(index_t idx)
{
// Note: This operation has O(1) time complexity. It copies (aka "swaps") the
// trailing element "m_BufferList[m_BufferList.size()-1]" to the slot
// at "m_BufferList[idx]" and removes the last element (pop_back()).
if(idx == INVALID_INDEX)
((BaseListener*)NULL)->setPerformanceBuffer(NULL);
// Override the element to be deleted (= copy the last element to the slot
// of the element to be deleted) and update their attributes:
if (!m_BufferList.empty() && idx != INVALID_INDEX) {
m_BufferList[idx]->setPerformanceBuffer(NULL);
m_BufferList[idx]->setLocation(INVALID_INDEX);
// Do we have at least 2 elements (so that there *is* a trailing element
// to be used for swapping)? If idx == m_BufferList.size()-1, the last
// element should be removed so there is no need to swap:
if (m_BufferList.size() > 1 && idx != m_BufferList.size()-1) {
// Update the index of the element object itself:
m_BufferList[idx] = m_BufferList[m_BufferList.size()-1];
index_t oldLoc = m_BufferList[idx]->getLocation();
m_BufferList[idx]->setLocation(idx);
// Update the (unique) index within it's performance buffer-list (if existing):
PerfBufferBase* pBuf = m_BufferList[idx]->getPerformanceBuffer();
if (pBuf != NULL) {
pBuf->remove(oldLoc);
pBuf->add(idx);
}
}
// (pop_back()'s behaviour is undefined if the vector is empty)
m_BufferList.pop_back(); // remove the last element
}
}
ListenerManager::iterator ListenerManager::m_remove(iterator it, bool skip_deletelist) ListenerManager::iterator ListenerManager::m_remove(iterator it, bool skip_deletelist)
{ {
if (!skip_deletelist) { if (!skip_deletelist) {
@ -93,37 +163,68 @@ ListenerManager::iterator ListenerManager::m_remove(iterator it, bool skip_delet
// BufferCache<T>::remove() from m_remove(). // BufferCache<T>::remove() from m_remove().
// NOTE: in case the semantics of skip_deletelist change, please adapt the following lines // NOTE: in case the semantics of skip_deletelist change, please adapt the following lines
removeFromCaches(*it); removeFromCaches(*it); // FIXME: REMOVE (incl. comments)
} }
return m_BufferList.erase(it); // This has O(1) time complexity due to a underlying std::vector (-> random access iterator)
index_t dist = std::distance(begin(), it);
assert((*it)->getPerformanceBuffer() == NULL &&
"FATAL ERROR: makeActive(iterator) cannot be called on listeners stored in a perf. \
buff-list! Use makeActive(BaseListener*) instead!");
// Remove the element from the buffer-list:
m_remove((*it)->getLocation());
// Create a new iterator, pointing to the element after *it
// (the new iterator actually points to the same slot in the vector, but now
// this slot stores the element which has previously been stored in the last slot):
// This is required because the provided iterator "it" isn't valid anymore (due
// to the deletion of the last element within m_remove(index_t)).
return begin() + dist; // O(1)
// Note: "begin() + dist" yields end() if dist is "large enough" (as computed above)
} }
void ListenerManager::remove(ExperimentFlow* flow) void ListenerManager::remove(ExperimentFlow* flow)
{ {
// all listeners? // All listeners (in all flows)?
if (flow == 0) { if (flow == 0) {
for (bufferlist_t::iterator it = m_BufferList.begin(); // We have to remove *all* indices in *all* (possibly added) performance implementations.
it != m_BufferList.end(); it++) // Therefore we collect the ptr to these impl. in order to call clear() for each of them.
std::set<PerfBufferBase*> perfBufLists;
for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++) {
(*it)->onDeletion(); // invoke listener handler (*it)->onDeletion(); // invoke listener handler
clearCaches(); if((*it)->getPerformanceBuffer() != NULL)
perfBufLists.insert((*it)->getPerformanceBuffer());
(*it)->setPerformanceBuffer(NULL);
(*it)->setLocation(INVALID_INDEX);
}
for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++) {
(*it)->onDeletion();
// See remove(BaseListener*).
}
m_BufferList.clear(); m_BufferList.clear();
// Remove the indices within each performance buffer-list (maybe empty):
for (std::set<PerfBufferBase*>::iterator it = perfBufLists.begin();
it != perfBufLists.end(); ++it)
(*it)->clear();
clearCaches(); // FIXME: REMOVE
// All remaining active listeners must not fire anymore
m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end());
} else { // remove all listeners corresponding to a specific experiment ("flow"): } else { // remove all listeners corresponding to a specific experiment ("flow"):
for (bufferlist_t::iterator it = m_BufferList.begin(); for (index_t i = 0; i < m_BufferList.size(); ) {
it != m_BufferList.end(); ) { if (m_BufferList[i]->getParent() == flow) {
if ((*it)->getParent() == flow) { m_BufferList[i]->onDeletion();
(*it)->onDeletion(); if (m_BufferList[i]->getPerformanceBuffer() != NULL) // perf. buffer-list existing?
it = m_BufferList.erase(it); m_BufferList[i]->getPerformanceBuffer()->remove(i); // delete it!
m_remove(i);
} else { } else {
++it; ++i;
} }
} }
} }
// listeners that just fired / are about to fire ... // listeners that just fired / are about to fire ...
for (firelist_t::const_iterator it = m_FireList.begin(); for (firelist_t::const_iterator it = m_FireList.begin();
it != m_FireList.end(); it++) { it != m_FireList.end(); it++) {
if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it) if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it) != m_DeleteList.end()) {
!= m_DeleteList.end()) {
continue; // (already in the delete-list? -> skip!) continue; // (already in the delete-list? -> skip!)
} }
// ... need to be pushed into m_DeleteList, as we're currently // ... need to be pushed into m_DeleteList, as we're currently
@ -142,8 +243,7 @@ ListenerManager::~ListenerManager()
ListenerManager::iterator ListenerManager::makeActive(iterator it) ListenerManager::iterator ListenerManager::makeActive(iterator it)
{ {
assert(it != m_BufferList.end() && assert(it != m_BufferList.end() && "FATAL ERROR: Iterator has already reached the end!");
"FATAL ERROR: Iterator has already reached the end!");
BaseListener* li = *it; BaseListener* li = *it;
assert(li && "FATAL ERROR: Listener object pointer cannot be NULL!"); assert(li && "FATAL ERROR: Listener object pointer cannot be NULL!");
li->decreaseCounter(); li->decreaseCounter();
@ -158,6 +258,24 @@ ListenerManager::iterator ListenerManager::makeActive(iterator it)
return it_next; return it_next;
} }
void ListenerManager::makeActive(BaseListener* pLi)
{
assert(pLi && "FATAL ERROR: Listener object pointer cannot be NULL!");
// If there is no performance buffer-list implementation, skip the operation:
if (pLi->getPerformanceBuffer() == NULL)
return;
// Decrease and check the listener counter:
pLi->decreaseCounter();
if (pLi->getCounter() > 0)
return;
pLi->resetCounter();
// Remove the index of the listener from the performance buffer-list:
pLi->getPerformanceBuffer()->remove(pLi->getLocation());
// Move the listener object from the buffer-list to the fire-list:
m_remove(pLi->getLocation()); // (updates the internals of the listener, too)
m_FireList.push_back(pLi);
}
void ListenerManager::triggerActiveListeners() void ListenerManager::triggerActiveListeners()
{ {
for (firelist_t::iterator it = m_FireList.begin(); for (firelist_t::iterator it = m_FireList.begin();
@ -180,6 +298,7 @@ void ListenerManager::triggerActiveListeners()
size_t ListenerManager::getContextCount() const size_t ListenerManager::getContextCount() const
{ {
// Note: This works even if there are active performance buffer-list implementations.
std::set<ExperimentFlow*> uniqueFlows; // count unique ExperimentFlow-ptr std::set<ExperimentFlow*> uniqueFlows; // count unique ExperimentFlow-ptr
for (bufferlist_t::const_iterator it = m_BufferList.begin(); for (bufferlist_t::const_iterator it = m_BufferList.begin();
it != m_BufferList.end(); it++) it != m_BufferList.end(); it++)

View File

@ -15,9 +15,9 @@ class ExperimentFlow;
/** /**
* Buffer-list for a specific experiment; acts as a simple storage container * Buffer-list for a specific experiment; acts as a simple storage container
* for listeners to watch for: * for listeners to watch for.
*/ */
typedef std::list<BaseListener*> bufferlist_t; typedef std::vector<BaseListener*> bufferlist_t;
/** /**
* List of listeners that match the current simulator listener; these listeners will * List of listeners that match the current simulator listener; these listeners will
* be triggered next (the list is used temporarily). * be triggered next (the list is used temporarily).
@ -55,7 +55,6 @@ typedef io_cache_t::iterator io_iter_t;
*/ */
class ListenerManager { class ListenerManager {
private: private:
// TODO: List separation of "critical types"? Hashing/sorted lists? (-> performance!)
bufferlist_t m_BufferList; //!< the storage for listeners added by exp. bufferlist_t m_BufferList; //!< the storage for listeners added by exp.
firelist_t m_FireList; //!< the active listeners (used temporarily) firelist_t m_FireList; //!< the active listeners (used temporarily)
deletelist_t m_DeleteList; //!< the deleted listeners (used temporarily) deletelist_t m_DeleteList; //!< the deleted listeners (used temporarily)
@ -65,6 +64,15 @@ private:
friend bp_iter_t bp_cache_t::makeActive(ListenerManager &ev_list, bp_iter_t idx); friend 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); friend io_iter_t io_cache_t::makeActive(ListenerManager &ev_list, io_iter_t idx);
public: public:
/**
* Determines the pointer to the listener base type, stored at index \c idx.
* @param idx the index within the buffer-list of the listener to retrieve
* @return the pointer to the (up-casted) base type (if \c idx is invalid and debug
* mode is enabled, an assertion is thrown)
* @note This operation has O(1) time complexity (due to the underlying \c std::vector).
*/
inline BaseListener* dereference(index_t idx) { assert(idx < m_BufferList.size() &&
"FATAL ERROR: Invalid index! Listener already deleted?"); return m_BufferList[idx]; }
/** /**
* The iterator of this class used to loop through the list of added * 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(). * listeners. To retrieve an iterator to the first element, call \c begin().
@ -81,15 +89,18 @@ public:
* @param li pointer to the listener object to be added (cannot be \c NULL) * @param li pointer to the listener object to be added (cannot be \c NULL)
* @param flow the listener context (a pointer to the experiment object * @param flow the listener context (a pointer to the experiment object
* which is interested in such listeners (cannot be \c NULL) * which is interested in such listeners (cannot be \c NULL)
* @return the id of the added listener object, that is ev->getId() * @note You need to overload this method if you are adding a (type-specific)
* performance buffer-list implementation.
*/ */
void add(BaseListener* li, ExperimentFlow* flow); void add(BaseListener* li, ExperimentFlow* flow);
/** /**
* Removes the listener based upon the specified \a ev pointer (requires * Removes the listener based upon the specified \c li pointer (requires
* to loop through the whole buffer-list). * to loop through the whole buffer-list).
* @param li the pointer of the listener to be removed; if ev is set to * @param li the pointer of the listener to be removed; if \c li is set to
* \c NULL, all listeners (for \a all experiments) will be * \c NULL, all listeners (for the \a current experiment) will be
* removed * removed
* @note There is no need to re-implement this method in a performance-
* buffer-list implementation.
*/ */
void remove(BaseListener* li); void remove(BaseListener* li);
/** /**
@ -98,7 +109,7 @@ public:
* @param it the iterator pointing to the Listener object to be removed * @param it the iterator pointing to the Listener object to be removed
* @return the updated iterator which will point to the next element * @return the updated iterator which will point to the next element
*/ */
iterator remove(iterator it); iterator remove(iterator it) { return m_remove(it, false); }
private: private:
/** /**
* Internal implementation of remove(iterator it) that allows * Internal implementation of remove(iterator it) that allows
@ -109,6 +120,16 @@ private:
* @return the updated iterator which will point to the next element * @return the updated iterator which will point to the next element
*/ */
iterator m_remove(iterator it, bool skip_deletelist); iterator m_remove(iterator it, bool skip_deletelist);
/**
* Updates the buffer-list by "removing" the element located at index \c idx.
* This is done by replacing the element with the last element of the vector.
* @param idx the index of the element to be removed
* @warning The internals of the listener, stored at index \c idx wont be
* updated.
* @note This method should typically be used in a performance buffer-list
* implemenation.
*/
void m_remove(index_t idx);
public: public:
/** /**
* Returns an iterator to the beginning of the internal data structure. * Returns an iterator to the beginning of the internal data structure.
@ -170,20 +191,38 @@ public:
/** /**
* Moves the listeners from the (internal) buffer-list to the fire-list. * Moves the listeners from the (internal) buffer-list to the fire-list.
* To actually fire the listeners, call triggerActiveListeners(). * To actually fire the listeners, call triggerActiveListeners().
* Returns an updated iterator which points to the next element. * Returns an updated iterator which points to the next element. Additionally,
* \c makeActive() decreases the listener counter and performs it's operation if
* and only if the *decreased* listener counter is zero.
* @param ev the listener to trigger * @param ev the listener to trigger
* @return returns the updated iteration, pointing to the next element * @return returns the updated iterator, pointing to the next element
* after makeActive returns, "it" is invalid, so the returned * after makeActive returns, "it" is *invalid*, so the *returned*
* iterator should be used to continue the iteration * iterator should be used to continue the iteration
* *
* TODO: Improve naming (instead of "makeActive")? * TODO: Improve naming (instead of "makeActive")?
*/ */
iterator makeActive(iterator it); iterator makeActive(iterator it);
/**
* Moves the listener \c pLi from the (internal "performance") buffer-list \c pSrc
* to the fire-list. This method should be called from a performance implemenation.
* It expects the member \c pLi->getLocation() to be valid, i.e. the element needs
* to be stored in the buffer-list previously. Additionally, \c makeActive()
* decreases the listener counter and performs it's operation if and only if the
* *decreased* listener counter is zero. Activated listener objects will be updated
* regarding their location and performance-buffer reference, i.e. their index and
* performance-buffer pointer will be invalidated (by setting \c INVALID_INDEX and
* \c NULL, respectively).
* To actually fire the listeners, call triggerActiveListeners().
* @param pLi the listener object pointer to trigger; \c pLi will be removed in
* \c pLi->getPerformanceBuffer(). If the performance buffer-list ptr is
* \c NULL, nothing will be done.
*/
void makeActive(BaseListener* pLi);
/** /**
* Triggers the active listeners. Each listener is triggered if it has not * 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 * recently been removed (i.e.: is not found in the delete-list). See
* makeActive() for more details. The recently triggered listener can be * \c makeActive() for more details. The recently triggered listener can be
* retrieved by calling \a getLastFired(). After all listeners have been * retrieved by calling \c getLastFired(). After all listeners have been
* triggered, the (internal) fire- and delete-list will be cleared. * triggered, the (internal) fire- and delete-list will be cleared.
*/ */
void triggerActiveListeners(); void triggerActiveListeners();

View File

@ -52,17 +52,18 @@ void SimulatorController::onBreakpoint(address_t instrPtr, address_t address_spa
// Loop through all listeners of type BP*Listener: // Loop through all listeners of type BP*Listener:
ListenerManager::iterator it = m_LstList.begin(); ListenerManager::iterator it = m_LstList.begin();
BPEvent tmp(instrPtr, address_space);
while (it != m_LstList.end()) { while (it != m_LstList.end()) {
BaseListener* pev = *it; BaseListener* pev = *it;
BPSingleListener* pbp; BPRangeListener* pbpr; BPSingleListener* pbp; BPRangeListener* pbpr;
if ((pbp = dynamic_cast<BPSingleListener*>(pev)) && pbp->isMatching(instrPtr, address_space)) { if ((pbp = dynamic_cast<BPSingleListener*>(pev)) && pbp->isMatching(&tmp)) {
pbp->setTriggerInstructionPointer(instrPtr); pbp->setTriggerInstructionPointer(instrPtr);
it = m_LstList.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<BPRangeListener*>(pev)) && } else if ((pbpr = dynamic_cast<BPRangeListener*>(pev)) &&
pbpr->isMatching(instrPtr, address_space)) { pbpr->isMatching(&tmp)) {
pbpr->setTriggerInstructionPointer(instrPtr); pbpr->setTriggerInstructionPointer(instrPtr);
it = m_LstList.makeActive(it); it = m_LstList.makeActive(it);
continue; // dito continue; // dito
@ -80,12 +81,13 @@ void SimulatorController::onMemoryAccess(address_t addr, size_t len,
is_write ? MemAccessEvent::MEM_WRITE is_write ? MemAccessEvent::MEM_WRITE
: MemAccessEvent::MEM_READ; : MemAccessEvent::MEM_READ;
MemAccessEvent tmp(addr, len, instrPtr, accesstype);
ListenerManager::iterator it = m_LstList.begin(); ListenerManager::iterator it = m_LstList.begin();
while (it != m_LstList.end()) { // check for active listeners while (it != m_LstList.end()) { // check for active listeners
BaseListener* pev = *it; BaseListener* pev = *it;
MemAccessListener* ev = dynamic_cast<MemAccessListener*>(pev); MemAccessListener* ev = dynamic_cast<MemAccessListener*>(pev);
// Is this a MemAccessListener? Correct access type? // Is this a MemAccessListener? Correct access type?
if (!ev || !ev->isMatching(addr, len, accesstype)) { if (!ev || !ev->isMatching(&tmp)) {
++it; ++it;
continue; // skip listener activation continue; // skip listener activation
} }
@ -101,10 +103,11 @@ void SimulatorController::onMemoryAccess(address_t addr, size_t len,
void SimulatorController::onInterrupt(unsigned interruptNum, bool nmi) void SimulatorController::onInterrupt(unsigned interruptNum, bool nmi)
{ {
ListenerManager::iterator it = m_LstList.begin(); ListenerManager::iterator it = m_LstList.begin();
InterruptEvent tmp(nmi, interruptNum);
while (it != m_LstList.end()) { // check for active listeners while (it != m_LstList.end()) { // check for active listeners
BaseListener* pev = *it; BaseListener* pev = *it;
InterruptListener* pie = dynamic_cast<InterruptListener*>(pev); InterruptListener* pie = dynamic_cast<InterruptListener*>(pev);
if (!pie || !pie->isMatching(interruptNum)) { if (!pie || !pie->isMatching(&tmp)) {
++it; ++it;
continue; // skip listener activation continue; // skip listener activation
} }
@ -156,11 +159,12 @@ bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum)
void SimulatorController::onTrap(unsigned trapNum) void SimulatorController::onTrap(unsigned trapNum)
{ {
TroubleEvent tmp(trapNum);
ListenerManager::iterator it = m_LstList.begin(); ListenerManager::iterator it = m_LstList.begin();
while (it != m_LstList.end()) { // check for active listeners while (it != m_LstList.end()) { // check for active listeners
BaseListener* pev = *it; BaseListener* pev = *it;
TrapListener* pte = dynamic_cast<TrapListener*>(pev); TrapListener* pte = dynamic_cast<TrapListener*>(pev);
if (!pte || !pte->isMatching(trapNum)) { if (!pte || !pte->isMatching(&tmp)) {
++it; ++it;
continue; // skip listener activation continue; // skip listener activation
} }

View File

@ -182,15 +182,15 @@ public:
*/ */
void removeFlow(ExperimentFlow* flow); void removeFlow(ExperimentFlow* flow);
/** /**
* Add listener \c li to the listener management. This causes the listener to be active. * Adds the listener \c li to the listener management. This causes the listener to be active.
* @param li the listener pointer to be added for the current flow * @param li the listener pointer to be added for the current flow
* @return \c true if the listener has been added successfully, \c false otherwise * @return \c true if the listener has been added successfully, \c false otherwise
*/ */
bool addListener(BaseListener* li); bool addListener(BaseListener* li);
/** /**
* Removes the listener with the specified pointer \c li. * Removes the listener with the specified pointer \c li.
* @param li the pointer of the listener-object to be removed; if \c li is * @param li the pointer of the listener object to be removed; if \c li is
* equal to \c NULL, all listeners (for all experiments) will be removed * equal to \c NULL, all listeners (for the \a current experiments) will be removed
*/ */
void removeListener(BaseListener* li) { m_LstList.remove(li); } void removeListener(BaseListener* li) { m_LstList.remove(li); }
/** /**
@ -209,15 +209,15 @@ public:
*/ */
BaseListener* resume(); BaseListener* resume();
/** /**
* Add listener \a ev to the global buffer and continues the simulation * Add listener \c li to the global buffer and continues the simulation
* (combines \c addListener() and \c resume()). * (combines \c addListener() and \c resume()).
* @param li the listener pointer to be added * @param li the listener pointer to be added
* @return the pointer of the occurred listener (it is not guaranteed that * @return the pointer of the occurred listener (it is not guaranteed that
* this pointer will be equal to li) * this pointer will be equal to \c li)
*/ */
BaseListener* addListenerAndResume(BaseListener* li); BaseListener* addListenerAndResume(BaseListener* li);
/** /**
* Checks whether any experiment flow has listeners in the listener-list. * Checks whether any experiment flow has listeners in the listener (buffer-)list.
* @return \c true if there are still listeners, or \c false otherwise * @return \c true if there are still listeners, or \c false otherwise
*/ */
bool hasListeners() const { return getListenerCount() > 0; } bool hasListeners() const { return getListenerCount() > 0; }
@ -227,6 +227,15 @@ public:
* @return the actual number of listeners * @return the actual number of listeners
*/ */
unsigned getListenerCount() const { return m_LstList.getListenerCount(); } unsigned getListenerCount() const { return m_LstList.getListenerCount(); }
/**
* Determines the pointer to the listener base type, stored at index \c idx.
* @param idx the index within the buffer-list of the listener to retrieve
* @return the pointer to the (up-casted) base type (if \c idx is invalid and debug
* mode is enabled, an assertion is thrown)
* @note This operation has O(1) time complexity (due to the underlying \c std::vector).
* @see ListenerManager::dereference()
*/
inline BaseListener* dereference(index_t idx) { return m_LstList.dereference(idx); }
}; };
// FIXME (see SimulatorController.cc): Weird, homeless global variable // FIXME (see SimulatorController.cc): Weird, homeless global variable

View File

@ -97,9 +97,10 @@ void BochsController::onBreakpoint(address_t instrPtr, address_t address_space)
// Check for active breakpoint-events: // Check for active breakpoint-events:
bp_cache_t &buffer_cache = m_LstList.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();
BPEvent tmp(instrPtr, address_space);
while (it != buffer_cache.end()) { while (it != buffer_cache.end()) {
BPListener* pEvBreakpt = *it; BPListener* pEvBreakpt = *it;
if (pEvBreakpt->isMatching(instrPtr, address_space)) { if (pEvBreakpt->isMatching(&tmp)) {
pEvBreakpt->setTriggerInstructionPointer(instrPtr); pEvBreakpt->setTriggerInstructionPointer(instrPtr);
it = buffer_cache.makeActive(m_LstList, it); it = buffer_cache.makeActive(m_LstList, it);
do_fire = true; do_fire = true;

View File

@ -47,10 +47,10 @@ public:
/** /**
* Instruction pointer modification handler implementing the onBreakpoint * Instruction pointer modification handler implementing the onBreakpoint
* handler of the SimulatorController. This method is called (from * handler of the SimulatorController. This method is called (from
* the Breakpoints aspect) every time the Bochs-internal IP changes. * the Breakpoints aspect) *every* time the Bochs-internal IP changes.
* The handler itself evaluates if a breakpoint event needs to be triggered. * The handler itself evaluates if a breakpoint event needs to be triggered.
* This handler needs to implement the breakpoint-mechanism in an indirect * This handler needs to implement the breakpoint-mechanism in an indirect
* fashion because the Bochs simulator doesn't support breakpoints explicitly. * fashion because the Bochs simulator doesn't support native breakpoints.
* To match the interface specified by the simulator class, we need to provide * To match the interface specified by the simulator class, we need to provide
* the two members \c m_CPUContext and \c m_CacheEntry. The elements are * the two members \c m_CPUContext and \c m_CacheEntry. The elements are
* being set before the handler is called (see \c updateBPEventInfo()). * being set before the handler is called (see \c updateBPEventInfo()).

View File

@ -22,7 +22,7 @@ aspect Breakpoints {
// Points to the *current* bxInstruction-object // Points to the *current* bxInstruction-object
bxICacheEntry_c* pEntry = *(tjp->arg<1>()); bxICacheEntry_c* pEntry = *(tjp->arg<1>());
// report this event to the Bochs controller: // Report this event to the Bochs controller:
fail::simulator.updateBPEventInfo(pThis, pEntry); fail::simulator.updateBPEventInfo(pThis, pEntry);
fail::simulator.onBreakpoint(pThis->get_instruction_pointer(), pThis->cr3); fail::simulator.onBreakpoint(pThis->get_instruction_pointer(), pThis->cr3);
// Note: get_bx_opcode_name(pInstr->getIaOpcode()) retrieves the mnemonics. // Note: get_bx_opcode_name(pInstr->getIaOpcode()) retrieves the mnemonics.

View File

@ -0,0 +1,23 @@
#include "BreakpointBuffer.hpp"
#include "../SALInst.hpp"
namespace fail {
// FIXME: not inlined
ResultSet& PerfVectorBreakpoints::gather(BPEvent* pData)
{
static ResultSet res;
res.clear();
// Search for all indices of matching listener objects:
for(std::vector<index_t>::iterator it = m_BufList.begin(); it != m_BufList.end(); ++it) {
BPListener* pLi = static_cast<BPListener*>(simulator.dereference(*it));
if (pLi->isMatching(pData)) {
// Update trigger IPtr:
pLi->setTriggerInstructionPointer(pData->getTriggerInstructionPointer());
res.add(*it);
}
}
return res;
}
} // end-of-namespace: fail

View File

@ -0,0 +1,157 @@
#ifndef __BREAKPOINT_BUFFER_HPP__
#define __BREAKPOINT_BUFFER_HPP__
#include "BufferInterface.hpp"
#include "../Listener.hpp"
#include <cassert>
#include <vector>
// TODOs:
// - Make these implementations even faster (see below: continue PerfVecSortedSingleBP).
// - The implementation of gather() (see below) in BreakpointBuffer.cc (not inlined in
// .hpp) avoids an include cycle. Unfortunately, this may cause a bad performance
// because gather() won't be inlined anymore! (The method is called quite often.)
namespace fail {
class BPEvent;
/**
* \class ResultSet
*
* Results (= indices of matching listeners) returned by the "gather"-method,
* see below. (This class can be seen as a "temporary fire-list".)
*/
class ResultSet {
std::vector<index_t> m_Res;
public:
ResultSet() { }
bool hasMore() const { return !m_Res.empty(); }
index_t getNext() { index_t idx = m_Res.back(); m_Res.pop_back(); return idx; }
void add(index_t idx) { m_Res.push_back(idx); }
size_t size() const { return m_Res.size(); }
void clear() { m_Res.clear(); }
};
/**
* Concrete implementation of the PerfBufferBase class for \c std::vector
* and \c BPSingleListener.
*/
class PerfVectorBreakpoints : public PerfBufferBase {
protected:
std::vector<index_t> m_BufList;
public:
void add(index_t idx) { m_BufList.push_back(idx); }
void remove(index_t idx)
{
for (std::vector<index_t>::iterator it = m_BufList.begin();
it != m_BufList.end(); ++it) {
if (*it == idx) {
m_BufList.erase(it);
break;
}
}
}
void clear() { m_BufList.clear(); }
size_t size() const { return m_BufList.size(); }
ResultSet& gather(BPEvent* pData);
};
/**
* \class PerfVecSortedSingleBP
*
* This class implements a faster mechanism to store BPSingleListener
* based on binary search on their corresponding instruction pointer.
*/
/*
class PerfVecSortedSingleBP : public PerfVectorBreakpoints {
private:
SimulatorController* m_Sim;
public:
PerfVecSortedSingleBP(SimulatorController* pSim) : m_Sim(pSim) { }
**
* TODO.
* @warning The method expects that \c idx is a valid index within the main
* buffer-list. Therefore we are allowed to call \c SimulatorController::dereference().
* Additionally, the indexed listener is epected to be of type \c BPListener*.
*
void add(index_t idx)
{
address_t watchIP = static_cast<BPSingleListener*>(m_Sim->dereference(idx))->getWatchInstructionPointer();
// Keep the indices sorted in ascending order regarding their instr. ptr.
// Search for the slot to insert the element (skip lower IPs):
int pos = binarySearch(m_BufList, 0, m_BufList.size()-1, idx);
assert(pos < 0 && "FATAL ERROR: Element already inserted!");
m_BufList.insert(it + (pos * (-1) + 1), idx);
// for (std::vector<index_t>::iterator it = m_BufList.begin(); it != m_BufList.end(); ++it) {
// if (static_cast<BPSingleListener*>(
// m_Sim->dereference(*it))->getWatchInstructionPointer() > watchIP) {
// m_BufList.insert(it, idx); // Insert the element before "it"
// break;
// }
}
}
static bool CompareInstrPtr(index_t arg1, index_t arg2, void* pStuff)
{
SimulatorController* pSim = static_cast<SimulatorController*>(pStuff);
#define VAL(idx) \
static_cast<BPSingleListener*>(pSim->dereference(idx))->getWatchInstructionPointer()
return (VAL(arg1) > VAL(arg2);
}
**
* Searches vec[first]...vec[last] for \c key. Returns the index of the matching
* element if it finds key, otherwise -(index where it could be inserted)-1.
* @param vec array of sorted (ascending) values.
* @param first lower subscript bound
* @param last upper subscript bound
* @param key value to search for
* @param pfnCmp a function pointer to a comparison function, returning \c true
* if the first argument is greater than the second, otherwise \c false.
* The last parameter equals the last parameter provided by calling this
* method. It can be used to pass additional data.
* @return index of key, or -insertion_position -1 if key is not in the array.
* This value can easily be transformed into the position to insert it.
*
int binarySearch(const std::vector<index_t>& vec, index_t first, index_t last, index_t key, bool (*pfnCmp)(index_t, index_t, void*), void* pStuff = NULL)
{
while (first <= last) {
int mid = (first + last) / 2; // compute mid point.
if (VAL(key) > VAL(vec[mid]))
first = mid + 1; // repeat search in top half.
else if (VAL(key) < VAL(vec[mid]))
last = mid - 1; // repeat search in bottom half.
else
return mid; // found it. return position
}
return -(first + 1); // failed to find key
}
void remove(index_t idx)
{
int pos = binarySearch(m_BufList, 0, m_BufList.size()-1, idx, CompareInstrPtr, m_Sim);
if (pos >= 0)
m_BufList.erase(m_BufList.begin() + pos);
}
ResultSet& gather(BPEvent* pData)
{
// TODO: Improve this by using binary search, too!
ResultSet res;
// Search for all indices of matching listener objects:
for(std::vector<index_t>::iterator it = m_BufList.begin(); it != m_BufList.end(); ++it) {
BPListener* pLi = static_cast<BPListener*>(simulator.dereference(*it));
if (pLi->isMatching(pData)) {
// Update trigger IPtr:
pLi->setTriggerInstructionPointer(pData->getTriggerInstructionPointer());
res.add(*it);
}
}
return res;
}
};
*/
} // end-of-namespace: fail
#endif // __BREAKPOINT_BUFFER_HPP__

View File

@ -0,0 +1,49 @@
#ifndef __BREAKPOINT_CONTROLLER_SLICE_AH__
#define __BREAKPOINT_CONTROLLER_SLICE_AH__
#include "config/FailConfig.hpp"
#ifdef CONFIG_FAST_BREAKPOINTS
#include <iostream>
#include "../Listener.hpp"
/**
* \class BreakpointControllerSlice
*
* The slice class definition to be used with FastBreakpoint.ah.
* The members of this class will be sliced into the \c SimulatorController class.
*/
slice class BreakpointControllerSlice {
public:
bool addListener(fail::BPSingleListener* sli)
{
assert(sli != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
m_LstList.add(sli, m_Flows.getCurrent());
// Call the common postprocessing function:
if (!sli->onAddition()) { // If the return value signals "false"...,
m_LstList.remove(sli); // ...skip the addition
return false;
}
return true;
}
fail::BaseListener* addListenerAndResume(fail::BPSingleListener* sli)
{ addListener(sli); return resume(); }
bool addListener(fail::BPRangeListener* rli)
{
assert(rli != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
m_LstList.add(rli, m_Flows.getCurrent());
// Call the common postprocessing function:
if (!rli->onAddition()) { // If the return value signals "false"...,
m_LstList.remove(rli); // ...skip the addition
return false;
}
return true;
}
fail::BaseListener* addListenerAndResume(fail::BPRangeListener* rli)
{ addListener(rli); return resume(); }
};
#endif // CONFIG_FAST_BREAKPOINTS
#endif // __BREAKPOINT_CONTROLLER_SLICE_AH__

View File

@ -0,0 +1,56 @@
#ifndef __BREAKPOINT_MANAGER_SLICE_AH__
#define __BREAKPOINT_MANAGER_SLICE_AH__
#include "config/FailConfig.hpp"
#ifdef CONFIG_FAST_BREAKPOINTS
#include "BreakpointBuffer.hpp"
/**
* \class BreakpointManagerSlice
*
* The slice class definition to be used with FastBreakpoint.ah.
* The members of this class will be sliced into the \c ListenerManager class.
*/
slice class BreakpointManagerSlice {
private:
fail::PerfVectorBreakpoints m_SingleListeners;
fail::PerfVectorBreakpoints m_RangeListeners;
public:
fail::PerfVectorBreakpoints& getSingleListeners() { return m_SingleListeners; }
fail::PerfVectorBreakpoints& getRangeListeners() { return m_RangeListeners; }
void add(fail::BPSingleListener* sli, fail::ExperimentFlow* flow)
{
assert(sli != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
sli->setParent(flow);
// (1) Add sli to the main buffer-list, ...
m_BufferList.push_back(sli);
// (2) ... update it's location (index) / performance-buffer (ptr) and ...
fail::index_t idx = m_BufferList.size()-1;
assert(m_BufferList[idx] == sli && "FATAL ERROR: Invalid index after push_back() unexpected!");
sli->setLocation(idx);
sli->setPerformanceBuffer(&m_SingleListeners);
// (3) ... add this index to the m_SingleListeners vector.
m_SingleListeners.add(idx);
}
void add(fail::BPRangeListener* rli, fail::ExperimentFlow* flow)
{
assert(rli != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
rli->setParent(flow);
// (1) Add sli to the main buffer-list, ...
m_BufferList.push_back(rli);
// (2) ... update it's location (index) / performance-buffer (ptr) and ...
fail::index_t idx = m_BufferList.size()-1;
assert(m_BufferList[idx] == rli && "FATAL ERROR: Invalid index after push_back() unexpected!");
rli->setLocation(idx);
rli->setPerformanceBuffer(&m_RangeListeners);
// (3) ... add this index to the m_RangeListeners vector.
m_RangeListeners.add(idx);
}
};
#endif // CONFIG_FAST_BREAKPOINTS
#endif // __BREAKPOINT_MANAGER_SLICE_AH__

View File

@ -0,0 +1,42 @@
#ifndef __BUFFER_INTERFACE_HPP__
#define __BUFFER_INTERFACE_HPP__
#include <cstddef>
namespace fail {
typedef std::size_t index_t; //!< the index type of elements, stored in the buffer-list
const index_t INVALID_INDEX = static_cast<index_t>(-1); //!< a constant, representing an invalid idx
/**
* \class PerfBufferBase
*
* Generic performance data structure for storing often used listener objects
* (This class can be seen as a "fast, type specific buffer-list".)
*/
class PerfBufferBase {
public:
/**
* Adds the index \c idx to the performance buffer-list.
* @param idx the element to be added
*/
virtual void add(index_t idx) = 0;
/**
* Removes the specified index \c idx.
* @param idx the element to be deleted
*/
virtual void remove(index_t idx) = 0;
/**
* Removes all elements from the perf. buffer-list.
*/
virtual void clear() = 0;
/**
* Retrieves the number of elements in this data structure.
* @return the number of elements in the perf. buffer-list
*/
virtual std::size_t size() const = 0;
};
} // end-of-namespace: fail
#endif // __BUFFER_INTERFACE_HPP__

View File

@ -0,0 +1,85 @@
#ifndef __FAST_BREAKPOINTS_AH__
#define __FAST_BREAKPOINTS_AH__
#include "config/FailConfig.hpp"
#ifdef CONFIG_FAST_BREAKPOINTS
#ifndef CONFIG_EVENT_BREAKPOINTS
#error Breakpoint events are required for fast breakpoints!
#endif
#include "BreakpointManagerSlice.ah" // slice class "BreakpointManagerSlice"
#include "BreakpointControllerSlice.ah" // slice class "BreakpointControllerSlice"
#include "../ListenerManager.hpp"
#include "../Listener.hpp"
#include "BreakpointBuffer.hpp"
#include "../SALInst.hpp"
// TODOs:
// - Check if this solutation works, even if there are more than one
// active performance aspects.
aspect FastBreakpoints {
// Refer to slice classes:
advice "fail::ListenerManager" : slice BreakpointManagerSlice;
advice "fail::SimulatorController" : slice BreakpointControllerSlice;
// These around-advices handle the (special) case where an experiment
// adds a listener object casted to the base type but actually has the
// BPSingle- or BPRangeListener type.
advice execution("bool fail::SimulatorController::addListener(fail::BaseListener*)") : around ()
{
// Note: The logic to update the performance buffer-list is
// placed in the addListener() methods.
fail::BaseListener* pLi = *(tjp->arg<0>());
fail::BPSingleListener* pSi = dynamic_cast<fail::BPSingleListener*>(pLi);
fail::BPRangeListener* pRi = dynamic_cast<fail::BPRangeListener*>(pLi);
if (pSi != NULL)
*(tjp->result()) = tjp->that()->addListener(pSi);
else if (pRi != NULL)
*(tjp->result()) = tjp->that()->addListener(pRi);
else
tjp->proceed();
}
advice call("BaseListener* fail::SimulatorController::addListenerAndResume(fail::BaseListener*)") : around ()
{
// Note: The logic to update the performance buffer-list is
// placed in the addListenerAndResume methods.
fail::BaseListener* pLi = *(tjp->arg<0>());
fail::BPSingleListener* pSi = dynamic_cast<fail::BPSingleListener*>(pLi);
fail::BPRangeListener* pRi = dynamic_cast<fail::BPRangeListener*>(pLi);
if (pSi != NULL)
*(tjp->result()) = tjp->that()->addListenerAndResume(pSi);
else if (pRi != NULL)
*(tjp->result()) = tjp->that()->addListenerAndResume(pRi);
else
tjp->proceed();
}
// The event handler for employing breakpoint events.
advice execution("void fail::%Controller::onBreakpoint(...)") : around ()
{
// Note: "BPListener" is an abstract class anyway.
fail::ListenerManager& ref = tjp->target()->m_LstList;
fail::BPEvent tmp(*(tjp->arg<0>()), *(tjp->arg<1>()));
// Check for matching BPSingleListeners:
fail::ResultSet& res1 = ref.getSingleListeners().gather(&tmp);
while (res1.hasMore())
ref.makeActive(ref.dereference(res1.getNext()));
// Check for matching BPRangeListeners:
fail::ResultSet& res2 = ref.getRangeListeners().gather(&tmp);
while (res2.hasMore())
ref.makeActive(ref.dereference(res2.getNext()));
ref.triggerActiveListeners();
}
};
#endif // CONFIG_FAST_BREAKPOINTS // see above
#endif // __FAST_BREAKPOINTS_AH__