From 5929eac85cfbf89e131907282922b046a1048935 Mon Sep 17 00:00:00 2001 From: Christian Dietrich Date: Mon, 14 Dec 2020 12:04:03 +0100 Subject: [PATCH] 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*. --- src/core/efw/CoroutineManager.hpp | 4 ++ src/core/sal/SimulatorController.cc | 64 +++++++++++++++++++- src/core/sal/SimulatorController.hpp | 25 +++++++- src/experiments/CMakeLists.txt | 7 ++- src/experiments/instantiate-experiment.ah.in | 2 +- 5 files changed, 95 insertions(+), 7 deletions(-) diff --git a/src/core/efw/CoroutineManager.hpp b/src/core/efw/CoroutineManager.hpp index 9a98b3c2..18d2ce6a 100644 --- a/src/core/efw/CoroutineManager.hpp +++ b/src/core/efw/CoroutineManager.hpp @@ -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. diff --git a/src/core/sal/SimulatorController.cc b/src/core/sal/SimulatorController.cc index d7830e28..c40462b9 100644 --- a/src/core/sal/SimulatorController.cc +++ b/src/core/sal/SimulatorController.cc @@ -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: diff --git a/src/core/sal/SimulatorController.hpp b/src/core/sal/SimulatorController.hpp index 3ea269f7..7abb5ad7 100644 --- a/src/core/sal/SimulatorController.hpp +++ b/src/core/sal/SimulatorController.hpp @@ -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 m_Experiments; //!< registered experiments, one is chosen on startup CoroutineManager m_Flows; //!< managed experiment flows MemoryManager *m_Mem; //!< access to memory pool std::vector 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 diff --git a/src/experiments/CMakeLists.txt b/src/experiments/CMakeLists.txt index 931c52d9..f6a71aeb 100644 --- a/src/experiments/CMakeLists.txt +++ b/src/experiments/CMakeLists.txt @@ -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) diff --git a/src/experiments/instantiate-experiment.ah.in b/src/experiments/instantiate-experiment.ah.in index d6cb24b1..5a254c6e 100644 --- a/src/experiments/instantiate-experiment.ah.in +++ b/src/experiments/instantiate-experiment.ah.in @@ -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@); } };