/****************************************************************************** * Copyright (c) 2000-2016 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Baji, Laszlo * Balasko, Jeno * Baranyi, Botond * Delic, Adam * Feher, Csaba * Forstner, Matyas * Kovacs, Ferenc * Lovassy, Arpad * Raduly, Csaba * Szabados, Kristof * Szabo, Bence Janos * Szabo, Janos Zoltan – initial implementation * Zalanyi, Balazs Andor * Pandi, Krisztian * ******************************************************************************/ #if defined(LINUX) && ! defined(_GNU_SOURCE) // in order to get the prototype of non-standard strsignal() # define _GNU_SOURCE #endif #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <ctype.h> #include <netdb.h> #include <sys/types.h> #include <sys/time.h> #include <signal.h> #include <sys/resource.h> #include <sys/wait.h> #ifdef SOLARIS8 #include <sys/utsname.h> #endif #include "../common/memory.h" #include "../common/version_internal.h" #include "Runtime.hh" #include "Communication.hh" #include "Error.hh" #include "Logger.hh" #include "Snapshot.hh" #include "Port.hh" #include "Timer.hh" #include "Module_list.hh" #include "Component.hh" #include "Default.hh" #include "Verdicttype.hh" #include "Charstring.hh" #include "Fd_And_Timeout_User.hh" #include <TitanLoggerApi.hh> #include "Profiler.hh" namespace API = TitanLoggerApi; #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 256 #endif #include "../common/dbgnew.hh" TTCN_Runtime::executor_state_enum TTCN_Runtime::executor_state = UNDEFINED_STATE; qualified_name TTCN_Runtime::component_type = { NULL, NULL }; char *TTCN_Runtime::component_name = NULL; boolean TTCN_Runtime::is_alive = FALSE; const char *TTCN_Runtime::control_module_name = NULL; qualified_name TTCN_Runtime::testcase_name = { NULL, NULL }; char *TTCN_Runtime::host_name = NULL; verdicttype TTCN_Runtime::local_verdict = NONE; unsigned int TTCN_Runtime::verdict_count[5] = { 0, 0, 0, 0, 0 }, TTCN_Runtime::control_error_count = 0; CHARSTRING TTCN_Runtime::verdict_reason(0, ""); // empty string boolean TTCN_Runtime::in_ttcn_try_block = FALSE; char *TTCN_Runtime::begin_controlpart_command = NULL, *TTCN_Runtime::end_controlpart_command = NULL, *TTCN_Runtime::begin_testcase_command = NULL, *TTCN_Runtime::end_testcase_command = NULL; component TTCN_Runtime::create_done_killed_compref = NULL_COMPREF; boolean TTCN_Runtime::running_alive_result = FALSE; alt_status TTCN_Runtime::any_component_done_status = ALT_UNCHECKED, TTCN_Runtime::all_component_done_status = ALT_UNCHECKED, TTCN_Runtime::any_component_killed_status = ALT_UNCHECKED, TTCN_Runtime::all_component_killed_status = ALT_UNCHECKED; int TTCN_Runtime::component_status_table_size = 0; component TTCN_Runtime::component_status_table_offset = FIRST_PTC_COMPREF; struct TTCN_Runtime::component_status_table_struct { alt_status done_status, killed_status; char *return_type; Text_Buf *return_value; } *TTCN_Runtime::component_status_table = NULL; struct TTCN_Runtime::component_process_struct { component component_reference; pid_t process_id; boolean process_killed; struct component_process_struct *prev_by_compref, *next_by_compref; struct component_process_struct *prev_by_pid, *next_by_pid; } **TTCN_Runtime::components_by_compref = NULL, **TTCN_Runtime::components_by_pid = NULL; boolean TTCN_Runtime::is_idle() { switch (executor_state) { case HC_IDLE: case HC_ACTIVE: case HC_OVERLOADED: case MTC_IDLE: case PTC_IDLE: case PTC_STOPPED: return TRUE; default: return FALSE; } } boolean TTCN_Runtime::verdict_enabled() { return executor_state == SINGLE_TESTCASE || (executor_state >= MTC_TESTCASE && executor_state <= MTC_EXIT) || (executor_state >= PTC_INITIAL && executor_state <= PTC_EXIT); } void TTCN_Runtime::wait_for_state_change() { executor_state_enum old_state = executor_state; do { TTCN_Snapshot::take_new(TRUE); } while (old_state == executor_state); } void TTCN_Runtime::clear_qualified_name(qualified_name& q_name) { Free(q_name.module_name); q_name.module_name = NULL; Free(q_name.definition_name); q_name.definition_name = NULL; } void TTCN_Runtime::clean_up() { clear_qualified_name(component_type); Free(component_name); component_name = NULL; control_module_name = NULL; clear_qualified_name(testcase_name); Free(host_name); host_name = NULL; clear_external_commands(); } void TTCN_Runtime::initialize_component_type() { TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::init__component__start, component_type.module_name, component_type.definition_name, 0, NULL, TTCN_Runtime::get_testcase_name()); Module_List::initialize_component(component_type.module_name, component_type.definition_name, TRUE); PORT::set_parameters((component)self, component_name); PORT::all_start(); TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::init__component__finish, component_type.module_name, component_type.definition_name); local_verdict = NONE; verdict_reason = ""; } void TTCN_Runtime::terminate_component_type() { if (component_type.module_name != NULL && component_type.definition_name != NULL) { TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::terminating__component, component_type.module_name, component_type.definition_name); TTCN_Default::deactivate_all(); TIMER::all_stop(); PORT::deactivate_all(); TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::component__shut__down, component_type.module_name, component_type.definition_name, 0, NULL, TTCN_Runtime::get_testcase_name()); clear_qualified_name(component_type); Free(component_name); component_name = NULL; } } void TTCN_Runtime::set_component_type(const char *component_type_module, const char *component_type_name) { if (component_type_module == NULL || component_type_module[0] == '\0' || component_type_name == NULL || component_type_name[0] == '\0') TTCN_error("Internal error: TTCN_Runtime::set_component_type: " "Trying to set an invalid component type."); if (component_type.module_name != NULL || component_type.definition_name != NULL) TTCN_error("Internal error: TTCN_Runtime::set_component_type: " "Trying to set component type %s.%s while another one is active.", component_type_module, component_type_name); component_type.module_name = mcopystr(component_type_module); component_type.definition_name = mcopystr(component_type_name); } void TTCN_Runtime::set_component_name(const char *new_component_name) { Free(component_name); if (new_component_name != NULL && new_component_name[0] != '\0') component_name = mcopystr(new_component_name); else component_name = NULL; } void TTCN_Runtime::set_testcase_name(const char *par_module_name, const char *par_testcase_name) { if (par_module_name == NULL || par_module_name[0] == '\0' || par_testcase_name == NULL || par_testcase_name[0] == '\0') TTCN_error("Internal error: TTCN_Runtime::set_testcase_name: " "Trying to set an invalid testcase name."); if (testcase_name.module_name != NULL || testcase_name.definition_name != NULL) TTCN_error("Internal error: TTCN_Runtime::set_testcase_name: " "Trying to set testcase name %s.%s while another one is active.", par_module_name, par_testcase_name); testcase_name.module_name = mcopystr(par_module_name); testcase_name.definition_name = mcopystr(par_testcase_name); } const char *TTCN_Runtime::get_host_name() { if (host_name == NULL) { #if defined(SOLARIS8) // Workaround for Solaris10 (lumped under SOLARIS8) + dynamic linking. // "g++ -shared" seems to produce a very strange kind of symbol // for gethostname in the .so, and linking fails with the infamous // "ld: libttcn3-dynamic.so: gethostname: invalid version 3 (max 0)" // The workaround is to use uname instead of gethostname. struct utsname uts; if (uname(&uts) < 0) { TTCN_Logger::begin_event(TTCN_Logger::WARNING_UNQUALIFIED); TTCN_Logger::log_event_str("System call uname() failed."); TTCN_Logger::OS_error(); TTCN_Logger::end_event(); host_name = mcopystr("unknown"); } else { host_name = mcopystr(uts.nodename); } #else char tmp_str[MAXHOSTNAMELEN + 1]; if (gethostname(tmp_str, MAXHOSTNAMELEN)) { TTCN_Logger::begin_event(TTCN_Logger::WARNING_UNQUALIFIED); TTCN_Logger::log_event_str("System call gethostname() failed."); TTCN_Logger::OS_error(); TTCN_Logger::end_event(); tmp_str[0] = '\0'; } else tmp_str[MAXHOSTNAMELEN] = '\0'; if (tmp_str[0] != '\0') host_name = mcopystr(tmp_str); else host_name = mcopystr("unknown"); #endif } return host_name; } CHARSTRING TTCN_Runtime::get_host_address(const CHARSTRING& type) { if (type != "Ipv4orIpv6" && type != "Ipv4" && type != "Ipv6") { TTCN_error("The argument of hostid function must be Ipv4orIpv6 or Ipv4" "or Ipv6. %s is not a valid argument.", (const char*)type); } // Return empty if no host address could be retrieved if (!TTCN_Communication::has_local_address()) { return CHARSTRING(""); } const IPAddress *address = TTCN_Communication::get_local_address(); // Return empty if the type is not matching the address type if (type == "Ipv4") { const IPv4Address * ipv4 = dynamic_cast<const IPv4Address*>(address); if (ipv4 == NULL) { return CHARSTRING(""); } } if (type == "Ipv6") { const IPv6Address * ipv6 = NULL; #if defined(LINUX) || defined(CYGWIN17) ipv6 = dynamic_cast<const IPv6Address*>(address); #endif // LINUX || CYGWIN17 if (ipv6 == NULL) { return CHARSTRING(""); } } // Return the string representation of the address return CHARSTRING(address->get_addr_str()); } CHARSTRING TTCN_Runtime::get_testcase_id_macro() { if (in_controlpart()) TTCN_error("Macro %%testcaseId cannot be used from " "the control part outside test cases."); if (testcase_name.definition_name == NULL || testcase_name.definition_name[0] == '\0') TTCN_error("Internal error: Evaluating macro %%testcaseId, but the " "name of the current testcase is not set."); return CHARSTRING(testcase_name.definition_name); } CHARSTRING TTCN_Runtime::get_testcasename() { if (in_controlpart() || is_hc()) return CHARSTRING(""); // No error here. if (!testcase_name.definition_name || testcase_name.definition_name[0] == 0) TTCN_error("Internal error: Evaluating predefined function testcasename()" ", but the name of the current testcase is not set."); return CHARSTRING(testcase_name.definition_name); } void TTCN_Runtime::load_logger_plugins() { TTCN_Logger::load_plugins((component)self, component_name); } void TTCN_Runtime::set_logger_parameters() { TTCN_Logger::set_plugin_parameters((component)self, component_name); } const char *TTCN_Runtime::get_signal_name(int signal_number) { const char *signal_name = strsignal(signal_number); if (signal_name != NULL) return signal_name; else return "Unknown signal"; } static void sigint_handler(int signum) { if (signum != SIGINT) { TTCN_warning("Unexpected signal %d (%s) was caught by the handler of " "SIGINT.", signum, TTCN_Runtime::get_signal_name(signum)); return; } if (TTCN_Runtime::is_single()) { TTCN_Logger::log_str(TTCN_Logger::WARNING_UNQUALIFIED, "Execution was interrupted by the user."); if (TTCN_Runtime::get_state() == TTCN_Runtime::SINGLE_TESTCASE) { TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::stopping__current__testcase); TTCN_Runtime::end_testcase(); } else { TIMER::all_stop(); } TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::exiting); exit(EXIT_FAILURE); } } void TTCN_Runtime::set_signal_handler(int signal_number, const char *signal_name, signal_handler_type signal_handler) { struct sigaction sig_act; if (sigaction(signal_number, NULL, &sig_act)) TTCN_error("System call sigaction() failed when getting signal " "handling information for %s.", signal_name); sig_act.sa_handler = signal_handler; sig_act.sa_flags = 0; if (sigaction(signal_number, &sig_act, NULL)) TTCN_error("System call sigaction() failed when changing the signal " "handling settings for %s.", signal_name); } void TTCN_Runtime::restore_default_handler(int signal_number, const char *signal_name) { struct sigaction sig_act; if (sigaction(signal_number, NULL, &sig_act)) TTCN_error("System call sigaction() failed when getting signal " "handling information for %s.", signal_name); sig_act.sa_handler = SIG_DFL; sig_act.sa_flags = 0; if (sigaction(signal_number, &sig_act, NULL)) TTCN_error("System call sigaction() failed when restoring the " "default signal handling settings for %s.", signal_name); } void TTCN_Runtime::ignore_signal(int signal_number, const char *signal_name) { struct sigaction sig_act; if (sigaction(signal_number, NULL, &sig_act)) TTCN_error("System call sigaction() failed when getting signal " "handling information for %s.", signal_name); sig_act.sa_handler = SIG_IGN; sig_act.sa_flags = 0; if (sigaction(signal_number, &sig_act, NULL)) TTCN_error("System call sigaction() failed when disabling signal " "%s.", signal_name); } void TTCN_Runtime::enable_interrupt_handler() { set_signal_handler(SIGINT, "SIGINT", sigint_handler); } void TTCN_Runtime::disable_interrupt_handler() { ignore_signal(SIGINT, "SIGINT"); } void TTCN_Runtime::install_signal_handlers() { if (is_single()) set_signal_handler(SIGINT, "SIGINT", sigint_handler); ignore_signal(SIGPIPE, "SIGPIPE"); } void TTCN_Runtime::restore_signal_handlers() { if (is_single()) restore_default_handler(SIGINT, "SIGINT"); restore_default_handler(SIGPIPE, "SIGPIPE"); } int TTCN_Runtime::hc_main(const char *local_addr, const char *MC_addr, unsigned short MC_port) { int ret_val = EXIT_SUCCESS; executor_state = HC_INITIAL; TTCN_Logger::log_HC_start(get_host_name()); TTCN_Logger::write_logger_settings(); TTCN_Snapshot::check_fd_setsize(); try { if (local_addr != NULL) TTCN_Communication::set_local_address(local_addr); TTCN_Communication::set_mc_address(MC_addr, MC_port); TTCN_Communication::connect_mc(); Module_List::send_versions(); executor_state = HC_IDLE; TTCN_Communication::send_version(); initialize_component_process_tables(); do { TTCN_Snapshot::take_new(TRUE); TTCN_Communication::process_all_messages_hc(); } while (executor_state >= HC_IDLE && executor_state < HC_EXIT); if (executor_state == HC_EXIT) { // called only on the HC TTCN_Communication::disconnect_mc(); clean_up(); } } catch (const TC_Error& tc_error) { ret_val = EXIT_FAILURE; clean_up(); } // called on the newly created MTC and PTCs as well because // the hashtables are inherited with fork() clear_component_process_tables(); if (is_hc()) TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::host__controller__finished); return ret_val; } int TTCN_Runtime::mtc_main() { int ret_val = EXIT_SUCCESS; TTCN_Runtime::load_logger_plugins(); TTCN_Runtime::set_logger_parameters(); TTCN_Logger::open_file(); TTCN_Logger::log_executor_component(API::ExecutorComponent_reason::mtc__started); TTCN_Logger::write_logger_settings(); try { TTCN_Communication::connect_mc(); executor_state = MTC_IDLE; TTCN_Communication::send_mtc_created(); do { TTCN_Snapshot::take_new(TRUE); TTCN_Communication::process_all_messages_tc(); } while (executor_state != MTC_EXIT); TTCN_Logger::close_file(); TTCN_Communication::disconnect_mc(); clean_up(); } catch (const TC_Error& tc_error) { ret_val = EXIT_FAILURE; } TTCN_Logger::log_executor_component(API::ExecutorComponent_reason::mtc__finished); return ret_val; } int TTCN_Runtime::ptc_main() { int ret_val = EXIT_SUCCESS; TTCN_Runtime::load_logger_plugins(); TTCN_Runtime::set_logger_parameters(); TTCN_Logger::open_file(); TTCN_Logger::begin_event(TTCN_Logger::EXECUTOR_COMPONENT); TTCN_Logger::log_event("TTCN-3 Parallel Test Component started on %s. " "Component reference: ", get_host_name()); self.log(); TTCN_Logger::log_event(", component type: %s.%s", component_type.module_name, component_type.definition_name); if (component_name != NULL) TTCN_Logger::log_event(", component name: %s", component_name); TTCN_Logger::log_event_str(". Version: " PRODUCT_NUMBER "."); TTCN_Logger::end_event(); TTCN_Logger::write_logger_settings(); try { TTCN_Communication::connect_mc(); executor_state = PTC_IDLE; TTCN_Communication::send_ptc_created((component)self); try { initialize_component_type(); } catch (const TC_Error& tc_error) { TTCN_Logger::log_executor_component(API::ExecutorComponent_reason::component__init__fail); ret_val = EXIT_FAILURE; } if (ret_val == EXIT_SUCCESS) { if (ttcn3_debugger.is_activated()) { ttcn3_debugger.open_output_file(); } try { do { TTCN_Snapshot::take_new(TRUE); TTCN_Communication::process_all_messages_tc(); } while (executor_state != PTC_EXIT); } catch (const TC_Error& tc_error) { TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::error__idle__ptc); ret_val = EXIT_FAILURE; } } if (ret_val != EXIT_SUCCESS) { // ignore errors in subsequent operations try { terminate_component_type(); } catch (const TC_Error& tc_error) { } try { TTCN_Communication::send_killed(local_verdict, (const char *)verdict_reason); } catch (const TC_Error& tc_error) { } TTCN_Logger::log_final_verdict(true, local_verdict, local_verdict, local_verdict, (const char *)verdict_reason); executor_state = PTC_EXIT; } TTCN_Communication::disconnect_mc(); clear_component_status_table(); clean_up(); } catch (const TC_Error& tc_error) { ret_val = EXIT_FAILURE; } TTCN_Logger::log_executor_component(API::ExecutorComponent_reason::ptc__finished); return ret_val; } component TTCN_Runtime::create_component( const char *created_component_type_module, const char *created_component_type_name, const char *created_component_name, const char *created_component_location, boolean created_component_alive) { if (in_controlpart()) TTCN_error("Create operation cannot be performed in the control part."); else if (is_single()) TTCN_error("Create operation cannot be performed in single mode."); if (created_component_name != NULL && created_component_name[0] == '\0') { TTCN_warning("Empty charstring value was ignored as component name " "in create operation."); created_component_name = NULL; } if (created_component_location != NULL && created_component_location[0] == '\0') { TTCN_warning("Empty charstring value was ignored as component location " "in create operation."); created_component_location = NULL; } TTCN_Logger::begin_event(TTCN_Logger::PARALLEL_UNQUALIFIED); TTCN_Logger::log_event("Creating new %sPTC with component type %s.%s", created_component_alive ? "alive ": "", created_component_type_module, created_component_type_name); if (created_component_name != NULL) TTCN_Logger::log_event(", component name: %s", created_component_name); if (created_component_location != NULL) TTCN_Logger::log_event(", location: %s", created_component_location); TTCN_Logger::log_char('.'); TTCN_Logger::end_event(); switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_CREATE; break; case PTC_FUNCTION: executor_state = PTC_CREATE; break; default: TTCN_error("Internal error: Executing create operation in invalid " "state."); } TTCN_Communication::send_create_req(created_component_type_module, created_component_type_name, created_component_name, created_component_location, created_component_alive); if (is_mtc()) { // updating the component status flags // 'any component.done' and 'any component.killed' might be successful // from now since the PTC can terminate by itself if (any_component_done_status == ALT_NO) any_component_done_status = ALT_UNCHECKED; if (any_component_killed_status == ALT_NO) any_component_killed_status = ALT_UNCHECKED; // 'all component.killed' must be re-evaluated later all_component_killed_status = ALT_UNCHECKED; } wait_for_state_change(); TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::ptc__created, created_component_type_module, created_component_type_name, create_done_killed_compref, created_component_name, created_component_location, created_component_alive); COMPONENT::register_component_name(create_done_killed_compref, created_component_name); return create_done_killed_compref; } void TTCN_Runtime::prepare_start_component(const COMPONENT& component_reference, const char *module_name, const char *function_name, Text_Buf& text_buf) { if (in_controlpart()) TTCN_error("Start test component operation cannot " "be performed in the control part."); else if (is_single()) TTCN_error("Start test component operation cannot " "be performed in single mode."); if (!component_reference.is_bound()) TTCN_error("Performing a start " "operation on an unbound component reference."); component compref = (component)component_reference; switch (compref) { case NULL_COMPREF: TTCN_error("Start operation cannot be performed on the null " "component reference."); case MTC_COMPREF: TTCN_error("Start operation cannot be performed on the component " "reference of MTC."); case SYSTEM_COMPREF: TTCN_error("Start operation cannot be performed on the component " "reference of system."); case ANY_COMPREF: TTCN_error("Internal error: 'any component' cannot be started."); case ALL_COMPREF: TTCN_error("Internal error: 'all component' cannot be started."); default: break; } if (self == compref) TTCN_error("Start operation cannot be performed on " "the own component reference of the initiating component (i.e. " "'self.start' is not allowed)."); if (in_component_status_table(compref)) { if (get_killed_status(compref) == ALT_YES) { TTCN_error("PTC with component reference %d is not alive anymore. " "Start operation cannot be performed on it.", compref); } // the done status of the PTC shall be invalidated cancel_component_done(compref); } TTCN_Communication::prepare_start_req(text_buf, compref, module_name, function_name); } void TTCN_Runtime::send_start_component(Text_Buf& text_buf) { switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_START; break; case PTC_FUNCTION: executor_state = PTC_START; break; default: TTCN_error("Internal error: Executing component start operation " "in invalid state."); } // text_buf already contains a complete START_REQ message. TTCN_Communication::send_message(text_buf); if (is_mtc()) { // updating the component status flags // 'all component.done' must be re-evaluated later all_component_done_status = ALT_UNCHECKED; } wait_for_state_change(); TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::function__started); } void TTCN_Runtime::start_function(const char *module_name, const char *function_name, Text_Buf& text_buf) { switch (executor_state) { case PTC_IDLE: case PTC_STOPPED: break; default: // the START message must be dropped here because normally it is // dropped in function_started() text_buf.cut_message(); TTCN_error("Internal error: Message START arrived in invalid state."); } try { Module_List::start_function(module_name, function_name, text_buf); // do nothing: the function terminated normally // the message STOPPED or STOPPED_KILLED is already sent out // and the state variable is updated return; } catch (const TC_End& TC_end) { // executor_state is already set by stop_execution or kill_execution switch (executor_state) { case PTC_STOPPED: TTCN_Logger::log(TTCN_Logger::PARALLEL_UNQUALIFIED, "Function %s was stopped. PTC remains alive and is waiting for next start.", function_name); // send a STOPPED message without return value TTCN_Communication::send_stopped(); // return and do nothing else return; case PTC_EXIT: TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::function__stopped, NULL, function_name); break; default: TTCN_error("Internal error: PTC was stopped in invalid state."); } } catch (const TC_Error& TC_error) { TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::function__error, NULL, function_name); executor_state = PTC_EXIT; } // the control reaches this code if the PTC has to be terminated // first terminate all ports and timers // this may affect the final verdict terminate_component_type(); // send a STOPPED_KILLED message without return value TTCN_Communication::send_stopped_killed(local_verdict, verdict_reason); TTCN_Logger::log_final_verdict(true, local_verdict, local_verdict, local_verdict, (const char *)verdict_reason); } void TTCN_Runtime::function_started(Text_Buf& text_buf) { // The buffer still contains the incoming START message. text_buf.cut_message(); executor_state = PTC_FUNCTION; // The remaining messages must be processed now. TTCN_Communication::process_all_messages_tc(); } void TTCN_Runtime::prepare_function_finished(const char *return_type, Text_Buf& text_buf) { if (executor_state != PTC_FUNCTION) TTCN_error("Internal error: PTC behaviour function finished in invalid " "state."); if (is_alive) { // Prepare a STOPPED message with the possible return value. TTCN_Communication::prepare_stopped(text_buf, return_type); } else { // First the ports and timers must be stopped and deactivated. The // user_unmap and user_stop functions of Test Ports may detect errors // that must be considered in the final verdict of the PTC. terminate_component_type(); // Prepare a STOPPED_KILLED message with the final verdict and the // possible return value. TTCN_Communication::prepare_stopped_killed(text_buf, local_verdict, return_type, verdict_reason); } } void TTCN_Runtime::send_function_finished(Text_Buf& text_buf) { // send out the STOPPED or STOPPED_KILLED message, which is already // complete and contains the return value TTCN_Communication::send_message(text_buf); // log the final verdict if necessary and update the state variable if (is_alive) executor_state = PTC_STOPPED; else { TTCN_Logger::log_final_verdict(true, local_verdict, local_verdict, local_verdict, (const char *)verdict_reason); executor_state = PTC_EXIT; } } void TTCN_Runtime::function_finished(const char *function_name) { TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::function__finished, NULL, function_name, 0, NULL, NULL, is_alive); Text_Buf text_buf; prepare_function_finished(NULL, text_buf); send_function_finished(text_buf); } alt_status TTCN_Runtime::component_done(component component_reference) { if (in_controlpart()) TTCN_error("Done operation cannot be performed " "in the control part."); switch (component_reference) { case NULL_COMPREF: TTCN_error("Done operation cannot be performed on the null " "component reference."); case MTC_COMPREF: TTCN_error("Done operation cannot be performed on the component " "reference of MTC."); case SYSTEM_COMPREF: TTCN_error("Done operation cannot be performed on the component " "reference of system."); case ANY_COMPREF: return any_component_done(); case ALL_COMPREF: return all_component_done(); default: return ptc_done(component_reference); } } alt_status TTCN_Runtime::component_done(component component_reference, const char *return_type, Text_Buf*& text_buf) { if (in_controlpart()) TTCN_error("Done operation cannot be performed " "in the control part."); switch (component_reference) { case NULL_COMPREF: TTCN_error("Done operation cannot be performed on the null " "component reference."); case MTC_COMPREF: TTCN_error("Done operation cannot be performed on the component " "reference of MTC."); case SYSTEM_COMPREF: TTCN_error("Done operation cannot be performed on the component " "reference of system."); case ANY_COMPREF: TTCN_error("Done operation with return value cannot be performed on " "'any component'."); case ALL_COMPREF: TTCN_error("Done operation with return value cannot be performed on " "'all component'."); default: // the argument refers to a PTC break; } if (is_single()) TTCN_error("Done operation on a component reference " "cannot be performed in single mode."); if (self == component_reference) { TTCN_warning("Done operation on the component reference of self " "will never succeed."); return ALT_NO; } else { int index = get_component_status_table_index(component_reference); // we cannot use the killed status because we need the return value switch (component_status_table[index].done_status) { case ALT_UNCHECKED: switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_DONE; break; case PTC_FUNCTION: executor_state = PTC_DONE; break; default: TTCN_error("Internal error: Executing done operation in " "invalid state."); } TTCN_Communication::send_done_req(component_reference); component_status_table[index].done_status = ALT_MAYBE; create_done_killed_compref = component_reference; // wait for DONE_ACK wait_for_state_change(); // always re-evaluate the current alternative using a new snapshot return ALT_REPEAT; case ALT_YES: if (component_status_table[index].return_type != NULL) { if (!strcmp(component_status_table[index].return_type, return_type)) { component_status_table[index].return_value->rewind(); text_buf = component_status_table[index].return_value; return ALT_YES; } else { TTCN_Logger::log_matching_done(return_type, component_reference, component_status_table[index].return_type, API::MatchingDoneType_reason::done__failed__wrong__return__type); return ALT_NO; } } else { TTCN_Logger::log_matching_done(return_type, component_reference, NULL, API::MatchingDoneType_reason::done__failed__no__return); return ALT_NO; } default: return ALT_MAYBE; } } } alt_status TTCN_Runtime::component_killed(component component_reference) { if (in_controlpart()) TTCN_error("Killed operation cannot be performed " "in the control part."); switch (component_reference) { case NULL_COMPREF: TTCN_error("Killed operation cannot be performed on the null " "component reference."); case MTC_COMPREF: TTCN_error("Killed operation cannot be performed on the component " "reference of MTC."); case SYSTEM_COMPREF: TTCN_error("Killed operation cannot be performed on the component " "reference of system."); case ANY_COMPREF: return any_component_killed(); case ALL_COMPREF: return all_component_killed(); default: return ptc_killed(component_reference); } } boolean TTCN_Runtime::component_running(component component_reference) { if (in_controlpart()) TTCN_error("Component running operation " "cannot be performed in the control part."); switch (component_reference) { case NULL_COMPREF: TTCN_error("Running operation cannot be performed on the null " "component reference."); case MTC_COMPREF: TTCN_error("Running operation cannot be performed on the component " "reference of MTC."); case SYSTEM_COMPREF: TTCN_error("Running operation cannot be performed on the component " "reference of system."); case ANY_COMPREF: return any_component_running(); case ALL_COMPREF: return all_component_running(); default: return ptc_running(component_reference); } } boolean TTCN_Runtime::component_alive(component component_reference) { if (in_controlpart()) TTCN_error("Alive operation cannot be performed " "in the control part."); switch (component_reference) { case NULL_COMPREF: TTCN_error("Alive operation cannot be performed on the null " "component reference."); case MTC_COMPREF: TTCN_error("Alive operation cannot be performed on the component " "reference of MTC."); case SYSTEM_COMPREF: TTCN_error("Alive operation cannot be performed on the component " "reference of system."); case ANY_COMPREF: return any_component_alive(); case ALL_COMPREF: return all_component_alive(); default: return ptc_alive(component_reference); } } void TTCN_Runtime::stop_component(component component_reference) { if (in_controlpart()) TTCN_error("Component stop operation cannot be " "performed in the control part."); if (self == component_reference) stop_execution(); switch (component_reference) { case NULL_COMPREF: TTCN_error("Stop operation cannot be performed on the null component " "reference."); case MTC_COMPREF: stop_mtc(); break; case SYSTEM_COMPREF: TTCN_error("Stop operation cannot be performed on the component " "reference of system."); case ANY_COMPREF: TTCN_error("Internal error: 'any component' cannot be stopped."); case ALL_COMPREF: stop_all_component(); break; default: stop_ptc(component_reference); } } void TTCN_Runtime::stop_execution() { if (in_controlpart()) { TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::stopping__control__part__execution); } else { TTCN_Logger::log_str(TTCN_Logger::PARALLEL_UNQUALIFIED, "Stopping test component execution."); if (is_ptc()) { // the state variable indicates whether the component remains alive // after termination or not if (is_alive) executor_state = PTC_STOPPED; else executor_state = PTC_EXIT; } } throw TC_End(); } void TTCN_Runtime::kill_component(component component_reference) { if (in_controlpart()) TTCN_error("Kill operation cannot be performed in " "the control part."); if (self == component_reference) kill_execution(); switch (component_reference) { case NULL_COMPREF: TTCN_error("Kill operation cannot be performed on the null component " "reference."); case MTC_COMPREF: // 'mtc.kill' means exactly the same as 'mtc.stop' stop_mtc(); break; case SYSTEM_COMPREF: TTCN_error("Kill operation cannot be performed on the component " "reference of system."); case ANY_COMPREF: TTCN_error("Internal error: 'any component' cannot be killed."); case ALL_COMPREF: kill_all_component(); break; default: kill_ptc(component_reference); } } void TTCN_Runtime::kill_execution() { TTCN_Logger::log_str(TTCN_Logger::PARALLEL_UNQUALIFIED, "Terminating test component execution."); if (is_ptc()) executor_state = PTC_EXIT; throw TC_End(); } alt_status TTCN_Runtime::ptc_done(component component_reference) { if (is_single()) TTCN_error("Done operation on a component reference " "cannot be performed in single mode."); if (self == component_reference) { TTCN_warning("Done operation on the component reference of self " "will never succeed."); return ALT_NO; } int index = get_component_status_table_index(component_reference); // a successful killed operation on the component reference implies done if (component_status_table[index].killed_status == ALT_YES) goto success; switch (component_status_table[index].done_status) { case ALT_UNCHECKED: switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_DONE; break; case PTC_FUNCTION: executor_state = PTC_DONE; break; default: TTCN_error("Internal error: Executing done operation in " "invalid state."); } TTCN_Communication::send_done_req(component_reference); component_status_table[index].done_status = ALT_MAYBE; create_done_killed_compref = component_reference; // wait for DONE_ACK wait_for_state_change(); // always re-evaluate the current alternative using a new snapshot return ALT_REPEAT; case ALT_YES: goto success; default: return ALT_MAYBE; } success: TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::ptc__done, NULL, NULL, component_reference); return ALT_YES; } alt_status TTCN_Runtime::any_component_done() { // the operation is never successful in single mode if (is_single()) goto failure; if (!is_mtc()) TTCN_error("Operation 'any component.done' can only be " "performed on the MTC."); // the operation is successful if there is a component reference with a // successful done or killed operation for (int i = 0; i < component_status_table_size; i++) { if (component_status_table[i].done_status == ALT_YES || component_status_table[i].killed_status == ALT_YES) goto success; } // a successful 'any component.killed' implies 'any component.done' if (any_component_killed_status == ALT_YES) goto success; switch (any_component_done_status) { case ALT_UNCHECKED: if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'any component.done' in invalid state."); executor_state = MTC_DONE; TTCN_Communication::send_done_req(ANY_COMPREF); any_component_done_status = ALT_MAYBE; create_done_killed_compref = ANY_COMPREF; // wait for DONE_ACK wait_for_state_change(); // always re-evaluate the current alternative using a new snapshot return ALT_REPEAT; case ALT_YES: goto success; case ALT_NO: goto failure; default: return ALT_MAYBE; } success: TTCN_Logger::log_matching_done(0, 0, 0, API::MatchingDoneType_reason::any__component__done__successful); return ALT_YES; failure: TTCN_Logger::log_matching_done(0, 0, 0, API::MatchingDoneType_reason::any__component__done__failed); return ALT_NO; } alt_status TTCN_Runtime::all_component_done() { // the operation is always successful in single mode if (is_single()) goto success; if (!is_mtc()) TTCN_error("Operation 'all component.done' can only be " "performed on the MTC."); // a successful 'all component.killed' implies 'all component.done' if (all_component_killed_status == ALT_YES) goto success; switch (all_component_done_status) { case ALT_UNCHECKED: if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'all component.done' in invalid state."); executor_state = MTC_DONE; TTCN_Communication::send_done_req(ALL_COMPREF); all_component_done_status = ALT_MAYBE; create_done_killed_compref = ALL_COMPREF; // wait for DONE_ACK wait_for_state_change(); // always re-evaluate the current alternative using a new snapshot return ALT_REPEAT; case ALT_YES: goto success; default: return ALT_MAYBE; } success: TTCN_Logger::log_matching_done(0, 0, 0, API::MatchingDoneType_reason::all__component__done__successful); return ALT_YES; } alt_status TTCN_Runtime::ptc_killed(component component_reference) { if (is_single()) TTCN_error("Killed operation on a component reference " "cannot be performed in single mode."); if (self == component_reference) { TTCN_warning("Killed operation on the component reference of self " "will never succeed."); return ALT_NO; } int index = get_component_status_table_index(component_reference); switch (component_status_table[index].killed_status) { case ALT_UNCHECKED: switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_KILLED; break; case PTC_FUNCTION: executor_state = PTC_KILLED; break; default: TTCN_error("Internal error: Executing killed operation in " "invalid state."); } TTCN_Communication::send_killed_req(component_reference); component_status_table[index].killed_status = ALT_MAYBE; create_done_killed_compref = component_reference; // wait for KILLED_ACK wait_for_state_change(); // always re-evaluate the current alternative using a new snapshot return ALT_REPEAT; case ALT_YES: goto success; default: return ALT_MAYBE; } success: TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::ptc__killed, NULL, NULL, component_reference); return ALT_YES; } alt_status TTCN_Runtime::any_component_killed() { // the operation is never successful in single mode if (is_single()) goto failure; if (!is_mtc()) TTCN_error("Operation 'any component.killed' can only be " "performed on the MTC."); // the operation is successful if there is a component reference with a // successful killed operation for (int i = 0; i < component_status_table_size; i++) { if (component_status_table[i].killed_status == ALT_YES) goto success; } switch (any_component_killed_status) { case ALT_UNCHECKED: if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'any component.killed' in invalid state."); executor_state = MTC_KILLED; TTCN_Communication::send_killed_req(ANY_COMPREF); any_component_killed_status = ALT_MAYBE; create_done_killed_compref = ANY_COMPREF; // wait for KILLED_ACK wait_for_state_change(); // always re-evaluate the current alternative using a new snapshot return ALT_REPEAT; case ALT_YES: goto success; case ALT_NO: goto failure; default: return ALT_MAYBE; } success: TTCN_Logger::log_matching_done(0, 0, 0, API::MatchingDoneType_reason::any__component__killed__successful); return ALT_YES; failure: TTCN_Logger::log_matching_done(0, 0, 0, API::MatchingDoneType_reason::any__component__killed__failed); return ALT_NO; } alt_status TTCN_Runtime::all_component_killed() { // the operation is always successful in single mode if (is_single()) goto success; if (!is_mtc()) TTCN_error("Operation 'all component.killed' can only be " "performed on the MTC."); switch (all_component_killed_status) { case ALT_UNCHECKED: if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'all component.killed' in invalid state."); executor_state = MTC_KILLED; TTCN_Communication::send_killed_req(ALL_COMPREF); all_component_killed_status = ALT_MAYBE; create_done_killed_compref = ALL_COMPREF; // wait for KILLED_ACK wait_for_state_change(); // always re-evaluate the current alternative using a new snapshot return ALT_REPEAT; case ALT_YES: goto success; default: return ALT_MAYBE; } success: TTCN_Logger::log_matching_done(0, 0, 0, API::MatchingDoneType_reason::all__component__killed__successful); return ALT_YES; } boolean TTCN_Runtime::ptc_running(component component_reference) { if (is_single()) TTCN_error("Running operation on a component reference " "cannot be performed in single mode."); // the answer is always true if the operation refers to self if (self == component_reference) { TTCN_warning("Running operation on the component reference of self " "always returns true."); return TRUE; } // look into the component status tables if (in_component_status_table(component_reference)) { int index = get_component_status_table_index(component_reference); // the answer is false if a successful done or killed operation was // performed on the component reference if (component_status_table[index].done_status == ALT_YES || component_status_table[index].killed_status == ALT_YES) return FALSE; } // status flags all_component_done or all_component_killed cannot be used // because the component reference might be invalid (e.g. stale) // the decision cannot be made locally, MC must be asked switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_RUNNING; break; case PTC_FUNCTION: executor_state = PTC_RUNNING; break; default: TTCN_error("Internal error: Executing component running operation " "in invalid state."); } TTCN_Communication::send_is_running(component_reference); // wait for RUNNING wait_for_state_change(); return running_alive_result; } boolean TTCN_Runtime::any_component_running() { // the answer is always false in single mode if (is_single()) return FALSE; if (!is_mtc()) TTCN_error("Operation 'any component.running' can only be " "performed on the MTC."); // the answer is false if 'all component.done' or 'all component.killed' // operation was successful if (all_component_done_status == ALT_YES || all_component_killed_status == ALT_YES) return FALSE; // the decision cannot be made locally, MC must be asked if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'any component.running' in invalid state."); TTCN_Communication::send_is_running(ANY_COMPREF); executor_state = MTC_RUNNING; // wait for RUNNING wait_for_state_change(); // update the status of 'all component.done' in case of negative answer if (!running_alive_result) all_component_done_status = ALT_YES; return running_alive_result; } boolean TTCN_Runtime::all_component_running() { // the answer is always true in single mode if (is_single()) return TRUE; if (!is_mtc()) TTCN_error("Operation 'all component.running' can only be " "performed on the MTC."); // return true if no PTCs exist if (any_component_done_status == ALT_NO) return TRUE; // the done and killed status flags cannot be used since the components // that were explicitly stopped or killed must be ignored // the decision cannot be made locally, MC must be asked if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'all component.running' in invalid state."); TTCN_Communication::send_is_running(ALL_COMPREF); executor_state = MTC_RUNNING; // wait for RUNNING wait_for_state_change(); return running_alive_result; } boolean TTCN_Runtime::ptc_alive(component component_reference) { if (is_single()) TTCN_error("Alive operation on a component reference " "cannot be performed in single mode."); // the answer is always true if the operation refers to self if (self == component_reference) { TTCN_warning("Alive operation on the component reference of self " "always returns true."); return TRUE; } // the answer is false if a successful killed operation was performed // on the component reference if (in_component_status_table(component_reference) && get_killed_status(component_reference) == ALT_YES) return FALSE; // status flag of 'all component.killed' cannot be used because the // component reference might be invalid (e.g. stale) // the decision cannot be made locally, MC must be asked switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_ALIVE; break; case PTC_FUNCTION: executor_state = PTC_ALIVE; break; default: TTCN_error("Internal error: Executing component running operation " "in invalid state."); } TTCN_Communication::send_is_alive(component_reference); // wait for ALIVE wait_for_state_change(); return running_alive_result; } boolean TTCN_Runtime::any_component_alive() { // the answer is always false in single mode if (is_single()) return FALSE; if (!is_mtc()) TTCN_error("Operation 'any component.alive' can only be " "performed on the MTC."); // the answer is false if 'all component.killed' operation was successful if (all_component_killed_status == ALT_YES) return FALSE; // the decision cannot be made locally, MC must be asked if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'any component.alive' in invalid state."); TTCN_Communication::send_is_alive(ANY_COMPREF); executor_state = MTC_ALIVE; // wait for ALIVE wait_for_state_change(); // update the status of 'all component.killed' in case of negative answer if (!running_alive_result) all_component_killed_status = ALT_YES; return running_alive_result; } boolean TTCN_Runtime::all_component_alive() { // the answer is always true in single mode if (is_single()) return TRUE; if (!is_mtc()) TTCN_error("Operation 'all component.alive' can only be " "performed on the MTC."); // return true if no PTCs exist if (any_component_killed_status == ALT_NO) return TRUE; // return false if at least one PTC has been created and // 'all component.killed' was successful after the create operation if (all_component_killed_status == ALT_YES) return FALSE; // the operation is successful if there is a component reference with a // successful killed operation for (int i = 0; i < component_status_table_size; i++) { if (component_status_table[i].killed_status == ALT_YES) return FALSE; } // the decision cannot be made locally, MC must be asked if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'all component.alive' in invalid state."); TTCN_Communication::send_is_alive(ALL_COMPREF); executor_state = MTC_ALIVE; // wait for ALIVE wait_for_state_change(); return running_alive_result; } void TTCN_Runtime::stop_mtc() { TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::stopping__mtc); TTCN_Communication::send_stop_req(MTC_COMPREF); stop_execution(); } void TTCN_Runtime::stop_ptc(component component_reference) { if (is_single()) TTCN_error("Stop operation on a component reference " "cannot be performed in single mode."); // do nothing if a successful done or killed operation was performed on // the component reference if (in_component_status_table(component_reference)) { int index = get_component_status_table_index(component_reference); if (component_status_table[index].done_status == ALT_YES || component_status_table[index].killed_status == ALT_YES) goto ignore; } // status flags all_component_done or all_component_killed cannot be used // because the component reference might be invalid (e.g. stale) // MC must be asked to stop the PTC switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_STOP; break; case PTC_FUNCTION: executor_state = PTC_STOP; break; default: TTCN_error("Internal error: Executing component stop operation " "in invalid state."); } TTCN_Logger::log(TTCN_Logger::PARALLEL_UNQUALIFIED, "Stopping PTC with component reference %d.", component_reference); TTCN_Communication::send_stop_req(component_reference); // wait for STOP_ACK wait_for_state_change(); // done status of the PTC cannot be updated because its return type and // return value is unknown TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::ptc__stopped, NULL, NULL, component_reference); return; ignore: TTCN_Logger::log(TTCN_Logger::PARALLEL_UNQUALIFIED, "PTC with component reference %d is not running. " "Stop operation had no effect.", component_reference); } void TTCN_Runtime::stop_all_component() { // do nothing in single mode if (is_single()) goto ignore; if (!is_mtc()) TTCN_error("Operation 'all component.stop' can only be " "performed on the MTC."); // do nothing if 'all component.done' or 'all component.killed' // was successful if (all_component_done_status == ALT_YES || all_component_killed_status == ALT_YES) goto ignore; // a request must be sent to MC if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'all component.stop' in invalid state."); executor_state = MTC_STOP; TTCN_Logger::log_str(TTCN_Logger::PARALLEL_UNQUALIFIED, "Stopping all components."); TTCN_Communication::send_stop_req(ALL_COMPREF); // wait for STOP_ACK wait_for_state_change(); // 'all component.done' will be successful later all_component_done_status = ALT_YES; TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::all__comps__stopped); return; ignore: TTCN_Logger::log_str(TTCN_Logger::PARALLEL_UNQUALIFIED, "No PTCs are running. " "Operation 'all component.stop' had no effect."); } void TTCN_Runtime::kill_ptc(component component_reference) { if (is_single()) TTCN_error("Kill operation on a component reference " "cannot be performed in single mode."); // do nothing if a successful killed operation was performed on // the component reference if (in_component_status_table(component_reference) && get_killed_status(component_reference) == ALT_YES) goto ignore; // status flags all_component_killed cannot be used because the component // reference might be invalid (e.g. stale) // MC must be asked to kill the PTC switch (executor_state) { case MTC_TESTCASE: executor_state = MTC_KILL; break; case PTC_FUNCTION: executor_state = PTC_KILL; break; default: TTCN_error("Internal error: Executing kill operation in invalid " "state."); } TTCN_Logger::log(TTCN_Logger::PARALLEL_UNQUALIFIED, "Killing PTC with component reference %d.", component_reference); TTCN_Communication::send_kill_req(component_reference); // wait for KILL_ACK wait_for_state_change(); // updating the killed status of the PTC { int index = get_component_status_table_index(component_reference); component_status_table[index].killed_status = ALT_YES; } TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::ptc__killed, NULL, NULL, component_reference); return; ignore: TTCN_Logger::log(TTCN_Logger::PARALLEL_UNQUALIFIED, "PTC with component reference %d is not alive anymore. " "Kill operation had no effect.", component_reference); } void TTCN_Runtime::kill_all_component() { // do nothing in single mode if (is_single()) goto ignore; if (!is_mtc()) TTCN_error("Operation 'all component.kill' can only be " "performed on the MTC."); // do nothing if 'all component.killed' was successful if (all_component_killed_status == ALT_YES) goto ignore; // a request must be sent to MC if (executor_state != MTC_TESTCASE) TTCN_error("Internal error: " "Executing 'all component.kill' in invalid state."); executor_state = MTC_KILL; TTCN_Logger::log_str(TTCN_Logger::PARALLEL_UNQUALIFIED, "Killing all components."); TTCN_Communication::send_kill_req(ALL_COMPREF); // wait for KILL_ACK wait_for_state_change(); // 'all component.done' and 'all component.killed' will be successful later all_component_done_status = ALT_YES; all_component_killed_status = ALT_YES; TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::all__comps__killed); return; ignore: TTCN_Logger::log_str(TTCN_Logger::PARALLEL_UNQUALIFIED, "There are no alive PTCs. Operation 'all component.kill' had no effect."); } void TTCN_Runtime::check_port_name(const char *port_name, const char *operation_name, const char *which_argument) { if (port_name == NULL) TTCN_error("Internal error: The port name in the %s argument of %s " "operation is a NULL pointer.", which_argument, operation_name); if (port_name[0] == '\0') TTCN_error("Internal error: The %s argument of %s operation contains " "an empty string as port name.", which_argument, operation_name); /** \todo check whether port_name contains a valid TTCN-3 identifier * (and array index) */ } void TTCN_Runtime::connect_port( const COMPONENT& src_compref, const char *src_port, const COMPONENT& dst_compref, const char *dst_port) { check_port_name(src_port, "connect", "first"); check_port_name(dst_port, "connect", "second"); TTCN_Logger::begin_event(TTCN_Logger::PARALLEL_UNQUALIFIED); TTCN_Logger::log_event_str("Connecting ports "); COMPONENT::log_component_reference(src_compref); TTCN_Logger::log_event(":%s and ", src_port); COMPONENT::log_component_reference(dst_compref); TTCN_Logger::log_event(":%s.", dst_port); TTCN_Logger::end_event(); if (!src_compref.is_bound()) TTCN_error("The first argument of connect " "operation contains an unbound component reference."); component src_component = src_compref; switch (src_component) { case NULL_COMPREF: TTCN_error("The first argument of connect operation contains the " "null component reference."); case SYSTEM_COMPREF: TTCN_error("The first argument of connect operation refers to a " "system port."); default: break; } if (!dst_compref.is_bound()) TTCN_error("The second argument of connect " "operation contains an unbound component reference."); component dst_component = dst_compref; switch (dst_component) { case NULL_COMPREF: TTCN_error("The second argument of connect operation contains the " "null component reference."); case SYSTEM_COMPREF: TTCN_error("The second argument of connect operation refers to a " "system port."); default: break; } switch (executor_state) { case SINGLE_TESTCASE: if (src_component != MTC_COMPREF || dst_component != MTC_COMPREF) TTCN_error("Both endpoints of connect operation must refer to " "ports of mtc in single mode."); PORT::make_local_connection(src_port, dst_port); break; case MTC_TESTCASE: TTCN_Communication::send_connect_req(src_component, src_port, dst_component, dst_port); executor_state = MTC_CONNECT; wait_for_state_change(); break; case PTC_FUNCTION: TTCN_Communication::send_connect_req(src_component, src_port, dst_component, dst_port); executor_state = PTC_CONNECT; wait_for_state_change(); break; default: if (in_controlpart()) { TTCN_error("Connect operation cannot be performed in the " "control part."); } else { TTCN_error("Internal error: Executing connect operation " "in invalid state."); } } TTCN_Logger::log_portconnmap(API::ParPort_operation::connect__, src_compref, src_port, dst_compref, dst_port); } void TTCN_Runtime::disconnect_port( const COMPONENT& src_compref, const char *src_port, const COMPONENT& dst_compref, const char *dst_port) { check_port_name(src_port, "disconnect", "first"); check_port_name(dst_port, "disconnect", "second"); TTCN_Logger::begin_event(TTCN_Logger::PARALLEL_UNQUALIFIED); TTCN_Logger::log_event_str("Disconnecting ports "); COMPONENT::log_component_reference(src_compref); TTCN_Logger::log_event(":%s and ", src_port); COMPONENT::log_component_reference(dst_compref); TTCN_Logger::log_event(":%s.", dst_port); TTCN_Logger::end_event(); if (!src_compref.is_bound()) TTCN_error("The first argument of disconnect " "operation contains an unbound component reference."); component src_component = src_compref; switch (src_component) { case NULL_COMPREF: TTCN_error("The first argument of disconnect operation contains the " "null component reference."); case SYSTEM_COMPREF: TTCN_error("The first argument of disconnect operation refers to a " "system port."); default: break; } if (!dst_compref.is_bound()) TTCN_error("The second argument of disconnect " "operation contains an unbound component reference."); component dst_component = dst_compref; switch (dst_component) { case NULL_COMPREF: TTCN_error("The second argument of disconnect operation contains the " "null component reference."); case SYSTEM_COMPREF: TTCN_error("The second argument of disconnect operation refers to a " "system port."); default: break; } switch (executor_state) { case SINGLE_TESTCASE: if (src_component != MTC_COMPREF || dst_component != MTC_COMPREF) TTCN_error("Both endpoints of disconnect operation must refer to " "ports of mtc in single mode."); PORT::terminate_local_connection(src_port, dst_port); break; case MTC_TESTCASE: TTCN_Communication::send_disconnect_req(src_component, src_port, dst_component, dst_port); executor_state = MTC_DISCONNECT; wait_for_state_change(); break; case PTC_FUNCTION: TTCN_Communication::send_disconnect_req(src_component, src_port, dst_component, dst_port); executor_state = PTC_DISCONNECT; wait_for_state_change(); break; default: if (in_controlpart()) { TTCN_error("Disonnect operation cannot be performed in the " "control part."); } else { TTCN_error("Internal error: Executing disconnect operation " "in invalid state."); } } TTCN_Logger::log_portconnmap(API::ParPort_operation::disconnect__, src_compref, src_port, dst_compref, dst_port); } void TTCN_Runtime::map_port( const COMPONENT& src_compref, const char *src_port, const COMPONENT& dst_compref, const char *dst_port) { check_port_name(src_port, "map", "first"); check_port_name(dst_port, "map", "second"); TTCN_Logger::begin_event(TTCN_Logger::PARALLEL_UNQUALIFIED); TTCN_Logger::log_event_str("Mapping port "); COMPONENT::log_component_reference(src_compref); TTCN_Logger::log_event(":%s to ", src_port); COMPONENT::log_component_reference(dst_compref); TTCN_Logger::log_event(":%s.", dst_port); TTCN_Logger::end_event(); if (!src_compref.is_bound()) TTCN_error("The first argument of map " "operation contains an unbound component reference."); component src_component = src_compref; if (src_component == NULL_COMPREF) TTCN_error("The first argument of " "map operation contains the null component reference."); if (!dst_compref.is_bound()) TTCN_error("The second argument of map " "operation contains an unbound component reference."); component dst_component = dst_compref; if (dst_component == NULL_COMPREF) TTCN_error("The second argument of " "map operation contains the null component reference."); component comp_reference; const char *comp_port, *system_port; if (src_component == SYSTEM_COMPREF) { if (dst_component == SYSTEM_COMPREF) TTCN_error("Both arguments of " "map operation refer to system ports."); comp_reference = dst_component; comp_port = dst_port; system_port = src_port; } else if (dst_component == SYSTEM_COMPREF) { comp_reference = src_component; comp_port = src_port; system_port = dst_port; } else { TTCN_error("Both arguments of map operation refer to test component " "ports."); // to avoid warnings return; } switch (executor_state) { case SINGLE_TESTCASE: if (comp_reference != MTC_COMPREF) TTCN_error("Only the ports of mtc " "can be mapped in single mode."); PORT::map_port(comp_port, system_port); break; case MTC_TESTCASE: TTCN_Communication::send_map_req(comp_reference, comp_port, system_port); executor_state = MTC_MAP; wait_for_state_change(); break; case PTC_FUNCTION: TTCN_Communication::send_map_req(comp_reference, comp_port, system_port); executor_state = PTC_MAP; wait_for_state_change(); break; default: if (in_controlpart()) { TTCN_error("Map operation cannot be performed in the " "control part."); } else { TTCN_error("Internal error: Executing map operation " "in invalid state."); } } TTCN_Logger::log_portconnmap(API::ParPort_operation::map__, src_compref, src_port, dst_compref, dst_port); } void TTCN_Runtime::unmap_port( const COMPONENT& src_compref, const char *src_port, const COMPONENT& dst_compref, const char *dst_port) { check_port_name(src_port, "unmap", "first"); check_port_name(dst_port, "unmap", "second"); TTCN_Logger::begin_event(TTCN_Logger::PARALLEL_UNQUALIFIED); TTCN_Logger::log_event_str("Unmapping port "); COMPONENT::log_component_reference(src_compref); TTCN_Logger::log_event(":%s from ", src_port); COMPONENT::log_component_reference(dst_compref); TTCN_Logger::log_event(":%s.", dst_port); TTCN_Logger::end_event(); if (!src_compref.is_bound()) TTCN_error("The first argument of unmap " "operation contains an unbound component reference."); component src_component = src_compref; if (src_component == NULL_COMPREF) TTCN_error("The first argument of " "unmap operation contains the null component reference."); if (!dst_compref.is_bound()) TTCN_error("The second argument of unmap " "operation contains an unbound component reference."); component dst_component = dst_compref; if (dst_component == NULL_COMPREF) TTCN_error("The second argument of " "unmap operation contains the null component reference."); component comp_reference; const char *comp_port, *system_port; if (src_component == SYSTEM_COMPREF) { if (dst_component == SYSTEM_COMPREF) TTCN_error("Both arguments of " "unmap operation refer to system ports."); comp_reference = dst_component; comp_port = dst_port; system_port = src_port; } else if (dst_component == SYSTEM_COMPREF) { comp_reference = src_component; comp_port = src_port; system_port = dst_port; } else { TTCN_error("Both arguments of unmap operation refer to test component " "ports."); // to avoid warnings return; } switch (executor_state) { case SINGLE_TESTCASE: if (comp_reference != MTC_COMPREF) TTCN_error("Only the ports of mtc " "can be unmapped in single mode."); PORT::unmap_port(comp_port, system_port); break; case MTC_TESTCASE: TTCN_Communication::send_unmap_req(comp_reference, comp_port, system_port); executor_state = MTC_UNMAP; wait_for_state_change(); break; case PTC_FUNCTION: TTCN_Communication::send_unmap_req(comp_reference, comp_port, system_port); executor_state = PTC_UNMAP; wait_for_state_change(); break; default: if (in_controlpart()) { TTCN_error("Unmap operation cannot be performed in the " "control part."); } else { TTCN_error("Internal error: Executing unmap operation " "in invalid state."); } } TTCN_Logger::log_portconnmap(API::ParPort_operation::unmap__, src_compref, src_port, dst_compref, dst_port); } void TTCN_Runtime::begin_controlpart(const char *module_name) { control_module_name = module_name; execute_command(begin_controlpart_command, module_name); TTCN_Logger::log_controlpart_start_stop(module_name, 0); } void TTCN_Runtime::end_controlpart() { TTCN_Default::deactivate_all(); TTCN_Default::reset_counter(); TIMER::all_stop(); TTCN_Logger::log_controlpart_start_stop(control_module_name, 1); execute_command(end_controlpart_command, control_module_name); control_module_name = NULL; } void TTCN_Runtime::check_begin_testcase(boolean has_timer, double timer_value) { if (!in_controlpart()) { if (is_single() || is_mtc()) TTCN_error("Test case cannot be executed " "while another one (%s.%s) is running.", testcase_name.module_name, testcase_name.definition_name); else if (is_ptc()) TTCN_error("Test case cannot be executed on a PTC."); else TTCN_error("Internal error: Executing a test case in an invalid " "state."); } if (has_timer && timer_value < 0.0) TTCN_error("The test case supervisor " "timer has negative duration (%g s).", timer_value); } void TTCN_Runtime::begin_testcase( const char *par_module_name, const char *par_testcase_name, const char *mtc_comptype_module, const char *mtc_comptype_name, const char *system_comptype_module, const char *system_comptype_name, boolean has_timer, double timer_value) { switch (executor_state) { case SINGLE_CONTROLPART: executor_state = SINGLE_TESTCASE; break; case MTC_CONTROLPART: TTCN_Communication::send_testcase_started(par_module_name, par_testcase_name, mtc_comptype_module, mtc_comptype_name, system_comptype_module, system_comptype_name); executor_state = MTC_TESTCASE; break; default: TTCN_error("Internal error: Executing a test case in an invalid " "state."); } TIMER::save_control_timers(); TTCN_Default::save_control_defaults(); set_testcase_name(par_module_name, par_testcase_name); char *command_arguments = mprintf("%s.%s", testcase_name.module_name, testcase_name.definition_name); execute_command(begin_testcase_command, command_arguments); Free(command_arguments); TTCN_Logger::log_testcase_started(testcase_name); if (has_timer) testcase_timer.start(timer_value); set_component_type(mtc_comptype_module, mtc_comptype_name); initialize_component_type(); // at the beginning of the testcase no PTCs exist any_component_done_status = ALT_NO; all_component_done_status = ALT_YES; any_component_killed_status = ALT_NO; all_component_killed_status = ALT_YES; } verdicttype TTCN_Runtime::end_testcase() { switch (executor_state) { case MTC_CREATE: case MTC_START: case MTC_STOP: case MTC_KILL: case MTC_RUNNING: case MTC_ALIVE: case MTC_DONE: case MTC_KILLED: case MTC_CONNECT: case MTC_DISCONNECT: case MTC_MAP: case MTC_UNMAP: executor_state = MTC_TESTCASE; case MTC_TESTCASE: break; case SINGLE_TESTCASE: disable_interrupt_handler(); break; default: TTCN_error("Internal error: Ending a testcase in an invalid state."); } testcase_timer.stop(); terminate_component_type(); if (executor_state == MTC_TESTCASE) { TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::waiting__for__ptcs__to__finish); TTCN_Communication::send_testcase_finished(local_verdict, verdict_reason); executor_state = MTC_TERMINATING_TESTCASE; wait_for_state_change(); } else if (executor_state == SINGLE_TESTCASE) { executor_state = SINGLE_CONTROLPART; enable_interrupt_handler(); } TTCN_Logger::log_testcase_finished(testcase_name, local_verdict, verdict_reason); verdict_count[local_verdict]++; // testcase name should come first for backward compatibility char *command_arguments = mprintf("%s.%s %s", testcase_name.module_name, testcase_name.definition_name, verdict_name[local_verdict]); execute_command(end_testcase_command, command_arguments); Free(command_arguments); clear_qualified_name(testcase_name); // clean up component status caches clear_component_status_table(); any_component_done_status = ALT_UNCHECKED; all_component_done_status = ALT_UNCHECKED; any_component_killed_status = ALT_UNCHECKED; all_component_killed_status = ALT_UNCHECKED; // restore the control part timers and defaults TTCN_Default::restore_control_defaults(); TIMER::restore_control_timers(); if (executor_state == MTC_PAUSED) { TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::user__paused__waiting__to__resume); wait_for_state_change(); if (executor_state != MTC_TERMINATING_EXECUTION) TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::resuming__execution); } if (executor_state == MTC_TERMINATING_EXECUTION) { executor_state = MTC_CONTROLPART; TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::terminating__execution); throw TC_End(); } return local_verdict; } void TTCN_Runtime::log_verdict_statistics() { unsigned int total_testcases = verdict_count[NONE] + verdict_count[PASS] + verdict_count[INCONC] + verdict_count[FAIL] + verdict_count[ERROR]; verdicttype overall_verdict; if (control_error_count > 0 || verdict_count[ERROR] > 0) overall_verdict = ERROR; else if (verdict_count[FAIL] > 0) overall_verdict = FAIL; else if (verdict_count[INCONC] > 0) overall_verdict = INCONC; else if (verdict_count[PASS] > 0) overall_verdict = PASS; else overall_verdict = NONE; if (total_testcases > 0) { TTCN_Logger::log_verdict_statistics(verdict_count[NONE], (100.0 * verdict_count[NONE]) / total_testcases, verdict_count[PASS], (100.0 * verdict_count[PASS]) / total_testcases, verdict_count[INCONC], (100.0 * verdict_count[INCONC]) / total_testcases, verdict_count[FAIL], (100.0 * verdict_count[FAIL]) / total_testcases, verdict_count[ERROR], (100.0 * verdict_count[ERROR]) / total_testcases); } else { TTCN_Logger::log_verdict_statistics(0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0); } if (control_error_count > 0) { TTCN_Logger::log_controlpart_errors(control_error_count); } TTCN_Logger::log(TTCN_Logger::STATISTICS_VERDICT, "Test execution summary: " "%u test case%s executed. Overall verdict: %s", total_testcases, total_testcases > 1 ? "s were" : " was", verdict_name[overall_verdict]); verdict_count[NONE] = 0; verdict_count[PASS] = 0; verdict_count[INCONC] = 0; verdict_count[FAIL] = 0; verdict_count[ERROR] = 0; control_error_count = 0; } void TTCN_Runtime::begin_action() { TTCN_Logger::begin_event(TTCN_Logger::ACTION_UNQUALIFIED); TTCN_Logger::log_event_str("Action: "); } void TTCN_Runtime::end_action() { TTCN_Logger::end_event(); } void TTCN_Runtime::setverdict(verdicttype new_value, const char* reason) { if (verdict_enabled()) { if (new_value == ERROR) TTCN_error("Error verdict cannot be set explicitly."); setverdict_internal(new_value, reason); } else if (in_controlpart()) { TTCN_error("Verdict cannot be set in the control part."); } else { TTCN_error("Internal error: Setting the verdict in invalid state."); } } void TTCN_Runtime::setverdict(const VERDICTTYPE& new_value, const char* reason) { if (!new_value.is_bound()) TTCN_error("The argument of setverdict " "operation is an unbound verdict value."); setverdict((verdicttype)new_value, reason); } void TTCN_Runtime::set_error_verdict() { if (verdict_enabled()) setverdict_internal(ERROR); else if (is_single() || is_mtc()) control_error_count++; } verdicttype TTCN_Runtime::getverdict() { if (verdict_enabled()) TTCN_Logger::log_getverdict(local_verdict); else if (in_controlpart()) TTCN_error("Getverdict operation cannot be " "performed in the control part."); else TTCN_error("Internal error: Performing getverdict operation in " "invalid state."); return local_verdict; } void TTCN_Runtime::setverdict_internal(verdicttype new_value, const char* reason) { if (new_value < NONE || new_value > ERROR) TTCN_error("Internal error: setting an invalid verdict value (%d).", new_value); verdicttype old_verdict = local_verdict; if (local_verdict < new_value) { verdict_reason = reason; local_verdict = new_value; if (reason == NULL || reason[0] == '\0') TTCN_Logger::log_setverdict(new_value, old_verdict, local_verdict); else TTCN_Logger::log_setverdict(new_value, old_verdict, local_verdict, reason, reason); } else if (local_verdict == new_value) { if (reason == NULL || reason[0] == '\0') TTCN_Logger::log_setverdict(new_value, old_verdict, local_verdict); else TTCN_Logger::log_setverdict(new_value, old_verdict, local_verdict, reason, reason); } if (new_value == FAIL) { ttcn3_debugger.breakpoint_entry(TTCN3_Debugger::SBP_FAIL_VERDICT); } else if (new_value == ERROR) { ttcn3_debugger.breakpoint_entry(TTCN3_Debugger::SBP_ERROR_VERDICT); } } void TTCN_Runtime::set_begin_controlpart_command(const char *new_command) { Free(begin_controlpart_command); begin_controlpart_command = shell_escape(new_command); } void TTCN_Runtime::set_end_controlpart_command(const char *new_command) { Free(end_controlpart_command); end_controlpart_command = shell_escape(new_command); } void TTCN_Runtime::set_begin_testcase_command(const char *new_command) { Free(begin_testcase_command); begin_testcase_command = shell_escape(new_command); } void TTCN_Runtime::set_end_testcase_command(const char *new_command) { Free(end_testcase_command); end_testcase_command = shell_escape(new_command); } void TTCN_Runtime::clear_external_commands() { Free(begin_controlpart_command); begin_controlpart_command = NULL; Free(end_controlpart_command); end_controlpart_command = NULL; Free(begin_testcase_command); begin_testcase_command = NULL; Free(end_testcase_command); end_testcase_command = NULL; } char *TTCN_Runtime::shell_escape(const char *command_str) { if (command_str == NULL || command_str[0] == '\0') return NULL; boolean has_special_char = FALSE; for (int i = 0; !has_special_char && command_str[i] != '\0'; i++) { switch (command_str[i]) { case ' ': case '*': case '?': case '[': case ']': case '<': case '>': case '|': case '&': case '$': case '{': case '}': case ';': case '(': case ')': case '#': case '!': case '=': case '"': case '`': case '\\': // special characters interpreted by the shell except ' has_special_char = TRUE; break; default: // non-printable characters also need special handling if (!isprint(command_str[i])) has_special_char = TRUE; } } char *ret_val = memptystr(); // indicates whether we are in an unclosed ' string boolean in_apostrophes = FALSE; for (int i = 0; command_str[i] != '\0'; i++) { if (command_str[i] == '\'') { if (in_apostrophes) { // close the open literal ret_val = mputc(ret_val, '\''); in_apostrophes = FALSE; } // substitute with \' ret_val = mputstr(ret_val, "\\'"); } else { if (has_special_char && !in_apostrophes) { // open the literal ret_val = mputc(ret_val, '\''); in_apostrophes = TRUE; } // append the single character ret_val = mputc(ret_val, command_str[i]); } } // close the open literal if (in_apostrophes) ret_val = mputc(ret_val, '\''); return ret_val; } void TTCN_Runtime::execute_command(const char *command_name, const char *argument_string) { if (command_name != NULL) { char *command_string = mprintf("%s %s", command_name, argument_string); try { TTCN_Logger::log_extcommand(TTCN_Logger::EXTCOMMAND_START, command_string); int return_status = system(command_string); if (return_status == -1) TTCN_error("Execution of external " "command `%s' failed.", command_string); else if (WIFEXITED(return_status)) { int exit_status = WEXITSTATUS(return_status); if (exit_status == EXIT_SUCCESS) TTCN_Logger::log_extcommand(TTCN_Logger::EXTCOMMAND_SUCCESS, command_string); else TTCN_warning("External command `%s' returned " "unsuccessful exit status (%d).", command_string, exit_status); } else if (WIFSIGNALED(return_status)) { int signal_number = WTERMSIG(return_status); TTCN_warning("External command `%s' was terminated by signal " "%d (%s).", command_string, signal_number, get_signal_name(signal_number)); } else { TTCN_warning("External command `%s' was terminated by an " "unknown reason (return status: %d).", command_string, return_status); } } catch (...) { // to prevent from memory leaks Free(command_string); throw; } Free(command_string); } } void TTCN_Runtime::process_create_mtc() { switch (executor_state) { case HC_ACTIVE: case HC_OVERLOADED: break; default: TTCN_Communication::send_error("Message CREATE_MTC arrived in invalid " "state."); return; } // clean Emergency log buffer before fork, to avoid duplication TTCN_Logger::ring_buffer_dump(false); pid_t mtc_pid = fork(); if (mtc_pid < 0) { // fork() failed TTCN_Communication::send_create_nak(MTC_COMPREF, "system call fork() " "failed (%s)", strerror(errno)); failed_process_creation(); TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED); TTCN_Logger::log_event_str("System call fork() failed when creating " "MTC."); TTCN_Logger::OS_error(); TTCN_Logger::end_event(); } else if (mtc_pid > 0) { // fork() was successful, this code runs on the parent process (HC) TTCN_Logger::log_mtc_created(mtc_pid); add_component(MTC_COMPREF, mtc_pid); successful_process_creation(); // let the HC's TTCN-3 Profiler know of the MTC ttcn3_prof.add_child_process(mtc_pid); } else { // fork() was successful, this code runs on the child process (MTC) // The inherited epoll fd has to be closed first, and then the mc fd // (The inherited epoll fd shares its database with the parent process.) Fd_And_Timeout_User::reopenEpollFd(); TTCN_Communication::close_mc_connection(); self = MTC_COMPREF; executor_state = MTC_INITIAL; } } void TTCN_Runtime::process_create_ptc(component component_reference, const char *component_type_module, const char *component_type_name, const char *par_component_name, boolean par_is_alive, const char *current_testcase_module, const char *current_testcase_name) { switch (executor_state) { case HC_ACTIVE: case HC_OVERLOADED: break; default: TTCN_Communication::send_error("Message CREATE_PTC arrived in invalid " "state."); return; } // clean Emergency log buffer before fork, to avoid duplication TTCN_Logger::ring_buffer_dump(false); pid_t ptc_pid = fork(); if (ptc_pid < 0) { // fork() failed TTCN_Communication::send_create_nak(component_reference, "system call " "fork() failed (%s)", strerror(errno)); failed_process_creation(); TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED); TTCN_Logger::log_event("System call fork() failed when creating PTC " "with component reference %d.", component_reference); TTCN_Logger::OS_error(); TTCN_Logger::end_event(); } else if (ptc_pid > 0) { // fork() was successful, this code runs on the parent process (HC) TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::ptc__created__pid, component_type_module, component_type_name, component_reference, par_component_name, current_testcase_name, ptc_pid); add_component(component_reference, ptc_pid); COMPONENT::register_component_name(component_reference, par_component_name); successful_process_creation(); // let the HC's TTCN-3 Profiler know of this new PTC ttcn3_prof.add_child_process(ptc_pid); } else { // fork() was successful, this code runs on the child process (PTC) // The inherited epoll fd has to be closed first, and then the mc fd // (The inherited epoll fd shares its database with the parent process.) Fd_And_Timeout_User::reopenEpollFd(); TTCN_Communication::close_mc_connection(); self = component_reference; set_component_type(component_type_module, component_type_name); set_component_name(par_component_name); is_alive = par_is_alive; set_testcase_name(current_testcase_module, current_testcase_name); executor_state = PTC_INITIAL; } } void TTCN_Runtime::process_create_ack(component new_component) { switch (executor_state) { case MTC_CREATE: executor_state = MTC_TESTCASE; case MTC_TERMINATING_TESTCASE: break; case PTC_CREATE: executor_state = PTC_FUNCTION; break; default: TTCN_error("Internal error: Message CREATE_ACK arrived in invalid " "state."); } create_done_killed_compref = new_component; } void TTCN_Runtime::process_running(boolean result_value) { switch (executor_state) { case MTC_RUNNING: executor_state = MTC_TESTCASE; case MTC_TERMINATING_TESTCASE: break; case PTC_RUNNING: executor_state = PTC_FUNCTION; break; default: TTCN_error("Internal error: Message RUNNING arrived in invalid state."); } running_alive_result = result_value; } void TTCN_Runtime::process_alive(boolean result_value) { switch (executor_state) { case MTC_ALIVE: executor_state = MTC_TESTCASE; case MTC_TERMINATING_TESTCASE: break; case PTC_ALIVE: executor_state = PTC_FUNCTION; break; default: TTCN_error("Internal error: Message ALIVE arrived in invalid state."); } running_alive_result = result_value; } void TTCN_Runtime::process_done_ack(boolean done_status, const char *return_type, int return_value_len, const void *return_value) { switch (executor_state) { case MTC_DONE: executor_state = MTC_TESTCASE; case MTC_TERMINATING_TESTCASE: break; case PTC_DONE: executor_state = PTC_FUNCTION; break; default: TTCN_error("Internal error: Message DONE_ACK arrived in invalid " "state."); } if (done_status) set_component_done(create_done_killed_compref, return_type, return_value_len, return_value); create_done_killed_compref = NULL_COMPREF; } void TTCN_Runtime::process_killed_ack(boolean killed_status) { switch (executor_state) { case MTC_KILLED: executor_state = MTC_TESTCASE; case MTC_TERMINATING_TESTCASE: break; case PTC_KILLED: executor_state = PTC_FUNCTION; break; default: TTCN_error("Internal error: Message KILLED_ACK arrived in invalid " "state."); } if (killed_status) set_component_killed(create_done_killed_compref); create_done_killed_compref = NULL_COMPREF; } void TTCN_Runtime::process_ptc_verdict(Text_Buf& text_buf) { if (executor_state != MTC_TERMINATING_TESTCASE) TTCN_error("Internal error: Message PTC_VERDICT arrived in invalid state."); TTCN_Logger::log_final_verdict(false, local_verdict, local_verdict, local_verdict, (const char *)verdict_reason, TitanLoggerApi::FinalVerdictType_choice_notification::setting__final__verdict__of__the__test__case); TTCN_Logger::log_final_verdict(false, local_verdict, local_verdict, local_verdict, (const char *)verdict_reason); int n_ptcs = text_buf.pull_int().get_val(); if (n_ptcs > 0) { for (int i = 0; i < n_ptcs; i++) { component ptc_compref = text_buf.pull_int().get_val(); char *ptc_name = text_buf.pull_string(); verdicttype ptc_verdict = (verdicttype)text_buf.pull_int().get_val(); char *ptc_verdict_reason = text_buf.pull_string(); if (ptc_verdict < NONE || ptc_verdict > ERROR) { delete [] ptc_name; TTCN_error("Internal error: Invalid PTC verdict was " "received from MC: %d.", ptc_verdict); } verdicttype new_verdict = local_verdict; if (ptc_verdict > local_verdict) { new_verdict = ptc_verdict; verdict_reason = CHARSTRING(ptc_verdict_reason); } TTCN_Logger::log_final_verdict(true, ptc_verdict, local_verdict, new_verdict, ptc_verdict_reason, -1, ptc_compref, ptc_name); delete [] ptc_name; delete [] ptc_verdict_reason; local_verdict = new_verdict; } } else { TTCN_Logger::log_final_verdict(false, local_verdict, local_verdict, local_verdict, (const char *)verdict_reason, TitanLoggerApi::FinalVerdictType_choice_notification::no__ptcs__were__created); } boolean continue_execution = (boolean)text_buf.pull_int().get_val(); if (continue_execution) executor_state = MTC_CONTROLPART; else executor_state = MTC_PAUSED; } void TTCN_Runtime::process_kill() { if (!is_ptc()) TTCN_error("Internal error: Message KILL arrived in invalid state."); switch (executor_state) { case PTC_IDLE: case PTC_STOPPED: TTCN_Logger::log_par_ptc(API::ParallelPTC_reason::kill__request__frm__mc); // This may affect the final verdict. terminate_component_type(); // Send a KILLED message so that the value returned by previous behaviour // function remains active. TTCN_Communication::send_killed(local_verdict); TTCN_Logger::log_final_verdict(true, local_verdict, local_verdict, local_verdict, (const char *)verdict_reason); executor_state = PTC_EXIT; case PTC_EXIT: break; default: TTCN_Logger::log_str(TTCN_Logger::PARALLEL_UNQUALIFIED, "Kill was requested from MC."); kill_execution(); } } void TTCN_Runtime::process_kill_process(component component_reference) { if (!is_hc()) TTCN_error("Internal error: Message KILL_PROCESS arrived " "in invalid state."); component_process_struct *comp = get_component_by_compref(component_reference); if (comp != NULL) { TTCN_Logger::log(TTCN_Logger::PARALLEL_UNQUALIFIED, "Killing component with component reference %d, process id: %ld.", component_reference, (long)comp->process_id); if (comp->process_killed) TTCN_warning("Process with process id %ld " "has been already killed. Killing it again.", (long)comp->process_id); if (kill(comp->process_id, SIGKILL)) { if (errno == ESRCH) { errno = 0; TTCN_Logger::log(TTCN_Logger::PARALLEL_UNQUALIFIED, "Process with process id %ld has already terminated.", (long)comp->process_id); } else TTCN_error("kill() system call failed on process id %ld.", (long)comp->process_id); } comp->process_killed = TRUE; } else { TTCN_Logger::log(TTCN_Logger::PARALLEL_UNQUALIFIED, "Component with component reference %d does not exist. " "Request for killing was ignored.", component_reference); } } void TTCN_Runtime::set_component_done(component component_reference, const char *return_type, int return_value_len, const void *return_value) { switch (component_reference) { case ANY_COMPREF: if (is_mtc()) any_component_done_status = ALT_YES; else TTCN_error("Internal error: TTCN_Runtime::set_component_done(" "ANY_COMPREF): can be used only on MTC."); break; case ALL_COMPREF: if (is_mtc()) all_component_done_status = ALT_YES; else TTCN_error("Internal error: TTCN_Runtime::set_component_done(" "ALL_COMPREF): can be used only on MTC."); break; case NULL_COMPREF: case MTC_COMPREF: case SYSTEM_COMPREF: TTCN_error("Internal error: TTCN_Runtime::set_component_done: " "invalid component reference: %d.", component_reference); break; default: { int index = get_component_status_table_index(component_reference); component_status_table[index].done_status = ALT_YES; Free(component_status_table[index].return_type); delete component_status_table[index].return_value; if (return_type != NULL && return_type[0] != '\0') { component_status_table[index].return_type = mcopystr(return_type); component_status_table[index].return_value = new Text_Buf; component_status_table[index].return_value->push_raw( return_value_len, return_value); } else { component_status_table[index].return_type = NULL; component_status_table[index].return_value = NULL; } } } } void TTCN_Runtime::set_component_killed(component component_reference) { switch (component_reference) { case ANY_COMPREF: if (is_mtc()) any_component_killed_status = ALT_YES; else TTCN_error("Internal error: TTCN_Runtime::set_component_killed(" "ANY_COMPREF): can be used only on MTC."); break; case ALL_COMPREF: if (is_mtc()) all_component_killed_status = ALT_YES; else TTCN_error("Internal error: TTCN_Runtime::set_component_killed(" "ALL_COMPREF): can be used only on MTC."); break; case NULL_COMPREF: case MTC_COMPREF: case SYSTEM_COMPREF: TTCN_error("Internal error: TTCN_Runtime::set_component_killed: " "invalid component reference: %d.", component_reference); default: component_status_table[get_component_status_table_index( component_reference)].killed_status = ALT_YES; } } void TTCN_Runtime::cancel_component_done(component component_reference) { switch (component_reference) { case ANY_COMPREF: if (is_mtc()) any_component_done_status = ALT_UNCHECKED; else TTCN_error("Internal error: TTCN_Runtime::cancel_component_done(" "ANY_COMPREF): can be used only on MTC."); break; case ALL_COMPREF: case NULL_COMPREF: case MTC_COMPREF: case SYSTEM_COMPREF: TTCN_error("Internal error: TTCN_Runtime::cancel_component_done: " "invalid component reference: %d.", component_reference); default: if (in_component_status_table(component_reference)) { int index = get_component_status_table_index(component_reference); component_status_table[index].done_status = ALT_UNCHECKED; Free(component_status_table[index].return_type); component_status_table[index].return_type = NULL; delete component_status_table[index].return_value; component_status_table[index].return_value = NULL; } } } int TTCN_Runtime::get_component_status_table_index( component component_reference) { if (component_reference < FIRST_PTC_COMPREF) { TTCN_error("Internal error: TTCN_Runtime::" "get_component_status_table_index: invalid component reference: " "%d.", component_reference); } if (component_status_table_size == 0) { // the table is empty // this will be the first entry component_status_table = (component_status_table_struct*) Malloc(sizeof(*component_status_table)); component_status_table[0].done_status = ALT_UNCHECKED; component_status_table[0].killed_status = ALT_UNCHECKED; component_status_table[0].return_type = NULL; component_status_table[0].return_value = NULL; component_status_table_size = 1; component_status_table_offset = component_reference; return 0; } else if (component_reference >= component_status_table_offset) { // the table contains at least one entry that is smaller than // component_reference int component_index = component_reference - component_status_table_offset; if (component_index >= component_status_table_size) { // component_reference is still not in the table // the table has to be extended at the end component_status_table = (component_status_table_struct*) Realloc(component_status_table, (component_index + 1) * sizeof(*component_status_table)); // initializing the new table entries at the end for (int i = component_status_table_size; i <= component_index; i++) { component_status_table[i].done_status = ALT_UNCHECKED; component_status_table[i].killed_status = ALT_UNCHECKED; component_status_table[i].return_type = NULL; component_status_table[i].return_value = NULL; } component_status_table_size = component_index + 1; } return component_index; } else { // component_reference has to be inserted before the existing table int offset_diff = component_status_table_offset - component_reference; // offset_diff indicates how many new elements have to be inserted // before the existing table int new_size = component_status_table_size + offset_diff; component_status_table = (component_status_table_struct*) Realloc(component_status_table, new_size * sizeof(*component_status_table)); // moving forward the existing table memmove(component_status_table + offset_diff, component_status_table, component_status_table_size * sizeof(*component_status_table)); // initializing the first table entries for (int i = 0; i < offset_diff; i++) { component_status_table[i].done_status = ALT_UNCHECKED; component_status_table[i].killed_status = ALT_UNCHECKED; component_status_table[i].return_type = NULL; component_status_table[i].return_value = NULL; } component_status_table_size = new_size; component_status_table_offset = component_reference; return 0; } } alt_status TTCN_Runtime::get_killed_status(component component_reference) { return component_status_table [get_component_status_table_index(component_reference)].killed_status; } boolean TTCN_Runtime::in_component_status_table(component component_reference) { return component_reference >= component_status_table_offset && component_reference < component_status_table_size + component_status_table_offset; } void TTCN_Runtime::clear_component_status_table() { for (component i = 0; i < component_status_table_size; i++) { Free(component_status_table[i].return_type); delete component_status_table[i].return_value; } Free(component_status_table); component_status_table = NULL; component_status_table_size = 0; component_status_table_offset = FIRST_PTC_COMPREF; } #define HASHTABLE_SIZE 97 void TTCN_Runtime::initialize_component_process_tables() { components_by_compref = new component_process_struct*[HASHTABLE_SIZE]; components_by_pid = new component_process_struct*[HASHTABLE_SIZE]; for (unsigned int i = 0; i < HASHTABLE_SIZE; i++) { components_by_compref[i] = NULL; components_by_pid[i] = NULL; } } void TTCN_Runtime::add_component(component component_reference, pid_t process_id) { if (component_reference != MTC_COMPREF && get_component_by_compref(component_reference) != NULL) TTCN_error("Internal error: TTCN_Runtime::add_component: " "duplicated component reference (%d)", component_reference); if (get_component_by_pid(process_id) != NULL) TTCN_error("Internal error: TTCN_Runtime::add_component: " "duplicated pid (%ld)", (long)process_id); component_process_struct *new_comp = new component_process_struct; new_comp->component_reference = component_reference; new_comp->process_id = process_id; new_comp->process_killed = FALSE; new_comp->prev_by_compref = NULL; component_process_struct*& head_by_compref = components_by_compref[component_reference % HASHTABLE_SIZE]; new_comp->next_by_compref = head_by_compref; if (head_by_compref != NULL) head_by_compref->prev_by_compref = new_comp; head_by_compref = new_comp; new_comp->prev_by_pid = NULL; component_process_struct*& head_by_pid = components_by_pid[process_id % HASHTABLE_SIZE]; new_comp->next_by_pid = head_by_pid; if (head_by_pid != NULL) head_by_pid->prev_by_pid = new_comp; head_by_pid = new_comp; } void TTCN_Runtime::remove_component(component_process_struct *comp) { if (comp->next_by_compref != NULL) comp->next_by_compref->prev_by_compref = comp->prev_by_compref; if (comp->prev_by_compref != NULL) comp->prev_by_compref->next_by_compref = comp->next_by_compref; else components_by_compref[comp->component_reference % HASHTABLE_SIZE] = comp->next_by_compref; if (comp->next_by_pid != NULL) comp->next_by_pid->prev_by_pid = comp->prev_by_pid; if (comp->prev_by_pid != NULL) comp->prev_by_pid->next_by_pid = comp->next_by_pid; else components_by_pid[comp->process_id % HASHTABLE_SIZE] = comp->next_by_pid; delete comp; } TTCN_Runtime::component_process_struct *TTCN_Runtime::get_component_by_compref( component component_reference) { component_process_struct *iter = components_by_compref[component_reference % HASHTABLE_SIZE]; while (iter != NULL) { if (iter->component_reference == component_reference) break; iter = iter->next_by_compref; } return iter; } TTCN_Runtime::component_process_struct *TTCN_Runtime::get_component_by_pid( pid_t process_id) { component_process_struct *iter = components_by_pid[process_id % HASHTABLE_SIZE]; while (iter != NULL) { if (iter->process_id == process_id) break; iter = iter->next_by_pid; } return iter; } void TTCN_Runtime::clear_component_process_tables() { if (components_by_compref == NULL) return; for (unsigned int i = 0; i < HASHTABLE_SIZE; i++) { while (components_by_compref[i] != NULL) remove_component(components_by_compref[i]); while (components_by_pid[i] != NULL) remove_component(components_by_pid[i]); } delete [] components_by_compref; components_by_compref = NULL; delete [] components_by_pid; components_by_pid = NULL; } void TTCN_Runtime::successful_process_creation() { if (is_overloaded()) { TTCN_Communication::send_hc_ready(); TTCN_Communication::disable_periodic_call(); executor_state = HC_ACTIVE; } } void TTCN_Runtime::failed_process_creation() { if (executor_state == HC_ACTIVE) { TTCN_Communication::enable_periodic_call(); executor_state = HC_OVERLOADED; } } void TTCN_Runtime::wait_terminated_processes() { // this function might be called from TCs too while returning from // TTCN_Communication::process_all_messages_hc() after fork() if (!is_hc()) return; errno = 0; for ( ; ; ) { int statuscode; struct rusage r_usage = {{0,0},{0,0},0,0,0,0,0,0,0,0,0,0,0,0,0,0}; #ifdef INTERIX pid_t child_pid = waitpid(-1, &statuscode, WNOHANG); getrusage(RUSAGE_CHILDREN, &r_usage); #else pid_t child_pid = wait3(&statuscode, WNOHANG, &r_usage); #endif if (child_pid <= 0) { switch (errno) { case ECHILD: errno = 0; // no break case 0: return; default: TTCN_error("System call wait3() failed when waiting for " "terminated test component processes."); } } component_process_struct *comp = get_component_by_pid(child_pid); if (comp != NULL) { int reason; const char *comp_name = NULL; if (comp->component_reference == MTC_COMPREF) { reason = API::ParallelPTC_reason::mtc__finished; } else { reason = API::ParallelPTC_reason::ptc__finished; comp_name = COMPONENT::get_component_name(comp->component_reference); } char *rusage = NULL; rusage = mprintf( "user time: %ld.%06ld s, system time: %ld.%06ld s, " "maximum resident set size: %ld, " "integral resident set size: %ld, " "page faults not requiring physical I/O: %ld, " "page faults requiring physical I/O: %ld, " "swaps: %ld, " "block input operations: %ld, block output operations: %ld, " "messages sent: %ld, messages received: %ld, " "signals received: %ld, " "voluntary context switches: %ld, " "involuntary context switches: %ld }", (long)r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec, (long)r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec, r_usage.ru_maxrss, r_usage.ru_idrss, r_usage.ru_minflt, r_usage.ru_majflt, r_usage.ru_nswap, r_usage.ru_inblock, r_usage.ru_oublock, r_usage.ru_msgsnd, r_usage.ru_msgrcv, r_usage.ru_nsignals, r_usage.ru_nvcsw, r_usage.ru_nivcsw); // There are too many different integer types in the rusage structure. // Just format them into a string and and pass that to the logger. TTCN_Logger::log_par_ptc(reason, NULL, NULL, comp->component_reference, comp_name, rusage, child_pid, statuscode); Free(rusage); remove_component(comp); } else { TTCN_warning("wait3() system call returned unknown process id %ld.", (long)child_pid); } } } void TTCN_Runtime::check_overload() { if (!is_hc()) TTCN_error("Internal error: TTCN_Runtime::check_overload() " "can be used on HCs only."); if (!is_overloaded()) return; TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::overload__check); pid_t child_pid = fork(); if (child_pid < 0) { // fork failed, the host is still overloaded TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::overload__check__fail); //TODO TTCN_Logger::OS_error(); if (executor_state == HC_OVERLOADED_TIMEOUT) { // increase the call interval if the function was called because of // a timeout TTCN_Communication::increase_call_interval(); executor_state = HC_OVERLOADED; } } else if (child_pid > 0) { // fork was successful, this code runs on the parent process (HC) int statuscode; // wait until the dummy child terminates pid_t result_pid = waitpid(child_pid, &statuscode, 0); if (result_pid != child_pid) TTCN_error("System call waitpid() " "returned unexpected status code %ld when waiting for the dummy " "child process with PID %ld.", (long)result_pid, (long)child_pid); successful_process_creation(); TTCN_Logger::log_executor_runtime( API::ExecutorRuntime_reason::overloaded__no__more); // FIXME pid is not logged; it would need a separate function // analyze the status code and issue a warning if something strange // happened if (WIFEXITED(statuscode)) { int exitstatus = WEXITSTATUS(statuscode); if (exitstatus != EXIT_SUCCESS) TTCN_warning("Dummy child process " "with PID %ld returned unsuccessful exit status (%d).", (long)child_pid, exitstatus); } else if (WIFSIGNALED(statuscode)) { int signum = WTERMSIG(statuscode); TTCN_warning("Dummy child process with PID %ld was terminated by " "signal %d (%s).", (long)child_pid, signum, get_signal_name(signum)); } else { TTCN_warning("Dummy child process with PID %ld was terminated by " "an unknown reason (return status: %d).", (long)child_pid, statuscode); } // try to clean up some more zombies if possible wait_terminated_processes(); } else { // fork was successful, this code runs on the dummy child process // the dummy child process shall exit immediately exit(EXIT_SUCCESS); } }