/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (C) 2018, 2019 embedded brains GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _RTEMS_RECORD_H
#define _RTEMS_RECORD_H
#include "recorddata.h"
#include <rtems/score/atomic.h>
#include <rtems/score/cpu.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;
unsigned int tail;
unsigned int mask;
Watchdog_Control Watchdog;
rtems_record_item Header[ 3 ];
rtems_record_item Items[ RTEMS_ZERO_LENGTH_ARRAY ];
};
typedef struct Record_Control Record_Control;
typedef RTEMS_ALIGNED( CPU_CACHE_LINE_BYTES )
struct Record_Configured_control Record_Configured_control;
typedef struct {
Record_Control *control;
unsigned int 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_start(
struct _Thread_Control *executing,
struct _Thread_Control *started
);
void _Record_Thread_restart(
struct _Thread_Control *executing,
struct _Thread_Control *restarted
);
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_begin( struct _Thread_Control *executing );
void _Record_Thread_exitted( struct _Thread_Control *executing );
void _Record_Thread_terminate(
struct _Thread_Control *executing
);
#define RECORD_EXTENSION \
{ \
_Record_Thread_create, \
_Record_Thread_start, \
_Record_Thread_restart, \
_Record_Thread_delete, \
_Record_Thread_switch, \
_Record_Thread_begin, \
_Record_Thread_exitted, \
NULL, \
_Record_Thread_terminate \
}
RTEMS_INLINE_ROUTINE unsigned int _Record_Index(
const Record_Control *control,
unsigned int index
)
{
return index & control->mask;
}
RTEMS_INLINE_ROUTINE unsigned int _Record_Head( const Record_Control *control )
{
return _Atomic_Load_uint( &control->head, ATOMIC_ORDER_RELAXED );
}
RTEMS_INLINE_ROUTINE unsigned int _Record_Tail( const Record_Control *control )
{
return control->tail;
}
RTEMS_INLINE_ROUTINE bool _Record_Is_overflow(
const Record_Control *control,
unsigned int tail,
unsigned int head
)
{
return head - tail >= control->mask + 1U;
}
RTEMS_INLINE_ROUTINE unsigned int _Record_Capacity(
const Record_Control *control,
unsigned int tail,
unsigned int head
)
{
return ( tail - head - 1U ) & control->mask;
}
RTEMS_INLINE_ROUTINE rtems_counter_ticks _Record_Now( void )
{
return rtems_counter_read();
}
typedef struct RTEMS_PACKED {
uint32_t format;
uint32_t magic;
rtems_record_item Version;
rtems_record_item Processor_maximum;
rtems_record_item Count;
rtems_record_item Frequency;
} Record_Stream_header;
void _Record_Stream_header_initialize( Record_Stream_header *header );
/**
* @addtogroup RTEMSRecord
*
* @{
*/
/**
* @brief Prepares to add and commit record items.
*
* This function disables interrupts.
*
* @param context The record context which must be used for the following
* rtems_record_add() and rtems_record_commit() calls. The record context
* may have an arbitrary content at function entry.
*
* @see rtems_record_produce().
*/
RTEMS_INLINE_ROUTINE void rtems_record_prepare( rtems_record_context *context )
{
rtems_interrupt_level level;
const Per_CPU_Control *cpu_self;
Record_Control *control;
unsigned int head;
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 );
context->head = head;
}
/**
* @brief Adds a record item.
*
* @param context The record context initialized via rtems_record_prepare().
* @param event The record event without a time stamp for the item.
* @param data The record data for the item.
*/
RTEMS_INLINE_ROUTINE void rtems_record_add(
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[ _Record_Index( control, head ) ];
context->head = head + 1;
item->event = context->now | event;
item->data = data;
}
/**
* @brief Commits a set of record items.
*
* @param context The record context initialized via rtems_record_prepare().
*/
RTEMS_INLINE_ROUTINE void rtems_record_commit( rtems_record_context *context )
{
_Atomic_Store_uint(
&context->control->head,
context->head,
ATOMIC_ORDER_RELEASE
);
rtems_interrupt_local_enable( context->level );
}
/**
* @brief Produces a record item.
*
* @param event The record event without a time stamp for the item.
* @param data The record data for the item.
*/
void rtems_record_produce( rtems_record_event event, rtems_record_data data );
/**
* @brief Produces two record items.
*
* @param event_0 The record event without a time stamp for the first item.
* @param data_0 The record data for the first item.
* @param event_1 The record event without a time stamp for the second item.
* @param data_1 The record data for the second item.
*/
void rtems_record_produce_2(
rtems_record_event event_0,
rtems_record_data data_0,
rtems_record_event event_1,
rtems_record_data data_1
);
/**
* @brief Produces n record items.
*
* @param item The record items without a time stamps.
* @param n The count of record items.
*/
void rtems_record_produce_n(
const rtems_record_item *items,
size_t n
);
typedef void ( *rtems_record_drain_visitor )(
const rtems_record_item *items,
size_t count,
void *arg
);
/**
* @brief Drains the record items on all processors.
*
* Calls the visitor function for each drained item set.
*
* @param visitor The visitor function.
* @param arg The argument for the visitor function.
*/
void rtems_record_drain( rtems_record_drain_visitor visitor, void *arg );
/**
* @brief Drains the record items on all processors an writes them to the file
* descriptor.
*
* @param fd The file descriptor.
* @param written Set to true if items were written to the file descriptor,
* otherwise set to false.
*
* @retval The bytes written to the file descriptor.
*/
ssize_t rtems_record_writev( int fd, bool *written );
/**
* @brief Runs a record TCP server loop.
*
* @param port The TCP port to listen in host byte order.
* @param period The drain period in clock ticks.
*/
void rtems_record_server( uint16_t port, rtems_interval period );
/**
* @brief Starts a record TCP server task.
*
* @param priority The task priority.
* @param port The TCP port to listen in host byte order.
* @param period The drain period in clock ticks.
*/
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 */