/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (C) 2018, 2019 embedded brains GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This file must be compatible to general purpose POSIX system, e.g. Linux,
* FreeBSD. It may be used for utility programs.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <rtems/recordclient.h>
#include <string.h>
static void set_to_bt_scaler(
rtems_record_client_context *ctx,
uint32_t frequency
)
{
uint64_t bin_per_s;
bin_per_s = UINT64_C( 1 ) << 32;
ctx->to_bt_scaler = ( ( bin_per_s << 31 ) + frequency - 1 ) / frequency;
}
static rtems_record_client_status call_handler(
const rtems_record_client_context *ctx,
uint64_t bt,
rtems_record_event event,
uint64_t data
)
{
uint32_t seconds;
uint32_t nanosec;
seconds = (uint32_t) ( bt >> 32 );
nanosec = (uint32_t) ( ( UINT64_C( 1000000000 ) * (uint32_t) bt ) >> 32 );
return ( *ctx->handler )(
seconds,
nanosec,
ctx->cpu,
event,
data,
ctx->handler_arg
);
}
static void check_overflow(
const rtems_record_client_context *ctx,
const rtems_record_client_per_cpu *per_cpu,
uint32_t new_head
)
{
uint32_t last_tail;
uint32_t last_head;
uint32_t capacity;
uint32_t new_content;
uint64_t bt;
last_tail = per_cpu->tail[ per_cpu->index ];
last_head = per_cpu->head[ per_cpu->index ];
if ( last_tail == last_head ) {
return;
}
capacity = ( last_tail - last_head - 1 ) & ( ctx->count - 1 );
new_content = new_head - last_head;
if ( new_content <= capacity ) {
return;
}
bt = ( per_cpu->uptime.time_accumulated * ctx->to_bt_scaler ) >> 31;
bt += per_cpu->uptime.bt;
call_handler(
ctx,
bt,
RTEMS_RECORD_PER_CPU_OVERFLOW,
new_content - capacity
);
}
static rtems_record_client_status visit( rtems_record_client_context *ctx )
{
rtems_record_client_per_cpu *per_cpu;
uint32_t time;
rtems_record_event event;
uint64_t data;
uint64_t bt;
per_cpu = &ctx->per_cpu[ ctx->cpu ];
time = RTEMS_RECORD_GET_TIME( ctx->event );
event = RTEMS_RECORD_GET_EVENT( ctx->event );
data = ctx->data;
switch ( event ) {
case RTEMS_RECORD_PROCESSOR:
if ( data >= RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT ) {
return RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_CPU;
}
ctx->cpu = (uint32_t) data;
per_cpu = &ctx->per_cpu[ ctx->cpu ];
break;
case RTEMS_RECORD_UPTIME_LOW:
per_cpu->uptime.bt = (uint32_t) data;
per_cpu->uptime.time_at_bt = time;
per_cpu->uptime.time_last = time;
per_cpu->uptime.time_accumulated = 0;
time = 0;
break;
case RTEMS_RECORD_UPTIME_HIGH:
per_cpu->uptime.bt += (int64_t) data << 32;
time = 0;
break;
case RTEMS_RECORD_PER_CPU_TAIL:
per_cpu->tail[ per_cpu->index ] = (uint32_t) data;
break;
case RTEMS_RECORD_PER_CPU_HEAD:
per_cpu->head[ per_cpu->index ]= (uint32_t) data;
per_cpu->index ^= 1;
check_overflow( ctx, per_cpu, (uint32_t) data );
break;
case RTEMS_RECORD_PER_CPU_COUNT:
ctx->count = (uint32_t) data;
break;
case RTEMS_RECORD_FREQUENCY:
set_to_bt_scaler( ctx, (uint32_t) data );
break;
case RTEMS_RECORD_VERSION:
if ( data != RTEMS_RECORD_THE_VERSION ) {
return RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_VERSION;
}
break;
default:
break;
}
if ( time != 0 ) {
uint32_t delta;
delta = ( time - per_cpu->uptime.time_last )
& ( ( UINT32_C( 1 ) << RTEMS_RECORD_TIME_BITS ) - 1 );
per_cpu->uptime.time_last = time;
per_cpu->uptime.time_accumulated += delta;
bt = ( per_cpu->uptime.time_accumulated * ctx->to_bt_scaler ) >> 31;
bt += per_cpu->uptime.bt;
} else {
bt = 0;
}
return call_handler( ctx, bt, event, data );
}
static rtems_record_client_status consume_32(
rtems_record_client_context *ctx,
const void *buf,
size_t n
)
{
while ( n > 0 ) {
size_t m;
char *pos;
m = ctx->todo < n ? ctx->todo : n;
pos = ctx->pos;
pos = memcpy( pos, buf, m );
n -= m;
buf = (char *) buf + m;
if ( m == ctx->todo ) {
rtems_record_client_status status;
ctx->todo = sizeof( ctx->item.format_32 );
ctx->pos = &ctx->item.format_32;
ctx->event = ctx->item.format_32.event;
ctx->data = ctx->item.format_32.data;
status = visit( ctx );
if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
return status;
}
} else {
ctx->todo -= m;
ctx->pos = pos + m;
}
}
return RTEMS_RECORD_CLIENT_SUCCESS;
}
static rtems_record_client_status consume_64(
rtems_record_client_context *ctx,
const void *buf,
size_t n
)
{
while ( n > 0 ) {
size_t m;
char *pos;
m = ctx->todo < n ? ctx->todo : n;
pos = ctx->pos;
pos = memcpy( pos, buf, m );
n -= m;
buf = (char *) buf + m;
if ( m == ctx->todo ) {
rtems_record_client_status status;
ctx->todo = sizeof( ctx->item.format_64 );
ctx->pos = &ctx->item.format_64;
ctx->event = ctx->item.format_64.event;
ctx->data = ctx->item.format_64.data;
status = visit( ctx );
if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
return status;
}
} else {
ctx->todo -= m;
ctx->pos = pos + m;
}
}
return RTEMS_RECORD_CLIENT_SUCCESS;
}
static rtems_record_client_status consume_swap_32(
rtems_record_client_context *ctx,
const void *buf,
size_t n
)
{
while ( n > 0 ) {
size_t m;
char *pos;
m = ctx->todo < n ? ctx->todo : n;
pos = ctx->pos;
pos = memcpy( pos, buf, m );
n -= m;
buf = (char *) buf + m;
if ( m == ctx->todo ) {
rtems_record_client_status status;
ctx->todo = sizeof( ctx->item.format_32 );
ctx->pos = &ctx->item.format_32;
ctx->event = __builtin_bswap32( ctx->item.format_32.event );
ctx->data = __builtin_bswap32( ctx->item.format_32.data );
status = visit( ctx );
if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
return status;
}
} else {
ctx->todo -= m;
ctx->pos = pos + m;
}
}
return RTEMS_RECORD_CLIENT_SUCCESS;
}
static rtems_record_client_status consume_swap_64(
rtems_record_client_context *ctx,
const void *buf,
size_t n
)
{
while ( n > 0 ) {
size_t m;
char *pos;
m = ctx->todo < n ? ctx->todo : n;
pos = ctx->pos;
pos = memcpy( pos, buf, m );
n -= m;
buf = (char *) buf + m;
if ( m == ctx->todo ) {
rtems_record_client_status status;
ctx->todo = sizeof( ctx->item.format_64 );
ctx->pos = &ctx->item.format_64;
ctx->event = __builtin_bswap32( ctx->item.format_64.event );
ctx->data = __builtin_bswap64( ctx->item.format_64.data );
status = visit( ctx );
if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
return status;
}
} else {
ctx->todo -= m;
ctx->pos = pos + m;
}
}
return RTEMS_RECORD_CLIENT_SUCCESS;
}
static rtems_record_client_status consume_init(
rtems_record_client_context *ctx,
const void *buf,
size_t n
)
{
while ( n > 0 ) {
size_t m;
char *pos;
m = ctx->todo < n ? ctx->todo : n;
pos = ctx->pos;
pos = memcpy( pos, buf, m );
n -= m;
buf = (char *) buf + m;
if ( m == ctx->todo ) {
uint32_t magic;
magic = ctx->header[ 1 ];
switch ( ctx->header[ 0 ] ) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
case RTEMS_RECORD_FORMAT_LE_32:
ctx->todo = sizeof( ctx->item.format_32 );
ctx->pos = &ctx->item.format_32;
ctx->consume = consume_32;
break;
case RTEMS_RECORD_FORMAT_LE_64:
ctx->todo = sizeof( ctx->item.format_64 );
ctx->pos = &ctx->item.format_64;
ctx->consume = consume_64;
break;
case RTEMS_RECORD_FORMAT_BE_32:
ctx->todo = sizeof( ctx->item.format_32 );
ctx->pos = &ctx->item.format_32;
ctx->consume = consume_swap_32;
magic = __builtin_bswap32( magic );
break;
case RTEMS_RECORD_FORMAT_BE_64:
ctx->todo = sizeof( ctx->item.format_64 );
ctx->pos = &ctx->item.format_64;
ctx->consume = consume_swap_64;
magic = __builtin_bswap32( magic );
break;
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
case RTEMS_RECORD_FORMAT_LE_32:
ctx->todo = sizeof( ctx->item.format_32 );
ctx->pos = &ctx->item.format_32;
ctx->consume = consume_swap_32;
magic = __builtin_bswap32( magic );
break;
case RTEMS_RECORD_FORMAT_LE_64:
ctx->todo = sizeof( ctx->item.format_64 );
ctx->pos = &ctx->item.format_64;
ctx->consume = consume_swap_64;
magic = __builtin_bswap32( magic );
break;
case RTEMS_RECORD_FORMAT_BE_32:
ctx->todo = sizeof( ctx->item.format_32 );
ctx->pos = &ctx->item.format_32;
ctx->consume = consume_32;
break;
case RTEMS_RECORD_FORMAT_BE_64:
ctx->todo = sizeof( ctx->item.format_64 );
ctx->pos = &ctx->item.format_64;
ctx->consume = consume_64;
break;
#else
#error "unexpected __BYTE_ORDER__"
#endif
default:
return RTEMS_RECORD_CLIENT_ERROR_UNKNOWN_FORMAT;
}
if ( magic != RTEMS_RECORD_MAGIC ) {
return RTEMS_RECORD_CLIENT_ERROR_INVALID_MAGIC;
}
return rtems_record_client_run( ctx, buf, n );
} else {
ctx->todo -= m;
ctx->pos = pos + m;
}
}
return RTEMS_RECORD_CLIENT_SUCCESS;
}
void rtems_record_client_init(
rtems_record_client_context *ctx,
rtems_record_client_handler handler,
void *arg
)
{
ctx = memset( ctx, 0, sizeof( *ctx ) );
ctx->to_bt_scaler = UINT64_C( 1 ) << 31;
ctx->handler = handler;
ctx->handler_arg = arg;
ctx->todo = sizeof( ctx->header );
ctx->pos = &ctx->header;
ctx->consume = consume_init;
}
rtems_record_client_status rtems_record_client_run(
rtems_record_client_context *ctx,
const void *buf,
size_t n
)
{
return ( *ctx->consume )( ctx, buf, n );
}