another directory rename: failstar -> fail
"failstar" sounds like a name for a cruise liner from the 80s. As "*" isn't a desirable part of directory names, just name the whole thing "fail/", the core parts being stored in "fail/core/". Additionally fixing two build system dependency issues: - missing jobserver -> protomessages dependency - broken bochs -> fail dependency (add_custom_target DEPENDS only allows plain file dependencies ... cmake for the win) git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@956 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
11
core/controller/CMakeLists.txt
Normal file
11
core/controller/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
set(SRCS
|
||||
CampaignManager.cc
|
||||
CoroutineManager.cc
|
||||
Event.cc
|
||||
EventList.cc
|
||||
ExperimentDataQueue.cc
|
||||
Signal.cc
|
||||
SynchronizedExperimentDataQueue.cc
|
||||
)
|
||||
|
||||
add_library(controller ${SRCS})
|
||||
31
core/controller/Campaign.hpp
Normal file
31
core/controller/Campaign.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef __CAMPAIGN_HPP__
|
||||
#define __CAMPAIGN_HPP__
|
||||
|
||||
// Author: Martin Hoffmann
|
||||
// Date: 09.12.2011
|
||||
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
/**
|
||||
* \class Campaign
|
||||
* Basic interface for user-defined campaigns. To create a new
|
||||
* campaign, derive your own class from Campaign,
|
||||
* define the run method, and add it to the CampaignManager.
|
||||
*/
|
||||
class Campaign
|
||||
{
|
||||
|
||||
public:
|
||||
Campaign() { };
|
||||
/**
|
||||
* Defines the campaign.
|
||||
* @return \c true if the campaign was successful, \c false otherwise
|
||||
*/
|
||||
virtual bool run() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* __CAMPAIGN_HPP__ */
|
||||
9
core/controller/CampaignManager.cc
Normal file
9
core/controller/CampaignManager.cc
Normal file
@ -0,0 +1,9 @@
|
||||
#include "CampaignManager.hpp"
|
||||
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
CampaignManager campaignmanager;
|
||||
|
||||
}//end-of-namespace
|
||||
90
core/controller/CampaignManager.hpp
Normal file
90
core/controller/CampaignManager.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* \brief The manager for an entire campaign
|
||||
*
|
||||
* The CampaignManager allows a user-campaign access to all constant
|
||||
* simulator information and forwards single experiments to the JobServer.
|
||||
*
|
||||
* \author Martin Hoffmann
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __CAMPAIGN_MANAGER_H__
|
||||
#define __CAMPAIGN_MANAGER_H__
|
||||
|
||||
#include "SAL/SALInst.hpp"
|
||||
#include "ExperimentData.hpp"
|
||||
#include "jobserver/JobServer.hpp"
|
||||
#include "controller/Campaign.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
/**
|
||||
* \class CampaignManager
|
||||
* Class manageing an FI campaign.
|
||||
*/
|
||||
class CampaignManager
|
||||
{
|
||||
|
||||
JobServer m_jobserver;
|
||||
Campaign* m_currentCampaign;
|
||||
|
||||
public:
|
||||
CampaignManager(){};
|
||||
|
||||
/**
|
||||
* Executes a user campaign
|
||||
*/
|
||||
bool runCampaign(Campaign* c){
|
||||
m_currentCampaign = c;
|
||||
bool ret = c->run();
|
||||
m_jobserver.done();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a const reference for acquiring constant simlator specific information.
|
||||
* e.g., Registernames, to ease experiment data construction.
|
||||
* The campaign description is not allowed to change the simulator
|
||||
* state, as the actual simulation runs within another process (Minion)
|
||||
* @return c constant reference to the current simulator backend.
|
||||
*/
|
||||
sal::SimulatorController const& getSimulator() const { return sal::simulator; };
|
||||
|
||||
/**
|
||||
* Add a experiment parameter set.
|
||||
* The user campaign has to allocate the Parameter object,
|
||||
* and deallocate it after result reception.
|
||||
* A Parameter set includes space for results.
|
||||
* @param exp A pointer to a ExperimentData set.
|
||||
*/
|
||||
void addParam(ExperimentData* exp){ m_jobserver.addParam(exp); };
|
||||
|
||||
/**
|
||||
* A user campaign can request a single result (blocking) from the queue.
|
||||
*
|
||||
* @return Pointer to a parameter object with filled result data @see addParam.
|
||||
*/
|
||||
ExperimentData* getDone() { return m_jobserver.getDone(); };
|
||||
|
||||
/**
|
||||
* Signal, that there will not come any further parameter sets.
|
||||
*/
|
||||
void noMoreParameters() { m_jobserver.setNoMoreExperiments(); };
|
||||
|
||||
/**
|
||||
* Wait actively, until all experiments expired.
|
||||
*/
|
||||
// void waitForCompletion();
|
||||
|
||||
/**
|
||||
* User campaign has finished..
|
||||
*/
|
||||
void done() { m_jobserver.done(); };
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
extern CampaignManager campaignmanager;
|
||||
} //end-of-namespace
|
||||
#endif
|
||||
96
core/controller/CoroutineManager.cc
Normal file
96
core/controller/CoroutineManager.cc
Normal file
@ -0,0 +1,96 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 05.10.2011
|
||||
|
||||
#include <iostream>
|
||||
#include "CoroutineManager.hpp"
|
||||
#include "../controller/ExperimentFlow.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
void CoroutineManager::m_invoke(void* pData)
|
||||
{
|
||||
//std::cerr << "CORO m_invoke " << co_current() << std::endl;
|
||||
reinterpret_cast<ExperimentFlow*>(pData)->coroutine_entry();
|
||||
co_exit(); // deletes the associated coroutine memory as well
|
||||
|
||||
// we really shouldn't get here
|
||||
std::cerr << "CoroutineManager::m_invoke() shitstorm unloading" << std::endl;
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
CoroutineManager::~CoroutineManager()
|
||||
{
|
||||
}
|
||||
|
||||
void CoroutineManager::toggle(ExperimentFlow* flow)
|
||||
{
|
||||
m_togglerstack.push(co_current());
|
||||
//std::cerr << "CORO toggle from " << m_togglerstack.top() << " to ";
|
||||
if (flow == SIM_FLOW) {
|
||||
co_call(m_simCoro);
|
||||
return;
|
||||
}
|
||||
|
||||
flowmap_t::iterator it = m_Flows.find(flow);
|
||||
assert(it != m_Flows.end() && "FATAL ERROR: Flow does not exist!");
|
||||
//std::cerr << it->second << std::endl;
|
||||
co_call(it->second);
|
||||
}
|
||||
|
||||
void CoroutineManager::create(ExperimentFlow* flow)
|
||||
{
|
||||
corohandle_t res = co_create(CoroutineManager::m_invoke, flow, NULL,
|
||||
STACK_SIZE_DEFAULT);
|
||||
//std::cerr << "CORO create " << res << std::endl;
|
||||
m_Flows.insert(std::pair<ExperimentFlow*,corohandle_t>(flow, res));
|
||||
}
|
||||
|
||||
void CoroutineManager::remove(ExperimentFlow* flow)
|
||||
{
|
||||
// find coroutine handle for this flow
|
||||
flowmap_t::iterator it = m_Flows.find(flow);
|
||||
if (it == m_Flows.end()) {
|
||||
assert(false && "FATAL ERROR: Cannot remove flow");
|
||||
return;
|
||||
}
|
||||
corohandle_t coro = it->second;
|
||||
//std::cerr << "CORO remove " << coro << std::endl;
|
||||
|
||||
// remove flow from active list
|
||||
m_Flows.erase(it);
|
||||
|
||||
// FIXME make sure resume() keeps working
|
||||
|
||||
// delete coroutine (and handle the special case we're removing
|
||||
// ourselves)
|
||||
if (coro == co_current()) {
|
||||
co_exit();
|
||||
} else {
|
||||
co_delete(coro);
|
||||
}
|
||||
}
|
||||
|
||||
void CoroutineManager::resume()
|
||||
{
|
||||
corohandle_t next = m_togglerstack.top();
|
||||
m_togglerstack.pop();
|
||||
//std::cerr << "CORO resume from " << co_current() << " to " << next << std::endl;
|
||||
co_call(next);
|
||||
}
|
||||
|
||||
ExperimentFlow* CoroutineManager::getCurrent()
|
||||
{
|
||||
coroutine_t cr = co_current();
|
||||
for(std::map<ExperimentFlow*,corohandle_t>::iterator it = m_Flows.begin();
|
||||
it != m_Flows.end(); it++)
|
||||
if(it->second == cr)
|
||||
return (it->first);
|
||||
assert(false && "FATAL ERROR: The current flow could not be retrieved!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ExperimentFlow* CoroutineManager::SIM_FLOW = NULL;
|
||||
|
||||
}
|
||||
78
core/controller/CoroutineManager.hpp
Normal file
78
core/controller/CoroutineManager.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
#ifndef __COROUTINE_MANAGER_HPP__
|
||||
#define __COROUTINE_MANAGER_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 05.10.2011
|
||||
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
#include <pcl.h> // the underlying "portable coroutine library"
|
||||
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
class ExperimentFlow;
|
||||
|
||||
/**
|
||||
* \class CoroutineManager
|
||||
* Manages the experiments flow encapsulated in coroutines. Each
|
||||
* experiment (flow) has it's associated data structure which is
|
||||
* represented by the ExperimentData-class.
|
||||
*/
|
||||
class CoroutineManager
|
||||
{
|
||||
private:
|
||||
//! the default stack size for coroutines (= experiment flows)
|
||||
static const unsigned STACK_SIZE_DEFAULT = 4096*4096;
|
||||
//! the abstraction for coroutine identification
|
||||
typedef coroutine_t corohandle_t;
|
||||
typedef std::map<ExperimentFlow*,corohandle_t> flowmap_t;
|
||||
//! the mapping "flows <-> coro-handle"
|
||||
flowmap_t m_Flows;
|
||||
//! the simulator/backend coroutine handle
|
||||
corohandle_t m_simCoro;
|
||||
//! stack of coroutines that explicitly activated another one with toggle()
|
||||
std::stack<corohandle_t> m_togglerstack;
|
||||
//! manages the run-calls for each ExperimentFlow-object
|
||||
static void m_invoke(void* pData);
|
||||
public:
|
||||
static const ExperimentFlow* SIM_FLOW; //!< the simulator coroutine flow
|
||||
|
||||
CoroutineManager() : m_simCoro(co_current()) { }
|
||||
~CoroutineManager();
|
||||
/**
|
||||
* Creates a new coroutine for the specified experiment flow.
|
||||
* @param flow the flow to be executed in the newly created coroutine
|
||||
*/
|
||||
void create(ExperimentFlow* flow);
|
||||
/**
|
||||
* Destroys coroutine for the specified experiment flow.
|
||||
* @param flow the flow to be removed
|
||||
*/
|
||||
void remove(ExperimentFlow* flow);
|
||||
/**
|
||||
* Switches the control flow to the experiment \a flow. If \a flow is
|
||||
* equal to \c SIM_FLOW, the control will be handed back to the
|
||||
* simulator. The current control flow is pushed onto an
|
||||
* internal stack.
|
||||
* @param flow the destination control flow or \c SIM_FLOW (= \c NULL )
|
||||
*/
|
||||
void toggle(ExperimentFlow* flow);
|
||||
/**
|
||||
* Gives the control back to the coroutine that toggle()d the
|
||||
* current one, by drawing from the internal stack built from
|
||||
* calls to toggle().
|
||||
*/
|
||||
void resume();
|
||||
/**
|
||||
* Retrieves the current (active) coroutine (= flow).
|
||||
* @return the current experiment flow.
|
||||
*/
|
||||
ExperimentFlow* getCurrent();
|
||||
};
|
||||
|
||||
} // end-of-namespace: fi
|
||||
|
||||
#endif /* __COROUTINE_MANAGER_HPP__ */
|
||||
79
core/controller/Event.cc
Normal file
79
core/controller/Event.cc
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 08.06.2011
|
||||
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
EventId BaseEvent::m_Counter = 0;
|
||||
|
||||
bool TroubleEvent::isMatching(unsigned troubleNum) const
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
if(m_WatchNumbers[i] == troubleNum ||
|
||||
m_WatchNumbers[i] == ANY_TRAP)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TroubleEvent::removeWatchNumber(unsigned troubleNum)
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
if(m_WatchNumbers[i] == troubleNum)
|
||||
{
|
||||
m_WatchNumbers.erase(m_WatchNumbers.begin()+i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TroubleEvent::addWatchNumber(unsigned troubleNumber)
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
|
||||
if(m_WatchNumbers[i] == troubleNumber)
|
||||
return false;
|
||||
}
|
||||
m_WatchNumbers.push_back(troubleNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemAccessEvent::isMatching(sal::address_t addr, accessType_t accesstype) const
|
||||
{
|
||||
if(!(m_WatchType & accesstype))
|
||||
return (false);
|
||||
else if(m_WatchAddr != addr &&
|
||||
m_WatchAddr != ANY_ADDR)
|
||||
return (false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
void BPRangeEvent::setWatchInstructionPointerRange(sal::address_t start, sal::address_t end)
|
||||
{
|
||||
m_WatchStartAddr = start;
|
||||
m_WatchEndAddr = end;
|
||||
}
|
||||
|
||||
bool BPRangeEvent::isMatching(sal::address_t addr) const
|
||||
{
|
||||
if ((m_WatchStartAddr != ANY_ADDR && addr < m_WatchStartAddr) ||
|
||||
(m_WatchEndAddr != ANY_ADDR && addr > m_WatchEndAddr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPEvent::isMatching(sal::address_t addr) const
|
||||
{
|
||||
if(m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr)
|
||||
return (true);
|
||||
return (false);
|
||||
}
|
||||
|
||||
} // end-of-namespace: fi
|
||||
486
core/controller/Event.hpp
Normal file
486
core/controller/Event.hpp
Normal file
@ -0,0 +1,486 @@
|
||||
#ifndef __EVENT_HPP__
|
||||
#define __EVENT_HPP__
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "../SAL/SALConfig.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
class ExperimentFlow;
|
||||
|
||||
typedef unsigned long EventId; //!< type of event ids
|
||||
|
||||
//! invalid event id (used as a return indicator)
|
||||
const EventId INVALID_EVENT = (EventId)-1;
|
||||
//! address wildcard (e.g. for BPEvent)
|
||||
const sal::address_t ANY_ADDR = static_cast<sal::address_t>(-1);
|
||||
//! instruction wildcard
|
||||
const unsigned ANY_INSTR = static_cast<unsigned>(-1);
|
||||
//! trap wildcard
|
||||
const unsigned ANY_TRAP = static_cast<unsigned>(-1);
|
||||
//! interrupt wildcard
|
||||
const unsigned ANY_INTERRUPT = static_cast<unsigned>(-1);
|
||||
|
||||
/**
|
||||
* \class BaseEvent
|
||||
* This is the base class for all event types.
|
||||
*/
|
||||
class BaseEvent
|
||||
{
|
||||
private:
|
||||
//! current class-scoped id counter to provide \a unique id's
|
||||
static EventId m_Counter;
|
||||
protected:
|
||||
EventId m_Id; //!< unique id of this event
|
||||
time_t m_tStamp; //!< time stamp of event
|
||||
unsigned int m_OccCounter; //!< event fires when 0 is reached
|
||||
unsigned int m_OccCounterInit; //!< initial value for m_OccCounter
|
||||
ExperimentFlow* m_Parent; //!< this event belongs to experiment m_Parent
|
||||
public:
|
||||
BaseEvent() : m_Id(++m_Counter), m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL)
|
||||
{ updateTime(); }
|
||||
virtual ~BaseEvent() { }
|
||||
/**
|
||||
* Retrieves the unique event id for this event.
|
||||
* @return the unique id
|
||||
*/
|
||||
EventId getId() const { return (m_Id); }
|
||||
/**
|
||||
* Retrieves the time stamp of this event. The time stamp is set when
|
||||
* the event gets created, id est the constructor is called. The meaning
|
||||
* of this value depends on the actual event type.
|
||||
* @return the time stamp
|
||||
*/
|
||||
std::time_t getTime() const { return (m_tStamp); }
|
||||
/**
|
||||
* Decreases the event counter by one. When this counter reaches zero, the
|
||||
* event will be triggered.
|
||||
*/
|
||||
void decreaseCounter() { --m_OccCounter; }
|
||||
/**
|
||||
* Sets the event counter to the specified value (default is one).
|
||||
* @param val the new counter value
|
||||
*/
|
||||
void setCounter(unsigned int val = 1) { m_OccCounter = m_OccCounterInit = val; }
|
||||
/**
|
||||
* Returns the current counter value.
|
||||
* @return the counter value
|
||||
*/
|
||||
unsigned int getCounter() const { return (m_OccCounter); }
|
||||
/**
|
||||
* Resets the event counter to its default value, or the last
|
||||
* value that was set through setCounter().
|
||||
*/
|
||||
void resetCounter() { m_OccCounter = m_OccCounterInit; }
|
||||
/**
|
||||
* Sets the time stamp for this event to the current system time.
|
||||
*/
|
||||
void updateTime() { time(&m_tStamp); }
|
||||
/**
|
||||
* Returns the parent experiment of this event (context).
|
||||
* If the event was created temporarily or wasn't linked to a context,
|
||||
* the return value is \c NULL (unknown identity).
|
||||
*/
|
||||
ExperimentFlow* getParent() const { return (m_Parent); }
|
||||
/**
|
||||
* Sets the parent (context) of this event. The default context
|
||||
* is \c NULL (unknown identity).
|
||||
*/
|
||||
void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; }
|
||||
};
|
||||
// FIXME: Dynamische Casts zur Laufzeit evtl. zu uneffizient?
|
||||
// (vgl. auf NULL evtl. akzeptabel?) Bessere Lösungen?
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Specialized events:
|
||||
//
|
||||
|
||||
/**
|
||||
* \class BPEvent
|
||||
* A Breakpoint event to observe specific instruction pointers.
|
||||
* FIXME consider renaming to BPSingleEvent, introducing common BPEvent base class
|
||||
*/
|
||||
class BPEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
sal::address_t m_WatchInstrPtr;
|
||||
sal::address_t m_TriggerInstrPtr;
|
||||
public:
|
||||
/**
|
||||
* Creates a new breakpoint event.
|
||||
* @param ip the instruction pointer of the breakpoint. If the control
|
||||
* flow reaches this address and its counter value is zero, the
|
||||
* event will be triggered. \a eip can be set to the ANY_ADDR
|
||||
* wildcard to allow arbitrary addresses.
|
||||
*/
|
||||
BPEvent(sal::address_t ip = 0)
|
||||
: m_WatchInstrPtr(ip), m_TriggerInstrPtr(ANY_ADDR) { }
|
||||
/**
|
||||
* Returns the instruction pointer this event waits for.
|
||||
*/
|
||||
sal::address_t getWatchInstructionPointer() const
|
||||
{ return m_WatchInstrPtr; }
|
||||
/**
|
||||
* Sets the instruction pointer this event waits for.
|
||||
*/
|
||||
void setWatchInstructionPointer(sal::address_t iptr)
|
||||
{ m_WatchInstrPtr = iptr; }
|
||||
/**
|
||||
* Checks whether a given address is matching.
|
||||
*/
|
||||
bool isMatching(sal::address_t addr) const;
|
||||
/**
|
||||
* Returns the instruction pointer that triggered this event.
|
||||
*/
|
||||
sal::address_t getTriggerInstructionPointer() const
|
||||
{ return m_TriggerInstrPtr; }
|
||||
/**
|
||||
* Sets the instruction pointer that triggered this event. Should not
|
||||
* be used by experiment code.
|
||||
*/
|
||||
void setTriggerInstructionPointer(sal::address_t iptr)
|
||||
{ m_TriggerInstrPtr = iptr; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BPRangeEvent
|
||||
* A event type to observe ranges of instruction pointers.
|
||||
*/
|
||||
class BPRangeEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
sal::address_t m_WatchStartAddr;
|
||||
sal::address_t m_WatchEndAddr;
|
||||
sal::address_t m_TriggerInstrPtr;
|
||||
public:
|
||||
/**
|
||||
* Creates a new breakpoint-range event. The range's ends are both
|
||||
* inclusive, i.e. an address matches if start <= addr <= end.
|
||||
* ANY_ADDR denotes the lower respectively the upper end of the address
|
||||
* space.
|
||||
*/
|
||||
BPRangeEvent(sal::address_t start = 0, sal::address_t end = 0)
|
||||
: m_WatchStartAddr(start), m_WatchEndAddr(end),
|
||||
m_TriggerInstrPtr(ANY_ADDR) { }
|
||||
/**
|
||||
* Sets the instruction pointer watch range. Both ends of the range
|
||||
* may be ANY_ADDR (cf. constructor).
|
||||
*/
|
||||
void setWatchInstructionPointerRange(sal::address_t start,
|
||||
sal::address_t end);
|
||||
/**
|
||||
* Checks whether a given address is within the range.
|
||||
*/
|
||||
bool isMatching(sal::address_t addr) const;
|
||||
/**
|
||||
* Returns the instruction pointer that triggered this event.
|
||||
*/
|
||||
sal::address_t getTriggerInstructionPointer() const
|
||||
{ return m_TriggerInstrPtr; }
|
||||
/**
|
||||
* Sets the instruction pointer that triggered this event. Should not
|
||||
* be used by experiment code.
|
||||
*/
|
||||
void setTriggerInstructionPointer(sal::address_t iptr)
|
||||
{ m_TriggerInstrPtr = iptr; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class MemAccessEvent
|
||||
* Observes memory read/write accesses.
|
||||
* FIXME? currently >8-bit accesses only match if their lowest address is being watched
|
||||
* (e.g., a 32-bit write to 0x4 also accesses 0x7, but this cannot be matched)
|
||||
*/
|
||||
class MemAccessEvent : virtual public BaseEvent
|
||||
{
|
||||
public:
|
||||
enum accessType_t
|
||||
{
|
||||
MEM_UNKNOWN = 0x0,
|
||||
MEM_READ = 0x1,
|
||||
MEM_WRITE = 0x2,
|
||||
MEM_READWRITE = 0x3
|
||||
};
|
||||
private:
|
||||
//! Specific guest system address to watch, or ANY_ADDR.
|
||||
sal::address_t m_WatchAddr;
|
||||
/**
|
||||
* Memory access type we want to watch
|
||||
* (MEM_READ || MEM_WRITE || MEM_READWRITE).
|
||||
*/
|
||||
accessType_t m_WatchType;
|
||||
//! Specific guest system address that actually triggered the event.
|
||||
sal::address_t m_TriggerAddr;
|
||||
//! Width of the memory access (# bytes).
|
||||
size_t m_TriggerWidth;
|
||||
//! Address of the instruction that caused the memory access.
|
||||
sal::address_t m_TriggerIP;
|
||||
//! Memory access type at m_TriggerAddr.
|
||||
accessType_t m_AccessType;
|
||||
public:
|
||||
MemAccessEvent(accessType_t watchtype = MEM_READWRITE)
|
||||
: m_WatchAddr(ANY_ADDR), m_WatchType(watchtype),
|
||||
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
||||
m_AccessType(MEM_UNKNOWN) { }
|
||||
MemAccessEvent(sal::address_t addr,
|
||||
accessType_t watchtype = MEM_READWRITE)
|
||||
: m_WatchAddr(addr), m_WatchType(watchtype),
|
||||
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
||||
m_AccessType(MEM_UNKNOWN) { }
|
||||
/**
|
||||
* Returns the memory address to be observed.
|
||||
*/
|
||||
sal::address_t getWatchAddress() const { return m_WatchAddr; }
|
||||
/**
|
||||
* Sets the memory address to be observed. (Wildcard: ANY_ADDR)
|
||||
*/
|
||||
void setWatchAddress(sal::address_t addr) { m_WatchAddr = addr; }
|
||||
/**
|
||||
* Checks whether a given address is matching.
|
||||
*/
|
||||
bool isMatching(sal::address_t addr, accessType_t accesstype) const;
|
||||
/**
|
||||
* Returns the specific memory address that actually triggered the
|
||||
* event.
|
||||
*/
|
||||
sal::address_t getTriggerAddress() const { return (m_TriggerAddr); }
|
||||
/**
|
||||
* Sets the specific memory address that actually triggered the event.
|
||||
* Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerAddress(sal::address_t addr) { m_TriggerAddr = addr; }
|
||||
/**
|
||||
* Returns the specific memory address that actually triggered the
|
||||
* event.
|
||||
*/
|
||||
size_t getTriggerWidth() const { return (m_TriggerWidth); }
|
||||
/**
|
||||
* Sets the specific memory address that actually triggered the event.
|
||||
* Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerWidth(size_t width) { m_TriggerWidth = width; }
|
||||
/**
|
||||
* Returns the address of the instruction causing this memory
|
||||
* access.
|
||||
*/
|
||||
sal::address_t getTriggerInstructionPointer() const
|
||||
{ return (m_TriggerIP); }
|
||||
/**
|
||||
* Sets the address of the instruction causing this memory
|
||||
* access. Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerInstructionPointer(sal::address_t addr)
|
||||
{ m_TriggerIP = addr; }
|
||||
/**
|
||||
* Returns type (MEM_READ || MEM_WRITE) of the memory access that
|
||||
* triggered this event.
|
||||
*/
|
||||
accessType_t getTriggerAccessType() const { return (m_AccessType); }
|
||||
/**
|
||||
* Sets type of the memory access that triggered this event. Should
|
||||
* not be used by experiment code.
|
||||
*/
|
||||
void setTriggerAccessType(accessType_t type) { m_AccessType = type; }
|
||||
/**
|
||||
* Returns memory access types (MEM_READ || MEM_WRITE || MEM_READWRITE)
|
||||
* this event watches. Should not be used by experiment code.
|
||||
*/
|
||||
accessType_t getWatchAccessType() const { return (m_WatchType); }
|
||||
};
|
||||
/**
|
||||
* \class MemReadEvent
|
||||
* Observes memory read accesses.
|
||||
*/
|
||||
class MemReadEvent : virtual public MemAccessEvent
|
||||
{
|
||||
public:
|
||||
MemReadEvent()
|
||||
: MemAccessEvent(MEM_READ) { }
|
||||
MemReadEvent(sal::address_t addr)
|
||||
: MemAccessEvent(addr, MEM_READ) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class MemWriteEvent
|
||||
* Observes memory write accesses.
|
||||
*/
|
||||
class MemWriteEvent : virtual public MemAccessEvent
|
||||
{
|
||||
public:
|
||||
MemWriteEvent()
|
||||
: MemAccessEvent(MEM_READ) { }
|
||||
MemWriteEvent(sal::address_t addr)
|
||||
: MemAccessEvent(addr, MEM_WRITE) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TroubleEvent
|
||||
* Observes interrupt/trap activties.
|
||||
*/
|
||||
class TroubleEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Specific guest system interrupt/trap number that actually
|
||||
* trigger the event.
|
||||
*/
|
||||
int m_TriggerNumber;
|
||||
/**
|
||||
* Specific guest system interrupt/trap numbers to watch,
|
||||
* or ANY_INTERRUPT/ANY_TRAP.
|
||||
*/
|
||||
std::vector<unsigned> m_WatchNumbers;
|
||||
public:
|
||||
TroubleEvent() : m_TriggerNumber (-1) { }
|
||||
TroubleEvent(unsigned troubleNumber)
|
||||
: m_TriggerNumber(-1)
|
||||
{ addWatchNumber(troubleNumber); }
|
||||
/**
|
||||
* Add an interrupt/trap-number which should be observed
|
||||
* @param troubleNumber number of an interrupt or trap
|
||||
* @return \c true if number is added to the list \c false if the number
|
||||
* was already in the list
|
||||
*/
|
||||
bool addWatchNumber(unsigned troubleNumber);
|
||||
/**
|
||||
* Remove an interrupt/trap-number which is in the list of observed
|
||||
* numbers
|
||||
* @param troubleNumber number of an interrupt or trap
|
||||
* @return \c true if the number was found and removed \c false if the
|
||||
* number was not in the list
|
||||
*/
|
||||
bool removeWatchNumber(unsigned troubleNum);
|
||||
/**
|
||||
* Returns the list of observed numbers
|
||||
* @return a copy of the list which contains all observed numbers
|
||||
*/
|
||||
std::vector<unsigned> getWatchNumbers() { return (m_WatchNumbers); }
|
||||
/**
|
||||
* Checks whether a given interrupt-/trap-number is matching.
|
||||
*/
|
||||
bool isMatching(unsigned troubleNum) const;
|
||||
/**
|
||||
* Sets the specific interrupt-/trap-number that actually triggered
|
||||
* the event. Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerNumber(unsigned troubleNum)
|
||||
{ m_TriggerNumber = troubleNum; }
|
||||
/**
|
||||
* Returns the specific interrupt-/trap-number that actually triggered
|
||||
* the event.
|
||||
*/
|
||||
unsigned getTriggerNumber() { return (m_TriggerNumber); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class InterruptEvent
|
||||
* Observes interrupts of the guest system.
|
||||
*/
|
||||
class InterruptEvent : virtual public TroubleEvent
|
||||
{
|
||||
private:
|
||||
bool m_IsNMI; //!< non maskable interrupt flag
|
||||
public:
|
||||
InterruptEvent() : m_IsNMI(false) { }
|
||||
InterruptEvent(unsigned interrupt) : m_IsNMI(false)
|
||||
{ addWatchNumber(interrupt); }
|
||||
/**
|
||||
* Returns \c true if the interrupt is non maskable, \c false otherwise.
|
||||
*/
|
||||
bool isNMI() { return (m_IsNMI); }
|
||||
/**
|
||||
* Sets the interrupt type (non maskable or not).
|
||||
*/
|
||||
void setNMI(bool enabled) { m_IsNMI = enabled; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrapEvent
|
||||
* Observes traps of the guest system.
|
||||
*/
|
||||
class TrapEvent : virtual public TroubleEvent
|
||||
{
|
||||
public:
|
||||
TrapEvent() { }
|
||||
TrapEvent(unsigned trap) { addWatchNumber(trap); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class GuestEvent
|
||||
* Used to receive data from the guest system.
|
||||
*/
|
||||
class GuestEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
char m_Data;
|
||||
unsigned m_Port;
|
||||
public:
|
||||
GuestEvent() : m_Data(0), m_Port(0) { }
|
||||
/**
|
||||
* Returns the data, transmitted by the guest system.
|
||||
*/
|
||||
char getData() const { return (m_Data); }
|
||||
/**
|
||||
* Sets the data which had been transmitted by the guest system.
|
||||
*/
|
||||
void setData(char data) { m_Data = data; }
|
||||
/**
|
||||
* Returns the data length, transmitted by the guest system.
|
||||
*/
|
||||
unsigned getPort() const { return (m_Data); }
|
||||
/**
|
||||
* Sets the data length which had been transmitted by the guest system.
|
||||
*/
|
||||
void setPort(unsigned port) { m_Port = port; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class JumpEvent
|
||||
* JumpEvents are used to observe condition jumps (if...else if...else).
|
||||
*/
|
||||
class JumpEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
unsigned m_Opcode;
|
||||
bool m_FlagTriggered;
|
||||
public:
|
||||
/**
|
||||
* Constructs a new event object.
|
||||
* @param parent the parent object
|
||||
* @param opcode the opcode of the jump-instruction to be observed
|
||||
* or ANY_INSTR to match all jump-instructions
|
||||
*/
|
||||
JumpEvent(unsigned opcode = ANY_INSTR)
|
||||
: m_Opcode(opcode), m_FlagTriggered(false) { }
|
||||
/**
|
||||
* Retrieves the opcode of the jump-instruction.
|
||||
*/
|
||||
unsigned getOpcode() const { return (m_Opcode); }
|
||||
/**
|
||||
* Returns \c true, of the event was triggered due to specific register
|
||||
* content, \c false otherwise.
|
||||
*/
|
||||
bool isRegisterTriggered() { return (!m_FlagTriggered); }
|
||||
/**
|
||||
* Returns \c true, of the event was triggered due to specific FLAG
|
||||
* state, \c false otherwise. This is the common case.
|
||||
*/
|
||||
bool isFlagTriggered() { return (m_FlagTriggered); }
|
||||
/**
|
||||
* Sets the requestet jump-instruction opcode.
|
||||
*/
|
||||
void setOpcode(unsigned oc) { oc = m_Opcode; }
|
||||
/**
|
||||
* Sets the trigger flag.
|
||||
*/
|
||||
void setFlagTriggered(bool flagTriggered)
|
||||
{ m_FlagTriggered = flagTriggered; }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fi
|
||||
|
||||
#endif /* __EVENT_HPP__ */
|
||||
136
core/controller/EventList.cc
Normal file
136
core/controller/EventList.cc
Normal file
@ -0,0 +1,136 @@
|
||||
#include <set>
|
||||
|
||||
#include "EventList.hpp"
|
||||
#include "../SAL/SALInst.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
EventId EventList::add(BaseEvent* ev, ExperimentFlow* pExp)
|
||||
{
|
||||
assert(ev != NULL && "FATAL ERROR: Event (of base type BaseEvent*) cannot be NULL!");
|
||||
// a zero counter does not make sense
|
||||
assert(ev->getCounter() != 0);
|
||||
ev->setParent(pExp); // event is linked to experiment flow
|
||||
m_BufferList.push_back(ev);
|
||||
return (ev->getId());
|
||||
}
|
||||
|
||||
bool EventList::remove(BaseEvent* ev)
|
||||
{
|
||||
if(ev != NULL)
|
||||
{
|
||||
iterator it = std::find(m_BufferList.begin(), m_BufferList.end(), ev);
|
||||
if(it != end())
|
||||
{
|
||||
m_BufferList.erase(it);
|
||||
m_DeleteList.push_back(ev);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(iterator it = m_BufferList.begin(); it != m_BufferList.end();
|
||||
it++)
|
||||
m_DeleteList.push_back(*it);
|
||||
m_BufferList.clear();
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
EventList::iterator EventList::remove(iterator it)
|
||||
{
|
||||
return (m_remove(it, false));
|
||||
}
|
||||
|
||||
EventList::iterator EventList::m_remove(iterator it, bool skip_deletelist)
|
||||
{
|
||||
if(!skip_deletelist)
|
||||
m_DeleteList.push_back(*it);
|
||||
return (m_BufferList.erase(it));
|
||||
}
|
||||
|
||||
void EventList::getEventsOf(ExperimentFlow* pWhat,
|
||||
std::vector<BaseEvent*>& dest) const
|
||||
{
|
||||
assert(pWhat && "FATAL ERROR: The context cannot be NULL!");
|
||||
for(bufferlist_t::const_iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
if((*it)->getParent() == pWhat)
|
||||
dest.push_back(*it);
|
||||
}
|
||||
|
||||
EventList::~EventList()
|
||||
{
|
||||
// nothing to do here yet
|
||||
}
|
||||
|
||||
BaseEvent* EventList::getEventFromId(EventId id)
|
||||
{
|
||||
// Loop through all events:
|
||||
for(bufferlist_t::iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
if((*it)->getId() == id)
|
||||
return (*it);
|
||||
return (NULL); // Nothing found.
|
||||
}
|
||||
|
||||
void EventList::makeActive(BaseEvent* ev)
|
||||
{
|
||||
assert(ev && "FATAL ERROR: Event object pointer cannot be NULL!");
|
||||
ev->decreaseCounter();
|
||||
if (ev->getCounter() > 0) {
|
||||
return;
|
||||
}
|
||||
ev->resetCounter();
|
||||
if(remove(ev)) // remove event from buffer-list
|
||||
m_FireList.push_back(ev);
|
||||
}
|
||||
|
||||
EventList::iterator EventList::makeActive(iterator it)
|
||||
{
|
||||
assert(it != m_BufferList.end() &&
|
||||
"FATAL ERROR: Iterator has already reached the end!");
|
||||
BaseEvent* ev = *it;
|
||||
assert(ev && "FATAL ERROR: Event object pointer cannot be NULL!");
|
||||
ev->decreaseCounter();
|
||||
if (ev->getCounter() > 0) {
|
||||
return ++it;
|
||||
}
|
||||
ev->resetCounter();
|
||||
// Note: This is the one and only situation in which remove() should NOT
|
||||
// store the removed item in the delete-list.
|
||||
iterator it_next = m_remove(it, true); // remove event from buffer-list
|
||||
m_FireList.push_back(ev);
|
||||
return (it_next);
|
||||
}
|
||||
|
||||
void EventList::fireActiveEvents()
|
||||
{
|
||||
for(firelist_t::iterator it = m_FireList.begin();
|
||||
it != m_FireList.end(); it++)
|
||||
{
|
||||
if(std::find(m_DeleteList.begin(), m_DeleteList.end(), *it)
|
||||
== m_DeleteList.end()) // not found in delete-list?
|
||||
{
|
||||
m_pFired = *it;
|
||||
ExperimentFlow* pFlow = m_pFired->getParent();
|
||||
assert(pFlow && "FATAL ERROR: The event has no parent experiment (owner)!");
|
||||
sal::simulator.m_Flows.toggle(pFlow);
|
||||
}
|
||||
}
|
||||
m_FireList.clear();
|
||||
m_DeleteList.clear();
|
||||
}
|
||||
|
||||
size_t EventList::getContextCount() const
|
||||
{
|
||||
set<ExperimentFlow*> uniqueFlows; // count unique ExperimentFlow-ptr
|
||||
for(bufferlist_t::const_iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
uniqueFlows.insert((*it)->getParent());
|
||||
return (uniqueFlows.size());
|
||||
}
|
||||
|
||||
} // end-of-namespace: fi
|
||||
201
core/controller/EventList.hpp
Normal file
201
core/controller/EventList.hpp
Normal file
@ -0,0 +1,201 @@
|
||||
#ifndef __EVENT_LIST_HPP__
|
||||
#define __EVENT_LIST_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 04.02.2012
|
||||
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
class ExperimentFlow;
|
||||
|
||||
/**
|
||||
* Buffer-list for a specific experiment; acts as a simple storage container
|
||||
* for events to watch for:
|
||||
*/
|
||||
typedef std::list<BaseEvent*> bufferlist_t;
|
||||
/**
|
||||
* List of events that match the current simulator event; these events will
|
||||
* be triggered next (the list is used temporarily).
|
||||
*/
|
||||
typedef std::vector<BaseEvent*> firelist_t;
|
||||
/**
|
||||
* List of events that have been deleted during a toggled experiment flow while
|
||||
* triggering the events in the fire-list. This list is used to skip already
|
||||
* deleted events (therefore it is used temporarily either).
|
||||
*/
|
||||
typedef std::vector<BaseEvent*> deletelist_t;
|
||||
|
||||
/**
|
||||
* \class EventList
|
||||
*
|
||||
* \brief This class manages the events of the Fail* implementation.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
* flow. (See makeActive() and fireActiveEvent() for implementation specific
|
||||
* details.) EventList is part of the SimulatorController and "outsources"
|
||||
* it's event management.
|
||||
*/
|
||||
class EventList
|
||||
{
|
||||
private:
|
||||
bufferlist_t m_BufferList; //!< the storage for events added by exp.
|
||||
firelist_t m_FireList; //!< the active events (used temporarily)
|
||||
deletelist_t m_DeleteList; //!< the deleted events (used temporarily)
|
||||
// TODO: Hashing?
|
||||
BaseEvent* m_pFired; //!< the recently fired Event-object
|
||||
public:
|
||||
/**
|
||||
* 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 bufferlist_t::iterator iterator;
|
||||
|
||||
EventList() : m_pFired(NULL) { }
|
||||
~EventList();
|
||||
/**
|
||||
* Adds the specified event object for the given ExperimentFlow to the
|
||||
* list of events to be watched for.
|
||||
* @param ev pointer to the event object to be added (cannot be \c NULL)
|
||||
* @param pExp the event context (a pointer to the experiment object
|
||||
* which is interested in such events, cannot be \c NULL)
|
||||
* @return the id of the added event object, that is ev->getId()
|
||||
*/
|
||||
EventId add(BaseEvent* ev, ExperimentFlow* pExp);
|
||||
/**
|
||||
* Removes the event based upon the specified \a ev pointer (requires
|
||||
* to loop through the whole buffer-list).
|
||||
* @param ev the pointer of the event to be removed; if ev is set to
|
||||
* \c NULL, all events (for \a all experiments) will be
|
||||
* removed
|
||||
* @return \c true if the object has been removed or \c false if the
|
||||
* pointer could not be found
|
||||
*/
|
||||
bool remove(BaseEvent* ev);
|
||||
/**
|
||||
* Behaves like remove(BaseEvent) and additionally updates the provided
|
||||
* iteration.
|
||||
* @return the updated iterator which will point to the next element
|
||||
*/
|
||||
iterator remove(iterator it);
|
||||
private:
|
||||
/**
|
||||
* Internal implementation of remove(iterator it) that allows
|
||||
* to skip the delete-list.
|
||||
* @return the updated iterator which will point to the next element
|
||||
*/
|
||||
iterator m_remove(iterator it, bool skip_deletelist);
|
||||
public:
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
iterator begin() { return (m_BufferList.begin()); }
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
iterator end() { return (m_BufferList.end()); }
|
||||
/**
|
||||
* Retrieves the event object for the given \a id. The internal data
|
||||
* remains unchanged.
|
||||
* @param id of event to be retrieved.
|
||||
* @return pointer to event or \c NULL of \a id could not be found
|
||||
*/
|
||||
BaseEvent* getEventFromId(EventId id);
|
||||
/**
|
||||
* Retrieves all events for the specified experiment.
|
||||
* @param pWhat pointer to experiment context (this pointer is expected
|
||||
* to be valid!)
|
||||
* @param dest a reference to a vector-object to be used as the
|
||||
* destination buffer for the machting event objects. This
|
||||
* objects may remains unchanged if no matching event objects
|
||||
* were found.
|
||||
*/
|
||||
void getEventsOf(ExperimentFlow* pWhat, std::vector<BaseEvent*>& dest) const;
|
||||
/**
|
||||
* Retrieves the number of experiments which currently have active
|
||||
* events. This number is trivially equal to the (current) total
|
||||
* number of ExperimentFlow-objects.
|
||||
* @return number of experiments having active events
|
||||
*/
|
||||
size_t getContextCount() const;
|
||||
/**
|
||||
* Retrieves the total number of events.
|
||||
* @return the total event count (for all flows)
|
||||
*/
|
||||
size_t getEventCount() const { return (m_BufferList.size()); }
|
||||
/**
|
||||
* Retrieves the recently triggered event object. To map this object to
|
||||
* it's context (id est, the related ExerimentFlow), use
|
||||
* \c getLastFiredDest().
|
||||
* @return a pointer to the recent event or \c NULL if nothing has been
|
||||
* triggered so far
|
||||
*/
|
||||
BaseEvent* getLastFired() { return (m_pFired); }
|
||||
/**
|
||||
* Retrieves the ExperimentFlow-object for the given BaseEvent (it's
|
||||
* \a context).
|
||||
* @param pEv the event object to be looked up
|
||||
* @return a pointer to the context of \a pEv or \c NULL if the
|
||||
* corresponding context could not be found
|
||||
*/
|
||||
ExperimentFlow* getExperimentOf(BaseEvent* pEv);
|
||||
/**
|
||||
* Moves the events from the (internal) buffer-list to the fire-list. To
|
||||
* actually fire the events activated by calling makeActive(), call
|
||||
* fireActiveEvents().
|
||||
* @param ev the event to trigger
|
||||
* TODO: besserer Name statt "makeActive"?
|
||||
*/
|
||||
void makeActive(BaseEvent* ev);
|
||||
/**
|
||||
* Behaves like makeActive(BaseEvent) and additionally returns an
|
||||
* updated iterator which points to the next BaseEvent-element.
|
||||
* @param ev the event to trigger
|
||||
* @return returns the updated iteration, pointing to the next element
|
||||
* after makeActive returns, "it" is invalid, so the returned
|
||||
* iterator should be used to continue the iteration
|
||||
* TODO: besserer Name statt "makeActive"?
|
||||
*/
|
||||
iterator makeActive(iterator it);
|
||||
/**
|
||||
* Triggers the active events. Each event 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 event can be
|
||||
* retrieved by calling \a getLastFired(). After all events have been
|
||||
* triggered, the (internal) fire- and delete-list will be cleared.
|
||||
* TODO: besserer Name statt "fireActiveEvents"?
|
||||
*/
|
||||
void fireActiveEvents();
|
||||
};
|
||||
|
||||
}; // end-of-namespace: fi
|
||||
|
||||
#endif /* __EVENT_LIST_HPP__ */
|
||||
56
core/controller/ExperimentData.hpp
Normal file
56
core/controller/ExperimentData.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* \brief ExperimentData interface
|
||||
*
|
||||
* This is the base class for all user-defined data types for
|
||||
* expirement parameter and results.
|
||||
*
|
||||
* \author Martin Hoffmann, Richard Hellwig
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __EXPERIMENT_DATA_H__
|
||||
#define __EXPERIMENT_DATA_H__
|
||||
|
||||
#include <string>
|
||||
#include <google/protobuf/message.h>
|
||||
using namespace std;
|
||||
|
||||
namespace fi{
|
||||
|
||||
/**
|
||||
* \class ExperimentData
|
||||
* Container for experiment data with wrapper methods for serialization and deserialization.
|
||||
*/
|
||||
|
||||
class ExperimentData
|
||||
{
|
||||
protected:
|
||||
google::protobuf::Message* msg;
|
||||
uint32_t m_workloadID;
|
||||
public:
|
||||
ExperimentData() : msg(0), m_workloadID(0) {};
|
||||
ExperimentData(google::protobuf::Message* m) : msg(m) , m_workloadID(0) {};
|
||||
|
||||
google::protobuf::Message& getMessage() { return *msg; };
|
||||
uint32_t getWorkloadID() const { return m_workloadID;};
|
||||
void setWorkloadID(uint32_t id) { m_workloadID = id; };
|
||||
/**
|
||||
* Serializes the ExperimentData.
|
||||
* @param ped output the target-stream.
|
||||
* @return \c true if the serialization was successful, \c false otherwise
|
||||
*/
|
||||
bool serialize(ostream * output) const { return msg->SerializeToOstream(output); }
|
||||
/**
|
||||
* Unserializes the ExperimentData.
|
||||
* @param ped input the stream which is read from
|
||||
* @return \c true if the unserialization was successful, \c false otherwise
|
||||
*/
|
||||
bool unserialize(istream * input) { return msg->ParseFromIstream(input); }
|
||||
string DebugString() const { return msg->DebugString(); };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif //__EXPERIMENT_DATA_H__
|
||||
|
||||
|
||||
22
core/controller/ExperimentDataQueue.cc
Normal file
22
core/controller/ExperimentDataQueue.cc
Normal file
@ -0,0 +1,22 @@
|
||||
#include "ExperimentDataQueue.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
void ExperimentDataQueue::addData(ExperimentData* exp)
|
||||
{
|
||||
assert(exp != 0);
|
||||
m_queue.push_front(exp);
|
||||
}
|
||||
|
||||
ExperimentData* ExperimentDataQueue::getData()
|
||||
{
|
||||
ExperimentData* ret = m_queue.back();
|
||||
m_queue.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
53
core/controller/ExperimentDataQueue.hpp
Normal file
53
core/controller/ExperimentDataQueue.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* \brief A queue for experiment data.
|
||||
*
|
||||
*
|
||||
* \author Martin Hoffmann, Richard Hellwig
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __EXPERIMENT_DATA_QUEUE_H__
|
||||
#define __EXPERIMENT_DATA_QUEUE_H__
|
||||
|
||||
|
||||
#include <deque>
|
||||
#include "ExperimentData.hpp"
|
||||
|
||||
|
||||
namespace fi{
|
||||
|
||||
/**
|
||||
* \class ExperimentDataQueue
|
||||
* Class which manage ExperimentData in a queue.
|
||||
*/
|
||||
class ExperimentDataQueue
|
||||
{
|
||||
protected:
|
||||
std::deque<ExperimentData*> m_queue;
|
||||
|
||||
public:
|
||||
ExperimentDataQueue() {}
|
||||
~ExperimentDataQueue() {}
|
||||
|
||||
/**
|
||||
* Adds ExperimentData to the queue.
|
||||
* @param exp ExperimentData that is to be added to the queue.
|
||||
*/
|
||||
void addData(ExperimentData* exp);
|
||||
/**
|
||||
* Returns an item from the queue
|
||||
* @return the next element of the queue
|
||||
*/
|
||||
ExperimentData* getData();
|
||||
/**
|
||||
* Returns the number of elements in the queue
|
||||
* @return the size of teh queue
|
||||
*/
|
||||
size_t size() const { return m_queue.size(); };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif //__EXPERIMENT_DATA_QUEUE_H__
|
||||
|
||||
|
||||
41
core/controller/ExperimentFlow.hpp
Normal file
41
core/controller/ExperimentFlow.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef __EXPERIMENT_FLOW_HPP__
|
||||
#define __EXPERIMENT_FLOW_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 09.09.2011
|
||||
|
||||
#include "../SAL/SALInst.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
/**
|
||||
* \class ExperimentFlow
|
||||
* Basic interface for user-defined experiments. To create a new
|
||||
* experiment, derive your own class from ExperimentFlow and
|
||||
* define the run method.
|
||||
*/
|
||||
class ExperimentFlow
|
||||
{
|
||||
public:
|
||||
ExperimentFlow() { }
|
||||
/**
|
||||
* Defines the experiment flow.
|
||||
* @return \c true if the experiment was successful, \c false otherwise
|
||||
*/
|
||||
virtual bool run() = 0;
|
||||
|
||||
/**
|
||||
* The entry point for this experiment's coroutine.
|
||||
* Should do some cleanup afterwards.
|
||||
*/
|
||||
void coroutine_entry()
|
||||
{
|
||||
run();
|
||||
sal::simulator.cleanup(this); // remove residual events
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* __EXPERIMENT_FLOW_HPP__ */
|
||||
67
core/controller/Minion.hpp
Normal file
67
core/controller/Minion.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* \brief The representation of a minion.
|
||||
*
|
||||
* \author Richard Hellwig
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MINION_HPP__
|
||||
#define __MINION_HPP__
|
||||
|
||||
#include "controller/ExperimentData.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
/**
|
||||
* \class Minion
|
||||
*
|
||||
* Contains all informations about a minion.
|
||||
*/
|
||||
class Minion
|
||||
{
|
||||
private:
|
||||
string hostname;
|
||||
bool isWorking;
|
||||
ExperimentData* currentExperimentData;
|
||||
int sockfd;
|
||||
public:
|
||||
Minion() : isWorking(false), currentExperimentData(0), sockfd(-1) { }
|
||||
|
||||
void setSocketDescriptor(int sock) { sockfd = sock; }
|
||||
int getSocketDescriptor() const { return (sockfd); }
|
||||
|
||||
/**
|
||||
* Returns the hostname of the minion.
|
||||
* @return the hostname
|
||||
*/
|
||||
string getHostname() { return (hostname); }
|
||||
/**
|
||||
* Sets the hostname of the minion.
|
||||
* @param host the hostname
|
||||
*/
|
||||
void setHostname(string host) { hostname = host; }
|
||||
/**
|
||||
* Returns the current ExperimentData which the minion is working with.
|
||||
* @return a pointer of the current ExperimentData
|
||||
*/
|
||||
ExperimentData* getCurrentExperimentData() { return currentExperimentData; }
|
||||
/**
|
||||
* Sets the current ExperimentData which the minion is working with.
|
||||
* @param exp the current ExperimentData
|
||||
*/
|
||||
void setCurrentExperimentData(ExperimentData* exp) { currentExperimentData = exp; }
|
||||
/**
|
||||
* Returns the current state of the minion.
|
||||
* @return the current state
|
||||
*/
|
||||
bool isBusy() { return (isWorking); }
|
||||
/**
|
||||
* Sets the current state of the minion
|
||||
* @param state the current state
|
||||
*/
|
||||
void setBusy(bool state) { isWorking = state; }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* __MINION_HPP__ */
|
||||
14
core/controller/Signal.cc
Normal file
14
core/controller/Signal.cc
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 15.06.2011
|
||||
|
||||
#include "Signal.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
std::auto_ptr<Signal> Signal::m_This;
|
||||
Mutex Signal::m_InstanceMutex;
|
||||
|
||||
|
||||
} // end-of-namespace: fi
|
||||
136
core/controller/Signal.hpp
Normal file
136
core/controller/Signal.hpp
Normal file
@ -0,0 +1,136 @@
|
||||
#ifndef __SIGNAL_HPP__
|
||||
#define __SIGNAL_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 15.06.2011
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#ifndef __puma
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/interprocess/sync/named_semaphore.hpp>
|
||||
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#endif
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
#ifndef __puma
|
||||
typedef boost::mutex Mutex; // lock/unlock
|
||||
typedef boost::mutex::scoped_lock ScopeLock; // use RAII with lock/unlock mechanism
|
||||
typedef boost::condition_variable ConditionVariable; // wait/notify_one
|
||||
#else
|
||||
typedef int Mutex;
|
||||
typedef int ScopeLock;
|
||||
typedef int ConditionVariable;
|
||||
#endif
|
||||
|
||||
// Simulate a "private" semaphore using boost-mechanisms:
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
Mutex m_Mutex;
|
||||
ConditionVariable m_CondVar;
|
||||
unsigned long m_Value;
|
||||
public:
|
||||
// Create a semaphore object based on a mutex and a condition variable
|
||||
// and initialize it to value "init".
|
||||
Semaphore(unsigned long init = 0) : m_Value(init) { }
|
||||
|
||||
void post()
|
||||
{
|
||||
ScopeLock lock(m_Mutex);
|
||||
++m_Value; // increase semaphore value:
|
||||
#ifndef __puma
|
||||
m_CondVar.notify_one(); // wake up other thread, currently waiting on condition var.
|
||||
#endif
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
ScopeLock lock(m_Mutex);
|
||||
#ifndef __puma
|
||||
while(!m_Value) // "wait-if-zero"
|
||||
m_CondVar.wait(lock);
|
||||
#endif
|
||||
--m_Value; // decrease semaphore value
|
||||
}
|
||||
};
|
||||
|
||||
class Signal
|
||||
{
|
||||
private:
|
||||
static Mutex m_InstanceMutex; // used to sync calls to getInst()
|
||||
static std::auto_ptr<Signal> m_This; // the one and only instance
|
||||
|
||||
Semaphore m_semBochs;
|
||||
Semaphore m_semContr;
|
||||
Semaphore m_semSimCtrl;
|
||||
bool m_Locked; // prevent misuse of thread-sync
|
||||
|
||||
// Singleton class (forbid creation, copying and assignment):
|
||||
Signal()
|
||||
: m_semBochs(0), m_semContr(0),
|
||||
m_semSimCtrl(0), m_Locked(false) { }
|
||||
Signal(Signal const& s)
|
||||
: m_semBochs(), m_semContr(),
|
||||
m_semSimCtrl(), m_Locked(false) { } // never called.
|
||||
Signal& operator=(Signal const&) { return *this; } // dito.
|
||||
~Signal() { }
|
||||
friend class std::auto_ptr<Signal>;
|
||||
public:
|
||||
static Signal& getInst()
|
||||
{
|
||||
ScopeLock lock(m_InstanceMutex); // lock/unlock handled by RAII principle
|
||||
if(!m_This.get())
|
||||
m_This.reset(new Signal());
|
||||
return (*m_This);
|
||||
}
|
||||
|
||||
// Called from Experiment-Controller class ("beyond Bochs"):
|
||||
void lockExperiment()
|
||||
{
|
||||
assert(!m_Locked &&
|
||||
"[Signal::lockExperiment]: lockExperiment called twice without calling unlockExperiment() in between.");
|
||||
m_Locked = true;
|
||||
m_semContr.wait(); // suspend experiment process
|
||||
}
|
||||
|
||||
// Called from Experiment-Controller class ("beyond Bochs"):
|
||||
void unlockExperiment()
|
||||
{
|
||||
assert(m_Locked &&
|
||||
"[Signal::unlockExperiment]: unlockExperiment called twice without calling lockExperiment() in between.");
|
||||
m_Locked = false;
|
||||
m_semBochs.post(); // resume experiment (continue bochs simulation)
|
||||
}
|
||||
|
||||
// Called from Advice-Code ("within Bochs") to trigger event occurrence:
|
||||
void signalEvent()
|
||||
{
|
||||
m_semContr.post(); // Signal event (to Experiment-Controller)
|
||||
m_semBochs.wait(); // Wait upon handling to finish
|
||||
}
|
||||
|
||||
// Called from Experiment-Controller to allow simulation start:
|
||||
void startSimulation()
|
||||
{
|
||||
m_semSimCtrl.post();
|
||||
}
|
||||
|
||||
// Called from Bochs, directly after thread creation for Experiment-Controller:
|
||||
// (This ensures that Bochs waits until the experiment has been set up in the
|
||||
// Experiment-Controller.)
|
||||
void waitForStartup()
|
||||
{
|
||||
m_semSimCtrl.wait();
|
||||
}
|
||||
};
|
||||
|
||||
} // end-of-namespace: fi
|
||||
|
||||
#endif /* __SIGNAL_HPP__ */
|
||||
|
||||
23
core/controller/SynchronizedExperimentDataQueue.cc
Normal file
23
core/controller/SynchronizedExperimentDataQueue.cc
Normal file
@ -0,0 +1,23 @@
|
||||
#include "SynchronizedExperimentDataQueue.hpp"
|
||||
|
||||
namespace fi {
|
||||
|
||||
void SynchronizedExperimentDataQueue::addData(ExperimentData* exp){
|
||||
//
|
||||
m_sema_full.wait();
|
||||
ExperimentDataQueue::addData(exp);
|
||||
m_sema_empty.post();
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
ExperimentData* SynchronizedExperimentDataQueue::getData(){
|
||||
//
|
||||
m_sema_empty.wait();
|
||||
return ExperimentDataQueue::getData();
|
||||
m_sema_full.post();
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
55
core/controller/SynchronizedExperimentDataQueue.hpp
Normal file
55
core/controller/SynchronizedExperimentDataQueue.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* \brief A queue for experiment data.
|
||||
*
|
||||
*
|
||||
* \author Martin Hoffmann, Richard Hellwig
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SYNC_EXPERIMENT_DATA_QUEUE_H__
|
||||
#define __SYNC_EXPERIMENT_DATA_QUEUE_H__
|
||||
|
||||
#include "ExperimentDataQueue.hpp"
|
||||
#include "Signal.hpp"
|
||||
|
||||
namespace fi{
|
||||
|
||||
/**
|
||||
* \class SynchronizedExperimentDataQueue
|
||||
* Class which manage ExperimentData in a queue.
|
||||
* Thread safe using semphores.
|
||||
*/
|
||||
class SynchronizedExperimentDataQueue : public ExperimentDataQueue
|
||||
{
|
||||
private:
|
||||
/// There are maxSize elements in at a time
|
||||
/// Or do we allow a really possibly huge queue?
|
||||
Semaphore m_sema_full;
|
||||
Semaphore m_sema_empty;
|
||||
|
||||
public:
|
||||
SynchronizedExperimentDataQueue(int maxSize = 1024) : m_sema_full(maxSize), m_sema_empty(0) {}
|
||||
~SynchronizedExperimentDataQueue() {}
|
||||
|
||||
/**
|
||||
* Adds ExperimentData to the queue.
|
||||
* @param exp ExperimentData that is to be added to the queue.
|
||||
*/
|
||||
void addData(ExperimentData* exp);
|
||||
/**
|
||||
* Returns an item from the queue
|
||||
* @return the next element of the queue
|
||||
*/
|
||||
ExperimentData* getData();
|
||||
/**
|
||||
* Returns the number of elements in the queue
|
||||
* @return the size of the queue
|
||||
*/
|
||||
size_t size() const { return m_queue.size(); };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif //__EXPERIMENT_DATA_QUEUE_H__
|
||||
|
||||
|
||||
Reference in New Issue
Block a user