summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/shell
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2015-03-30 22:19:17 +1100
committerChris Johns <chrisj@rtems.org>2015-03-31 17:04:43 +1100
commita300920de12ed9c14f2637961285588413cab6fb (patch)
treee7a5218ccc0e916dac2769767c8c159a2becd878 /cpukit/libmisc/shell
parentTMS570: Add board reset code to bsp_reset (diff)
downloadrtems-a300920de12ed9c14f2637961285588413cab6fb.tar.bz2
libmisc/shell: Add the rtrace command for buffered tracing support.
The rtrace command interfaces to the RTEMS Trace Linker's trace buffering data allowing users to capture and report trace data.
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