From 6fe01e4b3d52b47c916bc4989e042255ff38e2ca Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 14 Jan 2019 09:08:18 +0100 Subject: build: Move test support to librtemstest.a One reason to move the test support into a dedicated library are the standard output __wrap_*() functions. They may conflict with application level wrappers. Update #3199. --- cpukit/libtest/testbeginend.c | 104 ++++++++++++++++++++++ cpukit/libtest/testbusy.c | 124 ++++++++++++++++++++++++++ cpukit/libtest/testextension.c | 74 ++++++++++++++++ cpukit/libtest/testparallel.c | 196 +++++++++++++++++++++++++++++++++++++++++ cpukit/libtest/testwrappers.c | 48 ++++++++++ 5 files changed, 546 insertions(+) create mode 100644 cpukit/libtest/testbeginend.c create mode 100644 cpukit/libtest/testbusy.c create mode 100644 cpukit/libtest/testextension.c create mode 100644 cpukit/libtest/testparallel.c create mode 100644 cpukit/libtest/testwrappers.c (limited to 'cpukit/libtest') diff --git a/cpukit/libtest/testbeginend.c b/cpukit/libtest/testbeginend.c new file mode 100644 index 0000000000..b739e47b2e --- /dev/null +++ b/cpukit/libtest/testbeginend.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, 2018 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * Copyright (c) 2017 Chris Johns . All rights reserved. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include + +rtems_printer rtems_test_printer = { + .printer = rtems_printk_printer +}; + +static const char* const test_state_strings[] = +{ + "EXPECTED-PASS", + "EXPECTED-FAIL", + "USER_INPUT", + "INDETERMINATE", + "BENCHMARK" +}; + +int rtems_test_begin(const char* name, const RTEMS_TEST_STATE state) +{ + return rtems_printf( + &rtems_test_printer, + "\n\n*** BEGIN OF TEST %s ***\n" + "*** TEST VERSION: %s\n" + "*** TEST STATE: %s\n" + "*** TEST BUILD:" +#if RTEMS_DEBUG + " RTEMS_DEBUG" +#endif +#if RTEMS_MULTIPROCESSING + " RTEMS_MULTIPROCESSING" +#endif +#if RTEMS_NETWORKING + " RTEMS_NETWORKING" +#endif +#if RTEMS_PARAVIRT + " RTEMS_PARAVIRT" +#endif +#if RTEMS_POSIX_API + " RTEMS_POSIX_API" +#endif +#if RTEMS_PROFILING + " RTEMS_PROFILING" +#endif +#if RTEMS_SMP + " RTEMS_SMP" +#endif + "\n" + "*** TEST TOOLS: " __VERSION__ "\n", + name, + rtems_version(), + test_state_strings[state] + ); +} + +int rtems_test_end(const char* name) +{ + return rtems_printf( + &rtems_test_printer, + "\n*** END OF TEST %s ***\n\n", name + ); +} + +void rtems_test_exit(int status) +{ + (void) status; + rtems_shutdown_executive(0); +} + +int rtems_test_printf( + const char* format, + ... +) +{ + va_list ap; + int len; + va_start(ap, format); + len = rtems_vprintf( + &rtems_test_printer, + format, + ap + ); + va_end(ap); + return len; +} diff --git a/cpukit/libtest/testbusy.c b/cpukit/libtest/testbusy.c new file mode 100644 index 0000000000..4cc8aa01fe --- /dev/null +++ b/cpukit/libtest/testbusy.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014, 2018 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +static uint_fast32_t estimate_busy_loop_maximum( void ) +{ + uint_fast32_t units = 0; + uint_fast32_t initial = rtems_clock_get_ticks_since_boot(); + + while ( initial == rtems_clock_get_ticks_since_boot() ) { + ++units; + } + + return units; +} + +static uint_fast32_t wait_for_tick_change( void ) +{ + uint_fast32_t initial = rtems_clock_get_ticks_since_boot(); + uint_fast32_t now; + + do { + now = rtems_clock_get_ticks_since_boot(); + } while ( now == initial ); + + return now; +} + +/* + * It is important that we use actually use the same rtems_test_busy() function + * at the various places, since otherwise the obtained maximum value might be + * wrong. So, the compiler must not inline this function. + */ +RTEMS_NO_INLINE void rtems_test_busy( uint_fast32_t count ) +{ + uint_fast32_t i = 0; + + do { + __asm__ volatile (""); + ++i; + } while ( i < count ); +} + +uint_fast32_t rtems_test_get_one_tick_busy_count( void ) +{ + uint_fast32_t last; + uint_fast32_t now; + uint_fast32_t a; + uint_fast32_t b; + uint_fast32_t m; + + /* Choose a lower bound */ + a = 1; + + /* Estimate an upper bound */ + + wait_for_tick_change(); + b = 2 * estimate_busy_loop_maximum(); + + while ( true ) { + last = wait_for_tick_change(); + rtems_test_busy( b ); + now = rtems_clock_get_ticks_since_boot(); + + if ( now != last ) { + break; + } + + b *= 2; + last = now; + } + + /* Find a good value */ + do { + m = ( a + b ) / 2; + + last = wait_for_tick_change(); + rtems_test_busy( m ); + now = rtems_clock_get_ticks_since_boot(); + + if ( now != last ) { + b = m; + } else { + a = m; + } + } while ( b - a > 1 ); + + return m; +} + +void rtems_test_busy_cpu_usage( time_t seconds, long nanoseconds ) +{ + Thread_Control *executing; + Timestamp_Control busy; + Timestamp_Control start; + Timestamp_Control now; + + executing = _Thread_Get_executing(); + _Thread_Get_CPU_time_used( executing, &start ); + _Timestamp_Set( &busy, seconds, nanoseconds ); + + do { + _Thread_Get_CPU_time_used( executing, &now ); + } while ( now - start < busy ); +} diff --git a/cpukit/libtest/testextension.c b/cpukit/libtest/testextension.c new file mode 100644 index 0000000000..be4759742e --- /dev/null +++ b/cpukit/libtest/testextension.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include + +#if defined(RTEMS_PROFILING) +static bool report_done; + +RTEMS_INTERRUPT_LOCK_DEFINE( static, report_lock, "test report" ) +#endif + +void rtems_test_fatal_extension( + rtems_fatal_source source, + bool always_set_to_false, + rtems_fatal_code code +) +{ +#if defined(RTEMS_PROFILING) + rtems_interrupt_lock_context lock_context; + rtems_printer printer; + + rtems_print_printer_printk( &printer ); + + /* + * Ensures to report only once on SMP machines and ensures that the report is + * output completely. + */ + rtems_interrupt_lock_acquire( &report_lock, &lock_context ); + + if ( !report_done ) { + report_done = true; + + printk( + "\n*** PROFILING REPORT BEGIN %s ***\n", + rtems_test_name + ); + + rtems_profiling_report_xml( + rtems_test_name, + &printer, + 1, + " " + ); + + printk( + "*** PROFILING REPORT END %s ***\n", + rtems_test_name + ); + } + + rtems_interrupt_lock_release( &report_lock, &lock_context ); +#endif + + (void) source; + (void) always_set_to_false; + (void) code; +} diff --git a/cpukit/libtest/testparallel.c b/cpukit/libtest/testparallel.c new file mode 100644 index 0000000000..1182242a7b --- /dev/null +++ b/cpukit/libtest/testparallel.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2013, 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include +#include + +static void stop_worker_timer(rtems_id timer_id, void *arg) +{ + rtems_test_parallel_context *ctx = arg; + + _Atomic_Store_ulong(&ctx->stop, 1, ATOMIC_ORDER_RELAXED); +} + +static void start_worker_stop_timer( + rtems_test_parallel_context *ctx, + rtems_interval duration +) +{ + rtems_status_code sc; + + _Atomic_Store_ulong(&ctx->stop, 0, ATOMIC_ORDER_RELEASE); + + sc = rtems_timer_fire_after( + ctx->stop_worker_timer_id, + duration, + stop_worker_timer, + ctx + ); + _Assert(sc == RTEMS_SUCCESSFUL); + (void) sc; +} + +static void run_tests( + rtems_test_parallel_context *ctx, + const rtems_test_parallel_job *jobs, + size_t job_count +) +{ + SMP_barrier_State bs = SMP_BARRIER_STATE_INITIALIZER; + size_t i; + + _SMP_barrier_Wait(&ctx->barrier, &bs, ctx->worker_count); + + for (i = 0; i < job_count; ++i) { + const rtems_test_parallel_job *job = &jobs[i]; + size_t n = rtems_get_processor_count(); + size_t j = job->cascade ? 0 : rtems_get_processor_count() - 1; + + while (j < n) { + size_t active_worker = j + 1; + size_t worker_index; + rtems_interrupt_level level; + + /* + * Determine worker index via the current processor index to get + * consistent job runs with respect to the cache topology. + */ + rtems_interrupt_local_disable(level); + _SMP_barrier_Wait(&ctx->barrier, &bs, ctx->worker_count); + worker_index = rtems_get_current_processor(); + rtems_interrupt_local_enable(level); + + _Assert(worker_index < ctx->worker_count); + + if (rtems_test_parallel_is_master_worker(worker_index)) { + rtems_interval duration = (*job->init)(ctx, job->arg, active_worker); + + if (duration > 0) { + start_worker_stop_timer(ctx, duration); + } + } + + _SMP_barrier_Wait(&ctx->barrier, &bs, ctx->worker_count); + + if (worker_index <= j) { + (*job->body)(ctx, job->arg, active_worker, worker_index); + } + + _SMP_barrier_Wait(&ctx->barrier, &bs, ctx->worker_count); + + if (rtems_test_parallel_is_master_worker(worker_index)) { + (*job->fini)(ctx, job->arg, active_worker); + } + + _SMP_barrier_Wait(&ctx->barrier, &bs, ctx->worker_count); + + ++j; + } + } +} + +static void worker_task(rtems_task_argument arg) +{ + rtems_test_parallel_context *ctx = (rtems_test_parallel_context *) arg; + rtems_status_code sc; + + (void) sc; + + run_tests(ctx, ctx->jobs, ctx->job_count); + + while (true) { + /* Wait for delete by master worker */ + } +} + +void rtems_test_parallel( + rtems_test_parallel_context *ctx, + rtems_test_parallel_worker_setup worker_setup, + const rtems_test_parallel_job *jobs, + size_t job_count +) +{ + rtems_status_code sc; + size_t worker_index; + rtems_task_priority worker_priority; + + _Atomic_Init_ulong(&ctx->stop, 0); + _SMP_barrier_Control_initialize(&ctx->barrier); + ctx->worker_count = rtems_get_processor_count(); + ctx->worker_ids[0] = rtems_task_self(); + ctx->jobs = jobs; + ctx->job_count = job_count; + + if (RTEMS_ARRAY_SIZE(ctx->worker_ids) < ctx->worker_count) { + rtems_fatal_error_occurred(0xdeadbeef); + } + + sc = rtems_task_set_priority( + RTEMS_SELF, + RTEMS_CURRENT_PRIORITY, + &worker_priority + ); + if (sc != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(0xdeadbeef); + } + + sc = rtems_timer_create( + rtems_build_name('S', 'T', 'O', 'P'), + &ctx->stop_worker_timer_id + ); + if (sc != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(0xdeadbeef); + } + + for (worker_index = 1; worker_index < ctx->worker_count; ++worker_index) { + rtems_id worker_id; + + sc = rtems_task_create( + rtems_build_name('W', 'O', 'R', 'K'), + worker_priority, + RTEMS_MINIMUM_STACK_SIZE, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &worker_id + ); + if (sc != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(0xdeadbeef); + } + + ctx->worker_ids[worker_index] = worker_id; + + if (worker_setup != NULL) { + (*worker_setup)(ctx, worker_index, worker_id); + } + + sc = rtems_task_start(worker_id, worker_task, (rtems_task_argument) ctx); + _Assert(sc == RTEMS_SUCCESSFUL); + } + + run_tests(ctx, jobs, job_count); + + for (worker_index = 1; worker_index < ctx->worker_count; ++worker_index) { + sc = rtems_task_delete(ctx->worker_ids[worker_index]); + _Assert(sc == RTEMS_SUCCESSFUL); + } + + sc = rtems_timer_delete(ctx->stop_worker_timer_id); + _Assert(sc == RTEMS_SUCCESSFUL); +} diff --git a/cpukit/libtest/testwrappers.c b/cpukit/libtest/testwrappers.c new file mode 100644 index 0000000000..c86c4cc3f8 --- /dev/null +++ b/cpukit/libtest/testwrappers.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Chris Johns . All rights reserved. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +int __wrap_printf(const char* format, ...); +int __wrap_puts(const char *str); +int __wrap_putchar(int c); + +int __wrap_printf( + const char* format, + ... +) +{ + va_list ap; + int len; + va_start(ap, format); + len = rtems_vprintf( + &rtems_test_printer, + format, + ap + ); + va_end(ap); + return len; +} + +int __wrap_puts( + const char *str +) +{ + return rtems_test_printf( "%s\n", str ); +} + +int __wrap_putchar( + int c +) +{ + return rtems_test_printf( "%c", c ); +} -- cgit v1.2.3