diff options
Diffstat (limited to 'testsuites/isvv/15_clock_manager_undefined_behaviour/clock_manager_undefined_behaviour.c')
-rw-r--r-- | testsuites/isvv/15_clock_manager_undefined_behaviour/clock_manager_undefined_behaviour.c | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/testsuites/isvv/15_clock_manager_undefined_behaviour/clock_manager_undefined_behaviour.c b/testsuites/isvv/15_clock_manager_undefined_behaviour/clock_manager_undefined_behaviour.c new file mode 100644 index 0000000000..bb69f1b8b2 --- /dev/null +++ b/testsuites/isvv/15_clock_manager_undefined_behaviour/clock_manager_undefined_behaviour.c @@ -0,0 +1,579 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 Critical Software SA + * + * 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.h> +#include <rtems/bspIo.h> +#include "../shared/utils.h" +#include "../shared/isvv_rtems_aux.h" +#include <string.h> + +/** + * + * @brief Tests impact of undefined behaviours + * + * This test case performs the calculation of the Mandelbrot set in parallel with + * execution of various clock-related functions + */ + +#define MAX_TLS_SIZE RTEMS_ALIGN_UP(64, RTEMS_TASK_STORAGE_ALIGNMENT) + +#define TASK_STORAGE_SIZE \ + RTEMS_TASK_STORAGE_SIZE( \ + MAX_TLS_SIZE + RTEMS_MINIMUM_STACK_SIZE, \ + TASK_ATTRIBUTES) + +#define TASK_ATTRIBUTES RTEMS_FLOATING_POINT + +// test specific global vars +#define TASK_COUNT (TEST_PROCESSORS - 1) + +#define ITOA_STR_SIZE (8 * sizeof(int) + 1) + +// 1 (microsecond) is equal to (2^64 - 1)*10^-6 (64 bit fraction seconds). +// Using 7 significant figures (enough to preserve microsecond precision) this is 18446740000000. +#define FRAC_BINTIME_TO_USEC_CONV_FACTOR 18446740000000LLU + +#define NUM_TEST_FUNCTIONS 15 +#define RELATIVE_SLEEP 0 +#define FUNCTION_WAIT_MICROSECONDS 100 +#define FUNCTION_WAIT_NANOSECONDS (FUNCTION_WAIT_MICROSECONDS * 1000) +// Acceptable difference returned by subsequent clock-related function call +#define MARGIN_USEC 8 + +static rtems_id start_barrier; +static rtems_id TASK_COUNTER_SEMAPHORE; + +typedef struct timespec timespec_t; +typedef struct bintime bintime_t; +typedef struct timeval timeval_t; + +typedef enum +{ + TIMESPEC, + BINTIME, + TIMEVAL +} struct_type; + +// Create storage areas for each worker, using task construct forces +RTEMS_ALIGNED(RTEMS_TASK_STORAGE_ALIGNMENT) +static char calc_task_storage[TASK_COUNT][TASK_STORAGE_SIZE]; + +/* debug +RTEMS_ALIGNED(RTEMS_TASK_STORAGE_ALIGNMENT) +static uint32_t task_processor[TASK_COUNT]; +*/ + +RTEMS_ALIGNED(RTEMS_TASK_STORAGE_ALIGNMENT) +static uint32_t task_before_mandelbrot[TASK_COUNT]; + +RTEMS_ALIGNED(RTEMS_TASK_STORAGE_ALIGNMENT) +static uint32_t task_after_mandelbrot[TASK_COUNT]; + +/* + * Find whether later time struct is within margin_usec of same type time struct earlier. + */ +static bool close_in_time(void *earlier, void *later, uint32_t margin_usec, struct_type type) +{ + bool ret = false; + uint64_t earlier_total, later_total; + + /* + char str[ITOA_STR_SIZE]; + */ + + switch(type) + { + case TIMESPEC: + earlier_total = (uint64_t)(((timespec_t *)earlier)->tv_sec * 1000000 + ((timespec_t *)earlier)->tv_nsec / 1000); + later_total = (uint64_t)(((timespec_t *)later)->tv_sec * 1000000 + ((timespec_t *)later)->tv_nsec / 1000); + /* debug + print_string("timespec earlier: "); + print_string(itoa((int)earlier_total, &str[0], 10)); + print_string("\n"); + print_string("timespec later: "); + print_string(itoa((int)later_total, &str[0], 10)); + print_string("\n"); + */ + break; + case BINTIME: + earlier_total = (uint64_t)(((bintime_t *)earlier)->sec * 1000000 + ((bintime_t *)earlier)->frac / FRAC_BINTIME_TO_USEC_CONV_FACTOR); + later_total = (uint64_t)(((bintime_t *)later)->sec * 1000000 + ((bintime_t *)later)->frac / FRAC_BINTIME_TO_USEC_CONV_FACTOR); + /* debug + print_string("bintime earlier: "); + print_string(itoa((int)earlier_total, &str[0], 10)); + print_string("\n"); + print_string("bintime later: "); + print_string(itoa((int)later_total, &str[0], 10)); + print_string("\n"); + */ + break; + case TIMEVAL: + earlier_total = (uint64_t)(((timeval_t *)earlier)->tv_sec * 1000000 + ((timeval_t *)earlier)->tv_usec); + later_total = (uint64_t)(((timeval_t *)later)->tv_sec * 1000000 + ((timeval_t *)later)->tv_usec); + /* debug + print_string("timeval earlier: "); + print_string(itoa((int)earlier_total, &str[0], 10)); + print_string("\n"); + print_string("timeval later: "); + print_string(itoa((int)later_total, &str[0], 10)); + print_string("\n"); + */ + break; + default: + return ret; + } + + if(earlier_total + margin_usec >= later_total) + ret = true; + + return ret; +} + +static void calc_task_function(rtems_task_argument arg) +{ + uint8_t tile = (uint8_t) arg; + struct timespec uptime; + + WaitAtBarrier(start_barrier); + + rtems_clock_get_uptime(&uptime); + task_before_mandelbrot[tile - 1] = (uint32_t) (uptime.tv_sec * 1000000 + uptime.tv_nsec/1000); + + mandelbrot_tile(tile, TASK_COUNT); + + rtems_clock_get_uptime(&uptime); + task_after_mandelbrot[tile - 1] = (uint32_t) (uptime.tv_sec * 1000000 + uptime.tv_nsec/1000); + + // Task has finished so increment counter + ReleaseCounterSemaphore(TASK_COUNTER_SEMAPHORE); + SuspendSelf(); +} + +static void Init(rtems_task_argument arg) +{ + (void)arg; + /* debug + uint32_t main_task_processor; + */ + uint32_t functions_start, functions_end; + rtems_id task_id[TASK_COUNT]; + char ch; + /* debug + str[ITOA_STR_SIZE]; + */ + struct timespec uptime, function_wait; + uint8_t wait = 0; + uint8_t unaffected[NUM_TEST_FUNCTIONS]; + + function_wait.tv_sec = 0; + function_wait.tv_nsec = FUNCTION_WAIT_NANOSECONDS; + memset(&unaffected, 0, NUM_TEST_FUNCTIONS); + + TASK_COUNTER_SEMAPHORE = CreateCounterSemaphore(rtems_build_name('T', 'C', 'S', '0'), 0); + + rtems_task_config calc_task_config = { + .initial_priority = PRIO_VERY_HIGH, + .storage_size = TASK_STORAGE_SIZE, + .maximum_thread_local_storage_size = MAX_TLS_SIZE, + .initial_modes = RTEMS_DEFAULT_MODES, + .attributes = TASK_ATTRIBUTES}; + + start_barrier = CreateAutomaticBarrier(TASK_COUNT + 1); + + for (uint32_t i = 0; i < TASK_COUNT; i++) + { + ch = '0' + i; + + calc_task_config.name = rtems_build_name('R', 'U', 'N', ch); + calc_task_config.storage_area = &calc_task_storage[i][0]; + + task_id[i] = DoCreateTask(calc_task_config); + StartTask(task_id[i], calc_task_function, (void *)(i + 1)); + } + + /* + * Start execution of clock functions following formula of: + * Defined behaviour + * Undefined behaviour + * Defined behaviour + */ + + timespec_t ts_first, ts_third; + timespec_t *ts_null = NULL; + + bintime_t bt_first, bt_third; + bintime_t *bt_null = NULL; + + timeval_t tv_first, tv_third; + timeval_t *tv_null = NULL; + + WaitAtBarrier(start_barrier); + + /* debug + main_task_processor = rtems_scheduler_get_processor(); + */ + + // Wait FUNCTION_WAIT_MICROSECONDS microseconds to ensure function calls are concurrent with mandelbrot calculation + clock_nanosleep(CLOCK_MONOTONIC, RELATIVE_SLEEP, &function_wait, NULL); + + rtems_clock_get_uptime(&uptime); + functions_start = (uint32_t) (uptime.tv_sec* 1000000 + uptime.tv_nsec/1000); + + /* + * Check that BOOT_TIME subsequent call is not affected by undefined call. + * Boot time should not change so if the third call does not match the first + * an error has been found. + */ + rtems_clock_get_boot_time(&ts_first); + rtems_clock_get_boot_time_bintime(&bt_first); + rtems_clock_get_boot_time_timeval(&tv_first); + + rtems_clock_get_boot_time(ts_null); + rtems_clock_get_boot_time(&ts_third); + if ((ts_third.tv_sec == ts_first.tv_sec) && (ts_third.tv_nsec == ts_first.tv_nsec)) + unaffected[0] = true; + + rtems_clock_get_boot_time_bintime(bt_null); + rtems_clock_get_boot_time_bintime(&bt_third); + if ((bt_third.sec == bt_first.sec) && (bt_third.frac == bt_first.frac)) + unaffected[1] = true; + + rtems_clock_get_boot_time_timeval(tv_null); + rtems_clock_get_boot_time_timeval(&tv_third); + if ((tv_third.tv_sec == tv_first.tv_sec) && (tv_third.tv_usec == tv_first.tv_usec)) + unaffected[2] = true; + + /* + * Check that CLOCK_MONOTONIC subsequent call is not affected by undefined call. + * If third call is not close in time to the first call an error has been found. + */ + rtems_clock_get_monotonic(&ts_first); + rtems_clock_get_monotonic(ts_null); + rtems_clock_get_monotonic(&ts_third); + if(close_in_time(&ts_first, &ts_third, MARGIN_USEC, TIMESPEC)) + unaffected[3] = true; + + /* debug + print_string("clock_get_monotonic ts first tv_sec: "); + print_string(itoa(ts_first.tv_sec, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic ts first tv_nsec: "); + print_string(itoa(ts_first.tv_nsec, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic ts third tv_sec: "); + print_string(itoa(ts_third.tv_sec, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic ts third tv_nsec: "); + print_string(itoa(ts_third.tv_nsec, &str[0], 10)); + print_string("\n"); + */ + + rtems_clock_get_monotonic_bintime(&bt_first); + rtems_clock_get_monotonic_bintime(bt_null); + rtems_clock_get_monotonic_bintime(&bt_third); + if(close_in_time(&bt_first, &bt_third, MARGIN_USEC, BINTIME)) + unaffected[4] = true; + + /* debug + print_string("clock_get_monotonic bt first sec: "); + print_string(itoa(bt_first.sec, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic bt first frac: "); + print_string(itoa(bt_first.frac, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic bt third sec: "); + print_string(itoa(bt_third.sec, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic bt third frac: "); + print_string(itoa(bt_third.frac, &str[0], 10)); + print_string("\n"); + */ + + rtems_clock_get_monotonic_timeval(&tv_first); + rtems_clock_get_monotonic_timeval(tv_null); + rtems_clock_get_monotonic_timeval(&tv_third); + if(close_in_time(&tv_first, &tv_third, MARGIN_USEC, TIMEVAL)) + unaffected[5] = true; + + /* debug + print_string("clock_get_monotonic tv first tv_sec: "); + print_string(itoa(tv_first.tv_sec, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic tv first tv_nsec: "); + print_string(itoa(tv_first.tv_usec, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic tv third tv_sec: "); + print_string(itoa(tv_third.tv_sec, &str[0], 10)); + print_string("\n"); + print_string("clock_get_monotonic tv third tv_nsec: "); + print_string(itoa(tv_third.tv_usec, &str[0], 10)); + print_string("\n"); + */ + + /* + * Check that coarse CLOCK_MONOTONIC subsequent call is not affected by undefined call. + * If third call is not close in time to the first call an error has been found. + */ + rtems_clock_get_monotonic_coarse(&ts_first); + rtems_clock_get_monotonic_coarse(ts_null); + rtems_clock_get_monotonic_coarse(&ts_third); + if(close_in_time(&ts_first, &ts_third, MARGIN_USEC, TIMESPEC)) + unaffected[6] = true; + + rtems_clock_get_monotonic_coarse_bintime(&bt_first); + rtems_clock_get_monotonic_coarse_bintime(bt_null); + rtems_clock_get_monotonic_coarse_bintime(&bt_third); + if(close_in_time(&bt_first, &bt_third, MARGIN_USEC, BINTIME)) + unaffected[7] = true; + + rtems_clock_get_monotonic_coarse_timeval(&tv_first); + rtems_clock_get_monotonic_coarse_timeval(tv_null); + rtems_clock_get_monotonic_coarse_timeval(&tv_third); + if(close_in_time(&tv_first, &tv_third, MARGIN_USEC, TIMEVAL)) + unaffected[8] = true; + + /* + * Check that CLOCK_REALTIME subsequent call is not affected by undefined call. + * If third call is not close in time to the first call an error has been found. + */ + rtems_clock_get_realtime(&ts_first); + rtems_clock_get_realtime(ts_null); + rtems_clock_get_realtime(&ts_third); + if(close_in_time(&ts_first, &ts_third, MARGIN_USEC, TIMESPEC)) + unaffected[9] = true; + + rtems_clock_get_realtime_bintime(&bt_first); + rtems_clock_get_realtime_bintime(bt_null); + rtems_clock_get_realtime_bintime(&bt_third); + if(close_in_time(&bt_first, &bt_third, MARGIN_USEC, BINTIME)) + unaffected[10] = true; + + rtems_clock_get_realtime_timeval(&tv_first); + rtems_clock_get_realtime_timeval(tv_null); + rtems_clock_get_realtime_timeval(&tv_third); + if(close_in_time(&tv_first, &tv_third, MARGIN_USEC, TIMEVAL)) + unaffected[11] = true; + + /* + * Check that coarse CLOCK_REALTIME subsequent call is not affected by undefined call. + * If third call is not close in time to the first call an error has been found. + */ + rtems_clock_get_realtime_coarse(&ts_first); + rtems_clock_get_realtime_coarse(ts_null); + rtems_clock_get_realtime_coarse(&ts_third); + if(close_in_time(&ts_first, &ts_third, MARGIN_USEC, TIMESPEC)) + unaffected[12] = true; + + rtems_clock_get_realtime_coarse_bintime(&bt_first); + rtems_clock_get_realtime_coarse_bintime(bt_null); + rtems_clock_get_realtime_coarse_bintime(&bt_third); + if(close_in_time(&bt_first, &bt_third, MARGIN_USEC, BINTIME)) + unaffected[13] = true; + + rtems_clock_get_realtime_coarse_timeval(&tv_first); + rtems_clock_get_realtime_coarse_timeval(tv_null); + rtems_clock_get_realtime_coarse_timeval(&tv_third); + if(close_in_time(&tv_first, &tv_third, MARGIN_USEC, TIMEVAL)) + unaffected[14] = true; + + rtems_clock_get_uptime(&uptime); + functions_end = (uint32_t) (uptime.tv_sec* 1000000 + uptime.tv_nsec/1000) ; + + // Wait for all tasks to complete + while (wait < TASK_COUNT) + { + ObtainCounterSemaphore(TASK_COUNTER_SEMAPHORE); + wait++; + } + + // Ensure clock-based functions were executed at the same time as the mandlebrot set + uint32_t latest_task_start = 0; + uint32_t earliest_task_finish = -1; + + for(uint32_t task = 0; task < TASK_COUNT; task++) + { + if(task_before_mandelbrot[task] > latest_task_start) + latest_task_start = task_before_mandelbrot[task]; + + if(task_after_mandelbrot[task] < earliest_task_finish) + earliest_task_finish = task_after_mandelbrot[task]; + } + + if(functions_start > latest_task_start && functions_end < earliest_task_finish) + { + print_string("Functions and mandelbrot executed concurrently: true\n\n"); + } + else + { + print_string("Functions and mandelbrot executed concurrently: false\n\n"); + } + + if(unaffected[0] && unaffected[1] && unaffected[2]) + { + print_string("Boot time subsequent call unaffected: true\n"); + } + else + { + print_string("Boot time subsequent call unaffected: false\n"); + } + + if(unaffected[3] && unaffected[4] && unaffected[5]) + { + print_string("Clock monotonic subsequent call unaffected: true\n"); + } + else + { + print_string("Clock monotonic subsequent call unaffected: false\n"); + } + + if(unaffected[6] && unaffected[7] && unaffected[8]) + { + print_string("Coarse clock monotonic subsequent call unaffected: true\n"); + } + else + { + print_string("Coarse clock monotonic subsequent call unaffected: false\n"); + } + + if(unaffected[9] && unaffected[10] && unaffected[11]) + { + print_string("Clock realtime subsequent call unaffected: true\n"); + } + else + { + print_string("Clock realtime subsequent call unaffected: false\n"); + } + + if(unaffected[12] && unaffected[13] && unaffected[14]) + { + print_string("Coarse clock realtime subsequent call unaffected: true\n"); + } + else + { + print_string("Coarse clock realtime subsequent call unaffected: false\n"); + } + + print_test_results(); + /* debug + print_string("Functions processor: "); + print_string(itoa(main_task_processor, &str[0], 10)); + print_string("\n"); + print_string("Functions start time: "); + print_string(itoa(functions_start, &str[0], 10)); + print_string("\n"); +// -------------------------------------------------------------------------------------------- + print_string("\n\n\n"); + print_string("CheckPoints:"); + print_string("\n|TILE |PROC | START | END |TIME SPENT| (us)"); + print_string("\n"); + for (uint32_t task = 0; task < TASK_COUNT; task++) + { + print_string("| "); + print_string(itoa(task+1, &str[0], 10)); + print_string(" | "); + print_string(itoa(task_processor[task], &str[0], 10)); + print_string(" | "); + print_string(itoa(task_before_mandelbrot[task], &str[0], 10)); + print_string(" | "); + print_string(itoa(task_after_mandelbrot[task], &str[0], 10)); + print_string(" | "); + print_string(itoa(task_after_mandelbrot[task]-task_before_mandelbrot[task], &str[0], 10)); + print_string(" |\n"); + } + print_string("\n\nTICKS Per second: "); + print_string(itoa(rtems_clock_get_ticks_per_second(), &str[0], 10)); + print_string("\n\n"); +// -------------------------------------------------------------------------------------------- + print_string("Functions end time: "); + print_string(itoa(functions_end, &str[0], 10)); + print_string("\n"); + + for(uint8_t i = 0; i < NUM_TEST_FUNCTIONS; i++) + { + print_string("unaffected "); + print_string(itoa(i, &str[0], 10)); + print_string(": "); + print_string(itoa(unaffected[i], &str[0], 10)); + print_string("\n"); + } + */ + + for (uint32_t i = 0; i < TASK_COUNT; i++) + { + DeleteTask(task_id[i]); + } + + DeleteMutex(TASK_COUNTER_SEMAPHORE); + DeleteBarrier(start_barrier); + + rtems_fatal(RTEMS_FATAL_SOURCE_EXIT, 0); +} + +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER + +#define CONFIGURE_MAXIMUM_PROCESSORS TEST_PROCESSORS + +#define CONFIGURE_MAXIMUM_SEMAPHORES 1 + +#define CONFIGURE_MAXIMUM_BARRIERS 1 + +#define CONFIGURE_MAXIMUM_TASKS (TEST_PROCESSORS + 1) + +#define CONFIGURE_SCHEDULER_EDF_SMP + +#define CONFIGURE_MINIMUM_TASK_STACK_SIZE RTEMS_MINIMUM_STACK_SIZE + CPU_STACK_ALIGNMENT + +#define CONFIGURE_EXTRA_TASK_STACKS RTEMS_MINIMUM_STACK_SIZE + +#define CONFIGURE_INIT_TASK_CONSTRUCT_STORAGE_SIZE 2 * TASK_STORAGE_SIZE + +#define CONFIGURE_MINIMUM_TASKS_WITH_USER_PROVIDED_STORAGE \ + CONFIGURE_MAXIMUM_TASKS + +#define CONFIGURE_MICROSECONDS_PER_TICK 1000 + +#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 0 + +#define CONFIGURE_DISABLE_NEWLIB_REENTRANCY + +#define CONFIGURE_APPLICATION_DISABLE_FILESYSTEM + +#define CONFIGURE_MAXIMUM_THREAD_LOCAL_STORAGE_SIZE MAX_TLS_SIZE + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INIT_TASK_ATTRIBUTES (RTEMS_SYSTEM_TASK | TASK_ATTRIBUTES) + +#define CONFIGURE_INIT_TASK_INITIAL_MODE + +#define CONFIGURE_INIT + +#include <rtems/confdefs.h> |