Changed BufferCache Backend to STL (as proposed), added IOPortEvent-Cache, updated class diagram
git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1325 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
@ -7,87 +7,11 @@
|
||||
namespace fail {
|
||||
|
||||
template<class T>
|
||||
void BufferCache<T>::add(T val)
|
||||
typename BufferCache<T>::iterator BufferCache<T>::makeActive(EventList &ev_list, BufferCache<T>::iterator idx)
|
||||
{
|
||||
int new_size = getCount() + 1;
|
||||
int new_last_index = getCount();
|
||||
|
||||
int res = reallocate_buffer(new_size);
|
||||
assert (res == 0 && "FATAL ERROR: Could not add event to cache");
|
||||
|
||||
set(new_last_index, val);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void BufferCache<T>::remove(T val)
|
||||
{
|
||||
bool do_remove = false;
|
||||
for (int i = 0; i < getCount(); i++) {
|
||||
if (get(i) == val) {
|
||||
do_remove = true;
|
||||
}
|
||||
if (do_remove) {
|
||||
if (i > getCount() - 1) {
|
||||
set(i, get(i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (do_remove) {
|
||||
int new_size = getCount() - 1;
|
||||
int res = reallocate_buffer(new_size);
|
||||
assert (res == 0 && "FATAL ERROR: Could not remove event from cache");
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void BufferCache<T>::clear()
|
||||
{
|
||||
setCount(0);
|
||||
free(m_Buffer);
|
||||
m_Buffer = NULL;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int BufferCache<T>::erase(int idx)
|
||||
{
|
||||
if(idx < 0 || idx >= getCount())
|
||||
return -2;
|
||||
|
||||
for (int i = idx; i < getCount() - 1; i++) {
|
||||
set(i, get(i + 1));
|
||||
}
|
||||
|
||||
int new_size = getCount() - 1;
|
||||
if (reallocate_buffer(new_size) != 0)
|
||||
return -1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int BufferCache<T>::reallocate_buffer(int new_size)
|
||||
{
|
||||
if (new_size < 0)
|
||||
return 20;
|
||||
|
||||
if (new_size == 0) {
|
||||
clear();
|
||||
return 0;
|
||||
}
|
||||
void *new_buffer = realloc(m_Buffer, new_size * sizeof(T));
|
||||
if (new_buffer == NULL)
|
||||
return 10;
|
||||
m_Buffer = static_cast<T*>(new_buffer);
|
||||
setCount(new_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int BufferCache<T>::makeActive(EventList &ev_list, int idx)
|
||||
{
|
||||
assert(idx < getCount() &&
|
||||
assert(idx != end() &&
|
||||
"FATAL ERROR: Index larger than cache!");
|
||||
T ev = get(idx);
|
||||
T ev = *idx;
|
||||
assert(ev && "FATAL ERROR: Object pointer cannot be NULL!");
|
||||
ev->decreaseCounter();
|
||||
if (ev->getCounter() > 0) {
|
||||
@ -102,7 +26,8 @@ int BufferCache<T>::makeActive(EventList &ev_list, int idx)
|
||||
return erase(idx);
|
||||
}
|
||||
|
||||
// Declare whatever instances of the template you are going to use here:
|
||||
// Declare here whatever instances of the template you are going to use:
|
||||
template class BufferCache<BPEvent*>;
|
||||
template class BufferCache<IOPortEvent*>;
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define __BUFFER_CACHE_HPP__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <list>
|
||||
|
||||
namespace fail {
|
||||
|
||||
@ -12,74 +13,71 @@ class EventList;
|
||||
*
|
||||
* \brief A simple dynamic array
|
||||
*
|
||||
* This class is intended to serve as a kind of cache for the entirely STL-based,
|
||||
* This class is intended to serve as a kind of cache for the
|
||||
* untyped and therefore quite slow event handling mechanism of Fail*.
|
||||
* To keep the code easily readable, some buffer management methods
|
||||
* perform suboptimally (remove() and erase() have linear complexity).
|
||||
*
|
||||
* FIXME: Why not using std::vector? ("A simple dynamic array")
|
||||
*/
|
||||
template<class T>
|
||||
class BufferCache {
|
||||
private:
|
||||
// TODO: comments ("//!<") needed!
|
||||
T *m_Buffer;
|
||||
int m_BufferCount;
|
||||
/**
|
||||
* Changes m_BufferCount. Should be inlined.
|
||||
* @param new_count the new array length
|
||||
*/
|
||||
inline void setCount(int new_count) { if(new_count >= 0) m_BufferCount = new_count; }
|
||||
protected:
|
||||
/**
|
||||
* Reallocates the buffer. This implementation is extremely primitive,
|
||||
* but since the amount of entries is small,
|
||||
* this will not be significant, hopefully. Should be inlined.
|
||||
* @param new_size the new number of elements in the array
|
||||
* @return 0 if successful, an error code otherwise (10 if realloc() fails, 20 for an invalid new size)
|
||||
*/
|
||||
inline int reallocate_buffer(int new_size);
|
||||
public:
|
||||
BufferCache()
|
||||
: m_Buffer(NULL), m_BufferCount(0) {}
|
||||
/**
|
||||
* The list type inherent to this class. Like bufferlist_t in EventList.hpp,
|
||||
* but dynamically typed.
|
||||
*/
|
||||
typedef std::list<T> cachelist_t;
|
||||
/**
|
||||
* The iterator of this class used to loop through the list of
|
||||
* added events. To retrieve an iterator to the first element, call
|
||||
* begin(). end() returns the iterator, pointing after the last element.
|
||||
* (This behaviour equals the STL iterator in C++.)
|
||||
*/
|
||||
typedef typename cachelist_t::iterator iterator;
|
||||
private:
|
||||
cachelist_t m_Buffer; //!< The list holding the cached elements
|
||||
public:
|
||||
BufferCache() {}
|
||||
~BufferCache() {}
|
||||
/**
|
||||
* Add an element to the array. The object pointed to remains untouched.
|
||||
* @param val the element to add
|
||||
*/
|
||||
void add(T val);
|
||||
inline void add(const T &val) { m_Buffer.push_back(val); }
|
||||
/**
|
||||
* Remove an element from the array. The object pointed to remains untouched.
|
||||
* @param val the element to remove
|
||||
*/
|
||||
void remove(T val);
|
||||
inline void remove(const T &val) { m_Buffer.remove(val); }
|
||||
/**
|
||||
* Remove an element at a specific position. The object pointed to remains untouched.
|
||||
* @param val the element to remove
|
||||
* @return a pointer to the given element's successor if successful, a negative value otherwise
|
||||
*/
|
||||
int erase(int i);
|
||||
inline iterator erase(iterator i) { return m_Buffer.erase(i); }
|
||||
/**
|
||||
* Clears the array, removing all elements. The objects pointed to remain untouched.
|
||||
*/
|
||||
void clear();
|
||||
inline void clear() { m_Buffer.clear(); }
|
||||
/**
|
||||
* Retrieve an element from the array. Should be inlined.
|
||||
* @param idx the position to retrieve the element from
|
||||
* @return the element at the given position
|
||||
* Returns an iterator to the beginning of the internal data structure.
|
||||
* Don't forget to update the returned iterator when calling one of the
|
||||
* modifying methods like makeActive() or remove(). Therefore you need
|
||||
* to call the iterator-based variants of makeActive() and remove().
|
||||
* \code
|
||||
* [X|1|2| ... |n]
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
inline T get(int idx) const { return (idx >= 0 && idx < getCount() ? m_Buffer[idx] : NULL); }
|
||||
inline iterator begin() { return m_Buffer.begin(); }
|
||||
/**
|
||||
* Set an element at a given position. Should be inlined.
|
||||
* @param idx the position to change an element at
|
||||
* @param val the new value of the given element
|
||||
* Returns an iterator to the end of the interal data structure.
|
||||
* Don't forget to update the returned iterator when calling one of the
|
||||
* modifying methods like makeActive() or remove(). Therefore you need
|
||||
* to call the iterator-based variants of makeActive() and remove().
|
||||
* \code
|
||||
* [1|2| ... |n]X
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
inline void set(int idx, T val) { if(idx >= 0 && idx < getCount()) m_Buffer[idx] = val; }
|
||||
/**
|
||||
* Retrieves the current length of the array. Should be inlined.
|
||||
* @return the array length
|
||||
*/
|
||||
inline int getCount() const { return m_BufferCount; }
|
||||
inline iterator end() { return m_Buffer.end(); }
|
||||
/**
|
||||
* Acts as a replacement for EventList::makeActive, manipulating
|
||||
* the buffer cache exclusively. EventList::fireActiveEvents needs
|
||||
@ -88,7 +86,7 @@ public:
|
||||
* @param idx the index of the event to trigger
|
||||
* @returns an updated index which can be used to update a loop counter
|
||||
*/
|
||||
int makeActive(EventList &ev_list, int idx);
|
||||
iterator makeActive(EventList &ev_list, iterator idx);
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
@ -4,7 +4,34 @@
|
||||
#include "SALInst.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
|
||||
void EventList::addToCaches(BaseEvent *ev)
|
||||
{
|
||||
BPEvent *bps_ev;
|
||||
if((bps_ev = dynamic_cast<BPEvent*>(ev)) != NULL)
|
||||
m_Bp_cache.add(bps_ev);
|
||||
|
||||
IOPortEvent *io_ev;
|
||||
if((io_ev = dynamic_cast<IOPortEvent*>(ev)) != NULL)
|
||||
m_Io_cache.add(io_ev);
|
||||
}
|
||||
|
||||
void EventList::removeFromCaches(BaseEvent *ev)
|
||||
{
|
||||
BPEvent *bpr_ev;
|
||||
if((bpr_ev = dynamic_cast<BPEvent*>(ev)) != NULL)
|
||||
m_Bp_cache.remove(bpr_ev);
|
||||
|
||||
IOPortEvent *io_ev;
|
||||
if((io_ev = dynamic_cast<IOPortEvent*>(ev)) != NULL)
|
||||
m_Io_cache.remove(io_ev);
|
||||
}
|
||||
|
||||
void EventList::clearCaches() {
|
||||
m_Bp_cache.clear();
|
||||
m_Io_cache.clear();
|
||||
}
|
||||
|
||||
EventId EventList::add(BaseEvent* ev, ExperimentFlow* pExp)
|
||||
{
|
||||
assert(ev != NULL && "FATAL ERROR: Event (of base type BaseEvent*) cannot be NULL!");
|
||||
@ -12,9 +39,7 @@ EventId EventList::add(BaseEvent* ev, ExperimentFlow* pExp)
|
||||
assert(ev->getCounter() != 0);
|
||||
ev->setParent(pExp); // event is linked to experiment flow
|
||||
|
||||
BPEvent *bp_ev;
|
||||
if((bp_ev = dynamic_cast<BPEvent*>(ev)) != NULL)
|
||||
m_Bp_cache.add(bp_ev);
|
||||
addToCaches(ev);
|
||||
m_BufferList.push_back(ev);
|
||||
return (ev->getId());
|
||||
}
|
||||
@ -30,7 +55,7 @@ void EventList::remove(BaseEvent* ev)
|
||||
simulator.onEventDeletion(*it);
|
||||
for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++)
|
||||
simulator.onEventDeletion(*it);
|
||||
m_Bp_cache.clear();
|
||||
clearCaches();
|
||||
m_BufferList.clear();
|
||||
// all remaining active events must not fire anymore
|
||||
m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end());
|
||||
@ -41,9 +66,7 @@ void EventList::remove(BaseEvent* ev)
|
||||
} else {
|
||||
simulator.onEventDeletion(ev);
|
||||
|
||||
BPEvent *bp_ev;
|
||||
if((bp_ev = dynamic_cast<BPEvent*>(ev)) != NULL)
|
||||
m_Bp_cache.remove(bp_ev);
|
||||
removeFromCaches(ev);
|
||||
m_BufferList.remove(ev);
|
||||
firelist_t::const_iterator it =
|
||||
std::find(m_FireList.begin(), m_FireList.end(), ev);
|
||||
@ -75,9 +98,7 @@ EventList::iterator EventList::m_remove(iterator it, bool skip_deletelist)
|
||||
// BufferCache<T>::remove() from m_remove().
|
||||
|
||||
// NOTE: in case the semantics of skip_deletelist change, please adapt the following lines
|
||||
BPEvent *bp_ev;
|
||||
if((bp_ev = dynamic_cast<BPEvent*>(*it)) != NULL)
|
||||
m_Bp_cache.remove(bp_ev);
|
||||
removeFromCaches((*it));
|
||||
}
|
||||
|
||||
return (m_BufferList.erase(it));
|
||||
@ -99,7 +120,7 @@ void EventList::remove(ExperimentFlow* flow)
|
||||
for (bufferlist_t::iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
simulator.onEventDeletion(*it); // invoke event handler
|
||||
m_Bp_cache.clear();
|
||||
clearCaches();
|
||||
m_BufferList.clear();
|
||||
} else { // remove all events corresponding to a specific experiment ("flow"):
|
||||
for (bufferlist_t::iterator it = m_BufferList.begin();
|
||||
|
||||
@ -30,6 +30,14 @@ typedef std::vector<BaseEvent*> firelist_t;
|
||||
*/
|
||||
typedef std::vector<BaseEvent*> deletelist_t;
|
||||
|
||||
/**
|
||||
* Cache classes for the most commonly used types of events, utilising static typing.
|
||||
* Apart from that, they work like bufferlist_t.
|
||||
*/
|
||||
typedef BufferCache<BPEvent*> bp_cache_t;
|
||||
typedef bp_cache_t::iterator bp_iter_t;
|
||||
typedef BufferCache<IOPortEvent*> io_cache_t;
|
||||
typedef io_cache_t::iterator io_iter_t;
|
||||
/**
|
||||
* \class EventList
|
||||
*
|
||||
@ -37,7 +45,7 @@ typedef std::vector<BaseEvent*> deletelist_t;
|
||||
*
|
||||
* If a event is triggered, the internal data structure will be updated (id est,
|
||||
* the event will be removed from the so called buffer-list and added to the
|
||||
* fire-list). Additionaly, if an experiment-flow deletes an "active" event
|
||||
* fire-list). Additionally, if an experiment-flow deletes an "active" event
|
||||
* which is currently stored in the fire-list, the event (to be removed) will
|
||||
* be added to a -so called- delete-list. This ensures to prevent triggering
|
||||
* "active" events which have already been deleted by a previous experiment
|
||||
@ -53,8 +61,10 @@ class EventList
|
||||
firelist_t m_FireList; //!< the active events (used temporarily)
|
||||
deletelist_t m_DeleteList; //!< the deleted events (used temporarily)
|
||||
BaseEvent* m_pFired; //!< the recently fired Event-object
|
||||
BufferCache<BPEvent*> m_Bp_cache;
|
||||
friend int BufferCache<BPEvent*>::makeActive(EventList &ev_list, int idx);
|
||||
bp_cache_t m_Bp_cache; //!< the storage cache for breakpoint events
|
||||
io_cache_t m_Io_cache; //!< the storage cache for port i/o events
|
||||
friend bp_iter_t bp_cache_t::makeActive(EventList &ev_list, bp_iter_t idx);
|
||||
friend io_iter_t io_cache_t::makeActive(EventList &ev_list, io_iter_t idx);
|
||||
public:
|
||||
/**
|
||||
* The iterator of this class used to loop through the list of
|
||||
@ -186,7 +196,27 @@ class EventList
|
||||
* Retrieves the BPEvent buffer cache.
|
||||
* @returns the buffer cache
|
||||
*/
|
||||
inline BufferCache<BPEvent*> *getBPBuffer() { return &m_Bp_cache; }
|
||||
inline bp_cache_t &getBPBuffer() { return m_Bp_cache; }
|
||||
/**
|
||||
* Retrieves the IOPortEvent buffer cache.
|
||||
* @returns the buffer cache
|
||||
*/
|
||||
inline io_cache_t &getIOBuffer() { return m_Io_cache; }
|
||||
private:
|
||||
/**
|
||||
* Add an event to its appropriate cache.
|
||||
* @param the event to add
|
||||
*/
|
||||
void addToCaches(BaseEvent *ev);
|
||||
/**
|
||||
* Remove an event from its cache.
|
||||
* @param the event to remove
|
||||
*/
|
||||
void removeFromCaches(BaseEvent *ev);
|
||||
/**
|
||||
* Clear the event caches.
|
||||
*/
|
||||
void clearCaches();
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
@ -91,33 +91,34 @@ void BochsController::dbgEnableInstrPtrOutput(unsigned regularity, std::ostream*
|
||||
|
||||
void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_space)
|
||||
{
|
||||
#if 0
|
||||
//the original code - performs magnitudes worse than
|
||||
//the code below and is responsible for most (~87 per cent)
|
||||
//of the slowdown of FailBochs
|
||||
#ifdef DEBUG
|
||||
if(m_Regularity != 0 && ++m_Counter % m_Regularity == 0)
|
||||
(*m_pDest) << "0x" << std::hex << instrPtr;
|
||||
#endif
|
||||
bool do_fire = false;
|
||||
// Check for active breakpoint-events:
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end())
|
||||
bp_cache_t &buffer_cache = m_EvList.getBPBuffer();
|
||||
bp_cache_t::iterator it = buffer_cache.begin();
|
||||
while(it != buffer_cache.end())
|
||||
{
|
||||
// FIXME: Maybe we need to improve the performance of this check.
|
||||
BPEvent* pEvBreakpt = dynamic_cast<BPEvent*>(*it);
|
||||
if(pEvBreakpt && pEvBreakpt->isMatching(instrPtr, address_space))
|
||||
BPEvent* pEvBreakpt = *it;
|
||||
if(pEvBreakpt->isMatching(instrPtr, address_space))
|
||||
{
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
it = buffer_cache.makeActive(m_EvList, it);
|
||||
do_fire = true;
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
}
|
||||
it++;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
#endif
|
||||
//this code is highly optimised for the average case, so it me appear a bit ugly
|
||||
if(do_fire)
|
||||
m_EvList.fireActiveEvents();
|
||||
// Note: SimulatorController::onBreakpointEvent will not be invoked in this
|
||||
// implementation.
|
||||
#if 0
|
||||
//deprecated - this code is ugly
|
||||
bool do_fire = false;
|
||||
int i = 0;
|
||||
BufferCache<BPEvent*> *buffer_cache = m_EvList.getBPBuffer();
|
||||
@ -140,21 +141,20 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp
|
||||
}
|
||||
if(do_fire)
|
||||
m_EvList.fireActiveEvents();
|
||||
// Note: SimulatorController::onBreakpointEvent will not be invoked in this
|
||||
// implementation.
|
||||
#endif
|
||||
}
|
||||
|
||||
void BochsController::onIOPortEvent(unsigned char data, unsigned port, bool out) {
|
||||
// Check for active breakpoint-events:
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end())
|
||||
io_cache_t &buffer_cache = m_EvList.getIOBuffer();
|
||||
io_cache_t::iterator it = buffer_cache.begin();
|
||||
while(it != buffer_cache.end())
|
||||
{
|
||||
// FIXME: Maybe we need to improve the performance of this check.
|
||||
IOPortEvent* pIOPt = dynamic_cast<IOPortEvent*>(*it);
|
||||
if(pIOPt && pIOPt->isMatching(port, out))
|
||||
IOPortEvent* pIOPt = (*it);
|
||||
if(pIOPt->isMatching(port, out))
|
||||
{
|
||||
pIOPt->setData(data);
|
||||
it = m_EvList.makeActive(it);
|
||||
it = buffer_cache.makeActive(m_EvList, it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
|
||||
Reference in New Issue
Block a user