SimulatorController: Selection between multiple experiments
If the user enabled multiple experiments, it does not make sense to execute them concurrently. Therefore, we now only register all enabled experiments and choose one experiment on fail-client startup. For this selection, the user can either set the FAIL_EXPERIMENT environment variable or invoke the tool with a specific argv[0]. For the bin/-folder, we create symlinks for this mechanism. With this change, the generic-tracing and generic-experiment can be compiled into the same executable and we could distribute a "standard" version of FAIL*.
This commit is contained in:
committed by
Horst Schirmeier
parent
2ecdba92a5
commit
5929eac85c
@ -60,6 +60,10 @@ public:
|
||||
* calls to toggle().
|
||||
*/
|
||||
void resume();
|
||||
/**
|
||||
* Returns the number of added coroutines.
|
||||
*/
|
||||
unsigned size() { return m_Flows.size(); }
|
||||
/**
|
||||
* Retrieves the current (active) coroutine (= flow).
|
||||
* @return the current experiment flow.
|
||||
|
||||
@ -38,7 +38,14 @@ BaseListener* SimulatorController::resume(void)
|
||||
|
||||
void SimulatorController::startup(int& argc, char **& argv)
|
||||
{
|
||||
if (argv) {
|
||||
if (argv && argc > 0) {
|
||||
// Collect invocation name and strip dirname
|
||||
m_argv0 = std::string(argv[0]);
|
||||
auto const pos = m_argv0.find_last_of('/');
|
||||
if (pos != std::string::npos) {
|
||||
m_argv0 = m_argv0.substr(pos+1);
|
||||
}
|
||||
|
||||
CommandLine::Inst().collect_args(argc, argv);
|
||||
}
|
||||
startup();
|
||||
@ -47,14 +54,60 @@ void SimulatorController::startup(int& argc, char **& argv)
|
||||
void SimulatorController::startup()
|
||||
{
|
||||
// Some greetings to the user:
|
||||
std::cout << "[SimulatorController] Initializing..." << std::endl;
|
||||
// TODO: Log-Level?
|
||||
m_log << "Initializing..." << std::endl;
|
||||
|
||||
// Set FAIL* as initialized
|
||||
m_isInitialized = true;
|
||||
|
||||
// Activate previously added experiments to allow initialization:
|
||||
initExperiments();
|
||||
|
||||
if (m_Experiments.size() == 0) {
|
||||
// Experiment was initialized indirecty, therefore there
|
||||
// should be at least one experiment flow.
|
||||
assert(m_flows.size() > 0 && "No experiment was added (directly or indirectly)");
|
||||
} else {
|
||||
std::string experiment_name;
|
||||
|
||||
if (m_Experiments.size() == 1) {
|
||||
// exactly one experiment. choose it
|
||||
experiment_name = m_Experiments.begin()->first;
|
||||
} else {
|
||||
m_log << "Multiple experiments are available: " << std::endl;;
|
||||
for (auto experiment : m_Experiments) {
|
||||
m_log << " - " << experiment.first << std::endl;
|
||||
}
|
||||
|
||||
// Check against environment variable
|
||||
const char *env_name = getenv("FAIL_EXPERIMENT");
|
||||
if (env_name) {
|
||||
for (auto experiment : m_Experiments) {
|
||||
if (experiment.first == env_name) {
|
||||
experiment_name = experiment.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check against invocation name (higher priority than environment)
|
||||
if (m_argv0 != "") {
|
||||
for (auto experiment : m_Experiments) {
|
||||
if (strstr(m_argv0.c_str(), experiment.first.c_str())) {
|
||||
experiment_name = experiment.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (experiment_name == "") {
|
||||
m_log << "ERROR, no experiment selected. Use FAIL_EXPERIMENT or change argv[0]" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
m_log << "Starting experiment: " << experiment_name << std::endl;
|
||||
addFlow(m_Experiments[experiment_name]);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorController::initExperiments()
|
||||
@ -193,6 +246,11 @@ ConcreteCPU& SimulatorController::getCPU(size_t i) const
|
||||
return *m_CPUs[i];
|
||||
}
|
||||
|
||||
void SimulatorController::addExperiment(const std::string &name, ExperimentFlow* flow)
|
||||
{
|
||||
m_Experiments[name] = flow;
|
||||
}
|
||||
|
||||
void SimulatorController::addFlow(ExperimentFlow* flow)
|
||||
{
|
||||
// Store the (flow,corohandle)-tuple internally and create its coroutine:
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
#include "ListenerManager.hpp"
|
||||
#include "SALConfig.hpp"
|
||||
#include "ConcreteCPU.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
|
||||
|
||||
/// All classes, functions, constants, etc. are encapsulated in the namespace "fail".
|
||||
@ -34,15 +36,26 @@ class MemoryManager;
|
||||
*/
|
||||
class SimulatorController {
|
||||
protected:
|
||||
fail::Logger m_log;
|
||||
bool m_isInitialized;
|
||||
ListenerManager m_LstList; //!< storage where listeners are being buffered
|
||||
std::map<std::string, ExperimentFlow *> m_Experiments; //!< registered experiments, one is chosen on startup
|
||||
CoroutineManager m_Flows; //!< managed experiment flows
|
||||
MemoryManager *m_Mem; //!< access to memory pool
|
||||
std::vector<ConcreteCPU*> m_CPUs; //!< list of CPUs in the target system
|
||||
friend class ListenerManager; //!< "outsources" the listener management
|
||||
std::string m_argv0; //!< Invocation name of simulator process
|
||||
public:
|
||||
SimulatorController() : m_isInitialized(false), m_Mem(NULL) { }
|
||||
SimulatorController(MemoryManager* mem) : m_isInitialized(false), m_Mem(mem) { }
|
||||
SimulatorController()
|
||||
: m_log("SimulatorController", false),
|
||||
m_isInitialized(false),
|
||||
m_Mem(nullptr)
|
||||
{ /* blank */ }
|
||||
SimulatorController(MemoryManager* mem)
|
||||
: m_log("SimulatorController", false),
|
||||
m_isInitialized(false),
|
||||
m_Mem(mem)
|
||||
{ /* blank */ }
|
||||
virtual ~SimulatorController() { }
|
||||
/**
|
||||
* @brief Initialization function each implementation needs to call on
|
||||
@ -171,6 +184,14 @@ public:
|
||||
/* ********************************************************************
|
||||
* Experiment-Flow & Listener Management API:
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Register the specified experiment with FAIL* under a given
|
||||
* name. On startup, FAIL* will select one experiment and
|
||||
* instantiate a coroutine with addFlow().
|
||||
* @param name the experiment name
|
||||
* @param flow the experiment flow object to be added
|
||||
*/
|
||||
void addExperiment(const std::string &name, ExperimentFlow* flow);
|
||||
/**
|
||||
* Adds the specified experiment or plugin and creates a coroutine to run it in.
|
||||
* @param flow the experiment flow object to be added
|
||||
|
||||
@ -2,5 +2,10 @@
|
||||
set(EXPERIMENTS_ACTIVATED "" CACHE STRING "Activated experiments (a semicolon-separated list of fail/src/experiments/ subdirectories)")
|
||||
|
||||
foreach(experiment_name ${EXPERIMENTS_ACTIVATED})
|
||||
add_subdirectory(${experiment_name})
|
||||
add_subdirectory(${experiment_name})
|
||||
# Create symlink
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
${EXECUTABLE_OUTPUT_PATH}/fail-client
|
||||
${EXECUTABLE_OUTPUT_PATH}/fail-${experiment_name}
|
||||
)
|
||||
endforeach(experiment_name)
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
aspect @EXPERIMENT_TYPE@ExperimentHook {
|
||||
advice execution ("void fail::SimulatorController::initExperiments()") : after () {
|
||||
fail::simulator.addFlow(new @EXPERIMENT_TYPE@);
|
||||
fail::simulator.addExperiment("@EXPERIMENT_NAME@", new @EXPERIMENT_TYPE@);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user