/* * 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 #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' : '-', (int) 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 */ };