summaryrefslogblamecommitdiffstats
path: root/cpukit/include/rtems/record.h
blob: cfadae79d6ca1ef38c4c45f295d5c89c3ec2211a (plain) (tree)













































































































































































































































































































































                                                                               
/*
 * 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 */