Simulator specific listener are now implemented using aspects instead of an additional inheritance level

git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1706 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
friemel
2012-10-02 11:42:18 +00:00
parent 4d5bab72b6
commit 7d49b6f063
20 changed files with 176 additions and 296 deletions

View File

@ -7,7 +7,6 @@ if(BUILD_BOCHS)
SimulatorController.cc
perf/BreakpointBuffer.cc
bochs/BochsController.cc
bochs/BochsListener.cc
)
elseif(BUILD_GEM5)
set(SRCS
@ -17,7 +16,6 @@ elseif(BUILD_GEM5)
Register.cc
SimulatorController.cc
gem5/Gem5Controller.cc
gem5/Gem5Listener.cc
gem5/Gem5PCEvent.cc
)
elseif(BUILD_OVP)
@ -39,7 +37,6 @@ elseif(BUILD_QEMU)
perf/BreakpointBuffer.cc
SimulatorController.cc
qemu/QEMUController.cc
qemu/QEMUListener.cc
qemu/wrappers.cc
)
endif(BUILD_BOCHS)

View File

@ -74,7 +74,7 @@ bool BPRangeListener::isMatching(const BPEvent* pEv) const
return true;
}
bool GenericBPSingleListener::isMatching(const BPEvent* pEv) const
bool BPSingleListener::isMatching(const BPEvent* pEv) const
{
if (aspaceIsMatching(pEv->getCR3())) {
if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == pEv->getTriggerInstructionPointer())

View File

@ -173,7 +173,7 @@ public:
* \class BPSingleListener
* A Breakpoint listener to observe specific instruction pointers.
*/
class GenericBPSingleListener : public BPListener {
class BPSingleListener : public BPListener {
protected:
address_t m_WatchInstrPtr;
public:
@ -189,7 +189,7 @@ public:
* Here, too, ANY_ADDR is a placeholder to allow debugging
* in a random address space.
*/
GenericBPSingleListener(address_t ip = 0, address_t address_space = ANY_ADDR)
BPSingleListener(address_t ip = 0, address_t address_space = ANY_ADDR)
: BPListener(address_space), m_WatchInstrPtr(ip) { }
/**
* Returns the instruction pointer this listener waits for.
@ -353,14 +353,14 @@ public:
};
/**
* \class GenericMemWriteListener
* \class MemWriteListener
* Observes memory write accesses.
*/
class GenericMemWriteListener : public MemAccessListener {
class MemWriteListener : public MemAccessListener {
public:
GenericMemWriteListener()
MemWriteListener()
: MemAccessListener(MemAccessEvent::MEM_READ) { }
GenericMemWriteListener(address_t addr)
MemWriteListener(address_t addr)
: MemAccessListener(addr, MemAccessEvent::MEM_WRITE) { }
};
@ -568,16 +568,16 @@ public:
};
/**
* \class GenericTimerListener
* \class TimerListener
* This listener type is used to create timeouts within in an experiment.
*
* Depending on your simulator backend, a concrete class needs to be derived from
* \c GenericTimerListener. \c onAddition should be used to register and \c onDeletion
* \c TimerListener. \c onAddition should be used to register and \c onDeletion
* to unregister the timer. \c onTrigger can be used to re-register the timer if a
* repetitive timer is requested and the back-end doesn't support such timer types
* natively.
*/
class GenericTimerListener : public BaseListener {
class TimerListener : public BaseListener {
protected:
unsigned m_Timeout; //!< timeout interval in milliseconds
GenericTimerEvent m_Data;
@ -589,8 +589,8 @@ public:
* @param timeout the time interval in milliseconds (ms)
* @see SimulatorController::addListener
*/
GenericTimerListener(unsigned timeout) : m_Timeout(timeout) { }
~GenericTimerListener() { }
TimerListener(unsigned timeout) : m_Timeout(timeout) { }
~TimerListener() { }
/**
* Retrieves the internal timer id. Maybe useful for debug output.
* @return the timer id
@ -610,16 +610,4 @@ public:
} // end-of-namespace: fail
#if defined BUILD_BOCHS
#include "bochs/BochsListener.hpp"
#elif defined BUILD_GEM5
#include "gem5/Gem5Listener.hpp"
#elif defined BUILD_OVP
// #include "ovp/OVPListener.hpp"
#elif defined BUILD_QEMU
#include "qemu/QEMUListener.hpp"
#else
#error SAL Config Target not defined
#endif
#endif // __LISTENER_HPP__

View File

@ -3,6 +3,8 @@
#include "ListenerManager.hpp"
#include "SALInst.hpp"
#include "Listener.hpp"
namespace fail {
void ListenerManager::add(BaseListener* li, ExperimentFlow* pExp)

View File

@ -6,11 +6,12 @@
#include <vector>
#include <algorithm>
#include "Listener.hpp"
#include "perf/BufferInterface.hpp"
namespace fail {
class ExperimentFlow;
class BaseListener;
/**
* Buffer-list for a specific experiment; acts as a simple storage container

View File

@ -1,6 +1,7 @@
#include "SimulatorController.hpp"
#include "SALInst.hpp"
#include "Event.hpp"
#include "Listener.hpp"
namespace fail {

View File

@ -9,7 +9,6 @@
#include "efw/CoroutineManager.hpp"
#include "ListenerManager.hpp"
#include "SALConfig.hpp"
#include "Listener.hpp"
namespace fail {

View File

@ -5,6 +5,7 @@
#include "BochsRegister.hpp"
#include "../Register.hpp"
#include "../SALInst.hpp"
#include "../Listener.hpp"
namespace fail {

View File

@ -8,10 +8,8 @@
#include <string.h>
#include "FailBochsGlobals.hpp"
#include "BochsListener.hpp"
#include "../SimulatorController.hpp"
#include "../Listener.hpp"
#include "bochs.h"
#include "cpu/cpu.h"

View File

@ -0,0 +1,51 @@
#ifndef __BOCHSLISTENER_AH__
#define __BOCHSLISTENER_AH__
#include "config/VariantConfig.hpp"
#include "config/FailConfig.hpp"
#if defined(BUILD_BOCHS) && defined(CONFIG_EVENT_BREAKPOINTS)
#include "bochs.h"
#include "BochsController.hpp"
aspect BochsListener
{
advice "fail::TimerListener" : slice class
{
public:
bool onAddition()
{
// Register the timer listener in the Bochs simulator:
setId(m_registerTimer(this));
if(getId() == -1)
return false; // unable to register the timer (error in Bochs' function call)
return true;
}
void onDeletion()
{
// Unregister the time listener:
m_unregisterTimer(this);
}
private:
timer_id_t m_registerTimer(TimerListener* pev)
{
assert(pev != NULL && "FATAL ERROR: TimerListener object ptr cannot be NULL!");
return static_cast<timer_id_t>(
bx_pc_system.register_timer(pev, BochsController::onTimerTrigger, pev->getTimeout(),
false, 1/*start immediately*/, "Fail*: BochsController"/*name*/));
}
bool m_unregisterTimer(TimerListener* pev)
{
assert(pev != NULL && "FATAL ERROR: TimerListener object ptr cannot be NULL!");
bx_pc_system.deactivate_timer(static_cast<unsigned>(pev->getId()));
return bx_pc_system.unregisterTimer(static_cast<unsigned>(pev->getId()));
}
};
};
#endif // BUILD_BOCHS && CONFIG_EVENT_BREAKPOINTS
#endif // __BOCHSLISTENER_AH__

