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:
Christian Dietrich
2020-12-14 12:04:03 +01:00
committed by Horst Schirmeier
parent 2ecdba92a5
commit 5929eac85c
5 changed files with 95 additions and 7 deletions

View File

@ -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.

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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@);
}
};