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
|
||||
Register.cc
|
||||
SimulatorController.cc
|
||||
perf/BreakpointBuffer.cc
|
||||
bochs/BochsController.cc
|
||||
bochs/BochsListener.cc
|
||||
)
|
||||
@ -29,6 +30,7 @@ elseif(BUILD_OVP)
|
||||
SALConfig.cc
|
||||
Register.cc
|
||||
SimulatorController.cc
|
||||
perf/BreakpointBuffer.cc
|
||||
${VARIANT}/OVPController.cc
|
||||
)
|
||||
elseif(BUILD_QEMU)
|
||||
@ -38,6 +40,7 @@ elseif(BUILD_QEMU)
|
||||
ListenerManager.cc
|
||||
SALConfig.cc
|
||||
Register.cc
|
||||
perf/BreakpointBuffer.cc
|
||||
SimulatorController.cc
|
||||
qemu/QEMUController.cc
|
||||
qemu/QEMUListener.cc
|
||||
|
||||
@ -201,8 +201,10 @@ public:
|
||||
/**
|
||||
* Creates a new \c InterruptEvent.
|
||||
* @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.
|
||||
* @return \c true if NMI flag is set, \c false otherwise
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
|
||||
namespace fail {
|
||||
|
||||
bool TroubleListener::isMatching(unsigned troubleNum) const
|
||||
bool TroubleListener::isMatching(const TroubleEvent* pEv) const
|
||||
{
|
||||
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)
|
||||
return true;
|
||||
}
|
||||
@ -34,13 +34,13 @@ bool TroubleListener::addWatchNumber(unsigned troubleNumber)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemAccessListener::isMatching(address_t addr, size_t width,
|
||||
MemAccessEvent::access_type_t accesstype) const
|
||||
bool MemAccessListener::isMatching(const MemAccessEvent* pEv) const
|
||||
{
|
||||
if (!(m_WatchType & accesstype)) {
|
||||
if (!(m_WatchType & pEv->getTriggerAccessType())) {
|
||||
return false;
|
||||
} 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 true;
|
||||
@ -59,20 +59,20 @@ void BPRangeListener::setWatchInstructionPointerRange(address_t start, address_t
|
||||
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;
|
||||
if ((m_WatchStartAddr != ANY_ADDR && addr < m_WatchStartAddr) ||
|
||||
(m_WatchEndAddr != ANY_ADDR && addr > m_WatchEndAddr))
|
||||
if ((m_WatchStartAddr != ANY_ADDR && pEv->getTriggerInstructionPointer() < m_WatchStartAddr) ||
|
||||
(m_WatchEndAddr != ANY_ADDR && pEv->getTriggerInstructionPointer() > m_WatchEndAddr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GenericBPSingleListener::isMatching(address_t addr, address_t aspace) const
|
||||
bool GenericBPSingleListener::isMatching(const BPEvent* pEv) const
|
||||
{
|
||||
if (aspaceIsMatching(aspace)) {
|
||||
if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr)
|
||||
if (aspaceIsMatching(pEv->getCR3())) {
|
||||
if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == pEv->getTriggerInstructionPointer())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -9,15 +9,12 @@
|
||||
|
||||
#include "SALConfig.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "perf/BufferInterface.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
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
|
||||
* 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_OccCounterInit; //!< initial value for m_OccCounter
|
||||
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:
|
||||
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
|
||||
/**
|
||||
@ -56,7 +56,6 @@ public:
|
||||
* corresponding coroutine is toggled.
|
||||
*/
|
||||
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
|
||||
* listener will be triggered.
|
||||
@ -71,7 +70,7 @@ public:
|
||||
* Returns the current 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
|
||||
* value that was set through \c setCounter().
|
||||
@ -81,13 +80,42 @@ public:
|
||||
* Returns the parent experiment of this listener (context).
|
||||
* If the listener was created temporarily or wasn't linked to a context,
|
||||
* the return value is \c NULL (unknown identity).
|
||||
* @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
|
||||
* is \c NULL (unknown identity).
|
||||
* @param pFlow the new parent ptr or \c NULL (= unknown identity)
|
||||
*/
|
||||
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:
|
||||
@ -125,10 +153,6 @@ public:
|
||||
* Checks whether a given address space is matching.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -138,6 +162,12 @@ public:
|
||||
* be used by experiment code.
|
||||
*/
|
||||
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; }
|
||||
/**
|
||||
* Checks whether a given address is matching.
|
||||
* @param addr address to check
|
||||
* @param address_space address space information
|
||||
* Checks whether a given address (encapsulated in \c pEv) is matching.
|
||||
* @param pEv address to check, including address space information
|
||||
* @return \c true if address is within the range, \c false otherwise
|
||||
*/
|
||||
bool isMatching(address_t addr, address_t address_space) const;
|
||||
bool isMatching(const BPEvent* pEv) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -219,7 +248,7 @@ public:
|
||||
* @return \c true if address is within the range (represented by
|
||||
* \c this), \c false otherwise
|
||||
*/
|
||||
bool isMatching(address_t addr, address_t address_space) const;
|
||||
bool isMatching(const BPEvent* pEv) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -260,10 +289,6 @@ public:
|
||||
* Sets the width of the memory area being watched (defaults to 1).
|
||||
*/
|
||||
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
|
||||
* listener.
|
||||
@ -310,6 +335,10 @@ public:
|
||||
* this listener watches. Should not be used by experiment code.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
bool isMatching(unsigned troubleNum) const;
|
||||
bool isMatching(const TroubleEvent* pEv) const;
|
||||
/**
|
||||
* Sets the specific interrupt-/trap-number that actually triggered
|
||||
* 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 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)
|
||||
*/
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
namespace fail {
|
||||
|
||||
void ListenerManager::addToCaches(BaseListener *li)
|
||||
void ListenerManager::addToCaches(BaseListener *li) // FIXME: REMOVE
|
||||
{
|
||||
BPListener *bps_li;
|
||||
if ((bps_li = dynamic_cast<BPListener*>(li)) != NULL)
|
||||
@ -16,7 +16,7 @@ void ListenerManager::addToCaches(BaseListener *li)
|
||||
m_Io_cache.add(io_li);
|
||||
}
|
||||
|
||||
void ListenerManager::removeFromCaches(BaseListener *li)
|
||||
void ListenerManager::removeFromCaches(BaseListener *li) // FIXME: REMOVE
|
||||
{
|
||||
BPListener *bpr_li;
|
||||
if ((bpr_li = dynamic_cast<BPListener*>(li)) != NULL)
|
||||
@ -27,7 +27,7 @@ void ListenerManager::removeFromCaches(BaseListener *li)
|
||||
m_Io_cache.remove(io_li);
|
||||
}
|
||||
|
||||
void ListenerManager::clearCaches()
|
||||
void ListenerManager::clearCaches() // FIXME: REMOVE
|
||||
{
|
||||
m_Bp_cache.clear();
|
||||
m_Io_cache.clear();
|
||||
@ -36,38 +36,66 @@ void ListenerManager::clearCaches()
|
||||
void ListenerManager::add(BaseListener* li, ExperimentFlow* pExp)
|
||||
{
|
||||
assert(li != NULL && "FATAL ERROR: Listener (of base type BaseListener*) cannot be NULL!");
|
||||
// a zero counter does not make sense
|
||||
assert(li->getCounter() != 0);
|
||||
// A zero counter does not make sense
|
||||
assert(li->getCounter() != 0 && "FATAL ERROR: Listener counter has already been zero!");
|
||||
li->setParent(pExp); // listener is linked to experiment flow
|
||||
|
||||
addToCaches(li);
|
||||
// Ensure that the listener is not associated with any performance impl.:
|
||||
li->setPerformanceBuffer(NULL);
|
||||
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)
|
||||
{
|
||||
// possible cases:
|
||||
// - li == 0 -> remove all listeners
|
||||
// * clear m_BufferList
|
||||
// * copy m_FireList to m_DeleteList
|
||||
// Possible cases:
|
||||
// - li == 0 -> remove all listeners for the current flow
|
||||
// * Inform the listeners (call onDeletion)
|
||||
// * Clear m_BufferList
|
||||
// * Remove indices in corresponding perf. buffer-lists (if existing)
|
||||
// * Copy m_FireList to m_DeleteList
|
||||
if (li == 0) {
|
||||
for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++)
|
||||
(*it)->onDeletion();
|
||||
for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++)
|
||||
(*it)->onDeletion();
|
||||
clearCaches();
|
||||
m_BufferList.clear();
|
||||
// all remaining active listeners must not fire anymore
|
||||
// We have to remove *all* indices in *all* (possibly added) performance implementations
|
||||
// of matching listeners.
|
||||
for (index_t i = 0; i < m_BufferList.size(); ) {
|
||||
if (m_BufferList[i]->getParent() == simulator.m_Flows.getCurrent()) {
|
||||
m_BufferList[i]->onDeletion();
|
||||
if(m_BufferList[i]->getPerformanceBuffer() != NULL)
|
||||
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());
|
||||
|
||||
// - li != 0 -> remove single listener
|
||||
// * find/remove 'li' in 'm_BufferList'
|
||||
// * if 'li' in 'm_FireList', copy to 'm_DeleteList'
|
||||
} else {
|
||||
// - li != 0 -> remove single listener (if added previously)
|
||||
// * Inform the listeners (call onDeletion)
|
||||
// * Remove the index in the perf. buffer-list (if existing)
|
||||
// * 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();
|
||||
|
||||
removeFromCaches(li);
|
||||
m_BufferList.remove(li);
|
||||
removeFromCaches(li); // FIXME: REMOVE
|
||||
if (li->getPerformanceBuffer() != NULL)
|
||||
li->getPerformanceBuffer()->remove(li->getLocation());
|
||||
m_remove(li->getLocation());
|
||||
firelist_t::const_iterator it =
|
||||
std::find(m_FireList.begin(), m_FireList.end(), li);
|
||||
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)
|
||||
{
|
||||
if (!skip_deletelist) {
|
||||
@ -93,37 +163,68 @@ ListenerManager::iterator ListenerManager::m_remove(iterator it, bool skip_delet
|
||||
// BufferCache<T>::remove() from m_remove().
|
||||
|
||||
// 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)
|
||||
{
|
||||
// all listeners?
|
||||
// All listeners (in all flows)?
|
||||
if (flow == 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.
|
||||
// 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
|
||||
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();
|
||||
// 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"):
|
||||
for (bufferlist_t::iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); ) {
|
||||
if ((*it)->getParent() == flow) {
|
||||
(*it)->onDeletion();
|
||||
it = m_BufferList.erase(it);
|
||||
for (index_t i = 0; i < m_BufferList.size(); ) {
|
||||
if (m_BufferList[i]->getParent() == flow) {
|
||||
m_BufferList[i]->onDeletion();
|
||||
if (m_BufferList[i]->getPerformanceBuffer() != NULL) // perf. buffer-list existing?
|
||||
m_BufferList[i]->getPerformanceBuffer()->remove(i); // delete it!
|
||||
m_remove(i);
|
||||
} else {
|
||||
++it;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
// listeners that just fired / are about to fire ...
|
||||
for (firelist_t::const_iterator it = m_FireList.begin();
|
||||
it != m_FireList.end(); it++) {
|
||||
if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it)
|
||||
!= m_DeleteList.end()) {
|
||||
if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it) != m_DeleteList.end()) {
|
||||
continue; // (already in the delete-list? -> skip!)
|
||||
}
|
||||
// ... need to be pushed into m_DeleteList, as we're currently
|
||||
@ -142,8 +243,7 @@ ListenerManager::~ListenerManager()
|
||||
|
||||
ListenerManager::iterator ListenerManager::makeActive(iterator it)
|
||||
{
|
||||
assert(it != m_BufferList.end() &&
|
||||
"FATAL ERROR: Iterator has already reached the end!");
|
||||
assert(it != m_BufferList.end() && "FATAL ERROR: Iterator has already reached the end!");
|
||||
BaseListener* li = *it;
|
||||
assert(li && "FATAL ERROR: Listener object pointer cannot be NULL!");
|
||||
li->decreaseCounter();
|
||||
@ -158,6 +258,24 @@ ListenerManager::iterator ListenerManager::makeActive(iterator it)
|
||||
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()
|
||||
{
|
||||
for (firelist_t::iterator it = m_FireList.begin();
|
||||
@ -180,6 +298,7 @@ void ListenerManager::triggerActiveListeners()
|
||||
|
||||
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
|
||||
for (bufferlist_t::const_iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
|
||||
@ -15,9 +15,9 @@ class ExperimentFlow;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* be triggered next (the list is used temporarily).
|
||||
@ -55,7 +55,6 @@ typedef io_cache_t::iterator io_iter_t;
|
||||
*/
|
||||
class ListenerManager {
|
||||
private:
|
||||
// TODO: List separation of "critical types"? Hashing/sorted lists? (-> performance!)
|
||||
bufferlist_t m_BufferList; //!< the storage for listeners added by exp.
|
||||
firelist_t m_FireList; //!< the active listeners (used temporarily)
|
||||
deletelist_t m_DeleteList; //!< the deleted listeners (used temporarily)
|
||||
@ -65,6 +64,15 @@ private:
|
||||
friend bp_iter_t bp_cache_t::makeActive(ListenerManager &ev_list, bp_iter_t idx);
|
||||
friend io_iter_t io_cache_t::makeActive(ListenerManager &ev_list, io_iter_t idx);
|
||||
public:
|
||||
/**
|
||||
* 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
|
||||
* 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 flow the listener context (a pointer to the experiment object
|
||||
* which is interested in such listeners (cannot be \c NULL)
|
||||
* @return the id of the added listener object, that is ev->getId()
|
||||
* @note You need to overload this method if you are adding a (type-specific)
|
||||
* performance buffer-list implementation.
|
||||
*/
|
||||
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).
|
||||
* @param li the pointer of the listener to be removed; if ev is set to
|
||||
* \c NULL, all listeners (for \a all experiments) will be
|
||||
* @param li the pointer of the listener to be removed; if \c li is set to
|
||||
* \c NULL, all listeners (for the \a current experiment) will be
|
||||
* removed
|
||||
* @note There is no need to re-implement this method in a performance-
|
||||
* buffer-list implementation.
|
||||
*/
|
||||
void remove(BaseListener* li);
|
||||
/**
|
||||
@ -98,7 +109,7 @@ public:
|
||||
* @param it the iterator pointing to the Listener object to be removed
|
||||
* @return the updated iterator which will point to the next element
|
||||
*/
|
||||
iterator remove(iterator it);
|
||||
iterator remove(iterator it) { return m_remove(it, false); }
|
||||
private:
|
||||
/**
|
||||
* Internal implementation of remove(iterator it) that allows
|
||||
@ -109,6 +120,16 @@ private:
|
||||
* @return the updated iterator which will point to the next element
|
||||
*/
|
||||
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:
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
* @return returns the updated iteration, pointing to the next element
|
||||
* after makeActive returns, "it" is invalid, so the returned
|
||||
* @return returns the updated iterator, pointing to the next element
|
||||
* after makeActive returns, "it" is *invalid*, so the *returned*
|
||||
* iterator should be used to continue the iteration
|
||||
*
|
||||
* TODO: Improve naming (instead of "makeActive")?
|
||||
*/
|
||||
iterator makeActive(iterator it);
|
||||
/**
|
||||
* 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
|
||||
* recently been removed (id est: is not found in the delete-list). See
|
||||
* makeActive() for more details. The recently triggered listener can be
|
||||
* retrieved by calling \a getLastFired(). After all listeners have been
|
||||
* recently been removed (i.e.: is not found in the delete-list). See
|
||||
* \c makeActive() for more details. The recently triggered listener can be
|
||||
* retrieved by calling \c getLastFired(). After all listeners have been
|
||||
* triggered, the (internal) fire- and delete-list will be cleared.
|
||||
*/
|
||||
void triggerActiveListeners();
|
||||
|
||||
@ -52,17 +52,18 @@ void SimulatorController::onBreakpoint(address_t instrPtr, address_t address_spa
|
||||
|
||||
// Loop through all listeners of type BP*Listener:
|
||||
ListenerManager::iterator it = m_LstList.begin();
|
||||
BPEvent tmp(instrPtr, address_space);
|
||||
while (it != m_LstList.end()) {
|
||||
BaseListener* pev = *it;
|
||||
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);
|
||||
it = m_LstList.makeActive(it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
} else if ((pbpr = dynamic_cast<BPRangeListener*>(pev)) &&
|
||||
pbpr->isMatching(instrPtr, address_space)) {
|
||||
pbpr->isMatching(&tmp)) {
|
||||
pbpr->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_LstList.makeActive(it);
|
||||
continue; // dito
|
||||
@ -80,12 +81,13 @@ void SimulatorController::onMemoryAccess(address_t addr, size_t len,
|
||||
is_write ? MemAccessEvent::MEM_WRITE
|
||||
: MemAccessEvent::MEM_READ;
|
||||
|
||||
MemAccessEvent tmp(addr, len, instrPtr, accesstype);
|
||||
ListenerManager::iterator it = m_LstList.begin();
|
||||
while (it != m_LstList.end()) { // check for active listeners
|
||||
BaseListener* pev = *it;
|
||||
MemAccessListener* ev = dynamic_cast<MemAccessListener*>(pev);
|
||||
// Is this a MemAccessListener? Correct access type?
|
||||
if (!ev || !ev->isMatching(addr, len, accesstype)) {
|
||||
if (!ev || !ev->isMatching(&tmp)) {
|
||||
++it;
|
||||
continue; // skip listener activation
|
||||
}
|
||||
@ -101,10 +103,11 @@ void SimulatorController::onMemoryAccess(address_t addr, size_t len,
|
||||
void SimulatorController::onInterrupt(unsigned interruptNum, bool nmi)
|
||||
{
|
||||
ListenerManager::iterator it = m_LstList.begin();
|
||||
InterruptEvent tmp(nmi, interruptNum);
|
||||
while (it != m_LstList.end()) { // check for active listeners
|
||||
BaseListener* pev = *it;
|
||||
InterruptListener* pie = dynamic_cast<InterruptListener*>(pev);
|
||||
if (!pie || !pie->isMatching(interruptNum)) {
|
||||
if (!pie || !pie->isMatching(&tmp)) {
|
||||
++it;
|
||||
continue; // skip listener activation
|
||||
}
|
||||
@ -156,11 +159,12 @@ bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum)
|
||||
|
||||
void SimulatorController::onTrap(unsigned trapNum)
|
||||
{
|
||||
TroubleEvent tmp(trapNum);
|
||||
ListenerManager::iterator it = m_LstList.begin();
|
||||
while (it != m_LstList.end()) { // check for active listeners
|
||||
BaseListener* pev = *it;
|
||||
TrapListener* pte = dynamic_cast<TrapListener*>(pev);
|
||||
if (!pte || !pte->isMatching(trapNum)) {
|
||||
if (!pte || !pte->isMatching(&tmp)) {
|
||||
++it;
|
||||
continue; // skip listener activation
|
||||
}
|
||||
|
||||
@ -182,15 +182,15 @@ public:
|
||||
*/
|
||||
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
|
||||
* @return \c true if the listener has been added successfully, \c false otherwise
|
||||
*/
|
||||
bool addListener(BaseListener* 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
|
||||
* equal to \c NULL, all listeners (for all experiments) will be removed
|
||||
* @param li the pointer of the listener object to be removed; if \c li is
|
||||
* equal to \c NULL, all listeners (for the \a current experiments) will be removed
|
||||
*/
|
||||
void removeListener(BaseListener* li) { m_LstList.remove(li); }
|
||||
/**
|
||||
@ -209,15 +209,15 @@ public:
|
||||
*/
|
||||
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()).
|
||||
* @param li the listener pointer to be added
|
||||
* @return the pointer of the occurred listener (it is not guaranteed that
|
||||
* this pointer will be equal to li)
|
||||
* this pointer will be equal to \c 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
|
||||
*/
|
||||
bool hasListeners() const { return getListenerCount() > 0; }
|
||||
@ -227,6 +227,15 @@ public:
|
||||
* @return the actual number of listeners
|
||||
*/
|
||||
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
|
||||
|
||||
@ -97,9 +97,10 @@ void BochsController::onBreakpoint(address_t instrPtr, address_t address_space)
|
||||
// Check for active breakpoint-events:
|
||||
bp_cache_t &buffer_cache = m_LstList.getBPBuffer();
|
||||
bp_cache_t::iterator it = buffer_cache.begin();
|
||||
BPEvent tmp(instrPtr, address_space);
|
||||
while (it != buffer_cache.end()) {
|
||||
BPListener* pEvBreakpt = *it;
|
||||
if (pEvBreakpt->isMatching(instrPtr, address_space)) {
|
||||
if (pEvBreakpt->isMatching(&tmp)) {
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = buffer_cache.makeActive(m_LstList, it);
|
||||
do_fire = true;
|
||||
|
||||
@ -47,10 +47,10 @@ public:
|
||||
/**
|
||||
* Instruction pointer modification handler implementing the onBreakpoint
|
||||
* 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.
|
||||
* 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
|
||||
* the two members \c m_CPUContext and \c m_CacheEntry. The elements are
|
||||
* being set before the handler is called (see \c updateBPEventInfo()).
|
||||
|
||||
@ -22,7 +22,7 @@ aspect Breakpoints {
|
||||
// Points to the *current* bxInstruction-object
|
||||
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.onBreakpoint(pThis->get_instruction_pointer(), pThis->cr3);
|
||||
// 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