forked from Imagelibrary/rtems
CID 1399709: Dereference after null check in rtems_trace_buffering_shell_save(). Closes #4329
701 lines
16 KiB
C
701 lines
16 KiB
C
/*
|
|
* 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 <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 */
|
|
};
|