From a300920de12ed9c14f2637961285588413cab6fb Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Mon, 30 Mar 2015 22:19:17 +1100 Subject: 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. --- cpukit/libmisc/shell/main_rtrace.c | 698 +++++++++++++++++++++++++++++++++++++ 1 file changed, 698 insertions(+) create mode 100644 cpukit/libmisc/shell/main_rtrace.c (limited to 'cpukit/libmisc/shell/main_rtrace.c') 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * 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] \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 */ +}; -- cgit v1.2.3