View File

@ -1,36 +0,0 @@
#include "BochsListener.hpp"
#include "../SALInst.hpp"
namespace fail {
bool TimerListener::onAddition()
{
// Register the timer listener in the Bochs simulator:
setId(m_registerTimer(this));
if(getId() == -1)
return false; // unable to register the timer (error in Bochs' function call)
return true;
}
void TimerListener::onDeletion()
{
// Unregister the time listener:
m_unregisterTimer(this);
}
timer_id_t TimerListener::m_registerTimer(TimerListener* pev)
{
assert(pev != NULL && "FATAL ERROR: TimerListener object ptr cannot be NULL!");
return static_cast<timer_id_t>(
bx_pc_system.register_timer(pev, BochsController::onTimerTrigger, pev->getTimeout(),
false, 1/*start immediately*/, "Fail*: BochsController"/*name*/));
}
bool TimerListener::m_unregisterTimer(TimerListener* pev)
{
assert(pev != NULL && "FATAL ERROR: TimerListener object ptr cannot be NULL!");
bx_pc_system.deactivate_timer(static_cast<unsigned>(pev->getId()));
return bx_pc_system.unregisterTimer(static_cast<unsigned>(pev->getId()));
}
} // end-of-namespace: fail

View File

@ -1,81 +0,0 @@
#ifndef __BOCHS_LISTENER_HPP__
#define __BOCHS_LISTENER_HPP__
#include "../Listener.hpp"
namespace fail {
typedef GenericBPSingleListener BPSingleListener;
typedef GenericMemWriteListener MemWriteListener;
/**
* \class TimerListener
* Concrete TimerListener implementation of GenericTimerListener for the Bochs
* simulator backend. A (Fail)Bochs bug currently leads to the consequence that
* timers cannot be added/enabled at boot time (see BochsController.hpp for
* further details).
*/
class TimerListener : public GenericTimerListener {
private:
/**
* Registers a timer in the Bochs simulator. This timer triggers \a TimerListeners
* to inform the corresponding experiment-flow. Note that the number of timers
* (in Bochs) is limited to \c BX_MAX_TIMERS (defaults to 64 in v2.4.6).
* @param pli a pointer to the (experiment flow-) allocated TimerListener object,
* providing all required information to start the timer (e.g. the
* timeout value).
* @return The unique id of the timer recently created. This id is carried
* along with the TimerListener. On errors, -1 is returned (e.g. because
* a timer with the same id is already existing)
* @see TimerListener::getId()
*/
static timer_id_t m_registerTimer(TimerListener* pli);
/**
* Deletes a timer. No further events will be triggered by the timer.
* @param pli a pointer to the TimerListener-object to be removed
* @return \c true if the timer with \a pli->getId() has been removed
* successfully, \c false otherwise
*/
static bool m_unregisterTimer(TimerListener* pli);
public:
/**
* Creates a new timer event. This can be used to implement a timeout-
* mechanism in the experiment-flow. The timer starts automatically when
* added to FailBochs.
* @param timeout the time intervall in milliseconds (ms)
* @see SimulatorController::addListener
*/
TimerListener(unsigned timeout)
: GenericTimerListener(timeout) { }
~TimerListener() { onDeletion(); } // FIXME ~BaseListener should automatically dequeue a Listener, and then indirectly calls onDeletion. In the current implementation, no dequeueing happens at all.
/**
* This method is called when an experiment flow adds a new event by
* calling \c simulator.addListener() or \c simulator.addListenerAndResume().
* More specifically, the event will be added to the event-list first
* (buffer-list, to be precise) and then this event handler is called.
* @return You should return \c true to continue and \c false to prevent
* the addition of the event \a pev, yielding an error in the
* experiment flow (i.e. -1 is returned).
*/
bool onAddition();
/**
* This method is called when an experiment flow removes an event from
* the event-management by calling \c removeListener(prev), \c clearListeners()
* or by deleting a complete flow (\c removeFlow). More specifically, this
* event handler will be called *before* the event is actually deleted.
*/
void onDeletion();
/**
* This method is called when an previously added event is about to be
* triggered by the simulator-backend. More specifically, this event handler
* will be called *before* the event is actually triggered, i.e. before the
* corresponding coroutine is toggled.
*/
void onTrigger() { onDeletion(); } // FIXME It should NOT be a Listener's job to call its own onDeletion. ListenerManager should call onDeletion when it deletes a Listener from a queue.
// TODO/FIXME: bei neuer impl. anpassen
};
} // end-of-namespace: fail
#endif // __BOCHS_LISTENER_HPP__

