summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/shell
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libmisc/shell')
-rw-r--r--cpukit/libmisc/shell/main_rtrace.c698
-rw-r--r--cpukit/libmisc/shell/shellconfig.h10
2 files changed, 708 insertions, 0 deletions
diff --git a/cpukit/libmisc/shell/main_rtrace.c b/cpukit/libmisc/shell/main_rtrace.c
new file mode 100644
index 0000000000..b4bb1337ae
--- /dev/null
+++ b/cpukit/libmisc/shell/main_rtrace.c
@@ -0,0 +1,698 @@
+/*
+ * Copyright (c) 2015 Chris Johns <chrisj@rtems.org>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <rtems/shell.h>
+#include <rtems/trace/rtems-trace-buffer-vars.h>
+
+/**
+ * The type of the shell handlers we have.
+ */
+typedef int (*rtems_trace_buffering_shell_handler_t) (int argc, char *argv[]);
+
+/**
+ * Table of handlers we parse to invoke the command.
+ */
+typedef struct
+{
+ const char* name; /**< The sub-command's name. */
+ rtems_trace_buffering_shell_handler_t handler; /**< The sub-command's handler. */
+ const char* help; /**< The sub-command's help. */
+} rtems_trace_buffering_shell_cmd_t;
+
+static int
+rtems_trace_buffering_wrong_number_of_args (void)
+{
+ printf ("error: wrong number of arguments\n");
+ return 1;
+}
+
+static int
+rtems_trace_buffering_no_trace_buffer_code (void)
+{
+ printf("No trace buffer generated code in the application; see rtems-tld\n");
+ return 1;
+}
+
+static void
+rtems_trace_buffering_banner (const char* label)
+{
+ printf("RTEMS Trace Bufferring: %s\n", label);
+}
+
+static void
+rtems_trace_buffering_print_timestamp (uint64_t uptime)
+{
+ uint32_t hours;
+ uint32_t minutes;
+ uint32_t seconds;
+ uint32_t nanosecs;
+ uint64_t up_secs;
+
+ up_secs = uptime / 1000000000LLU;
+ minutes = up_secs / 60;
+ hours = minutes / 60;
+ minutes = minutes % 60;
+ seconds = up_secs % 60;
+ nanosecs = uptime % 1000000000;
+
+ printf ("%5" PRIu32 ":%02" PRIu32 ":%02" PRIu32".%09" PRIu32,
+ hours, minutes, seconds, nanosecs);
+}
+
+static int
+rtems_trace_buffering_shell_status (int argc, char *argv[])
+{
+ uint32_t buffer_size;
+ uint32_t buffer_in;
+ bool finished;
+ bool triggered;
+ uint32_t names;
+
+ if (argc != 1)
+ return rtems_trace_buffering_wrong_number_of_args ();
+
+ if (!rtems_trace_buffering_present ())
+ return rtems_trace_buffering_no_trace_buffer_code ();
+
+ buffer_size = rtems_trace_buffering_buffer_size ();
+ buffer_in = rtems_trace_buffering_buffer_in ();
+ finished = rtems_trace_buffering_finished ();
+ triggered = rtems_trace_buffering_triggered ();
+ names = rtems_trace_names_size ();
+
+ rtems_trace_buffering_banner ("status");
+ printf(" Running: %s\n", finished ? "no" : "yes");
+ printf(" Triggered: %s\n", triggered ? "yes" : "no");
+ printf(" Level: %3" PRIu32 "%%\n", (buffer_in * 100) / buffer_size);
+ printf(" Traces: %4" PRIu32 "\n", names);
+
+ return 0;
+}
+
+static int
+rtems_trace_buffering_shell_funcs (int argc, char *argv[])
+{
+ size_t traces = rtems_trace_names_size ();
+ size_t t;
+ size_t max = 0;
+
+ if (argc != 1)
+ return rtems_trace_buffering_wrong_number_of_args ();
+
+ if (!rtems_trace_buffering_present ())
+ return rtems_trace_buffering_no_trace_buffer_code ();
+
+ rtems_trace_buffering_banner ("trace functions");
+ printf(" Total: %4zu\n", traces);
+
+ for (t = 0; t < traces; ++t)
+ {
+ size_t l = strlen (rtems_trace_names (t));
+ if (l > max)
+ max = l;
+ }
+
+ for (t = 0; t < traces; ++t)
+ {
+ printf(" %4zu: %c%c %-*s\n", t,
+ rtems_trace_enable_set(t) ? 'E' : '-',
+ rtems_trace_trigger_set(t) ? 'T' : '-',
+ max, rtems_trace_names (t));
+ }
+
+ return 0;
+}
+
+static int
+rtems_trace_buffering_shell_start (int argc, char *argv[])
+{
+ if (argc != 1)
+ return rtems_trace_buffering_wrong_number_of_args ();
+
+ if (!rtems_trace_buffering_present ())
+ return rtems_trace_buffering_no_trace_buffer_code ();
+
+ rtems_trace_buffering_banner ("resume");
+
+ if (!rtems_trace_buffering_finished ())
+ {
+ printf("already running\n");
+ return 0;
+ }
+
+ rtems_trace_buffering_start ();
+
+ return 0;
+}
+
+static int
+rtems_trace_buffering_shell_stop (int argc, char *argv[])
+{
+ if (argc != 1)
+ return rtems_trace_buffering_wrong_number_of_args ();
+
+ if (!rtems_trace_buffering_present ())
+ return rtems_trace_buffering_no_trace_buffer_code ();
+
+ rtems_trace_buffering_banner ("stop");
+
+ if (rtems_trace_buffering_finished ())
+ {
+ printf("already stopped\n");
+ return 0;
+ }
+
+ rtems_trace_buffering_stop ();
+
+ return 0;
+}
+
+static int
+rtems_trace_buffering_shell_resume (int argc, char *argv[])
+{
+ if (argc != 1)
+ return rtems_trace_buffering_wrong_number_of_args ();
+
+ if (!rtems_trace_buffering_present ())
+ return rtems_trace_buffering_no_trace_buffer_code ();
+
+ rtems_trace_buffering_banner ("resume");
+
+ if (!rtems_trace_buffering_finished ())
+ {
+ printf("already running\n");
+ return 0;
+ }
+
+ rtems_trace_buffering_start ();
+
+ return 0;
+}
+
+static void rtems_trace_buffering_print_arg (const rtems_trace_sig_arg* arg,
+ const uint8_t* argv)
+{
+ if (arg->size)
+ {
+ union
+ {
+ uint8_t bytes[sizeof (uint64_t)];
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ void* pointer;
+ } variable;
+
+ if (arg->size <= sizeof(uint64_t))
+ memcpy (&variable.bytes[0], argv, arg->size);
+
+ printf ("(%s) ", arg->type);
+
+ if (strchr (arg->type, '*') != NULL)
+ {
+ printf ("%p", variable.pointer);
+ }
+ else
+ {
+ size_t b;
+ switch (arg->size)
+ {
+ case 2:
+ printf ("%04" PRIx16, variable.u16);
+ break;
+ case 4:
+ printf ("%08" PRIx32, variable.u32);
+ break;
+ case 8:
+ printf ("%016" PRIx64, variable.u64);
+ break;
+ default:
+ for (b = 0; b < arg->size; ++b)
+ printf ("%02" PRIx32, (uint32_t) *argv++);
+ break;
+ }
+ }
+ }
+}
+
+static int
+rtems_trace_buffering_shell_trace (int argc, char *argv[])
+{
+ uint32_t* trace_buffer;
+ uint32_t records;
+ uint32_t traces;
+ uint32_t r;
+ size_t start = 0;
+ size_t end = 40;
+ size_t count;
+ uint64_t last_sample = 0;
+
+ if (!rtems_trace_buffering_present ())
+ return rtems_trace_buffering_no_trace_buffer_code ();
+
+ trace_buffer = rtems_trace_buffering_buffer ();
+ records = rtems_trace_buffering_buffer_in ();
+ traces = rtems_trace_names_size ();
+
+ if (argc > 1)
+ {
+ if (argc > 3)
+ return rtems_trace_buffering_wrong_number_of_args ();
+
+ if (argv[1][0] == '+')
+ {
+ if (argc > 2)
+ return rtems_trace_buffering_wrong_number_of_args ();
+ end = strtoul (argv[1] + 1, 0, 0);
+ if (end == 0)
+ {
+ printf("error: invalid number of lines\n");
+ return 1;
+ }
+ ++end;
+ }
+ else
+ {
+ start = strtoul (argv[1], 0, 0);
+ if (start >= records)
+ {
+ printf ("error: start record out of range (max %" PRIu32 ")\n", records);
+ return 1;
+ }
+ }
+
+ end += start;
+
+ if (argc == 3)
+ {
+ if (argv[2][0] == '+')
+ {
+ end = strtoul (argv[2] + 1, 0, 0);
+ if (end == 0)
+ {
+ printf("error: invalid number of lines\n");
+ return 1;
+ }
+ end += start + 1;
+ }
+ else
+ {
+ end = strtoul (argv[2], 0, 0);
+ if (end < start)
+ {
+ printf ("error: end record before start\n");
+ return 1;
+ }
+ else if (end > records)
+ {
+ printf ("error: end record out of range (max %" PRIu32 ")\n", records);
+ }
+ }
+ }
+ }
+
+ rtems_trace_buffering_banner ("trace");
+
+ if (!rtems_trace_buffering_finished ())
+ {
+ printf("tracing still running\n");
+ return 0;
+ }
+
+ printf(" Trace buffer: %p\n", trace_buffer);
+ printf(" Words traced: %" PRIu32 "\n", records);
+ printf(" Traces: %" PRIu32 "\n", traces);
+
+ count = 0;
+ r = 0;
+
+ while ((r < records) && (count < end))
+ {
+ const uint32_t header = trace_buffer[r];
+ const uint32_t func_index = header & 0xffff;
+ const uint32_t len = (header >> 16) & 0x0fff;
+ const uint32_t task_id = trace_buffer[r + 1];
+ const uint32_t task_status = trace_buffer[r + 2];
+ const uint64_t when = (((uint64_t) trace_buffer[r + 4]) << 32) | trace_buffer[r + 5];
+ const uint8_t* argv = (uint8_t*) &trace_buffer[r + 6];
+ const bool ret = (header & (1 << 30)) == 0 ? false : true;
+ const bool irq = (header & (1 << 31)) == 0 ? false : true;
+
+ if (count > start)
+ {
+ const rtems_trace_sig* sig = rtems_trace_signatures (func_index);
+
+ rtems_trace_buffering_print_timestamp (when);
+ printf (" %10" PRIu32 " %c%08" PRIx32 " [%3" PRIu32 "/%3" PRIu32 "] %c %s",
+ (uint32_t) (when - last_sample),
+ irq ? '*' : ' ', task_id, (task_status >> 8) & 0xff, task_status & 0xff,
+ ret ? '<' : '>', rtems_trace_names (func_index));
+
+ if (sig->argc)
+ {
+ if (ret)
+ {
+ if (sig->args[0].size)
+ printf(" => ");
+ rtems_trace_buffering_print_arg (&sig->args[0], argv);
+ }
+ else
+ {
+ size_t a;
+ printf("(");
+ for (a = 1; a < sig->argc; ++a)
+ {
+ if (a > 1)
+ printf (", ");
+ rtems_trace_buffering_print_arg (&sig->args[a], argv);
+ argv += sig->args[a].size;
+ }
+ printf(")");
+ }
+ }
+
+ printf("\n");
+ }
+
+ r += ((len - 1) / sizeof (uint32_t)) + 1;
+ last_sample = when;
+ ++count;
+ }
+
+ return 0;
+}
+
+static ssize_t
+rtems_trace_buffering_file_write (int out, const void* vbuffer, ssize_t length)
+{
+ const uint8_t* buffer = vbuffer;
+ while (length)
+ {
+ ssize_t w = write (out, buffer, length);
+ if (w < 0)
+ {
+ printf ("error: write failed: %s\n", strerror(errno));
+ return false;
+ }
+ if (w == 0)
+ {
+ printf ("error: write failed: EOF\n");
+ return false;
+ }
+
+ length -= w;
+ buffer += w;
+ }
+ return true;
+}
+
+static int
+rtems_trace_buffering_shell_save (int argc, char *argv[])
+{
+ uint32_t* trace_buffer;
+ uint32_t records;
+ uint32_t traces;
+ uint8_t* buffer;
+ size_t length;
+ uint32_t r;
+ int out;
+ uint8_t* buf;
+ uint8_t* in;
+
+ if (argc != 2)
+ return rtems_trace_buffering_wrong_number_of_args ();
+
+ if (!rtems_trace_buffering_present ())
+ return rtems_trace_buffering_no_trace_buffer_code ();
+
+ rtems_trace_buffering_banner ("trace");
+
+ if (!rtems_trace_buffering_finished ())
+ {
+ printf("tracing still running\n");
+ return 0;
+ }
+
+ trace_buffer = rtems_trace_buffering_buffer ();
+ records = rtems_trace_buffering_buffer_in ();
+ traces = rtems_trace_names_size ();
+
+ printf(" Trace File: %s\n", argv[1]);
+ printf(" Trace buffer: %p\n", trace_buffer);
+ printf(" Words traced: %" PRIu32 "\n", records);
+ printf(" Traces: %" PRIu32 "\n", traces);
+
+ out = open (argv[1], O_WRONLY | O_TRUNC | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (out < 0)
+ {
+ printf ("error: opening file: %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+
+ #define SAVE_BUF_SIZE (1024)
+
+ buf = malloc(SAVE_BUF_SIZE);
+ if (!buf)
+ {
+ close (out);
+ printf ("error: no memory\n");
+ }
+
+ memset (buf, 0, SAVE_BUF_SIZE);
+
+ in = buf;
+
+ /*
+ * Header label.
+ */
+ memcpy (in, "RTEMS-TRACE", sizeof("RTEMS-TRACE"));
+ in += 12;
+
+ /*
+ * Endian detection.
+ */
+ *((uint32_t*) in) = 0x11223344;
+ in += sizeof(uint32_t);
+
+ /*
+ * Number of traces.
+ */
+ *((uint32_t*) in) = traces;
+ in += sizeof(uint32_t);
+
+ /*
+ * Write it.
+ */
+ if (!rtems_trace_buffering_file_write (out, buf, in - buf))
+ {
+ free (buf);
+ close (out);
+ return 1;
+ }
+
+ /*
+ * The trace names.
+ */
+ for (r = 0; r < traces; ++r)
+ {
+ const char* name = rtems_trace_names (r);
+ if (!rtems_trace_buffering_file_write (out, name, strlen (name) + 1))
+ {
+ free (buf);
+ close (out);
+ return 1;
+ }
+ }
+
+ /*
+ * The trace signatures.
+ */
+ for (r = 0; r < traces; ++r)
+ {
+ const rtems_trace_sig* sig = rtems_trace_signatures (r);
+ size_t s;
+
+ in = buf;
+
+ memcpy (in, &sig->argc, sizeof (sig->argc));
+ in += sizeof(uint32_t);
+
+ for (s = 0; s < sig->argc; ++s)
+ {
+ const rtems_trace_sig_arg* arg = &sig->args[s];
+ size_t arg_len = strlen (arg->type) + 1;
+
+ if ((in - buf) > SAVE_BUF_SIZE)
+ {
+ printf ("error: save temp buffer to small\n");
+ free (buf);
+ close (out);
+ return 1;
+ }
+
+ memcpy (in, &arg->size, sizeof (arg->size));
+ in += sizeof(uint32_t);
+ memcpy (in, arg->type, arg_len);
+ in += arg_len;
+ }
+
+ if (!rtems_trace_buffering_file_write (out, buf, in - buf))
+ {
+ free (buf);
+ close (out);
+ return 1;
+ }
+ }
+
+ free (buf);
+
+ buffer = (uint8_t*) trace_buffer;
+ length = records * sizeof (uint32_t);
+
+ while (length)
+ {
+ ssize_t w = write (out, buffer, length);
+ if (w < 0)
+ {
+ printf ("error: write failed: %s\n", strerror(errno));
+ close (out);
+ return 1;
+ }
+ if (w == 0)
+ {
+ printf ("error: write failed: EOF\n");
+ close (out);
+ return 1;
+ }
+
+ length -= w;
+ buffer += w;
+ }
+
+ close (out);
+
+ return 0;
+}
+
+static void
+rtems_trace_buffering_shell_usage (const char* arg)
+{
+ printf ("%s: Trace Buffer Help\n", arg);
+ printf (" %s [-hl] <command>\n", arg);
+ printf (" where:\n");
+ printf (" command: The TBG subcommand. See -l for a list plus help.\n");
+ printf (" -h: This help\n");
+ printf (" -l: The command list.\n");
+}
+
+static const rtems_trace_buffering_shell_cmd_t table[] =
+{
+ {
+ "status",
+ rtems_trace_buffering_shell_status,
+ " : Show the current status"
+ },
+ {
+ "funcs",
+ rtems_trace_buffering_shell_funcs,
+ " : List the trace functions"
+ },
+ {
+ "start",
+ rtems_trace_buffering_shell_start,
+ " : Start or restart tracing"
+ },
+ {
+ "stop",
+ rtems_trace_buffering_shell_stop,
+ " : Stop tracing"
+ },
+ {
+ "resume",
+ rtems_trace_buffering_shell_resume,
+ " : Resume tracing."
+ },
+ {
+ "trace",
+ rtems_trace_buffering_shell_trace,
+ " [start] [end/+length] : List the current trace records"
+ },
+ {
+ "save",
+ rtems_trace_buffering_shell_save,
+ " file : Save the trace buffer to a file"
+ },
+};
+
+#define RTEMS_TRACE_BUFFERING_COMMANDS \
+ (sizeof (table) / sizeof (const rtems_trace_buffering_shell_cmd_t))
+
+static int
+rtems_shell_main_rtrace (int argc, char* argv[])
+{
+ int arg;
+ size_t t;
+
+ for (arg = 1; arg < argc; arg++)
+ {
+ if (argv[arg][0] != '-')
+ break;
+
+ switch (argv[arg][1])
+ {
+ case 'h':
+ rtems_trace_buffering_shell_usage (argv[0]);
+ return 0;
+ case 'l':
+ printf ("%s: commands are:\n", argv[0]);
+ for (t = 0; t < RTEMS_TRACE_BUFFERING_COMMANDS; ++t)
+ printf (" %-7s %s\n", table[t].name, table[t].help);
+ return 0;
+ default:
+ printf ("error: unknown option: %s\n", argv[arg]);
+ return 1;
+ }
+ }
+
+ if ((argc - arg) < 1)
+ printf ("error: you need to provide a command, try %s -h\n", argv[0]);
+ else
+ {
+ for (t = 0; t < RTEMS_TRACE_BUFFERING_COMMANDS; ++t)
+ {
+ if (strncmp (argv[arg], table[t].name, strlen (argv[arg])) == 0)
+ {
+ int r = table[t].handler (argc - arg, argv + 1);
+ return r;
+ }
+ }
+ printf ("error: command not found: %s (try -h)\n", argv[arg]);
+ }
+
+ return 1;
+}
+
+rtems_shell_cmd_t rtems_shell_RTRACE_Command = {
+ "rtrace", /* name */
+ "rtrace [-l]", /* usage */
+ "misc", /* topic */
+ rtems_shell_main_rtrace, /* command */
+ NULL, /* alias */
+ NULL /* next */
+};
diff --git a/cpukit/libmisc/shell/shellconfig.h b/cpukit/libmisc/shell/shellconfig.h
index 2e52a92c2e..9023a60274 100644
--- a/cpukit/libmisc/shell/shellconfig.h
+++ b/cpukit/libmisc/shell/shellconfig.h
@@ -87,6 +87,7 @@ extern rtems_shell_cmd_t rtems_shell_PERIODUSE_Command;
extern rtems_shell_cmd_t rtems_shell_PROFREPORT_Command;
extern rtems_shell_cmd_t rtems_shell_WKSPACE_INFO_Command;
extern rtems_shell_cmd_t rtems_shell_MALLOC_INFO_Command;
+extern rtems_shell_cmd_t rtems_shell_RTRACE_Command;
#if RTEMS_NETWORKING
extern rtems_shell_cmd_t rtems_shell_IFCONFIG_Command;
extern rtems_shell_cmd_t rtems_shell_ROUTE_Command;
@@ -464,6 +465,15 @@ extern rtems_shell_alias_t * const rtems_shell_Initial_aliases[];
#endif
/*
+ * Tracing family commands
+ */
+ #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) && \
+ !defined(CONFIGURE_SHELL_NO_COMMAND_RTRACE)) || \
+ defined(CONFIGURE_SHELL_COMMAND_RTRACE)
+ &rtems_shell_RTRACE_Command,
+ #endif
+
+ /*
* Network related commands
*/
#if RTEMS_NETWORKING