Hello Chris,

On 20/12/2018 07:46, Sebastian Huber wrote:

Sorry but I have no time to review this and consider it until next year.

No problem, take your time. I work on this since April this year from time to time, so it can wait a couple of more weeks.

had you time to look at this?

The focus of this new stuff is the recording of high frequency events on multiple processors. It doesn't deal with filtering or event generation.

Attached is a very simple example program to get the records via a TCP stream from the target. It outputs text like this:

6769.471369714:4:THREAD_ID:a01000a
6769.471369714:4:THREAD_QUEUE_ENQUEUE:7df04b1c
6769.471369734:5:UMA_FREE_PTR:7ee6ce00
6769.471369734:5:UMA_FREE_ZONE:7fee5000
6769.471370660:4:THREAD_STATE_SET:1
6769.471370660:4:THREAD_ID:a01000a
6769.471371180:5:THREAD_ID:a01000a
6769.471371180:5:THREAD_QUEUE_SURRENDER:7df04b1c
6769.471372274:4:THREAD_STATE_CLEAR:30015fff
6769.471372274:4:THREAD_ID:a01000a
6769.471372476:5:THREAD_ID:a010029
6769.471372476:5:THREAD_QUEUE_ENQUEUE:7dd24e84
6769.471373040:5:THREAD_STATE_SET:1
6769.471373040:5:THREAD_ID:a010029
6769.471374835:5:THREAD_SWITCH_IN:9010017
6769.471374835:5:THREAD_STACK_CURRENT:1890
6769.471374835:5:THREAD_SWITCH_OUT:a010029
6769.471378612:4:THREAD_ID:a010029
6769.471378612:4:THREAD_QUEUE_SURRENDER:7dd24e84
6769.471380229:4:THREAD_STATE_CLEAR:30015fff
6769.471380229:4:THREAD_ID:a010029
6769.471384143:4:ETHER_INPUT:7dd77100
6769.471384246:5:THREAD_SWITCH_IN:a010029
6769.471384246:5:THREAD_STACK_CURRENT:1ce0
6769.471384246:5:THREAD_SWITCH_OUT:9010017
6769.471384591:4:IP_INPUT:7dd77100
6769.471385216:5:TCP_OUTPUT:7dd21000
6769.471385232:4:TCP_INPUT:7dd77100
6769.471387009:5:WRITE:77
6769.471388103:4:TCP_OUTPUT:7dd21000
6769.471388337:5:RECVFROM:76
6769.471389260:4:UMA_ALLOC_PTR:7d210300
6769.471389260:4:UMA_ALLOC_ZONE:7fee6900
6769.471389760:4:IP_OUTPUT:7d210300
6769.471390693:4:ETHER_OUTPUT:7d210300
6769.471391167:4:UMA_ALLOC_PTR:7d22d8c0
6769.471391167:4:UMA_ALLOC_ZONE:7fee5c00
6769.471393095:4:ETHER_INPUT:7da55900
6769.471393594:4:IP_INPUT:7da55900
6769.471394090:4:TCP_INPUT:7da55900
6769.471394277:5:UMA_FREE_PTR:7da57900
6769.471394277:5:UMA_FREE_ZONE:7fee5000
6769.471398185:4:ETHER_INPUT:7da9e900
6769.471398534:4:IP_INPUT:7da9e900
6769.471399020:4:TCP_INPUT:7da9e90

A potential GSoC project could be to use visualize this.

--
Sebastian Huber, embedded brains GmbH

Address : Dornierstr. 4, D-82178 Puchheim, Germany
Phone   : +49 89 189 47 41-16
Fax     : +49 89 189 47 41-09
E-Mail  : sebastian.hu...@embedded-brains.de
PGP     : Public key available on request.

Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG.

>From 3b42571634e0de038c37397812d9ba0cab34077c Mon Sep 17 00:00:00 2001
From: Sebastian Huber <sebastian.hu...@embedded-brains.de>
Date: Mon, 14 Jan 2019 13:28:31 +0100
Subject: [PATCH] rtems-record: New program

---
 misc/record/record-client.c      | 448 +++++++++++++++++++++++++++++++++++++++
 misc/record/record-main.c        | 307 +++++++++++++++++++++++++++
 misc/record/record-text.c        | 197 +++++++++++++++++
 misc/record/rtems/recordclient.h | 139 ++++++++++++
 misc/record/rtems/recorddata.h   | 317 +++++++++++++++++++++++++++
 misc/wscript                     |  10 +
 6 files changed, 1418 insertions(+)
 create mode 100644 misc/record/record-client.c
 create mode 100644 misc/record/record-main.c
 create mode 100644 misc/record/record-text.c
 create mode 100644 misc/record/rtems/recordclient.h
 create mode 100644 misc/record/rtems/recorddata.h

