-///////////////////////////////////////////////////////////////////////////////
-// Copyright (c) 2000-2015 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
-///////////////////////////////////////////////////////////////////////////////
+/******************************************************************************
+ * 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:
+ * Balasko, Jeno
+ * Baranyi, Botond
+ * Bene, Tamas
+ * Beres, Szabolcs
+ * Delic, Adam
+ * Forstner, Matyas
+ * Gecse, Roland
+ * Kovacs, Ferenc
+ * Lovassy, Arpad
+ * Raduly, Csaba
+ * Szabados, Kristof
+ * Szabo, Janos Zoltan – initial implementation
+ * Szalai, Gabor
+ * Zalanyi, Balazs Andor
+ * Roland Gecse - author
+ *
+ ******************************************************************************/
//
// Description: Implementation file for Cli
-// Author: Gecse Roland
-// mail: ethrge@eth.ericsson.se
-//
-// Copyright (c) 2000-2015 Ericsson Telecom AB
//
//----------------------------------------------------------------------------
#include "Cli.h"
#include "../../common/version_internal.h"
#include "../../common/memory.h"
#include "../../common/config_preproc.h"
+#include "../../core/DebugCommands.hh"
#define PROMPT "MC2> "
#define CMTC_TEXT "cmtc"
#define SHELL_TEXT "!"
#define EXIT_TEXT "quit"
#define EXIT_TEXT2 "exit"
+#define BATCH_TEXT "batch"
#define SHELL_ESCAPE '!'
#define TTCN3_HISTORY_FILENAME ".ttcn3_history"
"Execute commands in subshell." },
{ EXIT_TEXT, &Cli::exitCallback, EXIT_TEXT, "Exit Main Controller." },
{ EXIT_TEXT2, &Cli::exitCallback, EXIT_TEXT2, "Exit Main Controller." },
+ { BATCH_TEXT, &Cli::executeBatchFile, BATCH_TEXT " <batch_file>",
+ "Run commands from batch file." },
{ NULL, NULL, NULL, NULL }
};
+struct DebugCommand {
+ const char *name;
+ int commandID;
+ const char *synopsis;
+ const char *description;
+};
+
+static const DebugCommand debug_command_list[] = {
+ { D_SWITCH_TEXT, D_SWITCH, D_SWITCH_TEXT " on|off",
+ "Switch the debugger on or off." },
+ { D_SET_BREAKPOINT_TEXT, D_SET_BREAKPOINT,
+ D_SET_BREAKPOINT_TEXT " <module> <line>|<function> [<batch_file>]",
+ "Add a breakpoint at the specified location, or change the batch file of "
+ "an existing breakpoint." },
+ { D_REMOVE_BREAKPOINT_TEXT, D_REMOVE_BREAKPOINT,
+ D_REMOVE_BREAKPOINT_TEXT " all|<module> [all|<line>|<function>]",
+ "Remove a breakpoint, or all breakpoints from a module, or all breakpoints "
+ "from all modules." },
+ { D_SET_AUTOMATIC_BREAKPOINT_TEXT, D_SET_AUTOMATIC_BREAKPOINT,
+ D_SET_AUTOMATIC_BREAKPOINT_TEXT " error|fail on|off [<batch_file>]",
+ "Switch an automatic breakpoint (truggered by an event) on or off, and/or "
+ "change its batch file." },
+ { D_SET_OUTPUT_TEXT, D_SET_OUTPUT,
+ D_SET_OUTPUT_TEXT " console|file|both [file_name]",
+ "Set the output of the debugger." },
+ { D_SET_GLOBAL_BATCH_FILE_TEXT, D_SET_GLOBAL_BATCH_FILE,
+ D_SET_GLOBAL_BATCH_FILE_TEXT " on|off [batch_file_name]",
+ "Set whether a batch file should be executed automatically when test execution "
+ "is halted (breakpoint-specific batch files override this setting)." },
+ { D_FUNCTION_CALL_CONFIG_TEXT, D_FUNCTION_CALL_CONFIG,
+ D_FUNCTION_CALL_CONFIG_TEXT " file|<limit>|all [<file_name>]",
+ "Configure the storing of function call data." },
+ { D_PRINT_SETTINGS_TEXT, D_PRINT_SETTINGS, D_PRINT_SETTINGS_TEXT,
+ "Prints the debugger's settings." },
+ { D_LIST_COMPONENTS_TEXT, D_LIST_COMPONENTS, D_LIST_COMPONENTS_TEXT,
+ "List the test components currently running debuggable code." },
+ { D_SET_COMPONENT_TEXT, D_SET_COMPONENT,
+ D_SET_COMPONENT_TEXT " <component name>|<component_reference>",
+ "Set the test component to print debug information from." },
+ { D_PRINT_CALL_STACK_TEXT, D_PRINT_CALL_STACK, D_PRINT_CALL_STACK_TEXT,
+ "Print call stack." },
+ { D_SET_STACK_LEVEL_TEXT, D_SET_STACK_LEVEL, D_SET_STACK_LEVEL_TEXT " <level>",
+ "Set the stack level to print debug information from." },
+ { D_LIST_VARIABLES_TEXT, D_LIST_VARIABLES,
+ D_LIST_VARIABLES_TEXT " [local|global|comp|all] [pattern]",
+ "List variable names." },
+ { D_PRINT_VARIABLE_TEXT, D_PRINT_VARIABLE,
+ D_PRINT_VARIABLE_TEXT " <variable_name>|$ [{ <variable_name>|$}]",
+ "Print current value of one or more variables ('$' is substituted with the "
+ "result of the last " D_LIST_VARIABLES_TEXT " command)." },
+ { D_OVERWRITE_VARIABLE_TEXT, D_OVERWRITE_VARIABLE,
+ D_OVERWRITE_VARIABLE_TEXT " <variable_name> <value>",
+ "Overwrite the current value of a variable." },
+ { D_PRINT_FUNCTION_CALLS_TEXT, D_PRINT_FUNCTION_CALLS,
+ D_PRINT_FUNCTION_CALLS_TEXT " [all|<amount>]",
+ "Print function call data." },
+ { D_STEP_OVER_TEXT, D_STEP_OVER, D_STEP_OVER_TEXT,
+ "Resume test execution until the next line of code (in this function or the "
+ "caller function)." },
+ { D_STEP_INTO_TEXT, D_STEP_INTO, D_STEP_INTO_TEXT,
+ "Resume test execution until the next line of code (on any stack level)." },
+ { D_STEP_OUT_TEXT, D_STEP_OUT, D_STEP_OUT_TEXT,
+ "Resume test execution until the next line of code in the caller function." },
+ { D_RUN_TO_CURSOR_TEXT, D_RUN_TO_CURSOR,
+ D_RUN_TO_CURSOR_TEXT " <module> <line>|<function>",
+ "Resume test execution until the specified location." },
+ { D_HALT_TEXT, D_HALT, D_HALT_TEXT, "Halt test execution." },
+ { D_CONTINUE_TEXT, D_CONTINUE, D_CONTINUE_TEXT, "Resume halted test execution." },
+ { D_EXIT_TEXT, D_EXIT, D_EXIT_TEXT " test|all",
+ "Exit the current test or the execution of all tests." },
+ { NULL, D_ERROR, NULL, NULL }
+};
+
Cli::Cli()
{
loggingEnabled = TRUE;
perror("Cli::Cli: pthread_cond_init failed.");
exit(EXIT_FAILURE);
}
+ cfg_file_name = NULL;
}
Cli::~Cli()
{
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
+ Free(cfg_file_name);
}
//----------------------------------------------------------------------------
printWelcome();
if (argc == 2) {
- printf("Using configuration file: %s\n", argv[1]);
- if (process_config_read_file(argv[1], &mycfg)) {
+ cfg_file_name = mcopystr(argv[1]);
+ printf("Using configuration file: %s\n", cfg_file_name);
+ if (process_config_read_file(cfg_file_name, &mycfg)) {
puts("Error was found in the configuration file. Exiting.");
cleanUp();
return EXIT_FAILURE;
"*************************************************************************\n"
"* TTCN-3 Test Executor - Main Controller 2 *\n"
"* Version: %-40s *\n"
- "* Copyright (c) 2000-2015 Ericsson Telecom AB *\n"
+ "* Copyright (c) 2000-2016 Ericsson Telecom AB *\n"
"* All rights reserved. This program and the accompanying materials *\n"
"* are made available under the terms of the Eclipse Public License v1.0 *\n"
"* which accompanies this distribution, and is available at *\n"
void Cli::processCommand(char *line_read)
{
for (const Command *command = command_list; command->name != NULL;
- command++) {
+ command++) {
size_t command_name_len = strlen(command->name);
if (!strncmp(line_read, command->name, command_name_len)) {
memset(line_read, ' ', command_name_len);
return;
}
}
+ for (const DebugCommand* command = debug_command_list; command->name != NULL;
+ command++) {
+ size_t command_name_len = strlen(command->name);
+ if (!strncmp(line_read, command->name, command_name_len)) {
+ memset(line_read, ' ', command_name_len);
+ stripLWS(line_read);
+ MainController::debug_command(command->commandID, line_read);
+ if (waitState == WAIT_EXECUTE_LIST && command->commandID == D_EXIT &&
+ !strcmp(line_read, "all")) {
+ // stop executing the list from the config file
+ waitState = WAIT_NOTHING;
+ }
+ return;
+ }
+ }
puts("Unknown command, try again...");
}
void Cli::reconfCallback(const char *arguments)
{
- if(*arguments == 0) { // reconf called without its optional argument
- puts("Reconfiguration of MC and HCs using original configuration "
- "data\n -- not supported, yet.");
- } else { // reconf called with config_file argument
- puts("Reconfiguration of MC and HCs using configuration file"
- "specified in\ncommand line argument -- not supported, yet.");
+ if (!MainController::start_reconfiguring()) {
+ return;
+ }
+ if (*arguments != 0) {
+ Free(cfg_file_name);
+ cfg_file_name = mcopystr(arguments);
+ }
+
+ printf("Using configuration file: %s\n", cfg_file_name);
+ if (process_config_read_file(cfg_file_name, &mycfg)) {
+ puts("Error was found in the configuration file. Exiting.");
+ cleanUp();
+ puts("exit");
+ exitCallback("");
+ }
+ else {
+ MainController::set_kill_timer(mycfg.kill_timer);
+
+ for (int i = 0; i < mycfg.group_list_len; ++i) {
+ MainController::add_host(mycfg.group_list[i].group_name,
+ mycfg.group_list[i].host_name);
+ }
+
+ for (int i = 0; i < mycfg.component_list_len; ++i) {
+ MainController::assign_component(mycfg.component_list[i].host_or_group,
+ mycfg.component_list[i].component);
+ }
+
+ if (MainController::get_state() == mctr::MC_RECONFIGURING) {
+ MainController::configure(mycfg.config_read_buffer);
+ }
}
}
puts("Help is available for the following commands:");
for (const Command *command = command_list;
command->name != NULL; command++) {
- printf(" %s", command->name);
+ printf("%s ", command->name);
+ }
+ for (const DebugCommand *command = debug_command_list;
+ command->name != NULL; command++) {
+ printf("%s ", command->name);
}
putchar('\n');
} else {
return;
}
}
+ for (const DebugCommand *command = debug_command_list;
+ command->name != NULL; command++) {
+ if (!strncmp(arguments, command->name,
+ strlen(command->name))) {
+ printf("%s usage: %s\n%s\n", command->name,
+ command->synopsis,
+ command->description);
+ return;
+ }
+ }
printf("No help for %s.\n", arguments);
}
}
if (*arguments == 0) {
switch (MainController::get_state()) {
case mctr::MC_READY:
+ case mctr::MC_RECONFIGURING:
MainController::exit_mtc();
waitMCState(WAIT_MTC_TERMINATED);
case mctr::MC_LISTENING:
}
}
+void Cli::executeBatchFile(const char* filename)
+{
+ FILE* fp = fopen(filename, "r");
+ if (fp == NULL) {
+ printf("Failed to open file '%s' for reading.\n", filename);
+ return;
+ }
+ else {
+ printf("Executing batch file '%s'.\n", filename);
+ }
+ char line[1024];
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ size_t len = strlen(line);
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ --len;
+ }
+ if (len != 0) {
+ printf("%s\n", line);
+ processCommand(line);
+ }
+ }
+ if (!feof(fp)) {
+ printf("Error occurred while reading batch file '%s' (error code: %d).\n",
+ filename, ferror(fp));
+ }
+ fclose(fp);
+}
+
//----------------------------------------------------------------------------
// PRIVATE
// Command completion function implementation for readline() library.
char *Cli::completeCommand(const char *prefix, int state)
{
static int command_index;
+ static int debug_command_index;
static size_t prefix_len;
const char *command_name;
if(state == 0) {
command_index = 0;
+ debug_command_index = 0;
prefix_len = strlen(prefix);
}
return strdup(command_name);
}
}
+
+ while ((command_name = debug_command_list[debug_command_index].name)) {
+ ++debug_command_index;
+ if (strncmp(prefix, command_name, prefix_len) == 0) {
+ // Must allocate buffer for returned string (readline frees it)
+ return strdup(command_name);
+ }
+ }
// No match found
return NULL;
}