From 049b9b5c822e6be434fcde23339dc3edfc4db16b Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 19 Aug 2024 17:49:47 +0200 Subject: [PATCH] record: Add rtems_record_fetch() This function will replace rtems_record_drain() which turned out to be unreliable in SMP configurations. --- cpukit/include/rtems/record.h | 139 ++++++++++++- cpukit/libtrace/record/record-fetch.c | 211 ++++++++++++++++++++ spec/build/cpukit/librtemscpu.yml | 1 + spec/build/testsuites/libtests/grp.yml | 4 + spec/build/testsuites/libtests/record03.yml | 19 ++ spec/build/testsuites/libtests/record04.yml | 19 ++ testsuites/libtests/record03/init.c | 201 +++++++++++++++++++ testsuites/libtests/record04/init.c | 162 +++++++++++++++ 8 files changed, 754 insertions(+), 2 deletions(-) create mode 100644 cpukit/libtrace/record/record-fetch.c create mode 100644 spec/build/testsuites/libtests/record03.yml create mode 100644 spec/build/testsuites/libtests/record04.yml create mode 100644 testsuites/libtests/record03/init.c create mode 100644 testsuites/libtests/record04/init.c diff --git a/cpukit/include/rtems/record.h b/cpukit/include/rtems/record.h index 913cf8adec..cea68a6929 100644 --- a/cpukit/include/rtems/record.h +++ b/cpukit/include/rtems/record.h @@ -121,9 +121,17 @@ static inline unsigned int _Record_Index( return index & control->mask; } -static inline unsigned int _Record_Head( const Record_Control *control ) +static inline unsigned int _Record_Head( Record_Control *control ) { - return _Atomic_Load_uint( &control->head, ATOMIC_ORDER_RELAXED ); +#ifdef RTEMS_SMP + /* + * Use a read-modify-write operation to get the last value stored by the + * record producer. + */ + return _Atomic_Fetch_add_uint( &control->head, 0, ATOMIC_ORDER_ACQUIRE ); +#else + return _Atomic_Load_uint( &control->head, ATOMIC_ORDER_ACQUIRE ); +#endif } static inline unsigned int _Record_Tail( const Record_Control *control ) @@ -1906,6 +1914,133 @@ void _Record_Drain( */ void rtems_record_drain( rtems_record_drain_visitor visitor, void *arg ); +/** + * @brief This structure controls the record fetching performed by rtems_record_fetch(). + * + * The structure shall be initialized by rtems_record_fetch_initialize(). + */ +typedef struct { + /** + * @brief This member references the first item fetched by the last call to + * rtems_record_fetch(). + */ + rtems_record_item *fetched_items; + + /** + * @brief This member contains the count of items fetched by the last call to + * rtems_record_fetch(). + */ + size_t fetched_count; + + /** + * @brief The following members should only be accessed by + * rtems_record_fetch_initialize() and rtems_record_fetch(). + */ + struct { +#ifdef RTEMS_SMP + /** + * @brief This member contains the index of the processor from which the next + * records are fetched. + */ + uint32_t cpu_index; +#endif + + /** + * @brief This member contains the count of records which need to be fetched + * from the current processor before the next processor is selected. + */ + size_t cpu_todo; + + /** + * @brief This member references the item array used to store fetched items. + */ + rtems_record_item *storage_items; + + /** + * @brief This member contains the count of items of the array referenced by + * ``storage_items``. + */ + size_t storage_item_count; + } internal; +} rtems_record_fetch_control; + +/** + * @brief This enumeration provides status codes returned by + * rtems_record_fetch(). + */ +typedef enum { + /** + * @brief This enumerator indicates that the current round of record fetches + * for all configure processors is done. + */ + RTEMS_RECORD_FETCH_DONE, + + /** + * @brief This enumerator indicates that the current round of record fetches + * for all configure processors has to continue. + */ + RTEMS_RECORD_FETCH_CONTINUE, + + /** + * @brief This enumerator indicates that the item count passed to + * rtems_record_fetch() is invalid. + */ + RTEMS_RECORD_FETCH_INVALID_ITEM_COUNT +} rtems_record_fetch_status; + +/** + * @brief Returns the count of items which allows getting all available items + * for one processor through one call to rtems_record_fetch(). + * + * The value depends on @ref CONFIGURE_RECORD_PER_PROCESSOR_ITEMS and + * implementation details fo rtems_record_fetch(). + */ +size_t rtems_record_get_item_count_for_fetch( void ); + +/** + * @brief Initializes the record fetch control structure. + * + * This function shall be called before a record fetch control structure is + * passed to rtems_record_fetch(). + * + * @param[out] control is the structure to initialize. + * + * @param[out] items is a reference to an item array which is used to store the + * fetched items. + * + * @param count is the count of items in the referenced array. See + * rtems_record_get_item_count_for_fetch(). + */ +void rtems_record_fetch_initialize( + rtems_record_fetch_control *control, + rtems_record_item *items, + size_t count +); + +/** + * @brief Fetches records from the record buffers of the processors. + * + * The fetched items and count of fetched items is returned through the control + * structure. + * + * @param[in, out] control is the structure used to control the record + * fetching. Use rtems_record_fetch_initialize() to initialize it before the + * first call to rtems_record_fetch(). + * + * @retval RTEMS_RECORD_FETCH_DONE This return status indicates that the + * current round of record fetches for all configure processors is done. + * + * @retval RTEMS_RECORD_FETCH_CONTINUE This return status indicates that the + * current round of record fetches for all configure processors has to + * continue. + * + * @retval RTEMS_RECORD_FETCH_INVALID_ITEM_COUNT This return status indicates + * that the specified item count was invalid. + */ +rtems_record_fetch_status rtems_record_fetch( + rtems_record_fetch_control *control +); + /** @} */ #ifdef __cplusplus diff --git a/cpukit/libtrace/record/record-fetch.c b/cpukit/libtrace/record/record-fetch.c new file mode 100644 index 0000000000..907bcf7dc7 --- /dev/null +++ b/cpukit/libtrace/record/record-fetch.c @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSRecord + * + * @brief This source file contains the implementation of the record fetching. + */ + +/* + * Copyright (C) 2024 embedded brains GmbH & Co. KG + * + * 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 + +#include +#include + +/* + * One for RTEMS_RECORD_PROCESSOR, one for the optional + * RTEMS_RECORD_PER_CPU_OVERFLOW. + */ +#define RECORD_FETCH_HEADER_ITEMS 2 + +size_t rtems_record_get_item_count_for_fetch( void ) +{ + return _Record_Configuration.item_count + RECORD_FETCH_HEADER_ITEMS +#ifdef RTEMS_SMP + /* See red zone comment below */ + - 1 +#endif + ; +} + +void rtems_record_fetch_initialize( + rtems_record_fetch_control *control, + rtems_record_item *items, + size_t count +) +{ + control = memset( control, 0, sizeof( *control ) ); + control->internal.storage_items = items; + control->internal.storage_item_count = count; +} + + +rtems_record_fetch_status rtems_record_fetch( + rtems_record_fetch_control *control +) +{ + rtems_record_fetch_status status; + rtems_record_item *items; + size_t count; + uint32_t cpu_index; + Per_CPU_Control *cpu; + Record_Control *record_control; + rtems_record_item *item; + unsigned int mask; + unsigned int red_zone; + unsigned int capacity; + unsigned int tail; + unsigned int head; + unsigned int available; + unsigned int overflow; + unsigned int new_tail; + unsigned int new_items; + rtems_record_item *fetched_items; + size_t fetched_count; + + items = control->internal.storage_items; + count = control->internal.storage_item_count; + + if ( count < RECORD_FETCH_HEADER_ITEMS + 1 ) { + control->fetched_items = 0; + return RTEMS_RECORD_FETCH_INVALID_ITEM_COUNT; + } + + /* + * The red zone indicates an area of items before the head where new items + * are produced. + * + * In uniprocessor configurations, the record data and the record head is + * atomically produced so that there is no red zone. + * + * In SMP configurations, the atomic store release to update the head and the + * atomic fetch acquire to get the head guarantees that we read fully produced + * items and that the head is the last value stored by the producer. + * However, one item may be already in production which could be observed + * before the new head is stored. + */ +#ifdef RTEMS_SMP + red_zone = 1; +#else + red_zone = 0; +#endif + +#ifdef RTEMS_SMP + cpu_index = control->internal.cpu_index; +#else + cpu_index = 0; +#endif + cpu = _Per_CPU_Get_by_index( cpu_index ); + record_control = cpu->record; + mask = record_control->mask; + capacity = mask + 1 - red_zone; + tail = _Record_Tail( record_control ); + head = _Record_Head( record_control ); + + available = control->internal.cpu_todo; + control->internal.cpu_todo = 0; + + if ( available == 0 ) { + available = head - tail; + } + + if ( available > capacity ) { + overflow = available - capacity; + available = capacity; + tail = head - capacity; + } else { + overflow = 0; + } + + if ( available + RECORD_FETCH_HEADER_ITEMS > count ) { + control->internal.cpu_todo = available - count + RECORD_FETCH_HEADER_ITEMS; + available = count - RECORD_FETCH_HEADER_ITEMS; + status = RTEMS_RECORD_FETCH_CONTINUE; + } else { +#ifdef RTEMS_SMP + if ( cpu_index + 1 < rtems_scheduler_get_processor_maximum() ) { + control->internal.cpu_index = cpu_index + 1; + status = RTEMS_RECORD_FETCH_CONTINUE; + } else { + control->internal.cpu_index = 0; + status = RTEMS_RECORD_FETCH_DONE; + } +#else + status = RTEMS_RECORD_FETCH_DONE; +#endif + } + + new_tail = tail + available; + record_control->tail = new_tail; + item = items + 2; + + while ( tail != new_tail ) { + *item = record_control->Items[ tail & mask ]; + ++item; + ++tail; + } + + fetched_items = items + RECORD_FETCH_HEADER_ITEMS; + fetched_count = available; + new_items = _Record_Head( record_control ) - head; + + if ( available + new_items > capacity ) { + unsigned int overwritten; + + overwritten = available + new_items - capacity; + overflow += overwritten; + + if ( overwritten > available ) { + overwritten = available; + } + + fetched_items += overwritten; + fetched_count -= overwritten; + } + + if ( overflow > 0 ) { + --fetched_items; + ++fetched_count; + fetched_items->event = RTEMS_RECORD_PER_CPU_OVERFLOW; + fetched_items->data = overflow; + } + + --fetched_items; + ++fetched_count; + fetched_items->event = RTEMS_RECORD_PROCESSOR; + fetched_items->data = cpu_index; + + control->fetched_items = fetched_items; + control->fetched_count = fetched_count; + return status; +} diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml index ce6cf39d99..dea73948fc 100644 --- a/spec/build/cpukit/librtemscpu.yml +++ b/spec/build/cpukit/librtemscpu.yml @@ -994,6 +994,7 @@ source: - cpukit/libtrace/record/record-dump-zbase64.c - cpukit/libtrace/record/record-dump-zfatal.c - cpukit/libtrace/record/record-dump.c +- cpukit/libtrace/record/record-fetch.c - cpukit/libtrace/record/record-server.c - cpukit/libtrace/record/record-stream-header.c - cpukit/libtrace/record/record-sysinit.c diff --git a/spec/build/testsuites/libtests/grp.yml b/spec/build/testsuites/libtests/grp.yml index 8bbd4c4a55..cfe519569f 100644 --- a/spec/build/testsuites/libtests/grp.yml +++ b/spec/build/testsuites/libtests/grp.yml @@ -234,6 +234,10 @@ links: uid: record01 - role: build-dependency uid: record02 +- role: build-dependency + uid: record03 +- role: build-dependency + uid: record04 - role: build-dependency uid: regulator01 - role: build-dependency diff --git a/spec/build/testsuites/libtests/record03.yml b/spec/build/testsuites/libtests/record03.yml new file mode 100644 index 0000000000..c474022e11 --- /dev/null +++ b/spec/build/testsuites/libtests/record03.yml @@ -0,0 +1,19 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +build-type: test-program +cflags: [] +copyrights: +- Copyright (C) 2024 embedded brains GmbH & Co. KG +cppflags: [] +cxxflags: [] +enabled-by: true +features: c cprogram +includes: [] +ldflags: [] +links: [] +source: +- testsuites/libtests/record03/init.c +stlib: [] +target: testsuites/libtests/record03.exe +type: build +use-after: [] +use-before: [] diff --git a/spec/build/testsuites/libtests/record04.yml b/spec/build/testsuites/libtests/record04.yml new file mode 100644 index 0000000000..0cbadd9594 --- /dev/null +++ b/spec/build/testsuites/libtests/record04.yml @@ -0,0 +1,19 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +build-type: test-program +cflags: [] +copyrights: +- Copyright (C) 2024 embedded brains GmbH & Co. KG +cppflags: [] +cxxflags: [] +enabled-by: true +features: c cprogram +includes: [] +ldflags: [] +links: [] +source: +- testsuites/libtests/record04/init.c +stlib: [] +target: testsuites/libtests/record04.exe +type: build +use-after: [] +use-before: [] diff --git a/testsuites/libtests/record03/init.c b/testsuites/libtests/record03/init.c new file mode 100644 index 0000000000..be0e824dee --- /dev/null +++ b/testsuites/libtests/record03/init.c @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2024 embedded brains GmbH & Co. KG + * + * 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 + +#include +#include + +#include +#include + +#define CHECK_ITEM(item, expected_event, expected_data) \ + do { \ + T_eq_u32(RTEMS_RECORD_GET_EVENT((item)->event), expected_event); \ + T_eq_ulong((item)->data, expected_data); \ + } while (0) + +#ifdef RTEMS_SMP +#define COUNT 17 +#else +#define COUNT 18 +#endif + +static void +check_other_cpu(rtems_record_fetch_control *control) { +#ifdef RTEMS_SMP + rtems_record_fetch_status status; + rtems_record_item items_2[3]; + rtems_record_item *storage_items; + size_t storage_item_count; + + memset(&items_2, 0xff, sizeof(items_2)); + storage_items = control->internal.storage_items; + storage_item_count = control->internal.storage_item_count; + control->internal.storage_items = items_2; + control->internal.storage_item_count = RTEMS_ARRAY_SIZE(items_2); + status = rtems_record_fetch(control); + T_eq_int(status, RTEMS_RECORD_FETCH_CONTINUE); + T_eq_ptr(control->fetched_items, &items_2[1]); + T_eq_sz(control->fetched_count, 1); + CHECK_ITEM(&items_2[1], RTEMS_RECORD_PROCESSOR, 0); + control->internal.storage_items = storage_items; + control->internal.storage_item_count = storage_item_count; +#else + (void)control; +#endif +} + +static uint32_t set_affinity(uint32_t cpu) { +#ifdef RTEMS_SMP + rtems_status_code sc; + cpu_set_t set; + + CPU_ZERO(&set); + CPU_SET((int)cpu, &set); + sc = rtems_task_set_affinity(RTEMS_SELF, sizeof(set), &set); + T_rsc_success(sc); +#else + cpu = 0; +#endif + return cpu; +} + +T_TEST_CASE(RecordFetch) { + rtems_record_fetch_control control; + rtems_record_fetch_status status; + rtems_record_item items[COUNT]; + uint32_t i; + uint32_t cpu; + + T_eq_sz(rtems_record_get_item_count_for_fetch(), RTEMS_ARRAY_SIZE(items)); + + memset(&control, 0xff, sizeof(control)); + rtems_record_fetch_initialize(&control, items, RTEMS_ARRAY_SIZE(items)); +#ifdef RTEMS_SMP + T_eq_u32(control.internal.cpu_index, 0); +#endif + T_eq_sz(control.internal.cpu_todo, 0); + T_null(control.fetched_items); + T_eq_sz(control.fetched_count, 0); + + control.internal.storage_item_count = 2; + status = rtems_record_fetch(&control); + control.internal.storage_item_count = RTEMS_ARRAY_SIZE(items); + T_eq_int(status, RTEMS_RECORD_FETCH_INVALID_ITEM_COUNT); + + cpu = set_affinity(0); + + /* + * Fetch the initial uptime events produced by + * _Record_Initialize_watchdogs(). + */ + memset(&items, 0xff, sizeof(items)); + status = rtems_record_fetch(&control); +#ifdef RTEMS_SMP + T_eq_int(status, RTEMS_RECORD_FETCH_CONTINUE); +#else + T_eq_int(status, RTEMS_RECORD_FETCH_DONE); +#endif + T_eq_ptr(control.fetched_items, &items[1]); + T_eq_sz(control.fetched_count, 3); + CHECK_ITEM(&items[1], RTEMS_RECORD_PROCESSOR, 0); + CHECK_ITEM(&items[2], RTEMS_RECORD_UPTIME_LOW, 0); + CHECK_ITEM(&items[3], RTEMS_RECORD_UPTIME_HIGH, 1); +#ifdef RTEMS_SMP + status = rtems_record_fetch(&control); + T_eq_int(status, RTEMS_RECORD_FETCH_DONE); + T_eq_ptr(control.fetched_items, &items[1]); + T_eq_sz(control.fetched_count, 1); + CHECK_ITEM(&items[1], RTEMS_RECORD_PROCESSOR, 1); +#endif + + cpu = set_affinity(1); + + /* Fetch with no events available */ + memset(&items, 0xff, sizeof(items)); + check_other_cpu(&control); + status = rtems_record_fetch(&control); + T_eq_int(status, RTEMS_RECORD_FETCH_DONE); + T_eq_ptr(control.fetched_items, &items[1]); + T_eq_sz(control.fetched_count, 1); + CHECK_ITEM(&items[1], RTEMS_RECORD_PROCESSOR, cpu); + + /* Fetch with overflow */ + + for (i = 0; i < COUNT - 1; ++i) { + rtems_record_produce(2 * i, 2 * i + 1); + } + + memset(&items, 0xff, sizeof(items)); + check_other_cpu(&control); + control.internal.storage_item_count = 4; + status = rtems_record_fetch(&control); + control.internal.storage_item_count = RTEMS_ARRAY_SIZE(items); + T_eq_int(status, RTEMS_RECORD_FETCH_CONTINUE); + T_eq_ptr(control.fetched_items, &items[0]); + T_eq_sz(control.fetched_count, 4); + CHECK_ITEM(&items[0], RTEMS_RECORD_PROCESSOR, cpu); + CHECK_ITEM(&items[1], RTEMS_RECORD_PER_CPU_OVERFLOW, 1); + CHECK_ITEM(&items[2], 2, 3); + CHECK_ITEM(&items[3], 4, 5); + + status = rtems_record_fetch(&control); + T_eq_int(status, RTEMS_RECORD_FETCH_DONE); + T_eq_ptr(control.fetched_items, &items[1]); + T_eq_sz(control.fetched_count, COUNT - 3); + CHECK_ITEM(&items[1], RTEMS_RECORD_PROCESSOR, cpu); + + for (i = 0; i < COUNT - 4; ++i) { + CHECK_ITEM(&items[i + 2], 2 * i + 6, 2 * i + 7); + } +} + +const char rtems_test_name[] = "RECORD 3"; + +static void Init(rtems_task_argument argument) +{ + rtems_test_run(argument, TEST_STATE); +} + +#define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER + +#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER + +#define CONFIGURE_MAXIMUM_TASKS 1 + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION + +#define CONFIGURE_MAXIMUM_PROCESSORS 2 + +#define CONFIGURE_RECORD_PER_PROCESSOR_ITEMS 16 + +#define CONFIGURE_INIT + +#include diff --git a/testsuites/libtests/record04/init.c b/testsuites/libtests/record04/init.c new file mode 100644 index 0000000000..22a9ba3679 --- /dev/null +++ b/testsuites/libtests/record04/init.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2024 embedded brains GmbH & Co. KG + * + * 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 + +#include +#include + +#include +#include + +#define ITEM_CAPACITY 512 + +#define RECOMMENDED_ITEM_COUNT (ITEM_CAPACITY + 2) + +typedef struct { + bool early; + bool late; + bool interrupt; + bool good; + uint32_t how_many; + rtems_record_item items[RECOMMENDED_ITEM_COUNT]; +} RecordFetchConcurrentContext; + +static void action(void *arg) { + RecordFetchConcurrentContext *ctx; + rtems_record_fetch_control control; + + ctx = arg; + rtems_record_fetch_initialize(&control, &ctx->items[0], + RTEMS_ARRAY_SIZE(ctx->items)); + ctx->early = false; + (void)rtems_record_fetch(&control); + ctx->late = true; + + if (control.fetched_count < ITEM_CAPACITY) { + size_t i; + + ctx->good = true; + T_eq_u32(RTEMS_RECORD_GET_EVENT(control.fetched_items[0].event), + RTEMS_RECORD_PROCESSOR); + T_eq_ulong(control.fetched_items[0].data, 0); + T_eq_u32(RTEMS_RECORD_GET_EVENT(control.fetched_items[1].event), + RTEMS_RECORD_PER_CPU_OVERFLOW); + + for (i = 2; i < control.fetched_count; ++i) { + rtems_record_event event; + + event = RTEMS_RECORD_GET_EVENT(control.fetched_items[i].event); + T_true(event == RTEMS_RECORD_USER_0 || event == RTEMS_RECORD_UPTIME_LOW || + event == RTEMS_RECORD_UPTIME_HIGH); + } + } +} + +static void prepare(void *arg) { + RecordFetchConcurrentContext *ctx; + uint32_t i; + + ctx = arg; + ctx->early = true; + ctx->late = false; + ctx->interrupt = false; + + for (i = 0; i < RECOMMENDED_ITEM_COUNT - 2; ++i) { + rtems_record_produce(RTEMS_RECORD_USER_0, 0); + } +} + +static T_interrupt_test_state interrupt(void *arg) { + RecordFetchConcurrentContext *ctx; + + ctx = arg; + + if (ctx->good) { + return T_INTERRUPT_TEST_DONE; + } + + if (ctx->early) { + return T_INTERRUPT_TEST_EARLY; + } + + if (ctx->late) { + return T_INTERRUPT_TEST_LATE; + } + + if (!ctx->interrupt) { + uint32_t i; + + for (i = 0; i < ctx->how_many; ++i) { + rtems_record_produce(RTEMS_RECORD_USER_1, 0); + } + } + + ctx->interrupt = true; + return T_INTERRUPT_TEST_CONTINUE; +} + +static const T_interrupt_test_config config = {.prepare = prepare, + .action = action, + .interrupt = interrupt, + .max_iteration_count = 10000}; + +T_TEST_CASE(RecordFetchConcurrent) { + RecordFetchConcurrentContext ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.how_many = ITEM_CAPACITY + 1; + T_interrupt_test(&config, &ctx); + T_true(ctx.good); + + memset(&ctx, 0, sizeof(ctx)); + ctx.how_many = ITEM_CAPACITY / 2; + T_interrupt_test(&config, &ctx); + T_true(ctx.good); +} + +const char rtems_test_name[] = "RECORD 4"; + +static void Init(rtems_task_argument argument) { + rtems_test_run(argument, TEST_STATE); +} + +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER + +#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER + +#define CONFIGURE_MAXIMUM_TASKS 1 + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION + +#define CONFIGURE_RECORD_PER_PROCESSOR_ITEMS 512 + +#define CONFIGURE_INIT + +#include