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:
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -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++)
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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()).
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
23
src/core/sal/perf/BreakpointBuffer.cc
Normal file
23
src/core/sal/perf/BreakpointBuffer.cc
Normal 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
|
||||||
157
src/core/sal/perf/BreakpointBuffer.hpp
Normal file
157
src/core/sal/perf/BreakpointBuffer.hpp
Normal 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__
|
||||||
49
src/core/sal/perf/BreakpointControllerSlice.ah
Normal file
49
src/core/sal/perf/BreakpointControllerSlice.ah
Normal 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__
|
||||||
56
src/core/sal/perf/BreakpointManagerSlice.ah
Normal file
56
src/core/sal/perf/BreakpointManagerSlice.ah
Normal 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__
|
||||||
42
src/core/sal/perf/BufferInterface.hpp
Normal file
42
src/core/sal/perf/BufferInterface.hpp
Normal 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__
|
||||||
85
src/core/sal/perf/FastBreakpoints.ah
Normal file
85
src/core/sal/perf/FastBreakpoints.ah
Normal 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__
|
||||||
Reference in New Issue
Block a user