Files
fail/src/core/sal/SimulatorController.cc
Horst Schirmeier 6d4dfeb913 shutdown cleanups revisited
This change became necessary as we observed weird fail-client SIGSEGV
crashes with both Bochs and Gem5 backends and different experiments.

Some Fail* components are instantiated statically: the
SimulatorController instance "simulator", containing the
ListenerManager and the CoroutineManager, and the active
ExperimentFlow subclass(es)
(experiments/instantiate-experiment*.ah.in).  The experiment(s) is
registered as an active flow in the CoroutineManager at startup.

As plugins (which are ExperimentFlows themselves) are often created on
an experiment's stack, ExperimentFlows deregister themselves on
destruction (e.g., when leaving the plugin variable's scope).  The
core problem is, that the creation and destruction order of statically
instantiated objects depends on the link order; if the experiment is
destroyed after the CoroutineManager, its automatic self-deregistering
feature talks to the smoking ruins of the latter.

This change removes all static instantiations of ExperimentFlow and
replaces them with constructions on the heap.  Additionally it makes
sure that the CoroutineManager recognizes that a shutdown is in
progress, and refrains from touching potentially already destroyed
data structures when a (mistakenly globally instantiated)
ExperimentFlow deregisters in this case.

Change-Id: I8a7d42fb141222cd2cce6040ab1a01f9de61be24
2013-09-04 10:13:48 +02:00

226 lines
6.4 KiB
C++

#include "SimulatorController.hpp"
#include "SALInst.hpp"
#include "Event.hpp"
#include "Listener.hpp"
#include "util/CommandLine.hpp"
namespace fail {
// External reference declared in SALInst.hpp
ConcreteSimulatorController simulator;
bool SimulatorController::addListener(BaseListener* li)
{
assert(li != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
// If addListener() was called from onTrigger(), there is no parent
// to retrieve/assign. In this case, we simple expect the parent-member
// to be valid ('as is'). (Otherwise, the current flow is retrieved by
// calling CoroutineManager::getCurrent().)
ExperimentFlow* pFlow = m_Flows.getCurrent();
if (pFlow == CoroutineManager::SIM_FLOW)
pFlow = li->getParent();
m_LstList.add(li, pFlow);
// Call the common postprocessing function:
if (!li->onAddition()) { // If the return value signals "false"...,
m_LstList.remove(li); // ...skip the addition
return false;
}
return true;
}
BaseListener* SimulatorController::resume(void)
{
m_Flows.resume();
assert(m_LstList.getLastFired() != NULL &&
"FATAL ERROR: getLastFired() expected to be non-NULL!");
return m_LstList.getLastFired();
}
void SimulatorController::startup(int& argc, char **& argv)
{
if (argv) {
CommandLine::Inst().collect_args(argc, argv);
}
startup();
}
void SimulatorController::startup()
{
// Some greetings to the user:
std::cout << "[SimulatorController] Initializing..." << std::endl;
// TODO: Log-Level?
// Activate previously added experiments to allow initialization:
initExperiments();
}
void SimulatorController::initExperiments()
{
/* empty. */
}
void SimulatorController::onBreakpoint(ConcreteCPU* cpu, address_t instrPtr, address_t address_space)
{
// Check for active breakpoint-events:
ListenerManager::iterator it = m_LstList.begin();
BPEvent tmp(instrPtr, address_space, cpu);
while (it != m_LstList.end()) {
BaseListener* pLi = *it;
BPListener* pBreakpt = dynamic_cast<BPListener*>(pLi);
if (pBreakpt != NULL && pBreakpt->isMatching(&tmp)) {
pBreakpt->setTriggerCPU(cpu);
pBreakpt->setTriggerInstructionPointer(instrPtr);
it = m_LstList.makeActive(it);
// "it" has already been set to the next element (by calling
// makeActive()):
continue; // -> skip iterator increment
}
it++;
}
m_LstList.triggerActiveListeners();
}
void SimulatorController::onMemoryAccess(ConcreteCPU* cpu, address_t addr, size_t len,
bool is_write, address_t instrPtr)
{
MemAccessEvent::access_type_t accesstype =
is_write ? MemAccessEvent::MEM_WRITE
: MemAccessEvent::MEM_READ;
MemAccessEvent tmp(addr, len, instrPtr, accesstype, cpu);
ListenerManager::iterator it = m_LstList.begin();
while (it != m_LstList.end()) { // check for active listeners
BaseListener* pev = *it;
MemAccessListener* ev = dynamic_cast<MemAccessListener*>(pev);
// Is this a MemAccessListener? Correct access type?
if (!ev || !ev->isMatching(&tmp)) {
++it;
continue; // skip listener activation
}
ev->setTriggerAddress(addr);
ev->setTriggerWidth(len);
ev->setTriggerInstructionPointer(instrPtr);
ev->setTriggerAccessType(accesstype);
ev->setTriggerCPU(cpu);
it = m_LstList.makeActive(it);
}
m_LstList.triggerActiveListeners();
}
void SimulatorController::onInterrupt(ConcreteCPU* cpu, unsigned interruptNum, bool nmi)
{
ListenerManager::iterator it = m_LstList.begin();
InterruptEvent tmp(nmi, interruptNum, cpu);
while (it != m_LstList.end()) { // check for active listeners
BaseListener* pev = *it;
InterruptListener* pie = dynamic_cast<InterruptListener*>(pev);
if (!pie || !pie->isMatching(&tmp)) {
++it;
continue; // skip listener activation
}
pie->setTriggerNumber(interruptNum);
pie->setNMI(nmi);
pie->setTriggerCPU(cpu);
it = m_LstList.makeActive(it);
}
m_LstList.triggerActiveListeners();
}
void SimulatorController::onTrap(ConcreteCPU* cpu, unsigned trapNum)
{
TroubleEvent tmp(trapNum, cpu);
ListenerManager::iterator it = m_LstList.begin();
while (it != m_LstList.end()) { // check for active listeners
BaseListener* pev = *it;
TrapListener* pte = dynamic_cast<TrapListener*>(pev);
if (!pte || !pte->isMatching(&tmp)) {
++it;
continue; // skip listener activation
}
pte->setTriggerNumber(trapNum);
pte->setTriggerCPU(cpu);
it = m_LstList.makeActive(it);
}
m_LstList.triggerActiveListeners();
}
void SimulatorController::onGuestSystem(char data, unsigned port)
{
ListenerManager::iterator it = m_LstList.begin();
while (it != m_LstList.end()) { // check for active listeners
BaseListener* pev = *it;
GuestListener* pge = dynamic_cast<GuestListener*>(pev);
if (pge != NULL) {
pge->setData(data);
pge->setPort(port);
it = m_LstList.makeActive(it);
continue; // dito.
}
++it;
}
m_LstList.triggerActiveListeners();
}
void SimulatorController::onJump(ConcreteCPU* cpu, bool flagTriggered, unsigned opcode)
{
ListenerManager::iterator it = m_LstList.begin();
while (it != m_LstList.end()) { // check for active listeners
JumpListener* pje = dynamic_cast<JumpListener*>(*it);
if (pje != NULL) {
pje->setOpcode(opcode);
pje->setFlagTriggered(flagTriggered);
pje->setTriggerCPU(cpu);
it = m_LstList.makeActive(it);
continue; // dito.
}
++it;
}
m_LstList.triggerActiveListeners();
}
void SimulatorController::addCPU(ConcreteCPU* cpu)
{
assert(cpu != NULL && "FATAL ERROR: Argument (cpu) cannot be NULL!");
m_CPUs.push_back(cpu);
}
ConcreteCPU& SimulatorController::getCPU(size_t i) const
{
assert(i < m_CPUs.size() && "FATAL ERROR: Invalid index provided!");
return *m_CPUs[i];
}
void SimulatorController::addFlow(ExperimentFlow* flow)
{
// Store the (flow,corohandle)-tuple internally and create its coroutine:
m_Flows.create(flow);
// let it run for the first time
m_Flows.toggle(flow);
}
void SimulatorController::removeFlow(ExperimentFlow* flow)
{
// remove all remaining listeners of this flow
clearListeners(flow);
// remove coroutine
m_Flows.remove(flow);
}
BaseListener* SimulatorController::addListenerAndResume(BaseListener* li)
{
addListener(li);
return resume();
}
void SimulatorController::terminate(int exCode)
{
// Attention: This could cause problems, e.g., because of non-closed sockets
std::cout << "[FAIL] Exit called by experiment with exit code: " << exCode << std::endl;
// TODO: (Non-)Verbose-Mode? Log-Level?
m_Flows.setTerminated(); // we are about to terminate
exit(exCode);
}
} // end-of-namespace: fail