/* ------------------------------------------------------------------------ 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 #include #include #include #include #include #include #include #include # #define RC_UNUSED __attribute__((unused)) #define RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS (20) /* * Counter used to count the number of active tasks. */ static int rtems_capture_cli_task_count = 0; /* * Array of tasks sorted by load. */ static rtems_tcb* rtems_capture_cli_load_tasks[RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS + 1]; /* * The load for each tcb at the moment rtems_capture_cli_load_tasks was generated. */ static unsigned long long rtems_capture_cli_load[RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS + 1]; /* * 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 RC_UNUSED, bool verbose RC_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 RC_UNUSED, char** argv RC_UNUSED, const rtems_monitor_command_arg_t* command_arg RC_UNUSED, bool verbose RC_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 RC_UNUSED, char** argv RC_UNUSED, const rtems_monitor_command_arg_t* command_arg RC_UNUSED, bool verbose RC_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 RC_UNUSED, char** argv RC_UNUSED, const rtems_monitor_command_arg_t* command_arg RC_UNUSED, bool verbose RC_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_catpure_cli_print_uptime * * DESCRIPTION: * * This function prints the nanosecond uptime to stdout. */ static void rtems_capture_cli_print_timestamp (uint64_t uptime) { uint32_t hours; uint32_t minutes; uint32_t seconds; uint32_t nanosecs; seconds = uptime / 1000000000LLU; minutes = seconds / 60; hours = minutes / 60; minutes = minutes % 60; seconds = seconds % 60; nanosecs = uptime % 1000000000; fprintf (stdout, "%5lu:%02lu:%02lu.%09lu", hours, minutes, seconds, nanosecs); } static void rtems_capture_cli_print_task (rtems_tcb *tcb) { rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); rtems_task_priority floor = rtems_capture_watch_get_floor (); rtems_task_priority priority; int length; priority = rtems_capture_task_real_priority (tcb); fprintf (stdout, " "); rtems_monitor_dump_id (rtems_capture_task_id (tcb)); fprintf (stdout, " "); rtems_monitor_dump_name (rtems_capture_task_id (tcb)); fprintf (stdout, " "); rtems_monitor_dump_priority (rtems_capture_task_start_priority (tcb)); fprintf (stdout, " "); rtems_monitor_dump_priority (rtems_capture_task_real_priority (tcb)); fprintf (stdout, " "); rtems_monitor_dump_priority (rtems_capture_task_curr_priority (tcb)); fprintf (stdout, " "); length = rtems_monitor_dump_state (rtems_capture_task_state (tcb)); fprintf (stdout, "%*c", 14 - length, ' '); fprintf (stdout, " %c%c", 'a', rtems_capture_task_flags (tcb) & RTEMS_CAPTURE_TRACED ? 't' : '-'); if ((floor > ceiling) && (ceiling > priority)) fprintf (stdout, "--"); else { uint32_t flags = rtems_capture_task_control_flags (tcb); fprintf (stdout, "%c%c", rtems_capture_task_control (tcb) ? (flags & RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', rtems_capture_watch_global_on () ? 'g' : '-'); } fprintf (stdout, "\n"); } static void rtems_caputure_cli_print_record_std(rtems_capture_record_t* rec, uint64_t diff) { uint32_t event; int e; event = rec->events >> RTEMS_CAPTURE_EVENT_START; for (e = RTEMS_CAPTURE_EVENT_START; e < RTEMS_CAPTURE_EVENT_END; e++) { if (event & 1) { rtems_capture_cli_print_timestamp (rec->time); fprintf (stdout, " %9" PRId64 " ", diff); rtems_monitor_dump_id (rec->task_id); 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; } } static void rtems_caputre_cli_print_record_task(rtems_capture_record_t* rec) { rtems_capture_task_record_t* task_rec = (rtems_capture_task_record_t*) rec; rtems_capture_cli_print_timestamp (rec->time); fprintf (stdout, " "); rtems_monitor_dump_id (rec->task_id); fprintf (stdout, " %c%c%c%c", (char) (task_rec->name >> 24) & 0xff, (char) (task_rec->name >> 16) & 0xff, (char) (task_rec->name >> 8) & 0xff, (char) (task_rec->name >> 0) & 0xff); fprintf (stdout, " %3" PRId32 " %3" PRId32 "\n", task_rec->start_priority, task_rec->stack_size); } /* * rtems_capture_cli_count_tasks * * DESCRIPTION: * * This function is called for each tcb and counts the * number of tasks. * */ static void rtems_capture_cli_count_tasks (rtems_tcb *tcb) { rtems_capture_cli_task_count++; } /* * 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 RC_UNUSED, char** argv RC_UNUSED, const rtems_monitor_command_arg_t* command_arg RC_UNUSED, bool verbose RC_UNUSED) { rtems_capture_time_t uptime; rtems_capture_time (&uptime); rtems_capture_cli_task_count = 0; rtems_iterate_over_all_threads (rtems_capture_cli_count_tasks); fprintf (stdout, "uptime: "); rtems_capture_cli_print_timestamp (uptime); fprintf (stdout, "\ntotal %i\n", rtems_capture_cli_task_count); rtems_iterate_over_all_threads (rtems_capture_cli_print_task); } static void rtems_capture_cli_task_sort (rtems_tcb* tcb) { int i; int j; if (tcb) { rtems_capture_time_t l = tcb->cpu_time_used; rtems_capture_cli_task_count++; for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) { if (rtems_capture_cli_load_tasks[i]) { if ((l == 0) || (l < rtems_capture_cli_load[i])) continue; for (j = (RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS - 1); j >= i; j--) { rtems_capture_cli_load_tasks[j + 1] = rtems_capture_cli_load_tasks[j]; rtems_capture_cli_load[j + 1] = rtems_capture_cli_load[j]; } } rtems_capture_cli_load_tasks[i] = tcb; rtems_capture_cli_load[i] = l; break; } } } /* * 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) { rtems_tcb* tcb; rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); rtems_task_priority floor = rtems_capture_watch_get_floor (); int last_count = 0; FILE* pstdout = (FILE*) arg; int i; int j; fileno(stdout); stdout = pstdout; while (true) { Timestamp_Control uptime, total, ran, uptime_at_last_reset; uint32_t seconds, nanoseconds; size_t size; 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. */ size = sizeof (rtems_capture_cli_load_tasks); memset (rtems_capture_cli_load_tasks, 0, size); memset (rtems_capture_cli_load, 0, sizeof (rtems_capture_cli_load)); _Timestamp_Set_to_zero( &total ); uptime_at_last_reset = CPU_usage_Uptime_at_last_reset; _TOD_Get_uptime( &uptime ); seconds = _Timestamp_Get_seconds( &uptime ); nanoseconds = _Timestamp_Get_nanoseconds( &uptime ) / TOD_NANOSECONDS_PER_MICROSECOND; rtems_iterate_over_all_threads (rtems_capture_cli_task_sort); fprintf (stdout, "\x1b[H\x1b[J Press ENTER to exit.\n\n"); fprintf (stdout, "uptime: "); fprintf (stdout, "%7" PRIu32 ".%06" PRIu32 "\n", seconds, nanoseconds); fprintf (stdout, "\n\n" " PID NAME RPRI CPRI STATE EXEC TIME %%CPU\n"); if (rtems_capture_cli_task_count > last_count) j = rtems_capture_cli_task_count; else j = last_count; for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) { rtems_task_priority priority; Timestamp_Control last; uint32_t ival, fval; if (!rtems_capture_cli_load_tasks[i]) break; j--; /* * If this is the currently executing thread, account for time * since the last context switch. */ tcb = rtems_capture_cli_load_tasks[i]; ran = rtems_capture_cli_load[i]; if ( _Thread_Get_time_of_last_context_switch( tcb, &last ) ) { Timestamp_Control used; _TOD_Get_uptime( &uptime ); _Timestamp_Subtract( &last, &uptime, &used ); _Timestamp_Add_to( &ran, &used ); } else { _TOD_Get_uptime( &uptime ); } _Timestamp_Subtract( &uptime_at_last_reset, &uptime, &total ); _Timestamp_Divide( &ran, &total, &ival, &fval ); seconds = _Timestamp_Get_seconds( &ran ); nanoseconds = _Timestamp_Get_nanoseconds( &ran ) / TOD_NANOSECONDS_PER_MICROSECOND; priority = rtems_capture_task_real_priority (tcb); fprintf (stdout, "\x1b[K"); rtems_monitor_dump_id (rtems_capture_task_id (tcb)); fprintf (stdout, " "); rtems_monitor_dump_name (rtems_capture_task_id (tcb)); fprintf (stdout, " "); rtems_monitor_dump_priority (priority); fprintf (stdout, " "); rtems_monitor_dump_priority (rtems_capture_task_curr_priority (tcb)); fprintf (stdout, " "); rtems_monitor_dump_state (rtems_capture_task_state (tcb)); fprintf (stdout, "%7" PRIu32 ".%06" PRIu32 " |%4" PRIu32 ".%03" PRIu32 , seconds, nanoseconds, ival, fval ); fprintf (stdout, " %c%c", 'a', rtems_capture_task_flags (tcb) & RTEMS_CAPTURE_TRACED ? 't' : '-'); if ((floor > ceiling) && (ceiling > priority)) fprintf (stdout, "--"); else fprintf (stdout, "%c%c", rtems_capture_task_control (tcb) ? (rtems_capture_task_control_flags (tcb) & RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', rtems_capture_watch_global_on () ? 'g' : '-'); fprintf (stdout, " "); fprintf (stdout, "\n"); } if (rtems_capture_cli_task_count < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS) { j = RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS - rtems_capture_cli_task_count; while (j > 0) { fprintf (stdout, "\x1b[K\n"); j--; } } last_count = rtems_capture_cli_task_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 RC_UNUSED, char** argv RC_UNUSED, const rtems_monitor_command_arg_t* command_arg RC_UNUSED, bool verbose RC_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, (intptr_t) stdout); 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 RC_UNUSED, char** argv RC_UNUSED, const rtems_monitor_command_arg_t* command_arg RC_UNUSED, bool verbose RC_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 RC_UNUSED, bool verbose RC_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 RC_UNUSED, bool verbose RC_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 RC_UNUSED, bool verbose RC_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 RC_UNUSED, bool verbose RC_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 RC_UNUSED, bool verbose RC_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 RC_UNUSED, bool verbose RC_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; bool is_to = 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 (from_valid_name || from_valid_id) fprintf (stdout, "warning: extra 'from' ignored\n"); is_from = true; continue; } if (strcmp (arg[argv], "to") == 0) { if (to_valid_name || from_valid_id) fprintf (stdout, "warning: extra 'to' ignored\n"); is_to = true; continue; } if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, &name, &id)) return; if (valid_name) { if (!is_from && !is_to) is_to = true; if (is_from) { if (!from_valid_name && !from_valid_id) { from_valid_name = true; from_name = name; } else fprintf (stdout, "warning: extra name arguments ignored\n"); } else if (!to_valid_name && !to_valid_id) { to_valid_name = true; to_name = name; } else fprintf (stdout, "warning: extra name arguments ignored\n"); } if (valid_id) { if (!is_from && !is_to) is_to = true; if (is_from) { if (!from_valid_name && !from_valid_id) { from_valid_id = true; from_id = id; } else fprintf (stdout, "warning: extra id arguments ignored\n"); } else if (!to_valid_name && !to_valid_id) { to_valid_id = true; to_id = id; } else fprintf (stdout, "warning: extra id 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, set ? "ctset" : "ctclear"); 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 RC_UNUSED, bool verbose RC_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 RC_UNUSED, bool verbose RC_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 RC_UNUSED, bool verbose RC_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; uint8_t* ptr; int arg; rtems_capture_time_t last_t = 0; 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; ptr = (uint8_t *) rec; while (count--) { rec = (rtems_capture_record_t*) ptr; if (csv) fprintf (stdout, "%08" PRIu32 ",%03" PRIu32 ",%03" PRIu32 ",%04" PRIx32 ",%" PRId64 "\n", rec->task_id, (rec->events >> RTEMS_CAPTURE_REAL_PRIORITY_EVENT) & 0xff, (rec->events >> RTEMS_CAPTURE_CURR_PRIORITY_EVENT) & 0xff, (rec->events >> RTEMS_CAPTURE_EVENT_START), (uint64_t) rec->time); else { if ((rec->events >> RTEMS_CAPTURE_EVENT_START) == 0) rtems_caputre_cli_print_record_task( rec ); else { uint64_t diff = 0; if (last_t) diff = rec->time - last_t; last_t = rec->time; rtems_caputure_cli_print_record_std( rec, diff ); } } ptr += rec->size; } 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 RC_UNUSED, bool verbose RC_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; }