// src/text_simulation.cc - Copyright 2005, 2006, University // of Padova, dept. of Pure and Applied // Mathematics // // This file is part of SGPEMv2. // // This is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // SGPEMv2 is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with SGPEMv2. If not, see http://www.gnu.org/licenses/. #include "text_simulation.hh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace sgpem; using Glib::Thread; using Glib::ustring; // overloaded operator for printing the state of // a schedulable static ostream& operator<<(ostream& os, Schedulable::state state); // overloaded operator for printing the state of // a request/subrequest static ostream& operator<<(ostream& os, Request::state state); namespace sgpem { /** * \brief A class for a generic "parameter" object */ template class CommandParameter { public: CommandParameter(const ustring& _description, const T& _low_bound, const T& _up_bound, bool _required, const T& _preset); CommandParameter(const PolicyParameters::Parameter& pparam); // a textual description of the parameter ustring description; T low_bound; T up_bound; bool required; // default value for the parameter T preset; // current value of the parameter T value; }; template CommandParameter::CommandParameter(const ustring& _description, const T& _low_bound, const T& _up_bound, bool _required, const T& _preset) : description(_description), low_bound(_low_bound), up_bound(_up_bound), required(_required), preset(_preset), value(_preset) {} template CommandParameter::CommandParameter(const PolicyParameters::Parameter& pparam) : description(pparam.get_name()), low_bound(pparam.get_lower_bound()), up_bound(pparam.get_upper_bound()), required(pparam.is_required()), preset(pparam.get_default()), value(pparam.get_value()) {} } TextSimulation::TextSimulation() : _saved(true) {} TextSimulation::~TextSimulation() {} bool TextSimulation::check_arguments_num(const Tokens& arguments, unsigned int num) { if (arguments.size() < num) { ostringstream oss; oss << _("ERROR: this command requires at least ") << num << _(" arguments\n"); p_stderr(oss.str()); return false; } else if (arguments.size() > num) p_stderr(_("WARNING: some arguments will be ignored\n")); return true; } bool TextSimulation::unsaved_ask_confirm() const { if (!_saved) { p_stderr(_("WARNING: Simulation was not recently saved. " "If you continue some changes to the simulation might be lost.\n")); // keep ascking the user until a correct response has been obtained for (bool bad_arg = true; bad_arg;) { p_stdout(_("Continue? [y/n] ")); const ustring buf = readline(); const Tokens tokens = tokenize(buf); if (tokens.size() == 1 && tokens[0].size() == 1) { if (tokens[0].lowercase() == _("n")) return false; else if (tokens[0].lowercase() == _("y")) bad_arg = false; } } } return true; } template void TextSimulation::show(const Container& entities) { for (unsigned int i = 0; i < entities.size(); ++i) { ostringstream oss; oss << i + 1 << ". " << entities[i]->get_name() << endl; p_stdout(oss.str()); } } // Specializations need to go explicitly inside the namespace namespace sgpem { template <> void TextSimulation::show > (const vector& entities) { for (unsigned int i = 0; i < entities.size(); ++i) { ostringstream oss; oss << i + 1 << ". instant: " << entities[i]->get_instant() << endl; p_stdout(oss.str()); } } template <> void TextSimulation::show > (const vector& entities) { for (unsigned int i = 0; i < entities.size(); ++i) { ostringstream oss; oss << i + 1 << ". resource: " << entities[i]->get_resource_key() << endl; p_stdout(oss.str()); } } template <> void TextSimulation::show > (const map& entities) { typedef map::const_iterator ResourceIt; for (ResourceIt it = entities.begin(); it != entities.end(); ++it) { ostringstream oss; oss << it->first << ". " << it->second->get_name(); oss << "[" << it->second->get_places() << "]" << endl; p_stdout(oss.str()); } } } //~ namespace sgpem template void TextSimulation::get_parameter(CommandParameter& parameter) { bool correct; do { correct = true; ostringstream buf; // print an asterisk before name of required parameters if (parameter.required) buf << "*"; buf << parameter.description << " (range: [" << parameter.low_bound << ", " << parameter.up_bound << "] current: " << parameter.value << ") : "; p_stdout(buf.str()); const ustring input = readline(); T value; if (tokenize(input).size() > 0) { try { value = string_to(input); if (value > parameter.up_bound || value < parameter.low_bound) { p_stderr(_("ERROR: Provided value is out of range\n")); correct = false; } } catch (domain_error e) { p_stderr(_("ERROR: Please provide a valid numeric value\n")); correct = false; } if (correct) parameter.value = value; } else if (parameter.required) { p_stderr(_("ERROR: This is a mandatory attribute; you MUST provide a valid value!\n")); correct = false; } } // keep looping until the input is in a correct format while (!correct); } // Specializations need to go explicitly inside the namespace namespace sgpem { template <> void TextSimulation::get_parameter(CommandParameter& parameter) { bool loop = true; // will loop until at least a nonwhite character is typed // for a required parameter while (loop) { ustring buf; if (parameter.required) buf += "*"; p_stdout(buf + parameter.description + " (current: \"" + parameter.value + "\") : "); buf = readline(); const Tokens tokens = tokenize(buf); if (tokens.size() == 0 && parameter.required) p_stderr(_("ERROR: This is a mandatory atribute; you MUST provide a valid value!\n")); else { parameter.value = buf; loop = false; } } } template <> void TextSimulation::get_parameter(CommandParameter& parameter) { bool correct; do { correct = true; ostringstream buf; if (parameter.required) buf << "*"; buf << parameter.description << " (current: " << boolalpha << parameter.value << ") : "; p_stdout(buf.str()); const ustring str = readline(); const Tokens tokens = tokenize(str); // there are nonwhite characters on the input if (tokens.size() != 0) { try { parameter.value = string_to(str); } catch (domain_error e) { p_stderr(_("ERROR: Please provide a valid boolean value ('true' or 'false')\n")); correct = false; } } // input is empty. check if required else if (parameter.required) { p_stderr(_("ERROR: This is a mandatory atribute; you MUST provide a valid value!\n")); correct = false; } } // keep looping until input is correct while (!correct); } } void TextSimulation::on_run(const Tokens& arguments) { check_arguments_num(arguments, 0); // Listen for updates only during scheduling Simulation::get_instance().attach(*this); try { Simulation& sim = Simulation::get_instance(); switch(sim.get_mode()) { case Simulation::mode_step_by_step: sim.run(); break; case Simulation::mode_continuous: int interval = GlobalPreferences::get_instance().get_speed() * 1000; do { sim.run(); Glib::usleep(interval); } while(sim.get_state() != Simulation::state_stopped); break; } } catch (const UserInterruptException& e) { p_stderr(_("ERROR: ")); p_stderr(e.what()); p_stderr(_("\nSimulation is now stopped\n")); } catch (const MalformedPolicyException& e) { p_stderr(_("ERROR: ")); p_stderr(e.what()); p_stderr(_("\nSimulation is now stopped, and " "the current policy will be deactivated\n")); try { // policy is malformed. attempting to unset it Simulation::get_instance().set_policy(NULL); } catch(const CPUPolicyException& f) { // should never happen p_stderr(_("FATAL ERROR: unable to deactivate the policy: ")); p_stderr(f.what()); abort(); } } catch (const NullPolicyException& e) { p_stderr(_("ERROR: ")); p_stderr(e.what()); p_stderr(_("\nSimulation is now stopped\n")); } catch (const CPUPolicyException& e) { p_stderr(_("UNKNOWN ERROR: ")); p_stderr(e.what()); p_stderr("\n"); } Simulation::get_instance().detach(*this); } void TextSimulation::on_pause(const Tokens& arguments) { check_arguments_num(arguments, 0); Simulation::get_instance().pause(); } void TextSimulation::on_jumpto(const Tokens& arguments) { if (!check_arguments_num(arguments, 1)) return; // string containing the destination of jumpto const ustring _position = arguments[0]; // destination position on the history History::position p; try { const int pos = string_to(_position) + 1; if (pos < 0) { p_stderr(_("ERROR: provided instant is out of range.\n")); return; } p = static_cast(pos); } catch (domain_error e) { p_stderr(_("ERROR: provided instant is not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the instant is not within range.\n")); // does this make sense? return; } Simulation::get_instance().attach(*this); try { Simulation::get_instance().jump_to(p); } catch (const UserInterruptException& e) { p_stderr(_("ERROR: ")); p_stderr(e.what()); p_stderr(_("\nSimulation is now stopped\n")); } catch (const MalformedPolicyException& e) { p_stderr(_("ERROR: ")); p_stderr(e.what()); p_stderr(_("\nSimulation is now stopped, and " "the current policy will be deactivated\n")); try { // policy is malformed. attempting to unset it Simulation::get_instance().set_policy(NULL); } catch(const CPUPolicyException& f) { // should never happen p_stderr(_("FATAL ERROR: unable to deactivate the policy: ")); p_stderr(f.what()); abort(); } } catch (const NullPolicyException& e) { p_stderr(_("ERROR: ")); p_stderr(e.what()); p_stderr(_("\nSimulation is now stopped\n")); } catch (const CPUPolicyException& e) { p_stderr(_("UNKNOWN ERROR: ")); p_stderr(e.what()); p_stderr("\n"); } Simulation::get_instance().detach(*this); } void TextSimulation::on_stop(const Tokens& arguments) { check_arguments_num(arguments, 0); Simulation::get_instance().stop(); } template void TextSimulation::configure_policy(PolicyType& policy) { PolicyParameters& parameters = policy.get_parameters(); typedef map > IntParams; typedef map > FloatParams; typedef map > StringParams; IntParams int_params = parameters.get_registered_int_parameters(); FloatParams float_params = parameters.get_registered_float_parameters(); StringParams string_params = parameters.get_registered_string_parameters(); // check there is at least a parameter to configure if(int_params.size() > 0 || float_params.size() > 0 || string_params.size() > 0) { p_stdout(_("Please provide a value for each attribute:\n")); p_stdout(_("Mandatory arguments are marked with an asterisk (*)\n\n")); } else p_stdout(_("Nothing to configure for this policy.\n")); if(int_params.size() > 0) p_stdout(_("Integer arguments:\n")); for (IntParams::iterator it = int_params.begin(); it != int_params.end();) { PolicyParameters::Parameter &p = it->second; CommandParameter cmd_p(p); get_parameter(cmd_p); parameters.set_int(p.get_name(), cmd_p.value); ++it; } // NOTE this piece code is a verbatim copy of the one above. // I tried solving this issue by using templates, but to make // it work will require adding to PolicyParameters a member template // method with 2 specializations... if(float_params.size() > 0) p_stdout(_("\nFloating-point arguments:\n")); for (FloatParams::iterator it = float_params.begin(); it != float_params.end();) { PolicyParameters::Parameter &p = it->second; CommandParameter cmd_p(p); get_parameter(cmd_p); parameters.set_float(p.get_name(), cmd_p.value); ++it; } if(string_params.size() > 0) p_stdout(_("\nString arguments:\n")); for (StringParams::iterator it = string_params.begin(); it != string_params.end();) { PolicyParameters::Parameter &p = it->second; CommandParameter cmd_p(p); get_parameter(cmd_p); parameters.set_string(p.get_name(), cmd_p.value); ++it; } } void TextSimulation::on_configure(const Tokens& arguments) { if(!check_arguments_num(arguments, 1)) return; const ustring what = arguments[0]; if(what == "cpu-policy") { CPUPolicy* policy = Simulation::get_instance().get_policy(); if (policy == NULL) p_stderr(_("ERROR: No CPU policy actually selected for the simulation\n")); else configure_policy(*policy); } else if(what == "resource-policy") { ResourcePolicy* policy = Simulation::get_instance().get_resource_policy(); if (policy == NULL) p_stderr(_("ERROR: No resource policy actually selected for the simulation\n")); else configure_policy(*policy); } else p_stderr(ustring(_("ERROR: Nothing to configure for ")) + what + "\n"); } void TextSimulation::on_help(const Tokens& arguments) { ustring command; if (arguments.size() > 0) { command = arguments[0].uppercase(); // print warning if necessary check_arguments_num(arguments, 1); } if (command.size() == 0) p_stdout(_("Available commands:\nRUN\nSTOP\nPAUSE\n" "CONFIGURE\nHELP\nGET\nSET\nSHOW\nADD\n" "REMOVE\nSAVE\nLOAD\nQUIT\n\n" "HELP followed by a command name shows help about it.\n" "ex. `HELP RUN` shows help about the command RUN\n")); else if (command == "RUN") p_stdout(_("-- RUN COMMAND --\nStarts the simulation. It can be " "continuous or step-by-step depending on the mode configured with " "SET CONTINUOUS (default=true).\n\n" "The output of RUN is a snapshot of the state of the simulation at each " "instant.\n" "The instant 0 represents the initial state, " "during which no process is running. The scheduler " "activity begins at instant 1.\n")); else if (command == "STOP") p_stdout(_("-- STOP COMMAND --\nStops the simulation. The next call to RUN will " "bring the simulation to the first instant and start it.\n")); else if (command == "PAUSE") p_stdout(_("-- PAUSE COMMAND --\nPauses the simulation. The next call to RUN will " "continue it.\n")); else if (command == "JUMPTO") p_stdout(_("-- JUMPTO COMMAND --\nPauses the simulation and jumps to the specified instant.\n")); else if (command == "CONFIGURE") p_stdout(_("-- CONFIGURE COMMAND --\nConfigures a configurable entity.\n\n" "Syntax: CONFIGURE \n" "\twhere may be cpu-policy or resource-policy.\n" "This is currently the only way to control the behaviour of " "policies without modifying their source code.\n")); else if (command == "HELP") p_stdout(_("-- HELP COMMAND --\nThe help you're reading.\n")); else if (command == "GET") p_stdout(_("-- GET COMMAND --\nSyntax: GET \n" "\twhere may be simulation-tick or continuous.\n")); else if (command == "SET") p_stdout(_("-- SET COMMAND --\nSyntax: SET [=] \n" "\twhere may be simulation-tick, continuous, cpu-policy or resource-policy.\n")); else if (command == "SHOW") p_stderr(_("-- SHOW COMMAND --\nDisplays the name of the entities (if available) " "and other informations prefixed by its numeric identifier.\n\n" "Syntax depends from entities being displayed:\n" "`SHOW processes|resources|cpu-policies|resource-policies`\n" "`SHOW threads ` with being the numeric identifier of " "the parent process\n" "`SHOW requests ` with being the numeric " "identifier of the thread child of process identified by \n" "`SHOW subrequests ` where the numeric ids " "follow the same logic of the previous commands\n" "`SHOW statistics`\n")); else if (command == "ADD") p_stderr(_("-- ADD COMMAND --\nAdds an entity by using a questionary-like approach.\n\n" "Syntax depends from entity being added:\n" "`ADD process|resource`\n" "`ADD thread ` with being the numeric identifier of " "the parent process\n" "`ADD request ` with being the numeric " "identifier of the thread child of process identified by \n" "`ADD subrequest ` where the numeric ids " "follow the same logic of the previous commands\n")); else if (command == "REMOVE") p_stderr(_("-- REMOVE COMMAND --\nRemoves an entity.\n\n" "Syntax depends from entity being removed:\n" "`REMOVE process|resource ` where is the process or resource identifier\n" "`REMOVE thread ` with being the identifier of " "the parent process, and the id of the thread to be removed\n" "`REMOVE request ` where the " "numeric ids follow the same logic of the previous commands\n" "`REMOVE subrequest ` where the " "numeric ids follow the same logic of the previous commands\n")); else if (command == "SAVE") p_stderr(_("-- SAVE COMMAND --\nSaves the simulation.\n\n" "Syntax: SAVE \n")); else if (command == "LOAD") p_stderr(_("-- LOAD COMMAND --\nLoads the simulation.\n\n" "Syntax: LOAD \n")); else if (command == "QUIT") p_stderr(_("-- QUIT COMMAND --\nGently closes the program.\n")); else p_stderr(_("ERROR: Sorry, no help available for this command.\n")); } void TextSimulation::on_quit(const Tokens& arguments) { check_arguments_num(arguments, 0); // check the user is sure of what he's doing if (!unsaved_ask_confirm()) return; p_stdout(_("\nBye.\n\n")); // Exit the program with a correct return code. exit(0); } void TextSimulation::on_get(const Tokens& arguments) { if (!check_arguments_num(arguments, 1)) return; const ustring attr = arguments[0].uppercase(); if (attr == "SIMULATION-TICK") { ostringstream oss; oss << "simulation-tick = " << GlobalPreferences::get_instance().get_speed() << "ms" << endl; p_stdout(oss.str()); } else if (attr == "CONTINUOUS") { ostringstream oss; oss << "continuous = " << boolalpha << Simulation::get_instance().get_mode() << endl; p_stdout(oss.str()); } else p_stderr(_("ERROR: invalid attribute name.\n")); } template typename GatekeeperType::Manager::Policy& policy_from_index(const Glib::ustring& value) throw(domain_error) { int index = string_to(value) - 1; if (index < 0) throw domain_error(""); typedef typename GatekeeperType::Managers ManagerVec; typedef typename GatekeeperType::Manager::Policies PolicyVec; typedef typename PolicyVec::iterator CPUPolicyIt; ManagerVec managers = GatekeeperType::get_instance().get_registered(); for (typename ManagerVec::iterator mit = managers.begin(); mit != managers.end(); ++mit) { PolicyVec policies = (*mit)->get_avail_policies(); for (CPUPolicyIt pit = policies.begin(); pit != policies.end(); ++pit) { if (index == 0) return *(*pit); --index; } } // no policy found, index was out of range throw domain_error(""); } void TextSimulation::on_set(const Tokens& arguments) { // handle the optional "=' (I knew that I was buying myself a problem when I // decided to support the assigment operator!) if (arguments.size() >= 3) check_arguments_num(arguments, 3); else if (!check_arguments_num(arguments, 2)) return; const ustring attr = arguments[0].uppercase(); ustring value; // if there is the assignment operator, take the second token, // otherwise the first if (arguments[1] == "=") value = arguments[2]; else value = arguments[1]; if (attr == "SIMULATION-TICK") { try { const int timer = string_to(value); if (timer < 0) throw domain_error(""); GlobalPreferences::get_instance().set_speed(timer); } catch (domain_error e) { p_stderr(_("ERROR: you must provide a valid unsigned integer value\n")); } } else if (attr == "CPU-POLICY") { try { CPUPolicy& p = policy_from_index(value); Simulation::get_instance().set_policy(&p); p_stdout("\n"); p_stdout(p.get_name() + _(" scheduling policy selected.\n")); } catch (domain_error e) { p_stderr(_("ERROR: invalid unsigned integer or not a valid scheduling policy index\n")); } catch (const CPUPolicyException& e) { p_stderr(_("ERROR: ")); p_stderr(e.what()); p_stderr("\n"); } } else if (attr == "RESOURCE-POLICY") { try { ResourcePolicy& p = policy_from_index(value); Simulation::get_instance().set_resource_policy(&p); p_stdout("\n"); p_stdout(p.get_name() + _(" resource policy selected.\n")); } catch (domain_error e) { p_stderr(_("ERROR: invalid unsigned integer or not a valid resource policy index\n")); } catch (const CPUPolicyException& e) { p_stderr(_("ERROR: ")); p_stderr(e.what()); p_stderr("\n"); } } else if (attr == "CONTINUOUS") { try { if(string_to(value) == true) Simulation::get_instance().set_mode(Simulation::mode_continuous); else Simulation::get_instance().set_mode(Simulation::mode_step_by_step); } catch (domain_error e) { p_stderr(_("ERROR: you must provide a valid boolean value ('true' or 'false')\n")); } } else p_stderr(_("ERROR: invalid attribute name.\n")); } void TextSimulation::on_show(const Tokens& arguments) { if (arguments.size() < 1) { //print error check_arguments_num(arguments, 1); return; } //make a local copy which we'll probably modify Tokens args = arguments; const ustring entities = args[0].uppercase(); args.erase(args.begin()); typedef void (TextSimulation::*f_ptr)(const Tokens&); map entities_handlers; entities_handlers["PROCESSES"] = &TextSimulation::on_show_processes; entities_handlers["RESOURCES"] = &TextSimulation::on_show_resources; entities_handlers["THREADS"] = &TextSimulation::on_show_threads; entities_handlers["REQUESTS"] = &TextSimulation::on_show_requests; entities_handlers["SUBREQUESTS"] = &TextSimulation::on_show_subrequests; entities_handlers["CPU-POLICIES"] = &TextSimulation::on_show_cpu_policies; entities_handlers["RESOURCE-POLICIES"] = &TextSimulation::on_show_resource_policies; entities_handlers["STATISTICS"] = &TextSimulation::on_show_statistics; if (entities_handlers.find(entities) == entities_handlers.end()) p_stderr(_("ERROR: invalid argument\n")); else (this->*(entities_handlers[entities]))(args); } void TextSimulation::on_show_processes(const Tokens& arguments) { check_arguments_num(arguments, 0); const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); show(processes); } void TextSimulation::on_show_resources(const Tokens& arguments) { check_arguments_num(arguments, 0); const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Resources& resources = env.get_resources(); show(resources); } void TextSimulation::on_show_threads(const Tokens& arguments) { if (!check_arguments_num(arguments, 1)) return; const ustring process = arguments[0]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); vector threads; try { const int pid = string_to(process) - 1; threads = processes.at(pid)->get_threads(); } catch (domain_error e) { p_stderr(_("ERROR: provided process identifier is not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: this process identifier does not belong to an existing process\n")); return; } show(threads); } void TextSimulation::on_show_requests(const Tokens& arguments) { if (!check_arguments_num(arguments, 2)) return; const ustring process = arguments[0]; const ustring thread = arguments[1]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); vector requests; try { const int pid = string_to(process) - 1; const int tid = string_to(thread) - 1; const vector threads = processes.at(pid)->get_threads(); requests = threads.at(tid)->get_requests(); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } show(requests); } void TextSimulation::on_show_subrequests(const Tokens& arguments) { if (!check_arguments_num(arguments, 3)) return; const ustring process = arguments[0]; const ustring thread = arguments[1]; const ustring request = arguments[2]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); vector subrequests; try { const int pid = string_to(process) - 1; const int tid = string_to(thread) - 1; const int rid = string_to(request) - 1; const vector threads = processes.at(pid)->get_threads(); const vector requests = threads.at(tid)->get_requests(); subrequests = requests.at(rid)->get_subrequests(); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } show(subrequests); } template void TextSimulation::show_policies(const Tokens& arguments) { typedef typename GatekeeperType::Managers ManagerVec; typedef typename GatekeeperType::Manager::Policies PolicyVec; typedef typename PolicyVec::iterator PolicyIt; check_arguments_num(arguments, 0); ManagerVec managers = GatekeeperType::get_instance().get_registered(); unsigned int index = 1; for (typename ManagerVec::iterator mit = managers.begin(); mit != managers.end(); ++mit) { PolicyVec policies = (*mit)->get_avail_policies(); for (PolicyIt pit = policies.begin(); pit != policies.end(); ++pit) { ostringstream oss; oss << endl << index << ". " << (*pit)->get_name() << endl; oss << "\t" << (*pit)->get_description() << endl; p_stdout(oss.str()); ++index; } } } void TextSimulation::on_show_cpu_policies(const Tokens& arguments) { show_policies(arguments); } void TextSimulation::on_show_resource_policies(const Tokens& arguments) { show_policies(arguments); } void TextSimulation::on_show_statistics(const Tokens& arguments) { check_arguments_num(arguments, 0); const unsigned int front = Simulation::get_instance().get_history().get_front(); Statistics::get_instance().calculateStatisticsAt(front); const SimulationStatistics* sim = Statistics::get_instance().get_simulation_statistics(); ostringstream oss; oss << "\n\n****** SIMULATION STATISTICS *******\n AVG_RESP= " << sim->get_average_response_time() << " AVG_INACT= " << sim->get_average_inactivity_time() << " AVG_EXEC= " << sim->get_average_execution_progress() << "% AVG_EFFIC= " << sim->get_average_efficiency() << "% AVG_TURN= " << sim->get_average_turn_around() << " TERM_PROCS= " << sim->get_terminated_processes() << " TERM_THRES= " << sim->get_terminated_threads() << " THRU_PROCS= " << sim->get_average_processes_throughput() << " THRU_THREA= " << sim->get_average_threads_throughput() << "\n\n"; p_stdout(oss.str()); } void TextSimulation::on_add(const Tokens& arguments) { if (arguments.size() < 1) { //print error check_arguments_num(arguments, 1); return; } if (Simulation::get_instance().get_state() != Simulation::state_stopped) { p_stderr(_("WARNING: Simulation is not stopped, it will be automatically stopped\n")); Simulation::get_instance().stop(); } //make a local copy which we'll probably modify Tokens args = arguments; const ustring entity = args[0].uppercase(); args.erase(args.begin()); typedef void (TextSimulation::*f_ptr)(const Tokens&); map entity_handlers; entity_handlers["PROCESS"] = &TextSimulation::on_add_process; entity_handlers["RESOURCE"] = &TextSimulation::on_add_resource; entity_handlers["THREAD"] = &TextSimulation::on_add_thread; entity_handlers["REQUEST"] = &TextSimulation::on_add_request; entity_handlers["SUBREQUEST"] = &TextSimulation::on_add_subrequest; if (entity_handlers.find(entity) == entity_handlers.end()) p_stderr(_("ERROR: invalid argument\n")); else (this->*(entity_handlers[entity]))(args); } void TextSimulation::on_add_process(const Tokens& arguments) { check_arguments_num(arguments, 0); CommandParameter name(_("name"), "", "", false, ""); CommandParameter arrival_time(_("arrival time"), 0, INT_MAX, false, 0); CommandParameter base_priority(_("priority"), 0, INT_MAX, false, 0); get_parameter(name); get_parameter(arrival_time); get_parameter(base_priority); History& h = Simulation::get_instance().get_history(); h.add_process(name.value, arrival_time.value, base_priority.value); } void TextSimulation::on_add_resource(const Tokens& arguments) { check_arguments_num(arguments, 0); CommandParameter name(_("name"), "", "", false, ""); CommandParameter places(_("places"), 0, INT_MAX, false, 1); get_parameter(name); get_parameter(places); History& h = Simulation::get_instance().get_history(); h.add_resource(name.value, false, places.value); } void TextSimulation::on_add_thread(const Tokens& arguments) { if (!check_arguments_num(arguments, 1)) return; const ustring process = arguments[0]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); Process* p; try { p = processes.at(string_to(process) - 1); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } CommandParameter name(_("name"), "", "", false, ""); CommandParameter cpu_time(_("cpu time"), 1, INT_MAX, true, 0); CommandParameter arrival_time(_("arrival time"), 0, INT_MAX, false, 0); CommandParameter base_priority(_("base priority"), 0, INT_MAX, false, 0); get_parameter(name); get_parameter(cpu_time); get_parameter(arrival_time); get_parameter(base_priority); History& h = Simulation::get_instance().get_history(); h.add_thread(name.value, *p, cpu_time.value, arrival_time.value, base_priority.value); } void TextSimulation::on_add_request(const Tokens& arguments) { if (!check_arguments_num(arguments, 2)) return; const ustring process = arguments[0]; const ustring thread = arguments[1]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); Thread* t; try { const int pid = string_to(process) - 1; const int tid = string_to(thread) - 1; const vector threads = processes.at(pid)->get_threads(); t = threads.at(tid); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } CommandParameter instant(_("instant"), 0, INT_MAX, true, 0); get_parameter(instant); History& h = Simulation::get_instance().get_history(); h.add_request(*t, instant.value); } void TextSimulation::on_add_subrequest(const Tokens& arguments) { if (!check_arguments_num(arguments, 3)) return; const ustring process = arguments[0]; const ustring thread = arguments[1]; const ustring request = arguments[2]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); Request* r; try { const int pid = string_to(process) - 1; const int tid = string_to(thread) - 1; const int rid = string_to(request) - 1; const vector threads = processes.at(pid)->get_threads(); const vector requests = threads.at(tid)->get_requests(); r = requests.at(rid); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } CommandParameter resource_key(_("resource key"), 0, INT_MAX, true, 0); CommandParameter duration(_("duration"), 0, INT_MAX, true, 0); const Environment::Resources& resources = env.get_resources(); do { get_parameter(resource_key); if (resources.find(resource_key.value) == resources.end()) p_stderr(_("ERROR: invalid resource identifier\n")); } while (resources.find(resource_key.value) == resources.end()); get_parameter(duration); History& h = Simulation::get_instance().get_history(); h.add_subrequest(*r, resource_key.value, duration.value); } void TextSimulation::on_remove(const Tokens& arguments) { if (arguments.size() < 1) { //print error check_arguments_num(arguments, 1); return; } if (Simulation::get_instance().get_state() != Simulation::state_stopped) p_stderr(_("WARNING: Simulation is not stopped, it will be automatically stopped")); //make a local copy which we'll probably modify Tokens args = arguments; const ustring entity = args[0].uppercase(); args.erase(args.begin()); typedef void (TextSimulation::*f_ptr)(const Tokens&); map entity_handlers; entity_handlers["PROCESS"] = &TextSimulation::on_remove_process; entity_handlers["RESOURCE"] = &TextSimulation::on_remove_resource; entity_handlers["THREAD"] = &TextSimulation::on_remove_thread; entity_handlers["REQUEST"] = &TextSimulation::on_remove_request; entity_handlers["SUBREQUEST"] = &TextSimulation::on_remove_subrequest; if (entity_handlers.find(entity) == entity_handlers.end()) p_stderr(_("ERROR: invalid argument\n")); else (this->*(entity_handlers[entity]))(args); } void TextSimulation::on_remove_process(const Tokens& arguments) { if (!check_arguments_num(arguments, 1)) return; const ustring process = arguments[0]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); Process* p; try { p = processes.at(string_to(process) - 1); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } History& h = Simulation::get_instance().get_history(); h.remove(*p); } void TextSimulation::on_remove_resource(const Tokens& arguments) { if (!check_arguments_num(arguments, 1)) return; const ustring resource = arguments[0]; History& h = Simulation::get_instance().get_history(); History::resource_key_t rid; try { rid = string_to(resource); const Environment::Resources& resources = h.get_environment_at(0).get_resources(); if (resources.find(rid) == resources.end()) throw out_of_range(_("invalid resource id")); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } h.remove(rid); } void TextSimulation::on_remove_thread(const Tokens& arguments) { if (!check_arguments_num(arguments, 2)) return; const ustring process = arguments[0]; const ustring thread = arguments[1]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); Thread* t; try { const int pid = string_to(process) - 1; const int tid = string_to(thread) - 1; const vector threads = processes.at(pid)->get_threads(); t = threads.at(tid); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } History& h = Simulation::get_instance().get_history(); h.remove(*t); } void TextSimulation::on_remove_request(const Tokens& arguments) { if (!check_arguments_num(arguments, 3)) return; const ustring process = arguments[0]; const ustring thread = arguments[1]; const ustring request = arguments[2]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); Request* r; try { const int pid = string_to(process) - 1; const int tid = string_to(thread) - 1; const int rid = string_to(request) - 1; const vector threads = processes.at(pid)->get_threads(); const vector requests = threads.at(tid)->get_requests(); r = requests.at(rid); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } History& h = Simulation::get_instance().get_history(); h.remove(*r); } void TextSimulation::on_remove_subrequest(const Tokens& arguments) { if (!check_arguments_num(arguments, 4)) return; const ustring process = arguments[0]; const ustring thread = arguments[1]; const ustring request = arguments[2]; const ustring subrequest = arguments[3]; const Environment& env = Simulation::get_instance().get_history().get_environment_at(0); const Environment::Processes& processes = env.get_processes(); SubRequest* r; try { const int pid = string_to(process) - 1; const int tid = string_to(thread) - 1; const int rid = string_to(request) - 1; const int srid = string_to(subrequest) - 1; const vector threads = processes.at(pid)->get_threads(); const vector requests = threads.at(tid)->get_requests(); const vector subrequests = requests.at(rid)->get_subrequests(); r = subrequests.at(srid); } catch (domain_error e) { p_stderr(_("ERROR: provided identifier(s) not a valid integer\n")); return; } catch (out_of_range e) { p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n")); return; } History& h = Simulation::get_instance().get_history(); h.remove(*r); } void TextSimulation::on_save(const Tokens& arguments) { if (!check_arguments_num(arguments, 1)) return; const ustring filename = arguments[0]; try { const vector serializers = SerializersGatekeeper::get_instance().get_registered(); // FIXME using the first serializer available, this // will need to be changed when multiple serializers will // be made available Serializer& serializer = *serializers.at(0); const History& history = Simulation::get_instance().get_history(); serializer.save_snapshot(filename, history); _saved = true; } catch (out_of_range e) { p_stderr(_("ERROR: No registered serializer available\n")); } catch (SerializerError e) { string msg = _("ERROR: "); p_stderr(msg + e.what() + "\n"); } } void TextSimulation::on_load(const Tokens& arguments) { if (!check_arguments_num(arguments, 1)) return; const ustring filename = arguments[0]; if (!unsaved_ask_confirm()) return; try { const vector serializers = SerializersGatekeeper::get_instance().get_registered(); // FIXME using the first serializer available, this // will need to be changed when multiple serializers will // be made available Serializer& serializer = *serializers.at(0); History& history = Simulation::get_instance().get_history(); serializer.restore_snapshot(filename, history); } catch (out_of_range e) { p_stderr(_("ERROR: No registered serializer available\n")); } catch (SerializerError e) { string msg = _("ERROR: "); p_stderr(msg + e.what() + "\n"); } } void TextSimulation::p_stdout(const ustring& str) { cout << str; } void TextSimulation::p_stderr(const ustring& str) { cerr << str; } ustring TextSimulation::readline() { // TODO throw an exception when the stream returned by // getline() has the fail, bad or eof bit set, because // in this case the application should quit string str; getline(cin, str); return str; } void TextSimulation::parse_command(TextSimulation& sim, const ustring& str) { typedef void (TextSimulation::*f_ptr)(const Tokens&); map command_handlers; command_handlers["RUN"] = &TextSimulation::on_run; command_handlers["STOP"] = &TextSimulation::on_stop; command_handlers["PAUSE"] = &TextSimulation::on_pause; command_handlers["JUMPTO"] = &TextSimulation::on_jumpto; command_handlers["CONFIGURE"] = &TextSimulation::on_configure; command_handlers["HELP"] = &TextSimulation::on_help; command_handlers["GET"] = &TextSimulation::on_get; command_handlers["SET"] = &TextSimulation::on_set; command_handlers["SHOW"] = &TextSimulation::on_show; command_handlers["ADD"] = &TextSimulation::on_add; command_handlers["REMOVE"] = &TextSimulation::on_remove; command_handlers["SAVE"] = &TextSimulation::on_save; command_handlers["LOAD"] = &TextSimulation::on_load; command_handlers["QUIT"] = &TextSimulation::on_quit; Tokens arguments = tokenize(str); // Ignore empty lines if (arguments.size() == 0) return; // Allow also comments into batch scripts (w/ echo): if(arguments[0].at(0) == '#') { cout << str; return; } ustring key = arguments[0].uppercase(); if (command_handlers.find(key) == command_handlers.end()) { p_stderr(_("ERROR: command not supported\n")); return; } arguments.erase(arguments.begin()); (sim.*(command_handlers[key]))(arguments); if (key == "ADD" || key == "REMOVE") sim._saved = false; } static ostream& operator<<(ostream& os, Schedulable::state state) { switch (state) { case Schedulable::state_running: os << _(">> RUNNING <<"); break; case Schedulable::state_ready: os << _("READY"); break; case Schedulable::state_blocked: os << _("BLOCKED"); break; case Schedulable::state_future: os << _("FUTURE"); break; case Schedulable::state_terminated: os << _("TERMINATED"); break; default: os.setstate(ios_base::failbit); } return os; } static ostream& operator<<(ostream& os, Request::state state) { switch (state) { case Request::state_unallocable: os << _("UNALLOCABLE"); break; case Request::state_allocated: os << _("ALLOCATED"); break; case Request::state_future: os << _("FUTURE"); break; case Request::state_exhausted: os << _("EXHAUSTED"); break; case Request::state_allocable: os << _("ALLOCABLE"); break; default: os.setstate(ios_base::failbit); } return os; } void TextSimulation::update(const Simulation& changed_simulation) { ostringstream oss; int printed_instant; static const std::string tab = " "; // Print header for each instant: printed_instant = static_cast(changed_simulation.get_history().get_front()) - 1; oss << endl << ">>>> " << printed_instant; // Print ready queue oss << endl << _("READY QUEUE: { "); const Environment& env = changed_simulation.get_history().get_last_environment(); const ReadyQueue& q = env.get_sorted_queue(); for (unsigned int i = 0; i < q.size(); ++i) { const Thread& t = q.get_item_at(i); oss << t.get_name() + _(" ~ "); } oss << _("}") << endl; // Flush buffer to screen p_stdout(oss.str()); oss.str(string()); const Environment::Resources& resources = env.get_resources(); const Environment::Processes& processes = env.get_processes(); typedef Environment::Resources::const_iterator ResourceIt; // Write the queue of requests for each resource oss << _("RESOURCES:") << endl; for (ResourceIt it = resources.begin(); it != resources.end(); ++it) { const Resource& r = *it->second; const Environment::resource_key_t key = it->first; oss << right << setw(3) << key << left << ". " << r.get_name() << _(", with "); oss << r.get_places() << _(" places") << endl; const Environment::SubRequestQueue& req_queue = env.get_request_queue(it->first); oss << setw(tab.size()*3) << ' ' << _("queue: { "); for (unsigned int i = 0; i < req_queue.size(); ++i) { if (i == r.get_places()) oss << " || "; else if (i != 0) oss << " ~ "; if (req_queue[i]->get_state() == Request::state_allocated) oss << "[" << req_queue[i]->get_request().get_thread().get_name() << "]"; else oss << req_queue[i]->get_request().get_thread().get_name(); } oss << _(" }") << endl; } // Flush buffer to screen p_stdout(oss.str()); oss.str(string()); // Set new format fillers static const unsigned int fill0 = 25; static const unsigned int fill1 = 15; // Minimum fill0 for the usage we make of it below: assert(fill0 > tab.size() * 2 + 6); oss << endl; // Print display header oss << left; oss << setw(fill0) << _("PROCESSES:"); oss << right; oss << setw(fill1) << _("state") << setw(fill1) << _("arrival") << setw(fill1) << _("requiring") << setw(fill1) << _("elapsed") << setw(fill1) << _("priority") << setw(fill1) << _("resource_id") << endl; // Display information for each schedulable/request in a tabular format for (unsigned int pi = 0; pi < processes.size(); ++pi) { // Display process Process& p = *processes[pi]; oss << setw(2) << right << (pi + 1) << left << ". " << setw(fill0 - 4) << p.get_name().substr(0, fill0 - 5); oss << right; oss << setw(fill1) << p.get_state(); oss << setw(fill1) << p.get_arrival_time(); oss << setw(fill1) << p.get_total_cpu_time(); oss << setw(fill1) << p.get_elapsed_time(); oss << setw(fill1) << p.get_current_priority(); oss << endl; p_stdout(oss.str()); oss.str(string()); // Display threads const vector threads = p.get_threads(); for (unsigned int ti = 0; ti < threads.size(); ++ti) { Thread& t = *threads[ti]; oss << right << setw(tab.size() + 2) << ti + 1 << left << ". " << setw(fill0 - 4 - tab.size()) << t.get_name().substr(0, fill0 - 5 - tab.size()); oss << right; oss << setw(fill1) << t.get_state(); oss << setw(fill1) << t.get_arrival_time(); oss << setw(fill1) << t.get_total_cpu_time(); oss << setw(fill1) << t.get_elapsed_time(); oss << setw(fill1) << t.get_current_priority(); // Temporary debug code. Remove me when done. // oss << setw(fill1 / 2) << t.get_last_acquisition(); // oss << setw(fill1 / 2) << t.get_last_release(); oss << endl; p_stdout(oss.str()); oss.str(string()); // Display requests const vector requests = t.get_requests(); for (unsigned int ri = 0; ri < requests.size(); ++ri) { Request& r = *requests[ri]; // Display subrequests const vector subrequests = r.get_subrequests(); for (unsigned int sri = 0; sri < subrequests.size(); ++sri) { SubRequest& sr = *subrequests[sri]; ResourceIt point = resources.find(sr.get_resource_key()); oss << setw(2 + tab.size() * 2) << ri + 1 << left << "." << setw(2) << sri + 1 << setw(fill0 - 5 - tab.size() * 2) << (point->second)->get_name().substr(0, fill0 - 6 - tab.size() * 2); oss << right; oss << setw(fill1) << sr.get_state(); oss << setw(fill1) << r.get_instant(); oss << setw(fill1) << sr.get_length(); oss << setw(fill1) << sr.get_length() - sr.get_remaining_time(); oss << setw(fill1 * 2) << sr.get_resource_key(); oss << endl; p_stdout(oss.str()); oss.str(string()); } } } } }