Files
rtems/cpukit/libmisc/shell/main_rtrace.c
Joel Sherrill 68baf9eead libmisc/shell/*: Relicense to 2-BSD
Many files in this directory were by persons and organizations
that have previously given relicensing permission. git history
was used to confirm authorship and contributions on files which
had unclear or missing copyrights.

Update #3053.
2025-03-01 20:21:56 +00:00

725 lines
17 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
* @brief rtrace Shell Command Implementation
*/
/*
* Copyright (c) 2015 Chris Johns <chrisj@rtems.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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' : '-',
(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");
return 1;
}
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 */
};