source: rtems/cpukit/libmisc/shell/main_rtrace.c @ a300920d

4.115
Last change on this file since a300920d was a300920d, checked in by Chris Johns <chrisj@…>, on 03/30/15 at 11:19:17

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.

  • Property mode set to 100644
File size: 15.6 KB
Line 
1/*
2 *  Copyright (c) 2015 Chris Johns <chrisj@rtems.org>
3 *
4 *  The license and distribution terms for this file may be
5 *  found in the file LICENSE in this distribution or at
6 *  http://www.rtems.org/license/LICENSE.
7 */
8
9#include <ctype.h>
10#include <errno.h>
11#include <fcntl.h>
12#include <inttypes.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <sys/types.h>
16#include <time.h>
17#include <unistd.h>
18
19#include <rtems/shell.h>
20#include <rtems/trace/rtems-trace-buffer-vars.h>
21
22/**
23 * The type of the shell handlers we have.
24 */
25typedef int (*rtems_trace_buffering_shell_handler_t) (int argc, char *argv[]);
26
27/**
28 * Table of handlers we parse to invoke the command.
29 */
30typedef struct
31{
32  const char*                           name;    /**< The sub-command's name. */
33  rtems_trace_buffering_shell_handler_t handler; /**< The sub-command's handler. */
34  const char*                           help;    /**< The sub-command's help. */
35} rtems_trace_buffering_shell_cmd_t;
36
37static int
38rtems_trace_buffering_wrong_number_of_args (void)
39{
40  printf ("error: wrong number of arguments\n");
41  return 1;
42}
43
44static int
45rtems_trace_buffering_no_trace_buffer_code (void)
46{
47  printf("No trace buffer generated code in the application; see rtems-tld\n");
48  return 1;
49}
50
51static void
52rtems_trace_buffering_banner (const char* label)
53{
54  printf("RTEMS Trace Bufferring: %s\n", label);
55}
56
57static void
58rtems_trace_buffering_print_timestamp (uint64_t uptime)
59{
60  uint32_t hours;
61  uint32_t minutes;
62  uint32_t seconds;
63  uint32_t nanosecs;
64  uint64_t up_secs;
65
66  up_secs  = uptime / 1000000000LLU;
67  minutes  = up_secs / 60;
68  hours    = minutes / 60;
69  minutes  = minutes % 60;
70  seconds  = up_secs % 60;
71  nanosecs = uptime % 1000000000;
72
73  printf ("%5" PRIu32 ":%02" PRIu32 ":%02" PRIu32".%09" PRIu32,
74          hours, minutes, seconds, nanosecs);
75}
76
77static int
78rtems_trace_buffering_shell_status (int argc, char *argv[])
79{
80  uint32_t buffer_size;
81  uint32_t buffer_in;
82  bool     finished;
83  bool     triggered;
84  uint32_t names;
85
86  if (argc != 1)
87    return rtems_trace_buffering_wrong_number_of_args ();
88
89  if (!rtems_trace_buffering_present ())
90    return rtems_trace_buffering_no_trace_buffer_code ();
91
92  buffer_size = rtems_trace_buffering_buffer_size ();
93  buffer_in = rtems_trace_buffering_buffer_in ();
94  finished = rtems_trace_buffering_finished ();
95  triggered = rtems_trace_buffering_triggered ();
96  names = rtems_trace_names_size ();
97
98  rtems_trace_buffering_banner ("status");
99  printf("    Running:  %s\n", finished ? "no" : "yes");
100  printf("  Triggered:  %s\n", triggered ? "yes" : "no");
101  printf("      Level: %3" PRIu32 "%%\n", (buffer_in * 100) / buffer_size);
102  printf("     Traces: %4" PRIu32 "\n", names);
103
104  return 0;
105}
106
107static int
108rtems_trace_buffering_shell_funcs (int argc, char *argv[])
109{
110  size_t traces = rtems_trace_names_size ();
111  size_t t;
112  size_t max = 0;
113
114  if (argc != 1)
115    return rtems_trace_buffering_wrong_number_of_args ();
116
117  if (!rtems_trace_buffering_present ())
118    return rtems_trace_buffering_no_trace_buffer_code ();
119
120  rtems_trace_buffering_banner ("trace functions");
121  printf(" Total: %4zu\n", traces);
122
123  for (t = 0; t < traces; ++t)
124  {
125    size_t l = strlen (rtems_trace_names (t));
126    if (l > max)
127      max = l;
128  }
129
130  for (t = 0; t < traces; ++t)
131  {
132    printf(" %4zu: %c%c %-*s\n", t,
133           rtems_trace_enable_set(t) ? 'E' : '-',
134           rtems_trace_trigger_set(t) ? 'T' : '-',
135           max, rtems_trace_names (t));
136  }
137
138  return 0;
139}
140
141static int
142rtems_trace_buffering_shell_start (int argc, char *argv[])
143{
144  if (argc != 1)
145    return rtems_trace_buffering_wrong_number_of_args ();
146
147  if (!rtems_trace_buffering_present ())
148    return rtems_trace_buffering_no_trace_buffer_code ();
149
150  rtems_trace_buffering_banner ("resume");
151
152  if (!rtems_trace_buffering_finished ())
153  {
154    printf("already running\n");
155    return 0;
156  }
157
158  rtems_trace_buffering_start ();
159
160  return 0;
161}
162
163static int
164rtems_trace_buffering_shell_stop (int argc, char *argv[])
165{
166  if (argc != 1)
167    return rtems_trace_buffering_wrong_number_of_args ();
168
169  if (!rtems_trace_buffering_present ())
170    return rtems_trace_buffering_no_trace_buffer_code ();
171
172  rtems_trace_buffering_banner ("stop");
173
174  if (rtems_trace_buffering_finished ())
175  {
176    printf("already stopped\n");
177    return 0;
178  }
179
180  rtems_trace_buffering_stop ();
181
182  return 0;
183}
184
185static int
186rtems_trace_buffering_shell_resume (int argc, char *argv[])
187{
188  if (argc != 1)
189    return rtems_trace_buffering_wrong_number_of_args ();
190
191  if (!rtems_trace_buffering_present ())
192    return rtems_trace_buffering_no_trace_buffer_code ();
193
194  rtems_trace_buffering_banner ("resume");
195
196  if (!rtems_trace_buffering_finished ())
197  {
198    printf("already running\n");
199    return 0;
200  }
201
202  rtems_trace_buffering_start ();
203
204  return 0;
205}
206
207static void rtems_trace_buffering_print_arg (const rtems_trace_sig_arg* arg,
208                                             const uint8_t*             argv)
209{
210  if (arg->size)
211  {
212    union
213    {
214      uint8_t  bytes[sizeof (uint64_t)];
215      uint16_t u16;
216      uint32_t u32;
217      uint64_t u64;
218      void*    pointer;
219    } variable;
220
221    if (arg->size <= sizeof(uint64_t))
222      memcpy (&variable.bytes[0], argv, arg->size);
223
224    printf ("(%s) ", arg->type);
225
226    if (strchr (arg->type, '*') != NULL)
227    {
228      printf ("%p", variable.pointer);
229    }
230    else
231    {
232      size_t b;
233      switch (arg->size)
234      {
235        case 2:
236          printf ("%04" PRIx16, variable.u16);
237          break;
238        case 4:
239          printf ("%08" PRIx32, variable.u32);
240          break;
241        case 8:
242          printf ("%016" PRIx64, variable.u64);
243          break;
244        default:
245          for (b = 0; b < arg->size; ++b)
246            printf ("%02" PRIx32, (uint32_t) *argv++);
247          break;
248      }
249    }
250  }
251}
252
253static int
254rtems_trace_buffering_shell_trace (int argc, char *argv[])
255{
256  uint32_t* trace_buffer;
257  uint32_t  records;
258  uint32_t  traces;
259  uint32_t  r;
260  size_t    start = 0;
261  size_t    end = 40;
262  size_t    count;
263  uint64_t  last_sample = 0;
264
265  if (!rtems_trace_buffering_present ())
266    return rtems_trace_buffering_no_trace_buffer_code ();
267
268  trace_buffer = rtems_trace_buffering_buffer ();
269  records = rtems_trace_buffering_buffer_in ();
270  traces = rtems_trace_names_size ();
271
272  if (argc > 1)
273  {
274    if (argc > 3)
275      return rtems_trace_buffering_wrong_number_of_args ();
276
277    if (argv[1][0] == '+')
278    {
279      if (argc > 2)
280        return rtems_trace_buffering_wrong_number_of_args ();
281      end = strtoul (argv[1] + 1, 0, 0);
282      if (end == 0)
283      {
284        printf("error: invalid number of lines\n");
285        return 1;
286      }
287      ++end;
288    }
289    else
290    {
291      start = strtoul (argv[1], 0, 0);
292      if (start >= records)
293      {
294        printf ("error: start record out of range (max %" PRIu32 ")\n", records);
295        return 1;
296      }
297    }
298
299    end += start;
300
301    if (argc == 3)
302    {
303      if (argv[2][0] == '+')
304      {
305        end = strtoul (argv[2] + 1, 0, 0);
306        if (end == 0)
307        {
308          printf("error: invalid number of lines\n");
309          return 1;
310        }
311        end += start + 1;
312      }
313      else
314      {
315        end = strtoul (argv[2], 0, 0);
316        if (end < start)
317        {
318          printf ("error: end record before start\n");
319          return 1;
320        }
321        else if (end > records)
322        {
323          printf ("error: end record out of range (max %" PRIu32 ")\n", records);
324        }
325      }
326    }
327  }
328
329  rtems_trace_buffering_banner ("trace");
330
331  if (!rtems_trace_buffering_finished ())
332  {
333    printf("tracing still running\n");
334    return 0;
335  }
336
337  printf(" Trace buffer: %p\n", trace_buffer);
338  printf(" Words traced: %" PRIu32 "\n", records);
339  printf("       Traces: %" PRIu32 "\n", traces);
340
341  count = 0;
342  r = 0;
343
344  while ((r < records) && (count < end))
345  {
346    const uint32_t header = trace_buffer[r];
347    const uint32_t func_index = header & 0xffff;
348    const uint32_t len = (header >> 16) & 0x0fff;
349    const uint32_t task_id = trace_buffer[r + 1];
350    const uint32_t task_status = trace_buffer[r + 2];
351    const uint64_t when = (((uint64_t) trace_buffer[r + 4]) << 32) | trace_buffer[r + 5];
352    const uint8_t* argv = (uint8_t*) &trace_buffer[r + 6];
353    const bool     ret = (header & (1 << 30)) == 0 ? false : true;
354    const bool     irq = (header & (1 << 31)) == 0 ? false : true;
355
356    if (count > start)
357    {
358      const rtems_trace_sig* sig = rtems_trace_signatures (func_index);
359
360      rtems_trace_buffering_print_timestamp (when);
361      printf (" %10" PRIu32 " %c%08" PRIx32 " [%3" PRIu32 "/%3" PRIu32 "] %c %s",
362              (uint32_t) (when - last_sample),
363              irq ? '*' : ' ', task_id, (task_status >> 8) & 0xff, task_status & 0xff,
364              ret ? '<' : '>', rtems_trace_names (func_index));
365
366      if (sig->argc)
367      {
368        if (ret)
369        {
370          if (sig->args[0].size)
371            printf(" => ");
372          rtems_trace_buffering_print_arg (&sig->args[0], argv);
373        }
374        else
375        {
376          size_t a;
377          printf("(");
378          for (a = 1; a < sig->argc; ++a)
379          {
380            if (a > 1)
381              printf (", ");
382            rtems_trace_buffering_print_arg (&sig->args[a], argv);
383            argv += sig->args[a].size;
384          }
385          printf(")");
386        }
387      }
388
389      printf("\n");
390    }
391
392    r += ((len - 1) / sizeof (uint32_t)) + 1;
393    last_sample = when;
394    ++count;
395  }
396
397  return 0;
398}
399
400static ssize_t
401rtems_trace_buffering_file_write (int out, const void* vbuffer, ssize_t length)
402{
403  const uint8_t* buffer = vbuffer;
404  while (length)
405  {
406    ssize_t w = write (out, buffer, length);
407    if (w < 0)
408    {
409      printf ("error: write failed: %s\n", strerror(errno));
410      return false;
411    }
412    if (w == 0)
413    {
414      printf ("error: write failed: EOF\n");
415      return false;
416    }
417
418    length -= w;
419    buffer += w;
420  }
421  return true;
422}
423
424static int
425rtems_trace_buffering_shell_save (int argc, char *argv[])
426{
427  uint32_t* trace_buffer;
428  uint32_t  records;
429  uint32_t  traces;
430  uint8_t*  buffer;
431  size_t    length;
432  uint32_t  r;
433  int       out;
434  uint8_t*  buf;
435  uint8_t*  in;
436
437  if (argc != 2)
438    return rtems_trace_buffering_wrong_number_of_args ();
439
440  if (!rtems_trace_buffering_present ())
441    return rtems_trace_buffering_no_trace_buffer_code ();
442
443  rtems_trace_buffering_banner ("trace");
444
445  if (!rtems_trace_buffering_finished ())
446  {
447    printf("tracing still running\n");
448    return 0;
449  }
450
451  trace_buffer = rtems_trace_buffering_buffer ();
452  records = rtems_trace_buffering_buffer_in ();
453  traces = rtems_trace_names_size ();
454
455  printf("   Trace File: %s\n", argv[1]);
456  printf(" Trace buffer: %p\n", trace_buffer);
457  printf(" Words traced: %" PRIu32 "\n", records);
458  printf("       Traces: %" PRIu32 "\n", traces);
459
460  out = open (argv[1], O_WRONLY | O_TRUNC | O_CREAT,
461              S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
462  if (out < 0)
463  {
464    printf ("error: opening file: %s: %s\n", argv[1], strerror(errno));
465    return 1;
466  }
467
468  #define SAVE_BUF_SIZE (1024)
469
470  buf = malloc(SAVE_BUF_SIZE);
471  if (!buf)
472  {
473    close (out);
474    printf ("error: no memory\n");
475  }
476
477  memset (buf, 0, SAVE_BUF_SIZE);
478
479  in = buf;
480
481  /*
482   * Header label.
483   */
484  memcpy (in, "RTEMS-TRACE", sizeof("RTEMS-TRACE"));
485  in += 12;
486
487  /*
488   * Endian detection.
489   */
490  *((uint32_t*) in) = 0x11223344;
491  in += sizeof(uint32_t);
492
493  /*
494   * Number of traces.
495   */
496  *((uint32_t*) in) = traces;
497  in += sizeof(uint32_t);
498
499  /*
500   * Write it.
501   */
502  if (!rtems_trace_buffering_file_write (out, buf, in - buf))
503  {
504    free (buf);
505    close (out);
506    return 1;
507  }
508
509  /*
510   * The trace names.
511   */
512  for (r = 0; r < traces; ++r)
513  {
514    const char* name = rtems_trace_names (r);
515    if (!rtems_trace_buffering_file_write (out, name, strlen (name) + 1))
516    {
517      free (buf);
518      close (out);
519      return 1;
520    }
521  }
522
523  /*
524   * The trace signatures.
525   */
526  for (r = 0; r < traces; ++r)
527  {
528    const rtems_trace_sig* sig = rtems_trace_signatures (r);
529    size_t                 s;
530
531    in = buf;
532
533    memcpy (in, &sig->argc, sizeof (sig->argc));
534    in += sizeof(uint32_t);
535
536    for (s = 0; s < sig->argc; ++s)
537    {
538      const rtems_trace_sig_arg* arg = &sig->args[s];
539      size_t                     arg_len = strlen (arg->type) + 1;
540
541      if ((in - buf) > SAVE_BUF_SIZE)
542      {
543        printf ("error: save temp buffer to small\n");
544        free (buf);
545        close (out);
546        return 1;
547      }
548
549      memcpy (in, &arg->size, sizeof (arg->size));
550      in += sizeof(uint32_t);
551      memcpy (in, arg->type, arg_len);
552      in += arg_len;
553    }
554
555    if (!rtems_trace_buffering_file_write (out, buf, in - buf))
556    {
557      free (buf);
558      close (out);
559      return 1;
560    }
561  }
562
563  free (buf);
564
565  buffer = (uint8_t*) trace_buffer;
566  length = records * sizeof (uint32_t);
567
568  while (length)
569  {
570    ssize_t w = write (out, buffer, length);
571    if (w < 0)
572    {
573      printf ("error: write failed: %s\n", strerror(errno));
574      close (out);
575      return 1;
576    }
577    if (w == 0)
578    {
579      printf ("error: write failed: EOF\n");
580      close (out);
581      return 1;
582    }
583
584    length -= w;
585    buffer += w;
586  }
587
588  close (out);
589
590  return 0;
591}
592
593static void
594rtems_trace_buffering_shell_usage (const char* arg)
595{
596  printf ("%s: Trace Buffer Help\n", arg);
597  printf ("  %s [-hl] <command>\n", arg);
598  printf ("   where:\n");
599  printf ("     command: The TBG subcommand. See -l for a list plus help.\n");
600  printf ("     -h:      This help\n");
601  printf ("     -l:      The command list.\n");
602}
603
604static const rtems_trace_buffering_shell_cmd_t table[] =
605{
606  {
607    "status",
608    rtems_trace_buffering_shell_status,
609    "                       : Show the current status"
610  },
611  {
612    "funcs",
613    rtems_trace_buffering_shell_funcs,
614    "                       : List the trace functions"
615  },
616  {
617    "start",
618    rtems_trace_buffering_shell_start,
619    "                       : Start or restart tracing"
620  },
621  {
622    "stop",
623    rtems_trace_buffering_shell_stop,
624    "                       : Stop tracing"
625  },
626  {
627    "resume",
628    rtems_trace_buffering_shell_resume,
629    "                       : Resume tracing."
630  },
631  {
632    "trace",
633    rtems_trace_buffering_shell_trace,
634    " [start] [end/+length] : List the current trace records"
635  },
636  {
637    "save",
638    rtems_trace_buffering_shell_save,
639    " file                  : Save the trace buffer to a file"
640  },
641};
642
643#define RTEMS_TRACE_BUFFERING_COMMANDS \
644  (sizeof (table) / sizeof (const rtems_trace_buffering_shell_cmd_t))
645
646static int
647rtems_shell_main_rtrace (int argc, char* argv[])
648{
649  int    arg;
650  size_t t;
651
652  for (arg = 1; arg < argc; arg++)
653  {
654    if (argv[arg][0] != '-')
655      break;
656
657    switch (argv[arg][1])
658    {
659      case 'h':
660        rtems_trace_buffering_shell_usage (argv[0]);
661        return 0;
662      case 'l':
663        printf ("%s: commands are:\n", argv[0]);
664        for (t = 0; t < RTEMS_TRACE_BUFFERING_COMMANDS; ++t)
665          printf ("  %-7s %s\n", table[t].name, table[t].help);
666        return 0;
667      default:
668        printf ("error: unknown option: %s\n", argv[arg]);
669        return 1;
670    }
671  }
672
673  if ((argc - arg) < 1)
674    printf ("error: you need to provide a command, try %s -h\n", argv[0]);
675  else
676  {
677    for (t = 0; t < RTEMS_TRACE_BUFFERING_COMMANDS; ++t)
678    {
679      if (strncmp (argv[arg], table[t].name, strlen (argv[arg])) == 0)
680      {
681        int r = table[t].handler (argc - arg, argv + 1);
682        return r;
683      }
684    }
685    printf ("error: command not found: %s (try -h)\n", argv[arg]);
686  }
687
688  return 1;
689}
690
691rtems_shell_cmd_t rtems_shell_RTRACE_Command = {
692  "rtrace",                      /* name */
693  "rtrace [-l]",                 /* usage */
694  "misc",                        /* topic */
695  rtems_shell_main_rtrace,       /* command */
696  NULL,                          /* alias */
697  NULL                           /* next */
698};
Note: See TracBrowser for help on using the repository browser.