diff options
Diffstat (limited to 'cpukit/libmisc/capture/capture-cli.c')
-rw-r--r-- | cpukit/libmisc/capture/capture-cli.c | 1622 |
1 files changed, 1622 insertions, 0 deletions
diff --git a/cpukit/libmisc/capture/capture-cli.c b/cpukit/libmisc/capture/capture-cli.c new file mode 100644 index 0000000000..98f76764f3 --- /dev/null +++ b/cpukit/libmisc/capture/capture-cli.c @@ -0,0 +1,1622 @@ +/* + ------------------------------------------------------------------------ + $Id$ + ------------------------------------------------------------------------ + + Copyright Objective Design Systems Pty Ltd, 2002 + All rights reserved Objective Design Systems Pty Ltd, 2002 + Chris Johns (ccj@acm.org) + + COPYRIGHT (c) 1989-1998. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution. + + This software with is provided ``as is'' and with NO WARRANTY. + + ------------------------------------------------------------------------ + + RTEMS Performance Monitoring and Measurement Framework. + + This is the Target Interface Command Line Interface. You need + start the RTEMS monitor. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include <rtems.h> +#include <rtems/capture-cli.h> +#include <rtems/monitor.h> + +#define RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS (20) + +/* + * The user capture timestamper. + */ +static rtems_capture_timestamp capture_timestamp; + +/* + * Common variable to sync the load monitor task. + */ +static volatile int cli_load_thread_active; + +/* + * rtems_capture_cli_open + * + * DESCRIPTION: + * + * This function opens the capture engine. We need the size of the + * capture buffer. + * + */ + +static const char* open_usage = "usage: copen [-i] size\n"; + +static void +rtems_capture_cli_open (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + uint32_t size = 0; + bool enable = false; + rtems_status_code sc; + int arg; + + if (argc <= 1) + { + fprintf (stdout, open_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'i') + enable = true; + else + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + size = strtoul (argv[arg], 0, 0); + + if (size < 100) + { + fprintf (stdout, "error: size must be greater than or equal to 100\n"); + return; + } + } + } + + sc = rtems_capture_open (size, capture_timestamp); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: open failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine opened.\n"); + + if (!enable) + return; + + sc = rtems_capture_control (enable); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: open enable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine enabled.\n"); +} + +/* + * rtems_capture_cli_close + * + * DESCRIPTION: + * + * This function closes the capture engine. + * + */ + +static void +rtems_capture_cli_close (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + + sc = rtems_capture_close (); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: close failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine closed.\n"); +} + +/* + * rtems_capture_cli_enable + * + * DESCRIPTION: + * + * This function enables the capture engine. + * + */ + +static void +rtems_capture_cli_enable (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + + sc = rtems_capture_control (1); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: enable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine enabled.\n"); +} + +/* + * rtems_capture_cli_disable + * + * DESCRIPTION: + * + * This function disables the capture engine. + * + */ + +static void +rtems_capture_cli_disable (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + + sc = rtems_capture_control (0); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: disable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "capture engine disabled.\n"); +} + +/* + * rtems_capture_cli_task_list + * + * DESCRIPTION: + * + * This function lists the tasks the capture engine knows about. + * + */ + +static void +rtems_capture_cli_task_list (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + rtems_capture_task_t* task = rtems_capture_get_task_list (); + uint32_t ticks; + uint32_t tick_offset; + unsigned long long total_time; + int count = rtems_capture_task_count (); + + if (capture_timestamp) + capture_timestamp (&ticks, &tick_offset); + else + { + ticks = _Watchdog_Ticks_since_boot; + tick_offset = 0; + } + + fprintf (stdout, "total %i\n", count); + + while (task) + { + rtems_task_priority priority; + int32_t stack_used; + int32_t time_used; + + stack_used = rtems_capture_task_stack_usage (task); + if (stack_used) + stack_used = (stack_used * 100) / stack_used; + + if (stack_used > 100) + stack_used = 100; + + total_time = (ticks * rtems_capture_task_time (task)) + tick_offset; + + time_used = (rtems_capture_task_time (task) * 100) / total_time; + + if (time_used > 100) + time_used = 100; + + priority = rtems_capture_task_real_priority (task); + + fprintf (stdout, " "); + rtems_monitor_dump_id (rtems_capture_task_id (task)); + fprintf (stdout, " "); + rtems_monitor_dump_name (rtems_capture_task_name (task)); + fprintf (stdout, " "); + rtems_monitor_dump_priority (rtems_capture_task_start_priority (task)); + fprintf (stdout, " "); + rtems_monitor_dump_priority (rtems_capture_task_real_priority (task)); + fprintf (stdout, " "); + rtems_monitor_dump_priority (rtems_capture_task_curr_priority (task)); + fprintf (stdout, " "); + rtems_monitor_dump_state (rtems_capture_task_state (task)); + fprintf (stdout, " %c%c", + rtems_capture_task_valid (task) ? 'a' : 'd', + rtems_capture_task_flags (task) & RTEMS_CAPTURE_TRACED ? 't' : '-'); + + if ((floor > ceiling) && (ceiling > priority)) + fprintf (stdout, "--"); + else + { + uint32_t flags = rtems_capture_task_control_flags (task); + fprintf (stdout, "%c%c", + rtems_capture_task_control (task) ? + (flags & RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', + rtems_capture_watch_global_on () ? 'g' : '-'); + } + fprintf (stdout, " %3" PRId32 "%% %3" PRId32 "%% (%" PRIu32 ")\n", + stack_used, time_used, rtems_capture_task_ticks (task)); + + task = rtems_capture_next_task (task); + } +} + +/* + * rtems_capture_cli_task_load_thread + * + * DESCRIPTION: + * + * This function displays the load of the tasks on an ANSI terminal. + * + */ + +static void +rtems_capture_cli_task_load_thread (rtems_task_argument arg __attribute__((unused))) +{ + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + int last_count = 0; + + for (;;) + { + rtems_capture_task_t* tasks[RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS + 1]; + unsigned long long load[RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS + 1]; + rtems_capture_task_t* task; + unsigned long long total_time; + int count = 0; + int i; + int j; + + cli_load_thread_active = 1; + + /* + * Iterate over the tasks and sort the highest load tasks + * into our local arrays. We only handle a limited number of + * tasks. + */ + + memset (tasks, 0, sizeof (tasks)); + memset (load, 0, sizeof (load)); + + task = rtems_capture_get_task_list (); + + total_time = 0; + + while (task) + { + if (rtems_capture_task_valid (task)) + { + unsigned long long l = rtems_capture_task_delta_time (task); + + count++; + + total_time += l; + + for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) + { + if (tasks[i]) + { + if ((l == 0) || (l < load[i])) + continue; + + for (j = (RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS - 1); j >= i; j--) + { + tasks[j + 1] = tasks[j]; + load[j + 1] = load[j]; + } + } + + tasks[i] = task; + load[i] = l; + break; + } + } + task = rtems_capture_next_task (task); + } + + fprintf (stdout, "\x1b[H\x1b[J Press ENTER to exit.\n\n"); + fprintf (stdout, + " PID NAME RPRI CPRI STATE %%CPU %%STK FLGS EXEC TIME\n"); + + if (count > last_count) + j = count; + else + j = last_count; + + for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) + { + rtems_task_priority priority; + int stack_used; + int task_load; + int k; + + if (!tasks[i]) + break; + + j--; + + stack_used = rtems_capture_task_stack_usage (tasks[i]); + if (stack_used) + stack_used = (stack_used * 100) / stack_used; + + if (stack_used > 100) + stack_used = 100; + + task_load = (int) ((load[i] * 100000) / total_time); + + priority = rtems_capture_task_real_priority (tasks[i]); + + fprintf (stdout, "\x1b[K"); + rtems_monitor_dump_id (rtems_capture_task_id (tasks[i])); + fprintf (stdout, " "); + rtems_monitor_dump_name (rtems_capture_task_name (tasks[i])); + fprintf (stdout, " "); + rtems_monitor_dump_priority (priority); + fprintf (stdout, " "); + rtems_monitor_dump_priority (rtems_capture_task_curr_priority (tasks[i])); + fprintf (stdout, " "); + k = rtems_monitor_dump_state (rtems_capture_task_state (tasks[i])); + fprintf (stdout, "%*c %3i.%03i%% ", 6 - k, ' ', + task_load / 1000, task_load % 1000); + fprintf (stdout, "%3i%% %c%c", stack_used, + rtems_capture_task_valid (tasks[i]) ? 'a' : 'd', + rtems_capture_task_flags (tasks[i]) & RTEMS_CAPTURE_TRACED ? 't' : '-'); + + if ((floor > ceiling) && (ceiling > priority)) + fprintf (stdout, "--"); + else + fprintf (stdout, "%c%c", + rtems_capture_task_control (tasks[i]) ? + (rtems_capture_task_control_flags (tasks[i]) & + RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', + rtems_capture_watch_global_on () ? 'g' : '-'); + + fprintf (stdout, " %qi\n", rtems_capture_task_time (tasks[i])); + } + + if (count < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS) + { + j = RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS - count; + while (j > 0) + { + fprintf (stdout, "\x1b[K\n"); + j--; + } + } + + last_count = count; + + cli_load_thread_active = 0; + + rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (5000000)); + } +} + +/* + * rtems_capture_cli_task_load + * + * DESCRIPTION: + * + * This function is a monitor command. + * + */ + +static void +rtems_capture_cli_task_load (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + rtems_task_priority priority; + rtems_name name; + rtems_id id; + + sc = rtems_task_set_priority (RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: cannot obtain the current priority: %s\n", + rtems_status_text (sc)); + return; + } + + name = rtems_build_name('C', 'P', 'l', 't'); + + sc = rtems_task_create (name, priority, 4 * 1024, + RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL, + RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR, + &id); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: cannot create helper thread: %s\n", + rtems_status_text (sc)); + return; + } + + sc = rtems_task_start (id, rtems_capture_cli_task_load_thread, 0); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: cannot start helper thread: %s\n", + rtems_status_text (sc)); + rtems_task_delete (id); + return; + } + + for (;;) + { + int c = getchar (); + + if ((c == '\r') || (c == '\n')) + { + int loops = 20; + + while (loops && cli_load_thread_active) + rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (100000)); + + rtems_task_delete (id); + + fprintf (stdout, "load monitoring stopped.\n"); + + return; + } + } +} + +/* + * rtems_capture_cli_watch_list + * + * DESCRIPTION: + * + * This function lists the controls in the capture engine. + * + */ + +static void +rtems_capture_cli_watch_list (int argc __attribute__((unused)), + char** argv __attribute__((unused)), + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_capture_control_t* control = rtems_capture_get_control_list (); + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + + fprintf (stdout, "watch priority ceiling is %" PRId32 "\n", ceiling); + fprintf (stdout, "watch priority floor is %" PRId32 "\n", floor); + fprintf (stdout, "global watch is %s\n", + rtems_capture_watch_global_on () ? "enabled" : "disabled"); + fprintf (stdout, "total %" PRId32 "\n", rtems_capture_control_count ()); + + while (control) + { + uint32_t flags; + int f; + int fshowed; + int lf; + + fprintf (stdout, " "); + rtems_monitor_dump_id (rtems_capture_control_id (control)); + fprintf (stdout, " "); + rtems_monitor_dump_name (rtems_capture_control_name (control)); + flags = rtems_capture_control_flags (control); + fprintf (stdout, " %c%c ", + rtems_capture_watch_global_on () ? 'g' : '-', + flags & RTEMS_CAPTURE_WATCH ? 'w' : '-'); + flags = rtems_capture_control_to_triggers (control); + fprintf (stdout, " T:%c%c%c%c%c%c%c", + flags & RTEMS_CAPTURE_SWITCH ? 'S' : '-', + flags & RTEMS_CAPTURE_CREATE ? 'C' : '-', + flags & RTEMS_CAPTURE_START ? 'S' : '-', + flags & RTEMS_CAPTURE_RESTART ? 'R' : '-', + flags & RTEMS_CAPTURE_DELETE ? 'D' : '-', + flags & RTEMS_CAPTURE_BEGIN ? 'B' : '-', + flags & RTEMS_CAPTURE_EXITTED ? 'E' : '-'); + flags = rtems_capture_control_from_triggers (control); + fprintf (stdout, " F:%c%c%c%c%c", + flags & RTEMS_CAPTURE_SWITCH ? 'S' : '-', + flags & RTEMS_CAPTURE_CREATE ? 'C' : '-', + flags & RTEMS_CAPTURE_START ? 'S' : '-', + flags & RTEMS_CAPTURE_RESTART ? 'R' : '-', + flags & RTEMS_CAPTURE_DELETE ? 'D' : '-'); + + for (f = 0, fshowed = 0, lf = 1; f < RTEMS_CAPTURE_TRIGGER_TASKS; f++) + { + if (rtems_capture_control_by_valid (control, f)) + { + if (lf && ((fshowed % 3) == 0)) + { + fprintf (stdout, "\n"); + lf = 0; + } + + fprintf (stdout, " %2i:", f); + rtems_monitor_dump_name (rtems_capture_control_by_name (control, f)); + fprintf (stdout, "/"); + rtems_monitor_dump_id (rtems_capture_control_by_id (control, f)); + flags = rtems_capture_control_by_triggers (control, f); + fprintf (stdout, ":%c%c%c%c%c", + flags & RTEMS_CAPTURE_SWITCH ? 'S' : '-', + flags & RTEMS_CAPTURE_CREATE ? 'C' : '-', + flags & RTEMS_CAPTURE_START ? 'S' : '-', + flags & RTEMS_CAPTURE_RESTART ? 'R' : '-', + flags & RTEMS_CAPTURE_DELETE ? 'D' : '-'); + fshowed++; + lf = 1; + } + } + + if (lf) + fprintf (stdout, "\n"); + + control = rtems_capture_next_control (control); + } +} + +/* + * rtems_capture_cli_get_name_id + * + * DESCRIPTION: + * + * This function checks arguments for a name or an id. + * + */ + +static bool +rtems_capture_cli_get_name_id (char* arg, + bool* valid_name, + bool* valid_id, + rtems_name* name, + rtems_id* id) +{ + size_t l; + size_t i; + + if (*valid_name && *valid_id) + { + fprintf (stdout, "error: too many arguments\n"); + return 0; + } + + /* + * See if the arg is all hex digits. + */ + + l = strlen (arg); + + for (i = 0; i < l; i++) + if (!isxdigit ((unsigned char)arg[i])) + break; + + if (i == l) + { + *id = strtoul (arg, 0, 16); + *valid_id = true; + } + else + { + /* + * This is a bit of hack but it should work on all platforms + * as it is what the score does with names. + * + * @warning The extra assigns play with the byte order so do not + * remove unless the score has been updated. + */ + rtems_name rname; + + rname = rtems_build_name(arg[0], arg[1], arg[2], arg[3]); + *name = rname; + *valid_name = true; + } + + return 1; +} + +/* + * rtems_capture_cli_watch_add + * + * DESCRIPTION: + * + * This function is a monitor command that add a watch to the capture + * engine. + * + */ + +static char const * watch_add_usage = "usage: cwadd [task name] [id]\n"; + +static void +rtems_capture_cli_watch_add (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + bool valid_name = false; + bool valid_id = false; + + if (argc <= 1) + { + fprintf (stdout, watch_add_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, + &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + fprintf (stdout, "error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_add (name, id); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, + "error: watch add failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch added.\n"); +} + +/* + * rtems_capture_cli_watch_del + * + * DESCRIPTION: + * + * This function is a monitor command that deletes a watch from the capture + * engine. + * + */ + +static char const * watch_del_usage = "usage: cwdel [task name] [id]\n"; + +static void +rtems_capture_cli_watch_del (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + bool valid_name = false; + bool valid_id = false; + + if (argc <= 1) + { + fprintf (stdout, watch_del_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, + &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + fprintf (stdout, "error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_del (name, id); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: watch delete failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch delete.\n"); +} + +/* + * rtems_capture_cli_watch_control + * + * DESCRIPTION: + * + * This function is a monitor command that controls a watch. + * + */ + +static char const * watch_control_usage = "usage: cwctl [task name] [id] on/off\n"; + +static void +rtems_capture_cli_watch_control (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + bool valid_name = false; + bool valid_id = false; + bool enable = false; + + if (argc <= 2) + { + fprintf (stdout, watch_control_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (strcmp (argv[arg], "on") == 0) + enable = true; + else if (strcmp (argv[arg], "off") == 0) + enable = false; + else if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, + &valid_id, &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + fprintf (stdout, "error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_ctrl (name, id, enable); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: watch control failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch %s.\n", enable ? "enabled" : "disabled"); +} + +/* + * rtems_capture_cli_watch_global + * + * DESCRIPTION: + * + * This function is a monitor command that sets a global watch. + * + */ + +static char const * watch_global_usage = "usage: cwglob on/off\n"; + +static void +rtems_capture_cli_watch_global (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + bool enable = false; + + if (argc <= 1) + { + fprintf (stdout, watch_global_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (strcmp (argv[arg], "on") == 0) + enable = true; + else if (strcmp (argv[arg], "off") == 0) + enable = false; + } + } + + sc = rtems_capture_watch_global (enable); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: global watch failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "global watch %s.\n", enable ? "enabled" : "disabled"); +} + +/* + * rtems_capture_cli_watch_ceiling + * + * DESCRIPTION: + * + * This function is a monitor command that sets watch ceiling. + * + */ + +static char const * watch_ceiling_usage = "usage: cwceil priority\n"; + +static void +rtems_capture_cli_watch_ceiling (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_task_priority priority = 0; + + if (argc <= 1) + { + fprintf (stdout, watch_ceiling_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + priority = strtoul (argv[arg], 0, 0); + } + } + + sc = rtems_capture_watch_ceiling (priority); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: watch ceiling failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch ceiling is %" PRId32 ".\n", priority); +} + +/* + * rtems_capture_cli_watch_floor + * + * DESCRIPTION: + * + * This function is a monitor command that sets watch floor. + * + */ + +static char const * watch_floor_usage = "usage: cwfloor priority\n"; + +static void +rtems_capture_cli_watch_floor (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + int arg; + rtems_task_priority priority = 0; + + if (argc <= 1) + { + fprintf (stdout, watch_floor_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + priority = strtoul (argv[arg], 0, 0); + } + } + + sc = rtems_capture_watch_floor (priority); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: watch floor failed: %s\n", + rtems_status_text (sc)); + return; + } + + fprintf (stdout, "watch floor is %" PRId32 ".\n", priority); +} + +/* + * rtems_capture_cli_trigger_worker + * + * DESCRIPTION: + * + * This function is a monitor command that sets or clears a trigger. + * + */ + +static char const *trigger_set_usage = + "usage: %s [-?] type [to name/id] [from] [from name/id]\n"; + +static char const *trigger_set_types = + " You can say 'type TASK' or 'type TO from FROM'\n" \ + " where TASK is the task the event is happening to\n" \ + " or you can say the event TO this task FROM this task.\n" \ + " No type defaults to 'switch'.\n" \ + " switch : context switch TASK or FROM or FROM->TO\n" \ + " create : create TASK, or create TO from FROM\n" \ + " start : start TASK, or start TO from FROM\n" \ + " restart : restart TASK, or restart TO from FROM\n" \ + " delete : delete TASK or delete TO from FROM\n" \ + " begin : begin TASK\n" \ + " exitted : exitted TASK\n"; + +/* + * Structure to handle the parsing of the trigger command line. + */ +typedef struct rtems_capture_cli_triggers_s +{ + char const * name; + rtems_capture_trigger_t type; + int to_only; +} rtems_capture_cli_triggers_t; + +static rtems_capture_cli_triggers_t rtems_capture_cli_triggers[] = +{ + { "switch", rtems_capture_switch, 0 }, /* must be first */ + { "create", rtems_capture_create, 0 }, + { "start", rtems_capture_start, 0 }, + { "restart", rtems_capture_restart, 0 }, + { "delete", rtems_capture_delete, 0 }, + { "begin", rtems_capture_begin, 1 }, + { "exitted", rtems_capture_exitted, 1 } +}; + +typedef enum rtems_capture_cli_trig_state_e +{ + trig_type, + trig_to, + trig_from_from, + trig_from +} rtems_capture_cli_trig_state_t; + +#define RTEMS_CAPTURE_CLI_TRIGGERS_NUM \ + (sizeof (rtems_capture_cli_triggers) / sizeof (rtems_capture_cli_triggers_t)) + +static void +rtems_capture_cli_trigger_worker (int set, int argc, char** argv) +{ + rtems_status_code sc; + int arg; + int trigger = 0; /* switch */ + rtems_capture_trigger_mode_t trigger_mode = rtems_capture_from_any; + bool trigger_set = false; + bool is_from = false; + rtems_name name = 0; + rtems_id id = 0; + bool valid_name = false; + bool valid_id = false; + rtems_name from_name = 0; + rtems_id from_id = 0; + bool from_valid_name = false; + bool from_valid_id = false; + rtems_name to_name = 0; + rtems_id to_id = 0; + bool to_valid_name = false; + bool to_valid_id = false; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + switch (argv[arg][1]) + { + case '?': + fprintf (stdout, trigger_set_usage, set ? "ctset" : "ctclear"); + fprintf (stdout, trigger_set_types); + return; + default: + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + break; + } + } + else + { + if (!trigger_set) + { + bool found = false; + int t; + + for (t = 0; t < RTEMS_CAPTURE_CLI_TRIGGERS_NUM; t++) + if (strcmp (argv[arg], rtems_capture_cli_triggers[t].name) == 0) + { + trigger = t; + found = true; + break; + } + + trigger_set = true; + + /* + * If a trigger was not found assume the default and + * assume the parameter is a task name or id. + */ + if (found) + continue; + } + + if (strcmp (arg[argv], "from") == 0) + { + if (is_from) + fprintf (stdout, "warning: extra 'from' ignored\n"); + + is_from = 1; + continue; + } + + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, + &name, &id)) + return; + + if (valid_name) + { + if (is_from) + { + if (!from_valid_name && !from_valid_id) + { + from_valid_name = true; + from_name = name; + } + else + fprintf (stdout, "warning: extra arguments ignored\n"); + } + else if (!to_valid_name && !to_valid_id) + { + to_valid_name = true; + to_name = name; + } + else + fprintf (stdout, "warning: extra arguments ignored\n"); + } + + if (valid_id) + { + if (is_from) + { + if (!from_valid_name && !from_valid_id) + { + from_valid_id = true; + from_id = id; + } + else + fprintf (stdout, "warning: extra arguments ignored\n"); + } + else if (!to_valid_name && !to_valid_id) + { + to_valid_id = true; + to_id = id; + } + else + fprintf (stdout, "warning: extra arguments ignored\n"); + } + } + } + + if (is_from && rtems_capture_cli_triggers[trigger].to_only) + { + fprintf (stdout, "error: a %s trigger can be a TO trigger\n", + rtems_capture_cli_triggers[trigger].name); + return; + } + + if (!to_valid_name && !to_valid_id && !from_valid_name && !from_valid_id) + { + fprintf (stdout, trigger_set_usage); + return; + } + + if (!is_from && !to_valid_name && !to_valid_id) + { + fprintf (stdout, "error: a %s trigger needs a TO name or id\n", + rtems_capture_cli_triggers[trigger].name); + return; + } + + if (is_from && !from_valid_name && !from_valid_id) + { + fprintf (stdout, "error: a %s trigger needs a FROM name or id\n", + rtems_capture_cli_triggers[trigger].name); + return; + } + + if ((from_valid_name || from_valid_id) && (to_valid_name || to_valid_id)) + trigger_mode = rtems_capture_from_to; + else if (from_valid_name || from_valid_id) + trigger_mode = rtems_capture_to_any; + else if (to_valid_name || to_valid_id) + trigger_mode = rtems_capture_from_any; + + if (set) + sc = rtems_capture_set_trigger (from_name, from_id, to_name, to_id, + trigger_mode, + rtems_capture_cli_triggers[trigger].type); + else + sc = rtems_capture_clear_trigger (from_name, from_id, to_name, to_id, + trigger_mode, + rtems_capture_cli_triggers[trigger].type); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: %sing the trigger failed: %s\n", + set ? "sett" : "clear", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "trigger %s.\n", set ? "set" : "cleared"); +} + +/* + * rtems_capture_cli_trigger_set + * + * DESCRIPTION: + * + * This function is a monitor command that sets a trigger. + * + */ + +static void +rtems_capture_cli_trigger_set (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_capture_cli_trigger_worker (1, argc, argv); +} + +/* + * rtems_capture_cli_trigger_clear + * + * DESCRIPTION: + * + * This function is a monitor command that clears a trigger. + * + */ + +static void +rtems_capture_cli_trigger_clear (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_capture_cli_trigger_worker (0, argc, argv); +} + +/* + * rtems_capture_cli_trace_records + * + * DESCRIPTION: + * + * This function is a monitor command that dumps trace records. + * + */ + +static void +rtems_capture_cli_trace_records (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + bool csv = false; + static int dump_total = 22; + int total; + int count; + uint32_t read; + rtems_capture_record_t* rec; + int arg; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'c') + csv = true; + else + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + else + { + size_t i; + size_t l; + + l = strlen (argv[arg]); + + for (i = 0; i < l; i++) + if (!isdigit ((unsigned char)argv[arg][i])) + { + fprintf (stdout, "error: not a number\n"); + return; + } + + dump_total = strtoul (argv[arg], 0, 0); + } + } + + total = dump_total; + + while (total) + { + sc = rtems_capture_read (0, 0, &read, &rec); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: trace read failed: %s\n", rtems_status_text (sc)); + rtems_capture_flush (0); + return; + } + + /* + * If we have no records then just exist. We still need to release + * the reader lock. + */ + + if (read == 0) + { + rtems_capture_release (read); + break; + } + + count = total < read ? total : read; + + while (count--) + { + if (csv) + fprintf (stdout, "%08" PRIxPTR ",%03" PRIu32 + ",%03" PRIu32 ",%04" PRIx32 ",%" PRId32 ",%" PRId32 "\n", + (uintptr_t) rec->task, + (rec->events >> RTEMS_CAPTURE_REAL_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_CURR_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_EVENT_START), + rec->ticks, rec->tick_offset); + else + { + unsigned long long t; + uint32_t event; + int e; + + event = rec->events >> RTEMS_CAPTURE_EVENT_START; + + t = rec->ticks; + t *= rtems_capture_tick_time (); + t += rec->tick_offset; + + for (e = RTEMS_CAPTURE_EVENT_START; e < RTEMS_CAPTURE_EVENT_END; e++) + { + if (event & 1) + { + fprintf (stdout, "%9li.%06li ", (unsigned long) (t / 1000000), + (unsigned long) (t % 1000000)); + rtems_monitor_dump_id (rtems_capture_task_id (rec->task)); + fprintf (stdout, " "); + rtems_monitor_dump_name (rtems_capture_task_name (rec->task)); + fprintf (stdout, " %3" PRId32 " %3" PRId32 " %s\n", + (rec->events >> RTEMS_CAPTURE_REAL_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_CURR_PRIORITY_EVENT) & 0xff, + rtems_capture_event_text (e)); + } + event >>= 1; + } + } + rec++; + } + + count = total < read ? total : read; + + if (count < total) + total -= count; + else + total = 0; + + rtems_capture_release (count); + } +} + +/* + * rtems_capture_cli_flush + * + * DESCRIPTION: + * + * This function is a monitor command that flushes and primes the capture + * engine. + * + */ + +static void +rtems_capture_cli_flush (int argc, + char** argv, + const rtems_monitor_command_arg_t* command_arg __attribute__((unused)), + bool verbose __attribute__((unused))) +{ + rtems_status_code sc; + bool prime = true; + int arg; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'n') + prime = false; + else + fprintf (stdout, "warning: option -%c ignored\n", argv[arg][1]); + } + } + + sc = rtems_capture_flush (prime); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf (stdout, "error: flush failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf (stdout, "trace buffer flushed and %s.\n", + prime ? "primed" : "not primed"); +} + +static rtems_monitor_command_entry_t rtems_capture_cli_cmds[] = +{ + { + "copen", + "usage: copen [-i] size\n", + 0, + rtems_capture_cli_open, + { 0 }, + 0 + }, + { + "cclose", + "usage: cclose\n", + 0, + rtems_capture_cli_close, + { 0 }, + 0 + }, + { + "cenable", + "usage: cenable\n", + 0, + rtems_capture_cli_enable, + { 0 }, + 0 + }, + { + "cdisable", + "usage: cdisable\n", + 0, + rtems_capture_cli_disable, + { 0 }, + 0 + }, + { + "ctlist", + "usage: ctlist \n", + 0, + rtems_capture_cli_task_list, + { 0 }, + 0 + }, + { + "ctload", + "usage: ctload \n", + 0, + rtems_capture_cli_task_load, + { 0 }, + 0 + }, + { + "cwlist", + "usage: cwlist\n", + 0, + rtems_capture_cli_watch_list, + { 0 }, + 0 + }, + { + "cwadd", + "usage: cwadd [task name] [id]\n", + 0, + rtems_capture_cli_watch_add, + { 0 }, + 0 + }, + { + "cwdel", + "usage: cwdel [task name] [id]\n", + 0, + rtems_capture_cli_watch_del, + { 0 }, + 0 + }, + { + "cwctl", + "usage: cwctl [task name] [id] on/off\n", + 0, + rtems_capture_cli_watch_control, + { 0 }, + 0 + }, + { + "cwglob", + "usage: cwglob on/off\n", + 0, + rtems_capture_cli_watch_global, + { 0 }, + 0 + }, + { + "cwceil", + "usage: cwceil priority\n", + 0, + rtems_capture_cli_watch_ceiling, + { 0 }, + 0 + }, + { + "cwfloor", + "usage: cwfloor priority\n", + 0, + rtems_capture_cli_watch_floor, + { 0 }, + 0 + }, + { + "ctrace", + "usage: ctrace [-c] [-r records]\n", + 0, + rtems_capture_cli_trace_records, + { 0 }, + 0 + }, + { + "ctset", + "usage: ctset -h\n", + 0, + rtems_capture_cli_trigger_set, + { 0 }, + 0 + }, + { + "ctclear", + "usage: ctclear -?\n", + 0, + rtems_capture_cli_trigger_clear, + { 0 }, + 0 + }, + { + "cflush", + "usage: cflush [-n]\n", + 0, + rtems_capture_cli_flush, + { 0 }, + 0 + } +}; + +/* + * rtems_capture_cli_init + * + * DESCRIPTION: + * + * This function initialises the command line interface to the capture + * engine. + * + */ + +rtems_status_code +rtems_capture_cli_init (rtems_capture_timestamp timestamp) +{ + size_t cmd; + + capture_timestamp = timestamp; + + for (cmd = 0; + cmd < sizeof (rtems_capture_cli_cmds) / sizeof (rtems_monitor_command_entry_t); + cmd++) + rtems_monitor_insert_cmd (&rtems_capture_cli_cmds[cmd]); + + return RTEMS_SUCCESSFUL; +} |