View File

@ -35,18 +35,21 @@ void Gem5Controller::reboot()
void Gem5Controller::onBreakpoint(address_t instrPtr, address_t address_space)
{
bool do_fire = false;
BPEvent tmp(instrPtr, address_space);
// Check for active breakpoint-events:
for (ListenerManager::iterator it = m_LstList.begin(); it != m_LstList.end(); it++) {
ListenerManager::iterator it = m_LstList.begin();
BPEvent tmp(instrPtr, address_space);
while (it != m_LstList.end()) {
BaseListener* pLi = *it;
BPListener* pBreakpt = dynamic_cast<BPListener*>(pLi);
if(pBreakpt != NULL && pBreakpt->isMatching(&tmp)) {
if (pBreakpt != NULL && pBreakpt->isMatching(&tmp)) {
pBreakpt->setTriggerInstructionPointer(instrPtr);
it = m_LstList.makeActive(it);
do_fire = true;
// "it" has already been set to the next element (by calling makeActive()):
// "it" has already been set to the next element (by calling
// makeActive()):
continue; // -> skip iterator increment
}
it++;
}
if (do_fire)
m_LstList.triggerActiveListeners();

View File

@ -0,0 +1,39 @@
#ifndef __GEM5LISTENER_AH__
#define __GEM5LISTENER_AH__
#include "config/VariantConfig.hpp"
#include "config/FailConfig.hpp"
#if defined(BUILD_GEM5) && defined(CONFIG_EVENT_BREAKPOINTS)
#include "Gem5PCEvent.hh"
#include "sim/system.hh"
aspect Gem5Listener
{
advice "fail::BPSingleListener" : slice class
{
private:
Gem5PCEvent* m_Breakpoint;
public:
virtual bool onAddition()
{
System* sys = *System::systemList.begin();
m_Breakpoint = new Gem5PCEvent(&sys->pcEventQueue, this->m_WatchInstrPtr);
return true;
}
virtual void onDeletion()
{
if(m_Breakpoint)
{
delete m_Breakpoint;
m_Breakpoint = 0;
}
}
};
};
#endif // BUILD_GEM5 && CONFIG_EVENT_BREAKPOINTS
#endif // __GEM5LISTENER_AH__

View File

@ -1,33 +0,0 @@
#include "Gem5Listener.hpp"
#include "../SALInst.hpp"
#include "sim/system.hh"
namespace fail {
Gem5BPSingleListener::Gem5BPSingleListener(address_t ip)
: GenericBPSingleListener(ip)
{
}
bool Gem5BPSingleListener::onAddition()
{
if(!m_Breakpoint)
{
System* sys = *System::systemList.begin();
m_Breakpoint = new Gem5PCEvent(&sys->pcEventQueue, this->m_WatchInstrPtr);
return true;
}
return false;
}
Gem5BPSingleListener::~Gem5BPSingleListener()
{
if(m_Breakpoint)
{
delete m_Breakpoint;
}
}
} // end-of-namespace: fail

View File

@ -1,25 +0,0 @@
#ifndef __GEM5_LISTENER_HPP__
#define __GEM5_LISTENER_HPP__
#include "../Listener.hpp"
#include "Gem5PCEvent.hh"
namespace fail {
typedef GenericMemWriteListener MemWriteListener;
class Gem5BPSingleListener : public GenericBPSingleListener
{
public:
Gem5BPSingleListener(address_t ip = 0);
virtual bool onAddition();
~Gem5BPSingleListener();
private:
Gem5PCEvent* m_Breakpoint;
};
typedef Gem5BPSingleListener BPSingleListener;
} // end-of-namespace: fail
#endif // __GEM5_LISTENER_HPP__

View File

@ -8,13 +8,13 @@
#include <string.h>
#include "../SimulatorController.hpp"
#include "../Listener.hpp"
struct CPUX86State;
namespace fail {
class ExperimentFlow;
class TimerListener;
/**
* \class QEMUController

View File

@ -0,0 +1,60 @@
#ifndef __QEMULISTENER_AH__
#define __QEMULISTENER_AH__
#include "config/VariantConfig.hpp"
#include "config/FailConfig.hpp"
#if defined(BUILD_QEMU) && defined(CONFIG_EVENT_BREAKPOINTS)
#include "../SALInst.hpp"
extern "C" {
#include "qemu/failqemu.h"
}
aspect QEMUListener
{
advice "fail::TimerListener" : slice class
{
public:
bool onAddition()
{
//std::cout << "QEMUTimerListener::onAddition" << std::endl;
setId(failqemu_register_timer(getTimeout(), (void *)this));
//std::cout << "this = " << std::hex << (unsigned) this << std::endl;
//std::cout << "id = " << std::hex << (unsigned) getId() << std::endl;
return true;
}
void onDeletion()
{
//std::cout << "QEMUTimerListener::onDeletion" << std::endl;
//std::cout << "this = " << std::hex << (unsigned) this << std::endl;
//std::cout << "id = " << std::hex << (unsigned) getId() << std::endl;
failqemu_unregister_timer(getId());
}
};
advice "fail::MemWriteListener" : slice class
{
public:
bool onAddition()
{
//std::cout << "QEMUMemWriteListener::onAddition" << std::endl;
if (failqemu_add_watchpoint(simulator.m_cpuenv, m_WatchAddr, m_WatchWidth, 1) != 0) {
std::cout << "adding watchpoint failed!" << std::endl;
return false;
}
return true;
}
void onDeletion()
{
//std::cout << "QEMUMemWriteListener::onDeletion" << std::endl;
failqemu_remove_watchpoint(simulator.m_cpuenv, m_WatchAddr, m_WatchWidth, 1);
}
};
};
#endif // BUILD_QEMU && CONFIG_EVENT_BREAKPOINTS
#endif // __QEMULISTENER_AH__

View File

@ -1,45 +0,0 @@
#include <iostream>
#include "QEMUListener.hpp"
#include "../SALInst.hpp"
extern "C" {
#include "qemu/failqemu.h"
}
namespace fail {
bool QEMUMemWriteListener::onAddition()
{
//std::cout << "QEMUMemWriteListener::onAddition" << std::endl;
if (failqemu_add_watchpoint(simulator.m_cpuenv, m_WatchAddr, m_WatchWidth, 1) != 0) {
std::cout << "adding watchpoint failed!" << std::endl;
return false;
}
return true;
}
void QEMUMemWriteListener::onDeletion()
{
//std::cout << "QEMUMemWriteListener::onDeletion" << std::endl;
failqemu_remove_watchpoint(simulator.m_cpuenv, m_WatchAddr, m_WatchWidth, 1);
}
bool QEMUTimerListener::onAddition()
{
//std::cout << "QEMUTimerListener::onAddition" << std::endl;
setId(failqemu_register_timer(getTimeout(), (void *)this));
//std::cout << "this = " << std::hex << (unsigned) this << std::endl;
//std::cout << "id = " << std::hex << (unsigned) getId() << std::endl;
return true;
}
void QEMUTimerListener::onDeletion()
{
//std::cout << "QEMUTimerListener::onDeletion" << std::endl;
//std::cout << "this = " << std::hex << (unsigned) this << std::endl;
//std::cout << "id = " << std::hex << (unsigned) getId() << std::endl;
failqemu_unregister_timer(getId());
}
} // end-of-namespace: fail

View File

@ -1,40 +0,0 @@
#ifndef __QEMU_LISTENER_HPP__
#define __QEMU_LISTENER_HPP__
#include "../Listener.hpp"
namespace fail {
typedef GenericBPSingleListener BPSingleListener;
/**
* \class QEMUTimerListener
* Concrete TimerListener implementation of GenericTimerListener for QEMU.
*/
class QEMUTimerListener : public GenericTimerListener {
private:
public:
QEMUTimerListener(unsigned timeout)
: GenericTimerListener(timeout) { }
~QEMUTimerListener() { onDeletion(); } // FIXME ~BaseListener should automatically dequeue a Listener, and then indirectly calls onDeletion. In the current implementation, no dequeueing happens at all.
bool onAddition();
void onDeletion();
};
typedef QEMUTimerListener TimerListener;
class QEMUMemWriteListener : public GenericMemWriteListener {
public:
QEMUMemWriteListener()
: GenericMemWriteListener() { }
QEMUMemWriteListener(address_t addr)
: GenericMemWriteListener(addr) { }
virtual bool onAddition();
virtual void onDeletion();
};
typedef QEMUMemWriteListener MemWriteListener;
} // end-of-namespace: fail
#endif // __QEMU_LISTENER_HPP__