-
ebensza authored
Signed-off-by:
ebensza <bence.janos.szabo@ericsson.com>
ebensza authoredSigned-off-by:
ebensza <bence.janos.szabo@ericsson.com>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Runtime.cc 103.57 KiB
/******************************************************************************
* 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);
}
}