diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-28 11:36:11 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-12-15 09:48:36 +0100 |
commit | 4880ce13bf981dac4b61e806d976217c6731e80b (patch) | |
tree | 354e37b5b4b27397858fff2475cee15b2460a28b | |
parent | cc9f28a631cd3428fff21142f2929e6d5c8d869a (diff) |
Add low level recording support
-rw-r--r-- | cpukit/Makefile.am | 4 | ||||
-rw-r--r-- | cpukit/headers.am | 2 | ||||
-rw-r--r-- | cpukit/include/rtems/confdefs.h | 30 | ||||
-rw-r--r-- | cpukit/include/rtems/record.h | 251 | ||||
-rw-r--r-- | cpukit/include/rtems/recorddata.h | 155 | ||||
-rw-r--r-- | cpukit/include/rtems/score/percpu.h | 4 | ||||
-rw-r--r-- | cpukit/include/rtems/sysinit.h | 1 | ||||
-rw-r--r-- | cpukit/libmisc/capture/record-server.c | 268 | ||||
-rw-r--r-- | cpukit/libmisc/capture/record-static.c | 110 | ||||
-rw-r--r-- | cpukit/libmisc/capture/record-userext.c | 121 | ||||
-rw-r--r-- | cpukit/libmisc/capture/record.c | 114 | ||||
-rw-r--r-- | testsuites/libtests/Makefile.am | 18 | ||||
-rw-r--r-- | testsuites/libtests/configure.ac | 2 | ||||
-rw-r--r-- | testsuites/libtests/record01/init.c | 580 | ||||
-rw-r--r-- | testsuites/libtests/record01/record01.doc | 0 | ||||
-rw-r--r-- | testsuites/libtests/record01/record01.scn | 0 | ||||
-rw-r--r-- | testsuites/libtests/record02/init.c | 74 | ||||
-rw-r--r-- | testsuites/libtests/record02/record02.doc | 0 | ||||
-rw-r--r-- | testsuites/libtests/record02/record02.scn | 0 |
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 |