diff --git a/misc/record/record-client.c b/misc/record/record-client.c
new file mode 100644
index 0000000..1d84050
--- /dev/null
+++ b/misc/record/record-client.c
@@ -0,0 +1,448 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 embedded brains GmbH
+ *
+ * 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.
+ */
+
+/*
+ * This file must be compatible to general purpose POSIX system, e.g. Linux,
+ * FreeBSD.  It may be used for utility programs.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/recordclient.h>
+
+#include <string.h>
+
+static void set_to_bt_scaler(
+  rtems_record_client_context *ctx,
+  uint32_t                     frequency
+)
+{
+  uint64_t bin_per_s;
+
+  bin_per_s = UINT64_C( 1 ) << 32;
+  ctx->to_bt_scaler = ( ( bin_per_s << 31 ) + frequency - 1 ) / frequency;
+}
+
+static rtems_record_client_status call_handler(
+  const rtems_record_client_context *ctx,
+  uint64_t                           bt,
+  rtems_record_event                 event,
+  uint64_t                           data
+)
+{
+  uint32_t seconds;
+  uint32_t nanosec;
+
+  seconds = (uint32_t) ( bt >> 32 );
+  nanosec = (uint32_t) ( ( UINT64_C( 1000000000 ) * (uint32_t) bt ) >> 32 );
+
+  return ( *ctx->handler )(
+    seconds,
+    nanosec,
+    ctx->cpu,
+    event,
+    data,
+    ctx->handler_arg
+  );
+}
+
+static void check_overflow(
+  const rtems_record_client_context *ctx,
+  const rtems_record_client_per_cpu *per_cpu,
+  uint32_t                           new_head
+)
+{
+  uint32_t last_tail;
+  uint32_t last_head;
+  uint32_t capacity;
+  uint32_t new_content;
+  uint64_t bt;
+
+  last_tail = per_cpu->tail[ per_cpu->index ];
+  last_head = per_cpu->head[ per_cpu->index ];
+
+  if ( last_tail == last_head ) {
+    return;
+  }
+
+  capacity = ( last_tail - last_head - 1 ) & ( ctx->count - 1 );
+  new_content = new_head - last_head;
+
+  if ( new_content <= capacity ) {
+    return;
+  }
+
+  bt = ( per_cpu->uptime.time_accumulated * ctx->to_bt_scaler ) >> 31;
+  bt += per_cpu->uptime.bt;
+
+  call_handler(
+    ctx,
+    bt,
+    RTEMS_RECORD_OVERFLOW,
+    new_content - capacity
+  );
+}
+
+static rtems_record_client_status visit( rtems_record_client_context *ctx )
+{
+  rtems_record_client_per_cpu *per_cpu;
+  uint32_t                     time;
+  rtems_record_event           event;
+  uint64_t                     data;
+  uint64_t                     bt;
+
+  per_cpu = &ctx->per_cpu[ ctx->cpu ];
+  time = RTEMS_RECORD_GET_TIME( ctx->event );
+  event = RTEMS_RECORD_GET_EVENT( ctx->event );
+  data = ctx->data;
+
+  switch ( event ) {
+    case RTEMS_RECORD_PROCESSOR:
+      if ( data >= RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT ) {
+        return RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_CPU;
+      }
+
+      ctx->cpu = (uint32_t) data;
+      per_cpu = &ctx->per_cpu[ ctx->cpu ];
+      break;
+    case RTEMS_RECORD_UPTIME_LOW:
+      per_cpu->uptime.bt = (uint32_t) data;
+      per_cpu->uptime.time_at_bt = time;
+      per_cpu->uptime.time_last = time;
+      per_cpu->uptime.time_accumulated = 0;
+      time = 0;
+      break;
+    case RTEMS_RECORD_UPTIME_HIGH:
+      per_cpu->uptime.bt += (int64_t) data << 32;
+      time = 0;
+      break;
+    case RTEMS_RECORD_TAIL:
+      per_cpu->tail[ per_cpu->index ] = (uint32_t) data;
+      break;
+    case RTEMS_RECORD_HEAD:
+      per_cpu->head[ per_cpu->index ]= (uint32_t) data;
+      per_cpu->index ^= 1;
+      check_overflow( ctx, per_cpu, (uint32_t) data );
+      break;
+    case RTEMS_RECORD_COUNT:
+      ctx->count = (uint32_t) data;
+      break;
+    case RTEMS_RECORD_FREQUENCY:
+      set_to_bt_scaler( ctx, (uint32_t) data );
+      break;
+    case RTEMS_RECORD_VERSION:
+      if ( data != RTEMS_RECORD_THE_VERSION ) {
+        return RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_VERSION;
+      }
+
+      break;
+    default:
+      break;
+  }
+
+  if ( time != 0 ) {
+    uint32_t delta;
+
+    delta = ( time - per_cpu->uptime.time_last )
+      & ( ( UINT32_C( 1 ) << RTEMS_RECORD_TIME_BITS ) - 1 );
+    per_cpu->uptime.time_last = time;
+    per_cpu->uptime.time_accumulated += delta;
+    bt = ( per_cpu->uptime.time_accumulated * ctx->to_bt_scaler ) >> 31;
+    bt += per_cpu->uptime.bt;
+  } else {
+    bt = 0;
+  }
+
+  return call_handler( ctx, bt, event, data );
+}
+
+static rtems_record_client_status consume_32(
+  rtems_record_client_context *ctx,
+  const void                  *buf,
+  size_t                       n
+)
+{
+  while ( n > 0 ) {
+    size_t m;
+    char *pos;
+
+    m = ctx->todo < n ? ctx->todo : n;
+    pos = ctx->pos;
+    pos = memcpy( pos, buf, m );
+    n -= m;
+    buf = (char *) buf + m;
+
+    if ( m == ctx->todo ) {
+      rtems_record_client_status status;
+
+      ctx->todo = sizeof( ctx->item.format_32 );
+      ctx->pos = &ctx->item.format_32;
+      ctx->event = ctx->item.format_32.event;
+      ctx->data = ctx->item.format_32.data;
+
+      status = visit( ctx );
+
+      if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+        return status;
+      }
+    } else {
+      ctx->todo -= m;
+      ctx->pos = pos + m;
+    }
+  }
+
+  return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status consume_64(
+  rtems_record_client_context *ctx,
+  const void                  *buf,
+  size_t                       n
+)
+{
+  while ( n > 0 ) {
+    size_t m;
+    char *pos;
+
+    m = ctx->todo < n ? ctx->todo : n;
+    pos = ctx->pos;
+    pos = memcpy( pos, buf, m );
+    n -= m;
+    buf = (char *) buf + m;
+
+    if ( m == ctx->todo ) {
+      rtems_record_client_status status;
+
+      ctx->todo = sizeof( ctx->item.format_64 );
+      ctx->pos = &ctx->item.format_64;
+      ctx->event = ctx->item.format_64.event;
+      ctx->data = ctx->item.format_64.data;
+
+      status = visit( ctx );
+
+      if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+        return status;
+      }
+    } else {
+      ctx->todo -= m;
+      ctx->pos = pos + m;
+    }
+  }
+
+  return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status consume_swap_32(
+  rtems_record_client_context *ctx,
+  const void                  *buf,
+  size_t                       n
+)
+{
+  while ( n > 0 ) {
+    size_t m;
+    char *pos;
+
+    m = ctx->todo < n ? ctx->todo : n;
+    pos = ctx->pos;
+    pos = memcpy( pos, buf, m );
+    n -= m;
+    buf = (char *) buf + m;
+
+    if ( m == ctx->todo ) {
+      rtems_record_client_status status;
+
+      ctx->todo = sizeof( ctx->item.format_32 );
+      ctx->pos = &ctx->item.format_32;
+      ctx->event = __builtin_bswap32( ctx->item.format_32.event );
+      ctx->data = __builtin_bswap32( ctx->item.format_32.data );
+
+      status = visit( ctx );
+
+      if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+        return status;
+      }
+    } else {
+      ctx->todo -= m;
+      ctx->pos = pos + m;
+    }
+  }
+
+  return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status consume_swap_64(
+  rtems_record_client_context *ctx,
+  const void                  *buf,
+  size_t                       n
+)
+{
+  while ( n > 0 ) {
+    size_t m;
+    char *pos;
+
+    m = ctx->todo < n ? ctx->todo : n;
+    pos = ctx->pos;
+    pos = memcpy( pos, buf, m );
+    n -= m;
+    buf = (char *) buf + m;
+
+    if ( m == ctx->todo ) {
+      rtems_record_client_status status;
+
+      ctx->todo = sizeof( ctx->item.format_64 );
+      ctx->pos = &ctx->item.format_64;
+      ctx->event = __builtin_bswap32( ctx->item.format_64.event );
+      ctx->data = __builtin_bswap64( ctx->item.format_64.data );
+
+      status = visit( ctx );
+
+      if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+        return status;
+      }
+    } else {
+      ctx->todo -= m;
+      ctx->pos = pos + m;
+    }
+  }
+
+  return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status consume_init(
+  rtems_record_client_context *ctx,
+  const void                  *buf,
+  size_t                       n
+)
+{
+  while ( n > 0 ) {
+    size_t m;
+    char *pos;
+
+    m = ctx->todo < n ? ctx->todo : n;
+    pos = ctx->pos;
+    pos = memcpy( pos, buf, m );
+    n -= m;
+    buf = (char *) buf + m;
+
+    if ( m == ctx->todo ) {
+      uint32_t magic;
+
+      magic = ctx->header[ 1 ];
+
+      switch ( ctx->header[ 0 ] ) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+        case RTEMS_RECORD_FORMAT_LE_32:
+          ctx->todo = sizeof( ctx->item.format_32 );
+          ctx->pos = &ctx->item.format_32;
+          ctx->consume = consume_32;
+          break;
+        case RTEMS_RECORD_FORMAT_LE_64:
+          ctx->todo = sizeof( ctx->item.format_64 );
+          ctx->pos = &ctx->item.format_64;
+          ctx->consume = consume_64;
+          break;
+        case RTEMS_RECORD_FORMAT_BE_32:
+          ctx->todo = sizeof( ctx->item.format_32 );
+          ctx->pos = &ctx->item.format_32;
+          ctx->consume = consume_swap_32;
+          magic = __builtin_bswap32( magic );
+          break;
+        case RTEMS_RECORD_FORMAT_BE_64:
+          ctx->todo = sizeof( ctx->item.format_64 );
+          ctx->pos = &ctx->item.format_64;
+          ctx->consume = consume_swap_64;
+          magic = __builtin_bswap32( magic );
+          break;
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+        case RTEMS_RECORD_FORMAT_LE_32:
+          ctx->todo = sizeof( ctx->item.format_32 );
+          ctx->pos = &ctx->item.format_32;
+          ctx->consume = consume_swap_32;
+          magic = __builtin_bswap32( magic );
+          break;
+        case RTEMS_RECORD_FORMAT_LE_64:
+          ctx->todo = sizeof( ctx->item.format_64 );
+          ctx->pos = &ctx->item.format_64;
+          ctx->consume = consume_swap_64;
+          magic = __builtin_bswap32( magic );
+          break;
+        case RTEMS_RECORD_FORMAT_BE_32:
+          ctx->todo = sizeof( ctx->item.format_32 );
+          ctx->pos = &ctx->item.format_32;
+          ctx->consume = consume_32;
+          break;
+        case RTEMS_RECORD_FORMAT_BE_64:
+          ctx->todo = sizeof( ctx->item.format_64 );
+          ctx->pos = &ctx->item.format_64;
+          ctx->consume = consume_64;
+          break;
+#else
+#error "unexpected __BYTE_ORDER__"
+#endif
+        default:
+          return RTEMS_RECORD_CLIENT_ERROR_UNKNOWN_FORMAT;
+      }
+
+      if ( magic != RTEMS_RECORD_MAGIC ) {
+        return RTEMS_RECORD_CLIENT_ERROR_INVALID_MAGIC;
+      }
+
+      return rtems_record_client_run( ctx, buf, n );
+    } else {
+      ctx->todo -= m;
+      ctx->pos = pos + m;
+    }
+  }
+
+  return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+void rtems_record_client_init(
+  rtems_record_client_context *ctx,
+  rtems_record_client_handler  handler,
+  void                        *arg
+)
+{
+  ctx = memset( ctx, 0, sizeof( *ctx ) );
+  ctx->to_bt_scaler = UINT64_C( 1 ) << 31;
+  ctx->handler = handler;
+  ctx->handler_arg = arg;
+  ctx->todo = sizeof( ctx->header );
+  ctx->pos = &ctx->header;
+  ctx->consume = consume_init;
+}
+
+rtems_record_client_status rtems_record_client_run(
+  rtems_record_client_context *ctx,
+  const void                  *buf,
+  size_t                       n
+)
+{
+  return ( *ctx->consume )( ctx, buf, n );
+}
diff --git a/misc/record/record-main.c b/misc/record/record-main.c
new file mode 100644
index 0000000..52933db
--- /dev/null
+++ b/misc/record/record-main.c
@@ -0,0 +1,307 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018, 2019 embedded brains GmbH
+ *
+ * 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 <rtems/recorddata.h>
+#include <rtems/recordclient.h>
+
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define THRESHOLD_IN_NS 1000000000
+
+static const struct option longopts[] = {
+  { "help", 0, NULL, 'h' },
+  { "host", 1, NULL, 'H' },
+  { "port", 1, NULL, 'p' },
+  { NULL, 0, NULL, 0 }
+};
+
+static const char *host = "127.0.0.1";
+
+static uint16_t port = 1234;
+
+typedef struct {
+  uint64_t            ns;
+  uint32_t            cpu;
+  rtems_record_event  event;
+  uint64_t            data;
+} client_item;
+
+typedef struct {
+  uint64_t     ns_threshold;
+  uint64_t     last_ns;
+  uint32_t     last_cpu;
+  bool         flush;
+  bool         only_one_cpu;
+  size_t       index;
+  size_t       count;
+  client_item *items;
+} client_context;
+
+static int item_cmp( const void *pa, const void *pb )
+{
+  const client_item *a;
+  const client_item *b;
+
+  a = (const client_item *) pa;
+  b = (const client_item *) pb;
+
+  if (a->ns > b->ns) {
+    return -1;
+  } else if (a->ns < b->ns) {
+    return 1;
+  } else if (a->cpu > b->cpu ) {
+    return -1;
+  } else if (a->cpu < b->cpu ) {
+    return 1;
+  } else if (a->event > b->event ) {
+    return -1;
+  } else if (a->event < b->event ) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+static void usage( char **argv )
+{
+  printf(
+    "%s --host=HOST --port=PORT\n"
+    "\n"
+    "Mandatory arguments to long options are mandatory for short options too.\n"
+    "  -h, --help                 print this help text\n"
+    "  -H, --host=HOST            the host IPv4 address of the record server\n"
+    "  -p, --port=PORT            the TCP port of the record server\n",
+    argv[ 0 ]
+  );
+}
+
+static int connect_client( void )
+{
+  struct sockaddr_in in_addr;
+  int fd;
+  int rv;
+
+  fd = socket( PF_INET, SOCK_STREAM, 0 );
+  assert( fd >= 0 );
+
+  memset( &in_addr, 0, sizeof( in_addr ) );
+  in_addr.sin_family = AF_INET;
+  in_addr.sin_port = htons( port );
+  in_addr.sin_addr.s_addr = inet_addr( host );
+  rv = connect( fd, (struct sockaddr *) &in_addr, sizeof( in_addr ) );
+  assert( rv == 0 );
+
+  return fd;
+}
+
+static void print_item( FILE *f, const client_item *item )
+{
+  const char *event_text;
+
+  if ( item->ns != 0 ) {
+    uint32_t seconds;
+    uint32_t nanoseconds;
+
+    seconds = (uint32_t) ( item->ns / 1000000000 );
+    nanoseconds = (uint32_t) ( item->ns % 1000000000 );
+    fprintf( f, "%" PRIu32 ".%09" PRIu32 ":", seconds, nanoseconds );
+  } else {
+    fprintf( f, "*:" );
+  }
+
+  event_text = rtems_record_event_text( item->event );
+  if ( event_text != NULL ) {
+    fprintf( f, "%" PRIu32 ":%s:%" PRIx64 "\n", item->cpu, event_text, item->data );
+  } else {
+    fprintf( f, "%" PRIu32 ":%i:%" PRIx64 "\n", item->cpu, item->event, item->data );
+  }
+}
+
+static void flush_items( client_context *cctx, uint64_t ns )
+{
+  size_t i;
+  size_t n;
+  uint64_t ns_threshold;
+
+  n = cctx->index;
+  ns_threshold = cctx->ns_threshold;
+
+  if ( ns >= ns_threshold ) {
+    cctx->ns_threshold = ( ( ns + THRESHOLD_IN_NS - 1 ) / THRESHOLD_IN_NS )
+      * THRESHOLD_IN_NS;
+    ns_threshold -= THRESHOLD_IN_NS;
+  } else {
+    ns_threshold = cctx->items[ n / 2 ].ns;
+  }
+
+  qsort( cctx->items, n, sizeof( cctx->items[ 0 ] ), item_cmp );
+
+  for ( i = 0; i < n; ++i ) {
+    const client_item *item;
+
+    item = &cctx->items[ n - i - 1 ];
+
+    if ( item->ns > ns_threshold ) {
+      break;
+    }
+
+    print_item( stdout, item );
+  }
+
+  cctx->index = n - i;
+}
+
+static rtems_record_client_status handler(
+  uint32_t            seconds,
+  uint32_t            nanoseconds,
+  uint32_t            cpu,
+  rtems_record_event  event,
+  uint64_t            data,
+  void               *arg
+)
+{
+  client_context *cctx;
+  client_item *item;
+  uint64_t ns;
+  bool flush;
+
+  cctx = arg;
+
+  if ( cpu != 0 ) {
+    cctx->only_one_cpu = false;
+  }
+
+  ns = ( (uint64_t) seconds * 1000000000 ) + nanoseconds;
+
+  if ( cctx->only_one_cpu ) {
+    flush = ( ns >= cctx->ns_threshold );
+  } else {
+    if ( cpu != cctx->last_cpu ) {
+      cctx->last_cpu = cpu;
+
+      if ( cpu == 0 ) {
+        flush = ( cctx->flush && cctx->last_ns >= cctx->ns_threshold );
+        cctx->flush = true;
+      } else {
+        flush = false;
+        cctx->flush = ( cctx->flush && cctx->last_ns >= cctx->ns_threshold );
+      }
+    } else {
+      flush = false;
+    }
+  }
+
+  if (
+    ns != 0
+      && event != RTEMS_RECORD_UPTIME_LOW
+      && event != RTEMS_RECORD_UPTIME_HIGH
+  ) {
+    cctx->last_ns = ns;
+
+    item = &cctx->items[ cctx->index ];
+    item->ns = ns;
+    item->cpu = cpu;
+    item->event = event;
+    item->data = data;
+    ++cctx->index;
+  }
+
+  if ( flush || cctx->index == cctx->count ) {
+    flush_items( cctx, ns );
+  }
+
+  return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+int main( int argc, char **argv )
+{
+  rtems_record_client_context ctx;
+  client_context cctx;
+  int fd;
+  int rv;
+  int opt;
+  int longindex;
+
+  while (
+    (opt = getopt_long(argc, argv, "hH:p:", &longopts[0], &longindex)) != -1
+  ) {
+    switch (opt) {
+      case 'h':
+        usage(argv);
+        exit(EXIT_SUCCESS);
+        break;
+      case 'H':
+        host = optarg;
+        break;
+      case 'p':
+        port = (uint16_t) strtoul(optarg, NULL, 10);
+        break;
+      default:
+        exit(EXIT_FAILURE);
+        break;
+    }
+  }
+
+  memset( &cctx, 0, sizeof( cctx ) );
+  cctx.only_one_cpu = true;
+  cctx.ns_threshold = 2 * THRESHOLD_IN_NS;
+  cctx.count = 32 * 1024 * 1024;
+  cctx.items = calloc( cctx.count, sizeof( cctx.items[ 0 ] ) );
+  assert( cctx.items != NULL );
+
+  fd = connect_client();
+  rtems_record_client_init( &ctx, handler, &cctx );
+
+  while ( true ) {
+    int buf[ 1024 ];
+    ssize_t n;
+
+    n = recv( fd, buf, sizeof( buf ), 0 );
+    if ( n >= 0 ) {
+      rtems_record_client_run( &ctx, buf, (size_t) n );
+    } else {
+      break;
+    }
+  }
+
+  rv = close( fd );
+  assert( rv == 0 );
+
+  return 0;
+}
diff --git a/misc/record/record-text.c b/misc/record/record-text.c
new file mode 100644
index 0000000..7ed3221
--- /dev/null
+++ b/misc/record/record-text.c
@@ -0,0 +1,197 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 embedded brains GmbH
+ *
+ * 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.
+ */
+
+/*
+ * This file must be compatible to general purpose POSIX system, e.g. Linux,
+ * FreeBSD.  It may be used for utility programs.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/recorddata.h>
+
+#include <stddef.h>
+
+static const char * const event_text[] = {
+  [ RTEMS_RECORD_EMPTY ] = "EMPTY",
+  [ RTEMS_RECORD_VERSION ] = "VERSION",
+  [ RTEMS_RECORD_ACCEPT ] = "ACCEPT",
+  [ RTEMS_RECORD_BIND ] = "BIND",
+  [ RTEMS_RECORD_BUFFER ] = "BUFFER",
+  [ RTEMS_RECORD_CHOWN ] = "CHOWN",
+  [ RTEMS_RECORD_CLOSE ] = "CLOSE",
+  [ RTEMS_RECORD_CONNECT ] = "CONNECT",
+  [ RTEMS_RECORD_COUNT ] = "COUNT",
+  [ RTEMS_RECORD_ETHER_INPUT ] = "ETHER_INPUT",
+  [ RTEMS_RECORD_ETHER_OUTPUT ] = "ETHER_OUTPUT",
+  [ RTEMS_RECORD_FCHMOD ] = "FCHMOD",
+  [ RTEMS_RECORD_FCNTL ] = "FCNTL",
+  [ RTEMS_RECORD_FDATASYNC ] = "FDATASYNC",
+  [ RTEMS_RECORD_FREQUENCY ] = "FREQUENCY",
+  [ RTEMS_RECORD_FSTAT ] = "FSTAT",
+  [ RTEMS_RECORD_FSYNC ] = "FSYNC",
+  [ RTEMS_RECORD_FTRUNCATE ] = "FTRUNCATE",
+  [ RTEMS_RECORD_GIT_HASH ] = "GIT_HASH",
+  [ RTEMS_RECORD_HEAD ] = "HEAD",
+  [ RTEMS_RECORD_HEAP_ALLOC ] = "HEAP_ALLOC",
+  [ RTEMS_RECORD_HEAP_FREE ] = "HEAP_FREE",
+  [ RTEMS_RECORD_HEAP_SIZE ] = "HEAP_SIZE",
+  [ RTEMS_RECORD_HEAP_USAGE ] = "HEAP_USAGE",
+  [ RTEMS_RECORD_INTERUPT_BEGIN ] = "INTERUPT_BEGIN",
+  [ RTEMS_RECORD_INTERUPT_END ] = "INTERUPT_END",
+  [ RTEMS_RECORD_INTERUPT_INSTALL ] = "INTERUPT_INSTALL",
+  [ RTEMS_RECORD_INTERUPT_REMOVE ] = "INTERUPT_REMOVE",
+  [ RTEMS_RECORD_IOCTL ] = "IOCTL",
+  [ RTEMS_RECORD_IP6_INPUT ] = "IP6_INPUT",
+  [ RTEMS_RECORD_IP6_OUTPUT ] = "IP6_OUTPUT",
+  [ RTEMS_RECORD_IP_INPUT ] = "IP_INPUT",
+  [ RTEMS_RECORD_IP_OUTPUT ] = "IP_OUTPUT",
+  [ RTEMS_RECORD_KEVENT ] = "KEVENT",
+  [ RTEMS_RECORD_KQUEUE ] = "KQUEUE",
+  [ RTEMS_RECORD_LENGTH ] = "LENGTH",
+  [ RTEMS_RECORD_LINK ] = "LINK",
+  [ RTEMS_RECORD_LSEEK ] = "LSEEK",
+  [ RTEMS_RECORD_MKNOD ] = "MKNOD",
+  [ RTEMS_RECORD_MMAP ] = "MMAP",
+  [ RTEMS_RECORD_MOUNT ] = "MOUNT",
+  [ RTEMS_RECORD_OPEN ] = "OPEN",
+  [ RTEMS_RECORD_OVERFLOW ] = "OVERFLOW",
+  [ RTEMS_RECORD_PAGE_ALLOC ] = "PAGE_ALLOC",
+  [ RTEMS_RECORD_PAGE_FREE ] = "PAGE_FREE",
+  [ RTEMS_RECORD_POLL ] = "POLL",
+  [ RTEMS_RECORD_PROCESSOR ] = "PROCESSOR",
+  [ RTEMS_RECORD_PROCESSOR_MAXIMUM ] = "PROCESSOR_MAXIMUM",
+  [ RTEMS_RECORD_READ ] = "READ",
+  [ RTEMS_RECORD_READLINK ] = "READLINK",
+  [ RTEMS_RECORD_READV ] = "READV",
+  [ RTEMS_RECORD_RECV ] = "RECV",
+  [ RTEMS_RECORD_RECVFROM ] = "RECVFROM",
+  [ RTEMS_RECORD_RECVMSG ] = "RECVMSG",
+  [ RTEMS_RECORD_RENAME ] = "RENAME",
+  [ RTEMS_RECORD_RTEMS_BARRIER_CREATE ] = "RTEMS_BARRIER_CREATE",
+  [ RTEMS_RECORD_RTEMS_BARRIER_DELETE ] = "RTEMS_BARRIER_DELETE",
+  [ RTEMS_RECORD_RTEMS_BARRIER_RELEASE ] = "RTEMS_BARRIER_RELEASE",
+  [ RTEMS_RECORD_RTEMS_BARRIER_WAIT ] = "RTEMS_BARRIER_WAIT",
+  [ RTEMS_RECORD_RTEMS_EVENT_RECEIVE ] = "RTEMS_EVENT_RECEIVE",
+  [ RTEMS_RECORD_RTEMS_EVENT_SEND ] = "RTEMS_EVENT_SEND",
+  [ RTEMS_RECORD_RTEMS_EVENT_SYSTEM_RECEIVE ] = "RTEMS_EVENT_SYSTEM_RECEIVE",
+  [ RTEMS_RECORD_RTEMS_EVENT_SYSTEM_SEND ] = "RTEMS_EVENT_SYSTEM_SEND",
+  [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_BROADCAST ] = "RTEMS_MESSAGE_QUEUE_BROADCAST",
+  [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_CREATE ] = "RTEMS_MESSAGE_QUEUE_CREATE",
+  [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_DELETE ] = "RTEMS_MESSAGE_QUEUE_DELETE",
+  [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_FLUSH ] = "RTEMS_MESSAGE_QUEUE_FLUSH",
+  [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_RECEIVE ] = "RTEMS_MESSAGE_QUEUE_RECEIVE",
+  [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_SEND ] = "RTEMS_MESSAGE_QUEUE_SEND",
+  [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_URGENT ] = "RTEMS_MESSAGE_QUEUE_URGENT",
+  [ RTEMS_RECORD_RTEMS_PARTITION_CREATE ] = "RTEMS_PARTITION_CREATE",
+  [ RTEMS_RECORD_RTEMS_PARTITION_DELETE ] = "RTEMS_PARTITION_DELETE",
+  [ RTEMS_RECORD_RTEMS_PARTITION_GET_BUFFER ] = "RTEMS_PARTITION_GET_BUFFER",
+  [ RTEMS_RECORD_RTEMS_PARTITION_RETURN_BUFFER ] = "RTEMS_PARTITION_RETURN_BUFFER",
+  [ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_CANCEL ] = "RTEMS_RATE_MONOTONIC_CANCEL",
+  [ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_CREATE ] = "RTEMS_RATE_MONOTONIC_CREATE",
+  [ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_DELETE ] = "RTEMS_RATE_MONOTONIC_DELETE",
+  [ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_PERIOD ] = "RTEMS_RATE_MONOTONIC_PERIOD",
+  [ RTEMS_RECORD_RTEMS_SEMAPHORE_CREATE ] = "RTEMS_SEMAPHORE_CREATE",
+  [ RTEMS_RECORD_RTEMS_SEMAPHORE_DELETE ] = "RTEMS_SEMAPHORE_DELETE",
+  [ RTEMS_RECORD_RTEMS_SEMAPHORE_FLUSH ] = "RTEMS_SEMAPHORE_FLUSH",
+  [ RTEMS_RECORD_RTEMS_SEMAPHORE_OBTAIN ] = "RTEMS_SEMAPHORE_OBTAIN",
+  [ RTEMS_RECORD_RTEMS_SEMAPHORE_RELEASE ] = "RTEMS_SEMAPHORE_RELEASE",
+  [ RTEMS_RECORD_RTEMS_TIMER_CANCEL ] = "RTEMS_TIMER_CANCEL",
+  [ RTEMS_RECORD_RTEMS_TIMER_CREATE ] = "RTEMS_TIMER_CREATE",
+  [ RTEMS_RECORD_RTEMS_TIMER_DELETE ] = "RTEMS_TIMER_DELETE",
+  [ RTEMS_RECORD_RTEMS_TIMER_FIRE_AFTER ] = "RTEMS_TIMER_FIRE_AFTER",
+  [ RTEMS_RECORD_RTEMS_TIMER_FIRE_WHEN ] = "RTEMS_TIMER_FIRE_WHEN",
+  [ RTEMS_RECORD_RTEMS_TIMER_RESET ] = "RTEMS_TIMER_RESET",
+  [ RTEMS_RECORD_RTEMS_TIMER_SERVER_FIRE_AFTER ] = "RTEMS_TIMER_SERVER_FIRE_AFTER",
+  [ RTEMS_RECORD_RTEMS_TIMER_SERVER_FIRE_WHEN ] = "RTEMS_TIMER_SERVER_FIRE_WHEN",
+  [ RTEMS_RECORD_SELECT ] = "SELECT",
+  [ RTEMS_RECORD_SEND ] = "SEND",
+  [ RTEMS_RECORD_SENDMSG ] = "SENDMSG",
+  [ RTEMS_RECORD_SENDTO ] = "SENDTO",
+  [ RTEMS_RECORD_SOCKET ] = "SOCKET",
+  [ RTEMS_RECORD_STATVFS ] = "STATVFS",
+  [ RTEMS_RECORD_SYMLINK ] = "SYMLINK",
+  [ RTEMS_RECORD_TAIL ] = "TAIL",
+  [ RTEMS_RECORD_TCP_INPUT ] = "TCP_INPUT",
+  [ RTEMS_RECORD_TCP_OUTPUT ] = "TCP_OUTPUT",
+  [ RTEMS_RECORD_THREAD_BEGIN ] = "THREAD_BEGIN",
+  [ RTEMS_RECORD_THREAD_CREATE ] = "THREAD_CREATE",
+  [ RTEMS_RECORD_THREAD_DELETE ] = "THREAD_DELETE",
+  [ RTEMS_RECORD_THREAD_EXIT ] = "THREAD_EXIT",
+  [ RTEMS_RECORD_THREAD_EXITTED ] = "THREAD_EXITTED",
+  [ RTEMS_RECORD_THREAD_ID ] = "THREAD_ID",
+  [ RTEMS_RECORD_THREAD_PRIO_CURRENT_HIGH ] = "THREAD_PRIO_CURRENT_HIGH",
+  [ RTEMS_RECORD_THREAD_PRIO_CURRENT_LOW ] = "THREAD_PRIO_CURRENT_LOW",
+  [ RTEMS_RECORD_THREAD_PRIO_REAL_HIGH ] = "THREAD_PRIO_REAL_HIGH",
+  [ RTEMS_RECORD_THREAD_PRIO_REAL_LOW ] = "THREAD_PRIO_REAL_LOW",
+  [ RTEMS_RECORD_THREAD_QUEUE_ENQUEUE ] = "THREAD_QUEUE_ENQUEUE",
+  [ RTEMS_RECORD_THREAD_QUEUE_ENQUEUE_STICKY ] = "THREAD_QUEUE_ENQUEUE_STICKY",
+  [ RTEMS_RECORD_THREAD_QUEUE_EXTRACT ] = "THREAD_QUEUE_EXTRACT",
+  [ RTEMS_RECORD_THREAD_QUEUE_SURRENDER ] = "THREAD_QUEUE_SURRENDER",
+  [ RTEMS_RECORD_THREAD_QUEUE_SURRENDER_STICKY ] = "THREAD_QUEUE_SURRENDER_STICKY",
+  [ RTEMS_RECORD_THREAD_RESTART ] = "THREAD_RESTART",
+  [ RTEMS_RECORD_THREAD_STACK_CURRENT ] = "THREAD_STACK_CURRENT",
+  [ RTEMS_RECORD_THREAD_STACK_SIZE ] = "THREAD_STACK_SIZE",
+  [ RTEMS_RECORD_THREAD_STACK_USAGE ] = "THREAD_STACK_USAGE",
+  [ RTEMS_RECORD_THREAD_START ] = "THREAD_START",
+  [ RTEMS_RECORD_THREAD_STATE_CLEAR ] = "THREAD_STATE_CLEAR",
+  [ RTEMS_RECORD_THREAD_STATE_SET ] = "THREAD_STATE_SET",
+  [ RTEMS_RECORD_THREAD_SWITCH_IN ] = "THREAD_SWITCH_IN",
+  [ RTEMS_RECORD_THREAD_SWITCH_OUT ] = "THREAD_SWITCH_OUT",
+  [ RTEMS_RECORD_THREAD_TERMINATE ] = "THREAD_TERMINATE",
+  [ RTEMS_RECORD_UDP_INPUT ] = "UDP_INPUT",
+  [ RTEMS_RECORD_UDP_OUTPUT ] = "UDP_OUTPUT",
+  [ RTEMS_RECORD_UMA_ALLOC_PTR ] = "UMA_ALLOC_PTR",
+  [ RTEMS_RECORD_UMA_ALLOC_ZONE ] = "UMA_ALLOC_ZONE",
+  [ RTEMS_RECORD_UMA_FREE_PTR ] = "UMA_FREE_PTR",
+  [ RTEMS_RECORD_UMA_FREE_ZONE ] = "UMA_FREE_ZONE",
+  [ RTEMS_RECORD_UNLINK ] = "UNLINK",
+  [ RTEMS_RECORD_UNMOUNT ] = "UNMOUNT",
+  [ RTEMS_RECORD_UPTIME_HIGH ] = "UPTIME_HIGH",
+  [ RTEMS_RECORD_UPTIME_LOW ] = "UPTIME_LOW",
+  [ RTEMS_RECORD_WORKSPACE_ALLOC ] = "WORKSPACE_ALLOC",
+  [ RTEMS_RECORD_WORKSPACE_FREE ] = "WORKSPACE_FREE",
+  [ RTEMS_RECORD_WORKSPACE_SIZE ] = "WORKSPACE_SIZE",
+  [ RTEMS_RECORD_WORKSPACE_USAGE ] = "WORKSPACE_USAGE",
+  [ RTEMS_RECORD_WRITE ] = "WRITE",
+  [ RTEMS_RECORD_WRITEV ] = "WRITEV"
+};
+
+const char *rtems_record_event_text( rtems_record_event event )
+{
+  size_t n;
+
+  n = event;
+
+  if ( n < sizeof( event_text ) / sizeof( event_text[ 0 ] ) ) {
+    return event_text[ n ];
+  }
+
+  return NULL;
+}
diff --git a/misc/record/rtems/recordclient.h b/misc/record/rtems/recordclient.h
new file mode 100644
index 0000000..cddb5f7
--- /dev/null
+++ b/misc/record/rtems/recordclient.h
@@ -0,0 +1,139 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 embedded brains GmbH
+ *
+ * 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.
+ */
+
+/*
+ * This file must be compatible to general purpose POSIX system, e.g. Linux,
+ * FreeBSD.  It may be used for utility programs.
+ */
+
+#ifndef _RTEMS_RECORDCLIENT_H
+#define _RTEMS_RECORDCLIENT_H
+
+#include "recorddata.h"
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @addtogroup RTEMSRecord
+ *
+ * @{
+ */
+
+#define RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT 32
+
+typedef enum {
+  RTEMS_RECORD_CLIENT_SUCCESS,
+  RTEMS_RECORD_CLIENT_ERROR_INVALID_MAGIC,
+  RTEMS_RECORD_CLIENT_ERROR_UNKNOWN_FORMAT,
+  RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_VERSION,
+  RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_CPU
+} rtems_record_client_status;
+
+typedef rtems_record_client_status ( *rtems_record_client_handler )(
+  uint32_t            seconds,
+  uint32_t            nanoseconds,
+  uint32_t            cpu,
+  rtems_record_event  event,
+  uint64_t            data,
+  void               *arg
+);
+
+typedef struct {
+  struct {
+    uint64_t bt;
+    uint32_t time_at_bt;
+    uint32_t time_last;
+    uint32_t time_accumulated;
+  } uptime;
+  uint32_t tail[ 2 ];
+  uint32_t head[ 2 ];
+  size_t index;
+} rtems_record_client_per_cpu;
+
+typedef struct rtems_record_client_context {
+  uint64_t to_bt_scaler;
+  rtems_record_client_per_cpu per_cpu[ RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT ];
+  uint64_t data;
+  uint32_t cpu;
+  uint32_t event;
+  uint32_t count;
+  union {
+    rtems_record_item_32 format_32;
+    rtems_record_item_64 format_64;
+  } item;
+  size_t todo;
+  void *pos;
+  rtems_record_client_status ( *consume )(
+    struct rtems_record_client_context *,
+    const void *,
+    size_t
+  );
+  rtems_record_client_handler handler;
+  void *handler_arg;
+  uint32_t header[ 2 ];
+} rtems_record_client_context;
+
+/**
+ * @brief Initializes a record client.
+ *
+ * The record client consumes a record item stream produces by the record
+ * server.
+ *
+ * @param ctx The record client context to initialize.
+ * @param handler The handler is invoked for each received record item.
+ * @param arg The handler argument.
+ */
+void rtems_record_client_init(
+  rtems_record_client_context *ctx,
+  rtems_record_client_handler  handler,
+  void                        *arg
+);
+
+/**
+ * @brief Runs the record client to consume new stream data.
+ *
+ * @param ctx The record client context.
+ * @param buf The buffer with new stream data.
+ * @param n The size of the buffer.
+ */
+rtems_record_client_status rtems_record_client_run(
+  rtems_record_client_context *ctx,
+  const void                  *buf,
+  size_t                       n
+);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RTEMS_RECORDCLIENT_H */
diff --git a/misc/record/rtems/recorddata.h b/misc/record/rtems/recorddata.h
new file mode 100644
index 0000000..5879980
--- /dev/null
+++ b/misc/record/rtems/recorddata.h
@@ -0,0 +1,317 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018, 2019 embedded brains GmbH
+ *
+ * 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.
+ */
+
+/*
+ * This file must be compatible to general purpose POSIX system, e.g. Linux,
+ * FreeBSD.  It may be used for utility programs.
+ */
+
+#ifndef _RTEMS_RECORDDATA_H
+#define _RTEMS_RECORDDATA_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup RTEMSRecord Event Recording
+ *
+ * @brief Low-level event recording support.
+ *
+ * @{
+ */
+
+/**
+ * @brief The record version.
+ *
+ * The record version reflects the record event definitions.  It is reported by
+ * the RTEMS_RECORD_VERSION event.
+ */
+#define RTEMS_RECORD_THE_VERSION 1
+
+/**
+ * @brief The items are in 32-bit little-endian format.
+ */
+#define RTEMS_RECORD_FORMAT_LE_32 0x11111111
+
+/**
+ * @brief The items are in 64-bit little-endian format.
+ */
+#define RTEMS_RECORD_FORMAT_LE_64 0x22222222
+
+/**
+ * @brief The items are in 32-bit big-endian format.
+ */
+#define RTEMS_RECORD_FORMAT_BE_32 0x33333333
+
+/**
+ * @brief The items are in 64-bit big-endian format.
+ */
+#define RTEMS_RECORD_FORMAT_BE_64 0x44444444
+
+/**
+ * @brief Magic number to identify a record item stream.
+ *
+ * This is a random number.
+ */
+#define RTEMS_RECORD_MAGIC 0x82e14ec1
+
+/**
+ * @brief The record events.
+ */
+typedef enum {
+  /* There are 512 events reserved for the system */
+  RTEMS_RECORD_EMPTY,
+  RTEMS_RECORD_VERSION,
+
+  /*
+   * Keep the following system events in lexicographical order, increment
+   * RTEMS_RECORD_THE_VERSION after each change.
+   */
+  RTEMS_RECORD_ACCEPT,
+  RTEMS_RECORD_BIND,
+  RTEMS_RECORD_BUFFER,
+  RTEMS_RECORD_CHOWN,
+  RTEMS_RECORD_CLOSE,
+  RTEMS_RECORD_CONNECT,
+  RTEMS_RECORD_COUNT,
+  RTEMS_RECORD_ETHER_INPUT,
+  RTEMS_RECORD_ETHER_OUTPUT,
+  RTEMS_RECORD_FCHMOD,
+  RTEMS_RECORD_FCNTL,
+  RTEMS_RECORD_FDATASYNC,
+  RTEMS_RECORD_FREQUENCY,
+  RTEMS_RECORD_FSTAT,
+  RTEMS_RECORD_FSYNC,
+  RTEMS_RECORD_FTRUNCATE,
+  RTEMS_RECORD_GIT_HASH,
+  RTEMS_RECORD_HEAD,
+  RTEMS_RECORD_HEAP_ALLOC,
+  RTEMS_RECORD_HEAP_FREE,
+  RTEMS_RECORD_HEAP_SIZE,
+  RTEMS_RECORD_HEAP_USAGE,
+  RTEMS_RECORD_INTERUPT_BEGIN,
+  RTEMS_RECORD_INTERUPT_END,
+  RTEMS_RECORD_INTERUPT_INSTALL,
+  RTEMS_RECORD_INTERUPT_REMOVE,
+  RTEMS_RECORD_IOCTL,
+  RTEMS_RECORD_IP6_INPUT,
+  RTEMS_RECORD_IP6_OUTPUT,
+  RTEMS_RECORD_IP_INPUT,
+  RTEMS_RECORD_IP_OUTPUT,
+  RTEMS_RECORD_KEVENT,
+  RTEMS_RECORD_KQUEUE,
+  RTEMS_RECORD_LENGTH,
+  RTEMS_RECORD_LINK,
+  RTEMS_RECORD_LSEEK,
+  RTEMS_RECORD_MKNOD,
+  RTEMS_RECORD_MMAP,
+  RTEMS_RECORD_MOUNT,
+  RTEMS_RECORD_OPEN,
+  RTEMS_RECORD_OVERFLOW,
+  RTEMS_RECORD_PAGE_ALLOC,
+  RTEMS_RECORD_PAGE_FREE,
+  RTEMS_RECORD_POLL,
+  RTEMS_RECORD_PROCESSOR,
+  RTEMS_RECORD_PROCESSOR_MAXIMUM,
+  RTEMS_RECORD_READ,
+  RTEMS_RECORD_READLINK,
+  RTEMS_RECORD_READV,
+  RTEMS_RECORD_RECV,
+  RTEMS_RECORD_RECVFROM,
+  RTEMS_RECORD_RECVMSG,
+  RTEMS_RECORD_RENAME,
+  RTEMS_RECORD_RTEMS_BARRIER_CREATE,
+  RTEMS_RECORD_RTEMS_BARRIER_DELETE,
+  RTEMS_RECORD_RTEMS_BARRIER_RELEASE,
+  RTEMS_RECORD_RTEMS_BARRIER_WAIT,
+  RTEMS_RECORD_RTEMS_EVENT_RECEIVE,
+  RTEMS_RECORD_RTEMS_EVENT_SEND,
+  RTEMS_RECORD_RTEMS_EVENT_SYSTEM_RECEIVE,
+  RTEMS_RECORD_RTEMS_EVENT_SYSTEM_SEND,
+  RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_BROADCAST,
+  RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_CREATE,
+  RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_DELETE,
+  RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_FLUSH,
+  RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_RECEIVE,
+  RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_SEND,
+  RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_URGENT,
+  RTEMS_RECORD_RTEMS_PARTITION_CREATE,
+  RTEMS_RECORD_RTEMS_PARTITION_DELETE,
+  RTEMS_RECORD_RTEMS_PARTITION_GET_BUFFER,
+  RTEMS_RECORD_RTEMS_PARTITION_RETURN_BUFFER,
+  RTEMS_RECORD_RTEMS_RATE_MONOTONIC_CANCEL,
+  RTEMS_RECORD_RTEMS_RATE_MONOTONIC_CREATE,
+  RTEMS_RECORD_RTEMS_RATE_MONOTONIC_DELETE,
+  RTEMS_RECORD_RTEMS_RATE_MONOTONIC_PERIOD,
+  RTEMS_RECORD_RTEMS_SEMAPHORE_CREATE,
+  RTEMS_RECORD_RTEMS_SEMAPHORE_DELETE,
+  RTEMS_RECORD_RTEMS_SEMAPHORE_FLUSH,
+  RTEMS_RECORD_RTEMS_SEMAPHORE_OBTAIN,
+  RTEMS_RECORD_RTEMS_SEMAPHORE_RELEASE,
+  RTEMS_RECORD_RTEMS_TIMER_CANCEL,
+  RTEMS_RECORD_RTEMS_TIMER_CREATE,
+  RTEMS_RECORD_RTEMS_TIMER_DELETE,
+  RTEMS_RECORD_RTEMS_TIMER_FIRE_AFTER,
+  RTEMS_RECORD_RTEMS_TIMER_FIRE_WHEN,
+  RTEMS_RECORD_RTEMS_TIMER_RESET,
+  RTEMS_RECORD_RTEMS_TIMER_SERVER_FIRE_AFTER,
+  RTEMS_RECORD_RTEMS_TIMER_SERVER_FIRE_WHEN,
+  RTEMS_RECORD_SELECT,
+  RTEMS_RECORD_SEND,
+  RTEMS_RECORD_SENDMSG,
+  RTEMS_RECORD_SENDTO,
+  RTEMS_RECORD_SOCKET,
+  RTEMS_RECORD_STATVFS,
+  RTEMS_RECORD_SYMLINK,
+  RTEMS_RECORD_TAIL,
+  RTEMS_RECORD_TCP_INPUT,
+  RTEMS_RECORD_TCP_OUTPUT,
+  RTEMS_RECORD_THREAD_BEGIN,
+  RTEMS_RECORD_THREAD_CREATE,
+  RTEMS_RECORD_THREAD_DELETE,
+  RTEMS_RECORD_THREAD_EXIT,
+  RTEMS_RECORD_THREAD_EXITTED,
+  RTEMS_RECORD_THREAD_ID,
+  RTEMS_RECORD_THREAD_PRIO_CURRENT_HIGH,
+  RTEMS_RECORD_THREAD_PRIO_CURRENT_LOW,
+  RTEMS_RECORD_THREAD_PRIO_REAL_HIGH,
+  RTEMS_RECORD_THREAD_PRIO_REAL_LOW,
+  RTEMS_RECORD_THREAD_QUEUE_ENQUEUE,
+  RTEMS_RECORD_THREAD_QUEUE_ENQUEUE_STICKY,
+  RTEMS_RECORD_THREAD_QUEUE_EXTRACT,
+  RTEMS_RECORD_THREAD_QUEUE_SURRENDER,
+  RTEMS_RECORD_THREAD_QUEUE_SURRENDER_STICKY,
+  RTEMS_RECORD_THREAD_RESTART,
+  RTEMS_RECORD_THREAD_STACK_CURRENT,
+  RTEMS_RECORD_THREAD_STACK_SIZE,
+  RTEMS_RECORD_THREAD_STACK_USAGE,
+  RTEMS_RECORD_THREAD_START,
+  RTEMS_RECORD_THREAD_STATE_CLEAR,
+  RTEMS_RECORD_THREAD_STATE_SET,
+  RTEMS_RECORD_THREAD_SWITCH_IN,
+  RTEMS_RECORD_THREAD_SWITCH_OUT,
+  RTEMS_RECORD_THREAD_TERMINATE,
+  RTEMS_RECORD_UDP_INPUT,
+  RTEMS_RECORD_UDP_OUTPUT,
+  RTEMS_RECORD_UMA_ALLOC_PTR,
+  RTEMS_RECORD_UMA_ALLOC_ZONE,
+  RTEMS_RECORD_UMA_FREE_PTR,
+  RTEMS_RECORD_UMA_FREE_ZONE,
+  RTEMS_RECORD_UNLINK,
+  RTEMS_RECORD_UNMOUNT,
+  RTEMS_RECORD_UPTIME_HIGH,
+  RTEMS_RECORD_UPTIME_LOW,
+  RTEMS_RECORD_WORKSPACE_ALLOC,
+  RTEMS_RECORD_WORKSPACE_FREE,
+  RTEMS_RECORD_WORKSPACE_SIZE,
+  RTEMS_RECORD_WORKSPACE_USAGE,
+  RTEMS_RECORD_WRITE,
+  RTEMS_RECORD_WRITEV,
+
+  /* There are 512 events reserved for the user */
+  RTEMS_RECORD_USER = 512,
+
+  RTEMS_RECORD_LAST = 1023
+} rtems_record_event;
+
+/**
+ * @brief Bits in the record item event member reserved for the actual event.
+ */
+#define RTEMS_RECORD_EVENT_BITS 10
+
+/**
+ * @brief Bits in the record item event member reserved for the time of the
+ * event.
+ */
+#define RTEMS_RECORD_TIME_BITS 22
+
+/**
+ * @brief Builds a time event for the specified time stamp and event.
+ *
+ * The events are stored in the record item with a time stamp.  There are 22
+ * bits allocated to the time stamp and 10 bits allocated to the event.  The 22
+ * bits are enough to get reliable time stamps on a system with a 4GHz CPU
+ * counter and a 1000Hz clock tick.
+ */
+#define RTEMS_RECORD_TIME_EVENT( time, event ) \
+  ( ( ( time ) << RTEMS_RECORD_EVENT_BITS ) | ( event ) )
+
+/**
+ * @brief Gets the time of a time event.
+ */
+#define RTEMS_RECORD_GET_TIME( time_event ) \
+  ( ( time_event ) >> RTEMS_RECORD_EVENT_BITS )
+
+/**
+ * @brief Gets the event of a time event.
+ */
+#define RTEMS_RECORD_GET_EVENT( time_event ) \
+  ( ( time_event ) & ( ( 1U << RTEMS_RECORD_EVENT_BITS ) - 1U ) )
+
+/**
+ * @brief The record data integer type.
+ *
+ * It is big enough to store 32-bit integers and pointers.
+ */
+typedef unsigned long rtems_record_data;
+
+/**
+ * @brief The native record item.
+ */
+typedef struct __attribute__((__packed__)) {
+  uint32_t          event;
+  rtems_record_data data;
+} rtems_record_item;
+
+/**
+ * @brief The 32-bit format record item.
+ */
+typedef struct {
+  uint32_t event;
+  uint32_t data;
+} rtems_record_item_32;
+
+/**
+ * @brief The 64-bit format record item.
+ */
+typedef struct __attribute__((__packed__)) {
+  uint32_t event;
+  uint64_t data;
+} rtems_record_item_64;
+
+const char *rtems_record_event_text( rtems_record_event event );
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RTEMS_RECORDDATA_H */
diff --git a/misc/wscript b/misc/wscript
index 95c7bde..d5070c1 100644
--- a/misc/wscript
+++ b/misc/wscript
@@ -67,5 +67,15 @@ def build(bld):
                 cflags = conf['cflags'] + conf['warningflags'],
                 linkflags = conf['linkflags'])
 
+    #
+    # Build rtems-record
+    #
+    bld.program(target = 'rtems-record',
+                source = ['record/record-client.c', 'record/record-main.c', 'record/record-text.c'],
+                includes = ['record'],
+                defines = defines,
+                cflags = conf['cflags'] + conf['warningflags'],
+                linkflags = conf['linkflags'])
+
 def tags(ctx):
     ctx.exec_command('etags $(find . -name \*.[sSch])', shell = True)
-- 
2.16.4

_______________________________________________
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel

Reply via email to