summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-28 11:36:11 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-12-15 09:48:36 +0100
commit4880ce13bf981dac4b61e806d976217c6731e80b (patch)
tree354e37b5b4b27397858fff2475cee15b2460a28b
parentcc9f28a631cd3428fff21142f2929e6d5c8d869a (diff)
Add low level recording support
-rw-r--r--cpukit/Makefile.am4
-rw-r--r--cpukit/headers.am2
-rw-r--r--cpukit/include/rtems/confdefs.h30
-rw-r--r--cpukit/include/rtems/record.h251
-rw-r--r--cpukit/include/rtems/recorddata.h155
-rw-r--r--cpukit/include/rtems/score/percpu.h4
-rw-r--r--cpukit/include/rtems/sysinit.h1
-rw-r--r--cpukit/libmisc/capture/record-server.c268
-rw-r--r--cpukit/libmisc/capture/record-static.c110
-rw-r--r--cpukit/libmisc/capture/record-userext.c121
-rw-r--r--cpukit/libmisc/capture/record.c114
-rw-r--r--testsuites/libtests/Makefile.am18
-rw-r--r--testsuites/libtests/configure.ac2
-rw-r--r--testsuites/libtests/record01/init.c580
-rw-r--r--testsuites/libtests/record01/record01.doc0
-rw-r--r--testsuites/libtests/record01/record01.scn0
-rw-r--r--testsuites/libtests/record02/init.c74
-rw-r--r--testsuites/libtests/record02/record02.doc0
-rw-r--r--testsuites/libtests/record02/record02.scn0
19 files changed, 1734 insertions, 0 deletions
diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am
index 143c5d6686..47ecda0c44 100644
--- a/cpukit/Makefile.am
+++ b/cpukit/Makefile.am
@@ -274,6 +274,10 @@ librtemscpu_a_SOURCES += libmisc/capture/capture.c
librtemscpu_a_SOURCES += libmisc/capture/capture-cli.c
librtemscpu_a_SOURCES += libmisc/capture/capture_support.c
librtemscpu_a_SOURCES += libmisc/capture/capture_user_extension.c
+librtemscpu_a_SOURCES += libmisc/capture/record.c
+librtemscpu_a_SOURCES += libmisc/capture/record-server.c
+librtemscpu_a_SOURCES += libmisc/capture/record-static.c
+librtemscpu_a_SOURCES += libmisc/capture/record-userext.c
librtemscpu_a_SOURCES += libmisc/capture/rtems-trace-buffer-vars.c
librtemscpu_a_SOURCES += libmisc/cpuuse/cpuinforeport.c
librtemscpu_a_SOURCES += libmisc/cpuuse/cpuusagedata.c
diff --git a/cpukit/headers.am b/cpukit/headers.am
index 3ab97a95ca..5ab45c8fb1 100644
--- a/cpukit/headers.am
+++ b/cpukit/headers.am
@@ -136,6 +136,8 @@ include_rtems_HEADERS += include/rtems/qreslib.h
include_rtems_HEADERS += include/rtems/ramdisk.h
include_rtems_HEADERS += include/rtems/rbheap.h
include_rtems_HEADERS += include/rtems/rbtree.h
+include_rtems_HEADERS += include/rtems/record.h
+include_rtems_HEADERS += include/rtems/recorddata.h
include_rtems_HEADERS += include/rtems/ringbuf.h
include_rtems_HEADERS += include/rtems/rtc.h
include_rtems_HEADERS += include/rtems/rtems-debugger-remote-tcp.h
diff --git a/cpukit/include/rtems/confdefs.h b/cpukit/include/rtems/confdefs.h
index 45e70313fb..26becd370b 100644
--- a/cpukit/include/rtems/confdefs.h
+++ b/cpukit/include/rtems/confdefs.h
@@ -195,6 +195,33 @@ extern rtems_initialization_tasks_table Initialization_tasks[];
#warning "CONFIGURE_SMP_APPLICATION is obsolete since RTEMS 5.1"
#endif
+#if defined(CONFIGURE_INIT) && CONFIGURE_RECORD_PER_PROCESSOR_COUNT > 0
+#include <rtems/record.h>
+
+#if (CONFIGURE_RECORD_PER_PROCESSOR_COUNT & (CONFIGURE_RECORD_PER_PROCESSOR_COUNT - 1)) != 0
+ #error "CONFIGURE_RECORD_PER_PROCESSOR_COUNT must be a power of two"
+#endif
+
+#if CONFIGURE_RECORD_PER_PROCESSOR_COUNT < 16
+ #error "CONFIGURE_RECORD_PER_PROCESSOR_COUNT must be at least 16"
+#endif
+
+const unsigned int _Record_Item_count = CONFIGURE_RECORD_PER_PROCESSOR_COUNT;
+
+struct Record_Configured_control {
+ Record_Control Control;
+ rtems_record_item Items[ CONFIGURE_RECORD_PER_PROCESSOR_COUNT ];
+};
+
+PER_CPU_DATA_ITEM( Record_Configured_control, _Record_Per_CPU );
+
+RTEMS_SYSINIT_ITEM(
+ _Record_Initialize,
+ RTEMS_SYSINIT_RECORD,
+ RTEMS_SYSINIT_ORDER_MIDDLE
+);
+#endif /* CONFIGURE_RECORD_PER_PROCESSOR_COUNT */
+
/*
* This sets up the resources for the FIFOs/pipes.
*/
@@ -2123,6 +2150,9 @@ extern rtems_initialization_tasks_table Initialization_tasks[];
defined(CONFIGURE_STACK_CHECKER_ENABLED) || \
(defined(RTEMS_NEWLIB) && !defined(CONFIGURE_DISABLE_NEWLIB_REENTRANCY))
static const rtems_extensions_table Configuration_Initial_Extensions[] = {
+ #if CONFIGURE_RECORD_PER_PROCESSOR_COUNT > 0
+ RECORD_EXTENSION,
+ #endif
#if !defined(CONFIGURE_DISABLE_NEWLIB_REENTRANCY)
RTEMS_NEWLIB_EXTENSION,
#endif
diff --git a/cpukit/include/rtems/record.h b/cpukit/include/rtems/record.h
new file mode 100644
index 0000000000..8535ce4d06
--- /dev/null
+++ b/cpukit/include/rtems/record.h
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+#ifndef _RTEMS_RECORD_H
+#define _RTEMS_RECORD_H
+
+#include <rtems/recorddata.h>
+
+#include <rtems/score/atomic.h>
+#include <rtems/score/percpudata.h>
+#include <rtems/score/watchdog.h>
+#include <rtems/rtems/intr.h>
+#include <rtems/rtems/tasks.h>
+#include <rtems/counter.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct Record_Control {
+ Atomic_Uint head;
+ Atomic_Uint tail;
+ unsigned int mask;
+ Watchdog_Control Watchdog;
+ rtems_record_item Items[ RTEMS_ZERO_LENGTH_ARRAY ];
+};
+
+typedef struct Record_Control Record_Control;
+
+typedef struct Record_Configured_control Record_Configured_control;
+
+typedef struct {
+ Record_Control *control;
+ unsigned int head;
+ unsigned int increment;
+ unsigned int final_head;
+ uint32_t now;
+ rtems_interrupt_level level;
+} rtems_record_context;
+
+PER_CPU_DATA_ITEM_DECLARE( Record_Configured_control, _Record_Per_CPU );
+
+extern const unsigned int _Record_Item_count;
+
+void _Record_Initialize( void );
+
+bool _Record_Thread_create(
+ struct _Thread_Control *executing,
+ struct _Thread_Control *created
+);
+
+void _Record_Thread_delete(
+ struct _Thread_Control *executing,
+ struct _Thread_Control *deleted
+);
+
+void _Record_Thread_switch(
+ struct _Thread_Control *executing,
+ struct _Thread_Control *heir
+);
+
+void _Record_Thread_terminate(
+ struct _Thread_Control *executing
+);
+
+#define RECORD_EXTENSION \
+ { \
+ _Record_Thread_create, \
+ NULL, NULL, \
+ _Record_Thread_delete, \
+ _Record_Thread_switch, \
+ NULL, NULL, NULL, \
+ _Record_Thread_terminate \
+ }
+
+static inline unsigned int _Record_Capacity(
+ const Record_Control *control,
+ unsigned int head,
+ unsigned int tail
+)
+{
+ return ( tail - head - 1U ) & control->mask;
+}
+
+static inline unsigned int _Record_Increment(
+ const Record_Control *control,
+ unsigned int index,
+ unsigned int increment
+)
+{
+ return ( index + increment ) & control->mask;
+}
+
+static inline unsigned int _Record_Head( const Record_Control *control )
+{
+ return _Atomic_Load_uint( &control->head, ATOMIC_ORDER_RELAXED );
+}
+
+static inline unsigned int _Record_Tail( const Record_Control *control )
+{
+ return _Atomic_Load_uint( &control->tail, ATOMIC_ORDER_RELAXED );
+}
+
+static inline rtems_counter_ticks _Record_Now( void )
+{
+ return rtems_counter_read();
+}
+
+static inline void rtems_record_prepare_inline(
+ rtems_record_context *context,
+ unsigned int count
+)
+{
+ rtems_interrupt_level level;
+ const Per_CPU_Control *cpu_self;
+ Record_Control *control;
+ unsigned int head;
+ unsigned int tail;
+ unsigned int capacity;
+
+ rtems_interrupt_local_disable( level );
+ context->now = RTEMS_RECORD_TIME_EVENT( _Record_Now(), 0 );
+ context->level = level;
+ cpu_self = _Per_CPU_Get();
+ control = cpu_self->record;
+ context->control = control;
+ head = _Record_Head( control );
+ tail = _Record_Tail( control );
+ capacity = _Record_Capacity( control, head, tail );
+ context->head = head;
+
+ if ( RTEMS_PREDICT_TRUE( capacity > count ) ) {
+ context->increment = 1;
+ } else {
+ context->increment = 0;
+ count = capacity > 0 ? 1U : 0U;
+ }
+
+ context->final_head = _Record_Increment( control, head, count );
+}
+
+static inline void rtems_record_add_inline(
+ rtems_record_context *context,
+ rtems_record_event event,
+ rtems_record_data data
+)
+{
+ Record_Control *control;
+ rtems_record_item *item;
+ unsigned int head;
+
+ control = context->control;
+ head = context->head;
+ item = &control->Items[ head ];
+ context->head = _Record_Increment( control, head, context->increment );
+
+ item->event = RTEMS_RECORD_TIME_EVENT(
+ context->now,
+ event & -( (uint32_t) context->increment )
+ );
+ item->data = data;
+}
+
+static inline void rtems_record_commit_inline(
+ rtems_record_context *context,
+ rtems_record_event event,
+ rtems_record_data data
+)
+{
+ rtems_record_add_inline( context, event, data );
+ _Atomic_Store_uint(
+ &context->control->head,
+ context->final_head,
+ ATOMIC_ORDER_RELEASE
+ );
+ rtems_interrupt_local_enable( context->level );
+}
+
+static inline bool rtems_record_is_overflow(
+ const rtems_record_context *context
+)
+{
+ return context->increment == 0;
+}
+
+rtems_record_context *rtems_record_prepare(
+ rtems_record_context *context,
+ unsigned int count
+);
+
+rtems_record_context *rtems_record_add(
+ rtems_record_context *context,
+ rtems_record_event event,
+ rtems_record_data data
+);
+
+void rtems_record_commit(
+ rtems_record_context *context,
+ rtems_record_event event,
+ rtems_record_data data
+);
+
+void rtems_record_produce( rtems_record_event event, rtems_record_data data );
+
+typedef void ( *rtems_record_visitor )(
+ const rtems_record_item *items,
+ size_t count,
+ void *arg
+);
+
+void rtems_record_drain( rtems_record_visitor visitor, void *arg );
+
+ssize_t rtems_record_writev( int fd, bool *written );
+
+void rtems_record_server( uint16_t port, rtems_interval period );
+
+rtems_status_code rtems_record_start_server(
+ rtems_task_priority priority,
+ uint16_t port,
+ rtems_interval period
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RTEMS_RECORD_H */
diff --git a/cpukit/include/rtems/recorddata.h b/cpukit/include/rtems/recorddata.h
new file mode 100644
index 0000000000..39d465aa7b
--- /dev/null
+++ b/cpukit/include/rtems/recorddata.h
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#ifndef _RTEMS_RECORDDATA_H
+#define _RTEMS_RECORDDATA_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define RTEMS_RECORD_VERSION 1
+
+#define RTEMS_RECORD_FORMAT_LE_32 0x11111111
+
+#define RTEMS_RECORD_FORMAT_LE_64 0x22222222
+
+#define RTEMS_RECORD_FORMAT_BE_32 0x33333333
+
+#define RTEMS_RECORD_FORMAT_BE_64 0x44444444
+
+typedef enum {
+ /* There are 512 events reserved for the system */
+ RTEMS_RECORD_EVENT_OVERFLOW,
+ RTEMS_RECORD_EVENT_VERSION,
+
+ /* Keep in lexicographical order */
+ RTEMS_RECORD_EVENT_ACCEPT,
+ RTEMS_RECORD_EVENT_BIND,
+ RTEMS_RECORD_EVENT_CHOWN,
+ RTEMS_RECORD_EVENT_CLOSE,
+ RTEMS_RECORD_EVENT_CONNECT,
+ RTEMS_RECORD_EVENT_FCHMOD,
+ RTEMS_RECORD_EVENT_FCNTL,
+ RTEMS_RECORD_EVENT_FDATASYNC,
+ RTEMS_RECORD_EVENT_FSTAT,
+ RTEMS_RECORD_EVENT_FSYNC,
+ RTEMS_RECORD_EVENT_FTRUNCATE,
+ RTEMS_RECORD_EVENT_HEAP_ALLOC,
+ RTEMS_RECORD_EVENT_HEAP_FREE,
+ RTEMS_RECORD_EVENT_HEAP_SIZE,
+ RTEMS_RECORD_EVENT_HEAP_USAGE,
+ RTEMS_RECORD_EVENT_INTERUPT_BEGIN,
+ RTEMS_RECORD_EVENT_INTERUPT_END,
+ RTEMS_RECORD_EVENT_INTERUPT_INSTALL,
+ RTEMS_RECORD_EVENT_INTERUPT_REMOVE,
+ RTEMS_RECORD_EVENT_IOCTL,
+ RTEMS_RECORD_EVENT_KEVENT,
+ RTEMS_RECORD_EVENT_KQUEUE,
+ RTEMS_RECORD_EVENT_LINK,
+ RTEMS_RECORD_EVENT_LSEEK,
+ RTEMS_RECORD_EVENT_MKNOD,
+ RTEMS_RECORD_EVENT_MMAP,
+ RTEMS_RECORD_EVENT_MOUNT,
+ RTEMS_RECORD_EVENT_OPEN,
+ RTEMS_RECORD_EVENT_POLL,
+ RTEMS_RECORD_EVENT_PROCESSOR,
+ RTEMS_RECORD_EVENT_READ,
+ RTEMS_RECORD_EVENT_READLINK,
+ RTEMS_RECORD_EVENT_READV,
+ RTEMS_RECORD_EVENT_RECV,
+ RTEMS_RECORD_EVENT_RECVFROM,
+ RTEMS_RECORD_EVENT_RECVMSG,
+ RTEMS_RECORD_EVENT_RENAME,
+ RTEMS_RECORD_EVENT_SELECT,
+ RTEMS_RECORD_EVENT_SEND,
+ RTEMS_RECORD_EVENT_SENDMSG,
+ RTEMS_RECORD_EVENT_SENDTO,
+ RTEMS_RECORD_EVENT_SOCKET,
+ RTEMS_RECORD_EVENT_STATVFS,
+ RTEMS_RECORD_EVENT_SYMLINK,
+ RTEMS_RECORD_EVENT_THREAD_CREATED,
+ RTEMS_RECORD_EVENT_THREAD_CREATED_BY,
+ RTEMS_RECORD_EVENT_THREAD_DELETED,
+ RTEMS_RECORD_EVENT_THREAD_DELETED_BY,
+ RTEMS_RECORD_EVENT_THREAD_PRIO_CURRENT_HIGH,
+ RTEMS_RECORD_EVENT_THREAD_PRIO_CURRENT_LOW,
+ RTEMS_RECORD_EVENT_THREAD_PRIO_REAL_HIGH,
+ RTEMS_RECORD_EVENT_THREAD_PRIO_REAL_LOW,
+ RTEMS_RECORD_EVENT_THREAD_STACK_CURRENT,
+ RTEMS_RECORD_EVENT_THREAD_STACK_SIZE,
+ RTEMS_RECORD_EVENT_THREAD_STACK_USAGE,
+ RTEMS_RECORD_EVENT_THREAD_SWITCH_IN,
+ RTEMS_RECORD_EVENT_THREAD_SWITCH_OUT,
+ RTEMS_RECORD_EVENT_THREAD_TERMINATED,
+ RTEMS_RECORD_EVENT_UMA_ALLOC_PTR,
+ RTEMS_RECORD_EVENT_UMA_ALLOC_ZONE,
+ RTEMS_RECORD_EVENT_UMA_FREE_PTR,
+ RTEMS_RECORD_EVENT_UMA_FREE_ZONE,
+ RTEMS_RECORD_EVENT_UNLINK,
+ RTEMS_RECORD_EVENT_UNMOUNT,
+ RTEMS_RECORD_EVENT_UPTIME_HIGH,
+ RTEMS_RECORD_EVENT_UPTIME_LOW,
+ RTEMS_RECORD_EVENT_WORKSPACE_ALLOC,
+ RTEMS_RECORD_EVENT_WORKSPACE_FREE,
+ RTEMS_RECORD_EVENT_WORKSPACE_SIZE,
+ RTEMS_RECORD_EVENT_WORKSPACE_USAGE,
+ RTEMS_RECORD_EVENT_WRITE,
+ RTEMS_RECORD_EVENT_WRITEV,
+
+ /* There are 512 events reserved for the user */
+ RTEMS_RECORD_EVENT_USER = 512,
+
+ RTEMS_RECORD_EVENT_LAST = 1023
+} rtems_record_event;
+
+#define RTEMS_RECORD_TIME_EVENT( time, event ) ( ( ( time ) << 10 ) | ( event ) )
+
+typedef unsigned long rtems_record_data;
+
+typedef struct __attribute__((__packed__)) {
+ uint32_t event;
+ rtems_record_data data;
+} rtems_record_item;
+
+typedef struct __attribute__((__packed__)) {
+ uint32_t event;
+ uint32_t data;
+} rtems_record_item_32;
+
+typedef struct __attribute__((__packed__)) {
+ uint32_t event;
+ uint64_t data;
+} rtems_record_item_64;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RTEMS_RECORDDATA_H */
diff --git a/cpukit/include/rtems/score/percpu.h b/cpukit/include/rtems/score/percpu.h
index 712d1cde36..22c5c30a00 100644
--- a/cpukit/include/rtems/score/percpu.h
+++ b/cpukit/include/rtems/score/percpu.h
@@ -69,6 +69,8 @@ extern "C" {
#if !defined( ASM )
+struct Record_Control;
+
struct _Thread_Control;
struct Scheduler_Context;
@@ -513,6 +515,8 @@ typedef struct Per_CPU_Control {
bool boot;
#endif
+ struct Record_Control *record;
+
Per_CPU_Stats Stats;
} Per_CPU_Control;
diff --git a/cpukit/include/rtems/sysinit.h b/cpukit/include/rtems/sysinit.h
index 2c718af8de..60fd9ddf41 100644
--- a/cpukit/include/rtems/sysinit.h
+++ b/cpukit/include/rtems/sysinit.h
@@ -33,6 +33,7 @@ extern "C" {
#define RTEMS_SYSINIT_MP_EARLY 000500
#define RTEMS_SYSINIT_DATA_STRUCTURES 000600
#define RTEMS_SYSINIT_MP 000700
+#define RTEMS_SYSINIT_RECORD 000800
#define RTEMS_SYSINIT_USER_EXTENSIONS 000900
#define RTEMS_SYSINIT_CLASSIC_TASKS 000a00
#define RTEMS_SYSINIT_CLASSIC_TIMER 000b00
diff --git a/cpukit/libmisc/capture/record-server.c b/cpukit/libmisc/capture/record-server.c
new file mode 100644
index 0000000000..f8ba186a35
--- /dev/null
+++ b/cpukit/libmisc/capture/record-server.c
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ */
+
+#include <rtems/record.h>
+
+#include <rtems.h>
+
+#include <sys/endian.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+
+#ifdef RTEMS_SMP
+#define CHUNKS (3 * CPU_MAXIMUM_PROCESSORS)
+#else
+#define CHUNKS 3
+#endif
+
+typedef struct {
+ int available;
+ struct iovec *current;
+ struct iovec iov[CHUNKS];
+} writev_visitor_context;
+
+static void writev_visitor(
+ const rtems_record_item *items,
+ size_t count,
+ void *arg
+)
+{
+ writev_visitor_context *ctx;
+
+ ctx = arg;
+
+ if ( ctx->available > 0 ) {
+ ctx->current->iov_base = RTEMS_DECONST( rtems_record_item *, items );
+ ctx->current->iov_len = count * sizeof( *items );
+ --ctx->available;
+ ++ctx->current;
+ }
+}
+
+ssize_t rtems_record_writev( int fd, bool *written )
+{
+ writev_visitor_context ctx;
+ int n;
+
+ ctx.available = CHUNKS;
+ ctx.current = &ctx.iov[ 0 ];
+ rtems_record_drain( writev_visitor, &ctx );
+ n = CHUNKS - ctx.available;
+
+ if ( n > 0 ) {
+ *written = true;
+ return writev( fd, &ctx.iov[ 0 ], CHUNKS - ctx.available );
+ } else {
+ *written = false;
+ return 0;
+ }
+}
+
+#define WAKEUP_EVENT RTEMS_EVENT_0
+
+static void wakeup( rtems_id task )
+{
+ (void) rtems_event_send( task, WAKEUP_EVENT );
+}
+
+static void wait( rtems_option options )
+{
+ rtems_event_set events;
+
+ (void) rtems_event_receive(
+ WAKEUP_EVENT,
+ RTEMS_EVENT_ANY | options,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+}
+
+static void wakeup_timer( rtems_id timer, void *arg )
+{
+ rtems_id *server;
+
+ server = arg;
+ wakeup( *server );
+ (void) rtems_timer_reset( timer );
+}
+
+static const struct RTEMS_PACKED {
+ uint32_t format;
+ rtems_record_item version;
+} header = {
+#if BYTE_ORDER == LITTLE_ENDIAN
+#if __INTPTR_WIDTH__ == 32
+ .format = RTEMS_RECORD_FORMAT_LE_32,
+#elif __INTPTR_WIDTH__ == 64
+ .format = RTEMS_RECORD_FORMAT_LE_64,
+#error "unexpected __INTPTR_WIDTH__"
+#endif
+#elif BYTE_ORDER == BIG_ENDIAN
+#if __INTPTR_WIDTH__ == 32
+ .format = RTEMS_RECORD_FORMAT_BE_32,
+#elif __INTPTR_WIDTH__ == 64
+ .format = RTEMS_RECORD_FORMAT_BE_64,
+#else
+#error "unexpected __INTPTR_WIDTH__"
+#endif
+#else
+#error "unexpected BYTE_ORDER"
+#endif
+ .version = {
+ .event = RTEMS_RECORD_TIME_EVENT( 0, RTEMS_RECORD_EVENT_VERSION ),
+ .data = RTEMS_RECORD_VERSION
+ }
+};
+
+void rtems_record_server( uint16_t port, rtems_interval period )
+{
+ rtems_status_code sc;
+ rtems_id self;
+ rtems_id timer;
+ struct sockaddr_in addr;
+ int sd;
+ int rv;
+
+ sd = -1;
+ self = rtems_task_self();
+
+ sc = rtems_timer_create( rtems_build_name( 'R', 'C', 'R', 'D' ), &timer );
+ if ( sc != RTEMS_SUCCESSFUL ) {
+ return;
+ }
+
+ sd = socket( PF_INET, SOCK_STREAM, 0 );
+ if (sd < 0) {
+ goto error;
+ }
+
+ memset( &addr, 0, sizeof( addr ) );
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons( port );
+ addr.sin_addr.s_addr = htonl( INADDR_ANY );
+
+ rv = bind( sd, (const struct sockaddr *) &addr, sizeof( addr ) );
+ if (rv != 0) {
+ goto error;
+ }
+
+ rv = listen( sd, 0 );
+ if (rv != 0) {
+ goto error;
+ }
+
+ while ( true ) {
+ int cd;
+ bool written;
+ ssize_t n;
+
+ cd = accept( sd, NULL, NULL );
+
+ if ( cd < 0 ) {
+ break;
+ }
+
+ wait( RTEMS_NO_WAIT );
+ (void) rtems_timer_fire_after( timer, period, wakeup_timer, &self );
+ (void) write( cd, &header, sizeof( header ) );
+
+ while ( true ) {
+ n = rtems_record_writev( cd, &written );
+
+ if ( written && n <= 0 ) {
+ break;
+ }
+
+ wait( RTEMS_WAIT );
+ }
+
+ (void) rtems_timer_cancel( timer );
+ (void) close( cd );
+ }
+
+error:
+
+ (void) close( sd );
+ (void) rtems_timer_delete( timer );
+}
+
+typedef struct {
+ rtems_id task;
+ uint16_t port;
+ rtems_interval period;
+} server_arg;
+
+static void server( rtems_task_argument arg )
+{
+ server_arg *sarg;
+ uint16_t port;
+ rtems_interval period;
+
+ sarg = (server_arg *) arg;
+ port = sarg->port;
+ period = sarg->period;
+ wakeup(sarg->task);
+ rtems_record_server( port, period );
+ rtems_task_exit();
+}
+
+rtems_status_code rtems_record_start_server(
+ rtems_task_priority priority,
+ uint16_t port,
+ rtems_interval period
+)
+{
+ rtems_status_code sc;
+ rtems_id id;
+ server_arg sarg;
+
+ sarg.port = port;
+ sarg.period = period;
+ sarg.task = rtems_task_self();
+
+ sc = rtems_task_create(
+ rtems_build_name( 'R', 'C', 'R', 'D' ),
+ priority,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &id
+ );
+ if ( sc != RTEMS_SUCCESSFUL ) {
+ return sc;
+ }
+
+ (void) rtems_task_start( id, server, (rtems_task_argument) &sarg );
+ wait( RTEMS_WAIT );
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libmisc/capture/record-static.c b/cpukit/libmisc/capture/record-static.c
new file mode 100644
index 0000000000..f6dfd8b000
--- /dev/null
+++ b/cpukit/libmisc/capture/record-static.c
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#include <rtems/record.h>
+#include <rtems/config.h>
+#include <rtems/sysinit.h>
+#include <rtems/score/timecounter.h>
+#include <rtems/score/watchdogimpl.h>
+
+void _Record_Initialize( void )
+{
+ uint32_t cpu_max;
+ uint32_t cpu_index;
+ uintptr_t offset;
+
+ cpu_max = rtems_configuration_get_maximum_processors();
+ offset = PER_CPU_DATA_OFFSET( _Record_Per_CPU );
+
+ for ( cpu_index = 0; cpu_index < cpu_max; ++cpu_index ) {
+ Per_CPU_Control *cpu;
+ Record_Control *control;
+
+ cpu = _Per_CPU_Get_by_index( cpu_index );
+ control = PER_CPU_DATA_GET_BY_OFFSET( cpu, Record_Control, offset );
+ control->mask = _Record_Item_count - 1U;
+ cpu->record = control;
+ }
+}
+
+static void _Record_Watchdog(
+ Watchdog_Control *watchdog,
+ Per_CPU_Control *cpu
+)
+{
+ rtems_record_context context_storage;
+ rtems_record_context *context;
+ sbintime_t now;
+
+ _Watchdog_Per_CPU_reinsert_ticks(
+ watchdog,
+ cpu,
+ _Watchdog_Ticks_per_second
+ );
+
+ context = rtems_record_prepare( &context_storage, 2 );
+ now = _Timecounter_Sbinuptime();
+ context = rtems_record_add(
+ context,
+ RTEMS_RECORD_EVENT_UPTIME_LOW,
+ (uint32_t) ( now >> 0 )
+ );
+ rtems_record_commit(
+ context,
+ RTEMS_RECORD_EVENT_UPTIME_HIGH,
+ (uint32_t) ( now >> 32 )
+ );
+}
+
+static void _Record_Initialize_watchdogs( void )
+{
+ uint32_t cpu_max;
+ uint32_t cpu_index;
+
+ cpu_max = rtems_configuration_get_maximum_processors();
+
+ for ( cpu_index = 0; cpu_index < cpu_max; ++cpu_index ) {
+ Per_CPU_Control *cpu;
+ Record_Control *control;
+
+ cpu = _Per_CPU_Get_by_index( cpu_index );
+ control = cpu->record;
+ _Watchdog_Preinitialize( &control->Watchdog, cpu );
+ _Watchdog_Initialize( &control->Watchdog, _Record_Watchdog );
+ _Watchdog_Per_CPU_reinsert_ticks(
+ &control->Watchdog,
+ cpu,
+ _Watchdog_Ticks_per_second
+ );
+ }
+}
+
+RTEMS_SYSINIT_ITEM(
+ _Record_Initialize_watchdogs,
+ RTEMS_SYSINIT_DEVICE_DRIVERS,
+ RTEMS_SYSINIT_ORDER_LAST
+);
diff --git a/cpukit/libmisc/capture/record-userext.c b/cpukit/libmisc/capture/record-userext.c
new file mode 100644
index 0000000000..39f61eace8
--- /dev/null
+++ b/cpukit/libmisc/capture/record-userext.c
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#include <rtems/record.h>
+#include <rtems/score/thread.h>
+
+bool _Record_Thread_create(
+ struct _Thread_Control *executing,
+ struct _Thread_Control *created
+)
+{
+ rtems_record_context context_storage;
+ rtems_record_context *context;
+ Objects_Id id;
+
+ context = rtems_record_prepare( &context_storage, 2 );
+ context = rtems_record_add(
+ context,
+ RTEMS_RECORD_EVENT_THREAD_CREATED,
+ created->Object.id
+ );
+
+ /* The idle threads are created without an executing thread */
+ if ( executing != NULL ) {
+ id = executing->Object.id;
+ } else {
+ id = 0;
+ }
+
+ rtems_record_commit(
+ context,
+ RTEMS_RECORD_EVENT_THREAD_CREATED_BY,
+ id
+ );
+
+ return true;
+}
+
+void _Record_Thread_delete(
+ struct _Thread_Control *executing,
+ struct _Thread_Control *deleted
+)
+{
+ rtems_record_context context_storage;
+ rtems_record_context *context;
+
+ context = rtems_record_prepare( &context_storage, 2 );
+ context = rtems_record_add(
+ context,
+ RTEMS_RECORD_EVENT_THREAD_DELETED,
+ deleted->Object.id
+ );
+ rtems_record_commit(
+ context,
+ RTEMS_RECORD_EVENT_THREAD_DELETED_BY,
+ executing->Object.id
+ );
+}
+
+void _Record_Thread_switch(
+ struct _Thread_Control *executing,
+ struct _Thread_Control *heir
+)
+{
+ rtems_record_context context_storage;
+ rtems_record_context *context;
+
+ context = rtems_record_prepare( &context_storage, 3 );
+ context = rtems_record_add(
+ context,
+ RTEMS_RECORD_EVENT_THREAD_SWITCH_OUT,
+ executing->Object.id
+ );
+ context = rtems_record_add(
+ context,
+ RTEMS_RECORD_EVENT_THREAD_STACK_CURRENT,
+#if defined(__GNUC__)
+ (uintptr_t) __builtin_frame_address( 0 )
+ - (uintptr_t) executing->Start.Initial_stack.area
+#else
+ 0
+#endif
+ );
+ rtems_record_commit(
+ context,
+ RTEMS_RECORD_EVENT_THREAD_SWITCH_IN,
+ heir->Object.id
+ );
+}
+
+void _Record_Thread_terminate( struct _Thread_Control *executing )
+{
+ rtems_record_produce(
+ RTEMS_RECORD_EVENT_THREAD_TERMINATED,
+ executing->Object.id
+ );
+}
diff --git a/cpukit/libmisc/capture/record.c b/cpukit/libmisc/capture/record.c
new file mode 100644
index 0000000000..b87043fbbc
--- /dev/null
+++ b/cpukit/libmisc/capture/record.c
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#include <rtems/record.h>
+#include <rtems/config.h>
+
+#include <string.h>
+
+rtems_record_context *rtems_record_prepare(
+ rtems_record_context *context,
+ unsigned int count
+)
+{
+ rtems_record_prepare_inline( context, count );
+ return context;
+}
+
+rtems_record_context *rtems_record_add(
+ rtems_record_context *context,
+ rtems_record_event event,
+ rtems_record_data data
+)
+{
+ rtems_record_add_inline( context, event, data );
+ return context;
+}
+
+void rtems_record_commit(
+ rtems_record_context *context,
+ rtems_record_event event,
+ rtems_record_data data
+)
+{
+ rtems_record_commit_inline( context, event, data );
+}
+
+void rtems_record_produce( rtems_record_event event, rtems_record_data data )
+{
+ rtems_record_context context;
+
+ rtems_record_prepare_inline( &context, 1 );
+ rtems_record_commit_inline( &context, event, data );
+}
+
+void rtems_record_drain( rtems_record_visitor visitor, void *arg )
+{
+ rtems_record_item item;
+ uint32_t cpu_max;
+ uint32_t cpu_index;
+
+ memset( &item, 0, sizeof( item ) );
+ item.event = RTEMS_RECORD_EVENT_PROCESSOR;
+ cpu_max = rtems_configuration_get_maximum_processors();
+
+ for ( cpu_index = 0; cpu_index < cpu_max; ++cpu_index ) {
+ Per_CPU_Control *cpu;
+ Record_Control *control;
+ unsigned int tail;
+ unsigned int head;
+
+ cpu = _Per_CPU_Get_by_index( cpu_index );
+ control = cpu->record;
+ item.data = cpu_index;
+
+ tail = _Record_Tail( control );
+ head = _Atomic_Load_uint( &control->head, ATOMIC_ORDER_ACQUIRE );
+
+ if ( tail == head ) {
+ continue;
+ }
+
+ ( *visitor )( &item, 1, arg );
+
+ if ( tail < head ) {
+ ( *visitor )( &control->Items[ tail ], head - tail, arg );
+ } else {
+ ( *visitor )( &control->Items[ tail ], control->mask + 1 - tail, arg );
+
+ if ( head > 0 ) {
+ ( *visitor )( &control->Items[ 0 ], head, arg );
+ }
+ }
+
+ _Atomic_Store_uint(
+ &control->tail,
+ head,
+ ATOMIC_ORDER_RELAXED
+ );
+ }
+}
diff --git a/testsuites/libtests/Makefile.am b/testsuites/libtests/Makefile.am
index 810f65db8e..765096c443 100644
--- a/testsuites/libtests/Makefile.am
+++ b/testsuites/libtests/Makefile.am
@@ -994,6 +994,24 @@ realloc_norun_SOURCES = POSIX/realloc.c
realloc_norun_LDADD = $(RTEMS_ROOT)cpukit/librtemsdefaultconfig.a $(LDADD)
endif
+if TEST_record01
+lib_tests += record01
+lib_screens += record01/record01.scn
+lib_docs += record01/record01.doc
+record01_SOURCES = record01/init.c
+record01_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_FLAGS_record01) \
+ $(support_includes) -I$(RTEMS_SOURCE_ROOT)/cpukit/libnetworking
+endif
+
+if TEST_record02
+lib_tests += record02
+lib_screens += record02/record02.scn
+lib_docs += record02/record02.doc
+record02_SOURCES = record02/init.c
+record02_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_FLAGS_record02) \
+ $(support_includes)
+endif
+
if TEST_rtmonuse
lib_tests += rtmonuse
lib_screens += rtmonuse/rtmonuse.scn
diff --git a/testsuites/libtests/configure.ac b/testsuites/libtests/configure.ac
index a2a0df01f1..c76fb799b4 100644
--- a/testsuites/libtests/configure.ac
+++ b/testsuites/libtests/configure.ac
@@ -189,6 +189,8 @@ RTEMS_TEST_CHECK([rbheap01])
RTEMS_TEST_CHECK([read])
RTEMS_TEST_CHECK([readv])
RTEMS_TEST_CHECK([realloc])
+RTEMS_TEST_CHECK([record01])
+RTEMS_TEST_CHECK([record02])
RTEMS_TEST_CHECK([rtmonuse])
RTEMS_TEST_CHECK([setjmp])
RTEMS_TEST_CHECK([sha])
diff --git a/testsuites/libtests/record01/init.c b/testsuites/libtests/record01/init.c
new file mode 100644
index 0000000000..8b71ed3c61
--- /dev/null
+++ b/testsuites/libtests/record01/init.c
@@ -0,0 +1,580 @@
+/*
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/record.h>
+#include <rtems.h>
+
+#include <sys/endian.h>
+#include <sys/socket.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+
+#ifdef RTEMS_NETWORKING
+#include <rtems/rtems_bsdnet.h>
+#endif
+
+#include <tmacros.h>
+
+const char rtems_test_name[] = "RECORD 1";
+
+#define ITEM_COUNT 4
+
+#define ITEM_SIZE (ITEM_COUNT * sizeof(rtems_record_item))
+
+typedef struct {
+ Record_Control control;
+ rtems_record_item items[ITEM_COUNT];
+} test_context;
+
+static test_context test_instance;
+
+#define UE(user) (RTEMS_RECORD_EVENT_USER + (user))
+
+#define TE(t, e) RTEMS_RECORD_TIME_EVENT(t, e)
+
+static const rtems_record_item expected_items_0[ITEM_COUNT] = {
+ { .event = TE(2, UE(1)), .data = 3 }
+};
+
+static const rtems_record_item expected_items_1[ITEM_COUNT] = {
+ { .event = TE(2, UE(1)), .data = 3 },
+ { .event = TE(6, UE(5)), .data = 7 }
+};
+
+static const rtems_record_item expected_items_2[ITEM_COUNT] = {
+ { .event = TE(2, UE(1)), .data = 3 },
+ { .event = TE(6, UE(5)), .data = 7 },
+ { .event = TE(10, RTEMS_RECORD_EVENT_OVERFLOW), .data = 11 }
+};
+
+static const rtems_record_item expected_items_3[ITEM_COUNT] = {
+ { .event = TE(2, UE(1)), .data = 3 },
+ { .event = TE(6, UE(5)), .data = 7 },
+ { .event = TE(10, RTEMS_RECORD_EVENT_OVERFLOW), .data = 11 },
+ { .event = TE(14, RTEMS_RECORD_EVENT_OVERFLOW), .data = 15 }
+};
+
+static const rtems_record_item expected_items_4[ITEM_COUNT] = {
+ { .event = TE(2, UE(1)), .data = 3 },
+ { .event = TE(6, UE(5)), .data = 7 },
+ { .event = TE(10, RTEMS_RECORD_EVENT_OVERFLOW), .data = 11 },
+ { .event = TE(18, RTEMS_RECORD_EVENT_OVERFLOW), .data = 19 }
+};
+
+static const rtems_record_item expected_items_5[ITEM_COUNT] = {
+ { .event = TE(2, RTEMS_RECORD_EVENT_OVERFLOW), .data = 3 }
+};
+
+static const rtems_record_item expected_items_6[ITEM_COUNT] = {
+ { .event = TE(6, RTEMS_RECORD_EVENT_OVERFLOW), .data = 7 }
+};
+
+static const rtems_record_item expected_items_7[ITEM_COUNT] = {
+ { .event = TE(10, RTEMS_RECORD_EVENT_OVERFLOW), .data = 11 }
+};
+
+static const rtems_record_item expected_items_8[] = {
+ { .event = TE(0, RTEMS_RECORD_EVENT_PROCESSOR), .data = 0 },
+ { .event = TE(2, UE(1)), .data = 3 },
+ { .event = TE(5, UE(4)), .data = 6 },
+ { .event = TE(8, RTEMS_RECORD_EVENT_OVERFLOW), .data = 9 }
+};
+
+static const rtems_record_item expected_items_9[] = {
+ { .event = TE(0, RTEMS_RECORD_EVENT_PROCESSOR), .data = 0 },
+ { .event = TE(11, UE(10)), .data = 12 },
+ { .event = TE(14, UE(13)), .data = 15 }
+};
+
+static const rtems_record_item expected_items_10[] = {
+ { .event = TE(0, RTEMS_RECORD_EVENT_PROCESSOR), .data = 0 },
+ { .event = TE(17, UE(16)), .data = 18 },
+ { .event = TE(20, UE(19)), .data = 21 },
+ { .event = TE(23, RTEMS_RECORD_EVENT_OVERFLOW), .data = 24 }
+};
+
+static const rtems_record_item expected_items_11[] = {
+ { .event = TE(0, RTEMS_RECORD_EVENT_PROCESSOR), .data = 0 },
+ { .event = TE(26, UE(25)), .data = 27 }
+};
+
+static void init_context(test_context *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->control.mask = ITEM_COUNT - 1;
+}
+
+static void test_capacity(const Record_Control *control)
+{
+ unsigned int capacity;
+
+ capacity = _Record_Capacity(control, 0, 0);
+ rtems_test_assert(capacity == 3);
+
+ capacity = _Record_Capacity(control, 1, 0);
+ rtems_test_assert(capacity == 2);
+
+ capacity = _Record_Capacity(control, 2, 0);
+ rtems_test_assert(capacity == 1);
+
+ capacity = _Record_Capacity(control, 3, 0);
+ rtems_test_assert(capacity == 0);
+
+ capacity = _Record_Capacity(control, 3, 3);
+ rtems_test_assert(capacity == 3);
+
+ capacity = _Record_Capacity(control, 0, 3);
+ rtems_test_assert(capacity == 2);
+
+ capacity = _Record_Capacity(control, 1, 3);
+ rtems_test_assert(capacity == 1);
+
+ capacity = _Record_Capacity(control, 2, 3);
+ rtems_test_assert(capacity == 0);
+}
+
+static void test_increment(const Record_Control *control)
+{
+ unsigned int next;
+
+ next = _Record_Increment(control, 0, 1);
+ rtems_test_assert(next == 1);
+
+ next = _Record_Increment(control, 1, 1);
+ rtems_test_assert(next == 2);
+
+ next = _Record_Increment(control, 2, 1);
+ rtems_test_assert(next == 3);
+
+ next = _Record_Increment(control, 3, 1);
+ rtems_test_assert(next == 0);
+
+ next = _Record_Increment(control, 3, 0);
+ rtems_test_assert(next == 3);
+}
+
+static void test_prepare_2(test_context *ctx, Record_Control *control)
+{
+ rtems_record_context rc;
+
+ init_context(ctx);
+
+ rtems_record_prepare(&rc, 2);
+ rtems_test_assert(!rtems_record_is_overflow(&rc));
+ rtems_test_assert(rc.control == control);
+ rtems_test_assert(rc.head == 0);
+ rtems_test_assert(rc.increment == 1);
+ rtems_test_assert(rc.final_head == 2);
+ rtems_test_assert(_Record_Head(control) == 0);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rc.now = 2;
+ rtems_record_add(&rc, UE(1), 3);
+ rtems_test_assert(rc.head == 1);
+ rtems_test_assert(memcmp(control->Items, expected_items_0, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 0);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rc.now = 6;
+ rtems_record_commit(&rc, UE(5), 7);
+ rtems_test_assert(rc.head == 2);
+ rtems_test_assert(memcmp(control->Items, expected_items_1, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 2);
+ rtems_test_assert(_Record_Tail(control) == 0);
+}
+
+static void test_prepare_3(test_context *ctx, Record_Control *control)
+{
+ rtems_record_context rc;
+
+ init_context(ctx);
+
+ rtems_record_prepare(&rc, 3);
+ rtems_test_assert(rtems_record_is_overflow(&rc));
+ rtems_test_assert(rc.control == control);
+ rtems_test_assert(rc.head == 0);
+ rtems_test_assert(rc.increment == 0);
+ rtems_test_assert(rc.final_head == 1);
+ rtems_test_assert(_Record_Head(control) == 0);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rc.now = 2;
+ rtems_record_add(&rc, UE(1), 3);
+ rtems_test_assert(rc.head == 0);
+ rtems_test_assert(memcmp(control->Items, expected_items_5, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 0);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rc.now = 6;
+ rtems_record_add(&rc, UE(5), 7);
+ rtems_test_assert(rc.head == 0);
+ rtems_test_assert(memcmp(control->Items, expected_items_6, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 0);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rc.now = 10;
+ rtems_record_commit(&rc, UE(9), 11);
+ rtems_test_assert(rc.head == 0);
+ rtems_test_assert(memcmp(control->Items, expected_items_7, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 1);
+ rtems_test_assert(_Record_Tail(control) == 0);
+}
+
+static void set_time(rtems_record_item *item, uint32_t time)
+{
+ uint32_t event;
+
+ event = item->event;
+ event &= 0x3ff;
+ event |= time << 10;
+ item->event = event;
+}
+
+static void test_produce(test_context *ctx, Record_Control *control)
+{
+ init_context(ctx);
+
+ rtems_record_produce(UE(1), 3);
+ set_time(&control->Items[0], 2);
+ rtems_test_assert(memcmp(control->Items, expected_items_0, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 1);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rtems_record_produce(UE(5), 7);
+ set_time(&control->Items[1], 6);
+ rtems_test_assert(memcmp(control->Items, expected_items_1, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 2);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rtems_record_produce(UE(9), 11);
+ set_time(&control->Items[2], 10);
+ rtems_test_assert(memcmp(control->Items, expected_items_2, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 3);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rtems_record_produce(UE(13), 15);
+ set_time(&control->Items[3], 14);
+ rtems_test_assert(memcmp(control->Items, expected_items_3, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 3);
+ rtems_test_assert(_Record_Tail(control) == 0);
+
+ rtems_record_produce(UE(17), 19);
+ set_time(&control->Items[3], 18);
+ rtems_test_assert(memcmp(control->Items, expected_items_4, ITEM_SIZE) == 0);
+ rtems_test_assert(_Record_Head(control) == 3);
+ rtems_test_assert(_Record_Tail(control) == 0);
+}
+
+typedef struct {
+ size_t todo;
+ const rtems_record_item *items;
+} visitor_context;
+
+static void visitor(const rtems_record_item *items, size_t count, void *arg)
+{
+ visitor_context *vctx;
+
+ vctx = arg;
+ rtems_test_assert(vctx->todo >= count);
+
+ while (count > 0) {
+ rtems_test_assert(memcmp(items, vctx->items, sizeof(*items)) == 0);
+ ++items;
+ ++vctx->items;
+ --count;
+ --vctx->todo;
+ }
+}
+
+static void test_drain(test_context *ctx, Record_Control *control)
+{
+ visitor_context vctx;
+
+ init_context(ctx);
+
+ vctx.todo = 0;
+ vctx.items = NULL;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+
+ rtems_record_produce(UE(1), 3);
+ set_time(&control->Items[0], 2);
+ rtems_record_produce(UE(4), 6);
+ set_time(&control->Items[1], 5);
+ rtems_record_produce(UE(7), 9);
+ set_time(&control->Items[2], 8);
+
+ vctx.todo = RTEMS_ARRAY_SIZE(expected_items_8);
+ vctx.items = expected_items_8;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+
+ vctx.todo = 0;
+ vctx.items = NULL;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+
+ rtems_record_produce(UE(10), 12);
+ set_time(&control->Items[3], 11);
+ rtems_record_produce(UE(13), 15);
+ set_time(&control->Items[0], 14);
+
+ vctx.todo = RTEMS_ARRAY_SIZE(expected_items_9);
+ vctx.items = expected_items_9;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+
+ vctx.todo = 0;
+ vctx.items = NULL;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+
+ rtems_record_produce(UE(16), 18);
+ set_time(&control->Items[1], 17);
+ rtems_record_produce(UE(19), 21);
+ set_time(&control->Items[2], 20);
+ rtems_record_produce(UE(22), 24);
+ set_time(&control->Items[3], 23);
+
+ vctx.todo = RTEMS_ARRAY_SIZE(expected_items_10);
+ vctx.items = expected_items_10;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+
+ vctx.todo = 0;
+ vctx.items = NULL;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+
+ rtems_record_produce(UE(25), 27);
+ set_time(&control->Items[0], 26);
+
+ vctx.todo = RTEMS_ARRAY_SIZE(expected_items_11);
+ vctx.items = expected_items_11;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+
+ vctx.todo = 0;
+ vctx.items = NULL;
+ rtems_record_drain(visitor, &vctx);
+ rtems_test_assert(vctx.todo == 0);
+}
+
+#ifdef RTEMS_NETWORKING
+#define PORT 1234
+
+static uint32_t get_format(void)
+{
+ uint32_t format;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#if __INTPTR_WIDTH__ == 32
+ format = RTEMS_RECORD_FORMAT_LE_32;
+#elif __INTPTR_WIDTH__ == 64
+ format = RTEMS_RECORD_FORMAT_LE_64;
+#else
+#error "unexpected __INTPTR_WIDTH__"
+#endif
+#elif BYTE_ORDER == BIG_ENDIAN
+#if __INTPTR_WIDTH__ == 32
+ format = RTEMS_RECORD_FORMAT_BE_32;
+#elif __INTPTR_WIDTH__ == 64
+ format = RTEMS_RECORD_FORMAT_BE_64;
+#else
+#error "unexpected __INTPTR_WIDTH__"
+#endif
+#else
+#error "unexpected BYTE_ORDER"
+#endif
+
+ return format;
+}
+
+static int connect_client(void)
+{
+ struct sockaddr_in addr;
+ int fd;
+ int rv;
+ ssize_t n;
+ uint32_t format;
+ rtems_record_item item;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ rtems_test_assert(fd >= 0);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PORT);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ rv = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
+ rtems_test_assert(rv == 0);
+
+ n = read(fd, &format, sizeof(format));
+ rtems_test_assert(n == 4);
+ rtems_test_assert(format == get_format());
+
+ n = read(fd, &item, sizeof(item));
+ rtems_test_assert(n == (ssize_t) sizeof(item));
+ rtems_test_assert(item.event == TE(0, RTEMS_RECORD_EVENT_VERSION));
+ rtems_test_assert(item.data == RTEMS_RECORD_VERSION);
+
+ return fd;
+}
+
+static void produce_and_read(int fd, Record_Control *control)
+{
+ rtems_record_item items[4];
+ ssize_t n;
+
+ rtems_record_produce(UE(1), 3);
+ set_time(&control->Items[0], 2);
+ rtems_record_produce(UE(4), 6);
+ set_time(&control->Items[1], 5);
+ rtems_record_produce(UE(7), 9);
+ set_time(&control->Items[2], 8);
+
+ n = read(fd, items, sizeof(expected_items_8));
+ rtems_test_assert(n == (ssize_t) sizeof(expected_items_8));
+ rtems_test_assert(
+ memcmp(items, expected_items_8, sizeof(expected_items_8)) == 0
+ );
+
+ rtems_record_produce(UE(10), 12);
+ set_time(&control->Items[3], 11);
+ rtems_record_produce(UE(13), 15);
+ set_time(&control->Items[0], 14);
+
+ n = read(fd, items, sizeof(expected_items_9));
+ rtems_test_assert(n == (ssize_t) sizeof(expected_items_9));
+ rtems_test_assert(
+ memcmp(items, expected_items_9, sizeof(expected_items_9)) == 0
+ );
+
+ rtems_record_produce(UE(16), 18);
+ set_time(&control->Items[1], 17);
+ rtems_record_produce(UE(19), 21);
+ set_time(&control->Items[2], 20);
+ rtems_record_produce(UE(22), 24);
+ set_time(&control->Items[3], 23);
+
+ n = read(fd, items, sizeof(expected_items_10));
+ rtems_test_assert(n == (ssize_t) sizeof(expected_items_10));
+ rtems_test_assert(
+ memcmp(items, expected_items_10, sizeof(expected_items_10)) == 0
+ );
+
+ rtems_record_produce(UE(25), 27);
+ set_time(&control->Items[0], 26);
+
+ n = read(fd, items, sizeof(expected_items_11));
+ rtems_test_assert(n == (ssize_t) sizeof(expected_items_11));
+ rtems_test_assert(
+ memcmp(items, expected_items_11, sizeof(expected_items_11)) == 0
+ );
+}
+
+static void test_server(test_context *ctx, Record_Control *control)
+{
+ rtems_status_code sc;
+ int rv;
+ int fd;
+
+ init_context(ctx);
+
+ rv = rtems_bsdnet_initialize_network();
+ rtems_test_assert(rv == 0);
+
+ sc = rtems_record_start_server(1, PORT, 1);
+ rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+ fd = connect_client();
+ produce_and_read(fd, control);
+
+ rv = close(fd);
+ rtems_test_assert(rv == 0);
+}
+#endif
+
+static void Init(rtems_task_argument arg)
+{
+ test_context *ctx;
+ Per_CPU_Control *cpu_self;
+
+ TEST_BEGIN();
+
+ ctx = &test_instance;
+
+ cpu_self = _Per_CPU_Get_snapshot();
+ cpu_self->record = &ctx->control;
+
+ init_context(ctx);
+ test_capacity(&ctx->control);
+ test_increment(&ctx->control);
+ test_prepare_2(ctx, &ctx->control);
+ test_prepare_3(ctx, &ctx->control);
+ test_produce(ctx, &ctx->control);
+ test_drain(ctx, &ctx->control);
+#ifdef RTEMS_NETWORKING
+ test_server(ctx, &ctx->control);
+#endif
+
+ TEST_END();
+ rtems_test_exit(0);
+}
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+
+#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
+
+#ifdef RTEMS_NETWORKING
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 7
+
+#define CONFIGURE_MAXIMUM_TASKS 3
+
+#define CONFIGURE_MAXIMUM_TIMERS 1
+#else
+#define CONFIGURE_MAXIMUM_TASKS 1
+#endif
+
+#define CONFIGURE_INIT_TASK_PRIORITY 2
+
+#define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES
+
+#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/libtests/record01/record01.doc b/testsuites/libtests/record01/record01.doc
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testsuites/libtests/record01/record01.doc
diff --git a/testsuites/libtests/record01/record01.scn b/testsuites/libtests/record01/record01.scn
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testsuites/libtests/record01/record01.scn
diff --git a/testsuites/libtests/record02/init.c b/testsuites/libtests/record02/init.c
new file mode 100644
index 0000000000..19cb7a23b5
--- /dev/null
+++ b/testsuites/libtests/record02/init.c
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/record.h>
+#include <rtems.h>
+
+#include <string.h>
+
+#include "tmacros.h"
+
+const char rtems_test_name[] = "RECORD 2";
+
+typedef struct {
+} test_context;
+
+static test_context test_instance;
+
+static void Init(rtems_task_argument arg)
+{
+ test_context *ctx;
+
+ TEST_BEGIN();
+
+ ctx = &test_instance;
+ rtems_task_wake_after(1000);
+
+
+ TEST_END();
+ rtems_test_exit(0);
+}
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+
+#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
+
+#define CONFIGURE_MAXIMUM_TASKS 1
+
+#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_RECORD_PER_PROCESSOR_COUNT 1024
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/libtests/record02/record02.doc b/testsuites/libtests/record02/record02.doc
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testsuites/libtests/record02/record02.doc
diff --git a/testsuites/libtests/record02/record02.scn b/testsuites/libtests/record02/record02.scn
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testsuites/libtests/record02/record02.scn