From 8d8573acc8f620c93afa8dd30ea8418d25ad2d21 Mon Sep 17 00:00:00 2001 From: Daniel Cederman Date: Wed, 4 Feb 2015 10:04:05 +0100 Subject: smpcapture02: Add test of functionality to add custom entries to capture trace --- testsuites/smptests/Makefile.am | 1 + testsuites/smptests/configure.ac | 1 + testsuites/smptests/smpcapture02/Makefile.am | 19 + testsuites/smptests/smpcapture02/init.c | 425 ++++++++++++++++++++++ testsuites/smptests/smpcapture02/smpcapture02.doc | 30 ++ testsuites/smptests/smpcapture02/smpcapture02.scn | 2 + 6 files changed, 478 insertions(+) create mode 100644 testsuites/smptests/smpcapture02/Makefile.am create mode 100644 testsuites/smptests/smpcapture02/init.c create mode 100644 testsuites/smptests/smpcapture02/smpcapture02.doc create mode 100644 testsuites/smptests/smpcapture02/smpcapture02.scn diff --git a/testsuites/smptests/Makefile.am b/testsuites/smptests/Makefile.am index 87c3d7f456..4b81a20b85 100644 --- a/testsuites/smptests/Makefile.am +++ b/testsuites/smptests/Makefile.am @@ -13,6 +13,7 @@ SUBDIRS += smpaffinity01 SUBDIRS += smpatomic01 SUBDIRS += smpcache01 SUBDIRS += smpcapture01 +SUBDIRS += smpcapture02 SUBDIRS += smpfatal01 SUBDIRS += smpfatal02 SUBDIRS += smpfatal03 diff --git a/testsuites/smptests/configure.ac b/testsuites/smptests/configure.ac index f25c9d76c1..5aee6ec34b 100644 --- a/testsuites/smptests/configure.ac +++ b/testsuites/smptests/configure.ac @@ -68,6 +68,7 @@ smpaffinity01/Makefile smpatomic01/Makefile smpcache01/Makefile smpcapture01/Makefile +smpcapture02/Makefile smpfatal01/Makefile smpfatal02/Makefile smpfatal03/Makefile diff --git a/testsuites/smptests/smpcapture02/Makefile.am b/testsuites/smptests/smpcapture02/Makefile.am new file mode 100644 index 0000000000..d7a172ced7 --- /dev/null +++ b/testsuites/smptests/smpcapture02/Makefile.am @@ -0,0 +1,19 @@ +rtems_tests_PROGRAMS = smpcapture02 +smpcapture02_SOURCES = init.c + +dist_rtems_tests_DATA = smpcapture02.scn smpcapture02.doc + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../automake/compile.am +include $(top_srcdir)/../automake/leaf.am + +AM_CPPFLAGS += -I$(top_srcdir)/../support/include + +LINK_OBJS = $(smpcapture02_OBJECTS) +LINK_LIBS = $(smpcapture02_LDLIBS) + +smpcapture02$(EXEEXT): $(smpcapture02_OBJECTS) $(smpcapture02_DEPENDENCIES) + @rm -f smpcapture02$(EXEEXT) + $(make-exe) + +include $(top_srcdir)/../automake/local.am diff --git a/testsuites/smptests/smpcapture02/init.c b/testsuites/smptests/smpcapture02/init.c new file mode 100644 index 0000000000..bd912ee8e7 --- /dev/null +++ b/testsuites/smptests/smpcapture02/init.c @@ -0,0 +1,425 @@ +/* + * COPYRIGHT (c) 2015 + * Cobham Gaisler + * + * 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. + */ + +/* + * SMP Capture Test 2 + * + * This program tests the functionality to add custom entries to + * the SMP capture trace. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "tmacros.h" + +const char rtems_test_name[] = "SMPCAPTURE 2"; + +#define MAX_CPUS 4 +#define TASKS_PER_CPU 5 +#define ITERATIONS 3 +#define TASK_PRIO 30 +#define CLOCK_TICKS 100 + +typedef struct { + rtems_id id; + rtems_id task_sem; + rtems_id prev_sem; +} task_data_t; + +typedef struct { + bool found; + const char *info; + rtems_option options; + rtems_interrupt_handler handler; + void *arg; +} clock_interrupt_handler; + +static rtems_id finished_sem; +static task_data_t task_data[ TASKS_PER_CPU * TASKS_PER_CPU ]; +static rtems_interrupt_handler org_clock_handler; + +enum cap_rec_types { + enter_add_number, + exit_add_number, + clock_tick +}; + +/* + * These records define the data stored in the capture trace. + * The records must be 64-bit aligned to make sure that the time + * attribute in rtems_capture_record_t is correctly aligned. + */ +typedef struct { + enum cap_rec_types id; + uint32_t a; + uint32_t b; +} __attribute__ ((aligned (8))) enter_add_number_record_t; + +typedef struct { + enum cap_rec_types id; + uint32_t res; +} __attribute__ ((aligned (8))) exit_add_number_record_t; + +typedef struct { + enum cap_rec_types id; + void *arg; +} __attribute__ ((aligned (8))) clock_tick_record_t; + +typedef struct { + enum cap_rec_types id; +} empty_record_t ; + +/* + * The function that we want to trace + */ +static uint32_t add_number(uint32_t a, uint32_t b) +{ + return a+b; +} + +/* + * The wrapper for the function we want to trace. Records + * input arguments and the result to the capture trace. + */ +static uint32_t add_number_wrapper(uint32_t a, uint32_t b) +{ + enter_add_number_record_t enter_rec; + exit_add_number_record_t exit_rec; + uint32_t res; + void* rec; + + enter_rec.id = enter_add_number; + enter_rec.a = a; + enter_rec.b = b; + + rtems_capture_begin_add_record(_Thread_Get_executing(), + RTEMS_CAPTURE_TIMESTAMP, sizeof(rtems_capture_record_t)+ + sizeof(enter_add_number_record_t), &rec); + rec = rtems_capture_append_to_record(rec, &enter_rec, sizeof(enter_rec)); + rtems_capture_end_add_record(rec); + + res = add_number(a, b); + + exit_rec.id = exit_add_number; + exit_rec.res = res; + + rtems_capture_begin_add_record(_Thread_Get_executing(), + RTEMS_CAPTURE_TIMESTAMP, sizeof(rtems_capture_record_t)+ + sizeof(exit_add_number_record_t), &rec); + rec = rtems_capture_append_to_record(rec, &exit_rec, sizeof(exit_rec)); + rtems_capture_end_add_record(rec); + + return res; +} + +/* + * Task that calls the function we want to trace + */ +static void task(rtems_task_argument arg) +{ + rtems_status_code sc; + uint32_t i; + + for ( i = 0; i < ITERATIONS; i++ ) { + /* + * Wait until the previous task in the task chain + * has completed its operation. + */ + sc = rtems_semaphore_obtain(task_data[arg].prev_sem, 0, 0); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + add_number_wrapper(arg, i); + + /* + * Signal the next task in the chain to continue + */ + sc = rtems_semaphore_release(task_data[arg].task_sem); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + } + + /* Signal the main task that this task has finished */ + sc = rtems_semaphore_release(finished_sem); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + rtems_task_suspend(rtems_task_self()); +} + +static void test(void) +{ + rtems_status_code sc; + uint32_t cpu_count; + uint32_t t; + uint32_t c; + rtems_task_argument idx; + cpu_set_t cpu_set; + + /* Semaphore to signal end of test */ + sc = rtems_semaphore_create(rtems_build_name('D', 'o', 'n', 'e'), 0, + RTEMS_LOCAL | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_FIFO, 0, &finished_sem); + + /* Get the number of processors that we are using. */ + cpu_count = rtems_get_processor_count(); + + /* + * Create a set of tasks per CPU. Chain them together using + * semaphores so that only one task can be active at any given + * time. + */ + for ( c = 0; c < cpu_count; c++ ) { + for ( t = 0; t < TASKS_PER_CPU; t++ ) { + idx = c * TASKS_PER_CPU + t; + + sc = rtems_task_create(rtems_build_name('T', 'A', '0' + c, '0' + t), + TASK_PRIO, + RTEMS_MINIMUM_STACK_SIZE, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &task_data[idx].id); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_semaphore_create(rtems_build_name('S', 'E', '0' + c, '0' + t), + 0, + RTEMS_LOCAL | + RTEMS_SIMPLE_BINARY_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_FIFO, 0, &task_data[idx].task_sem); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + task_data[(idx + 1) % (cpu_count * TASKS_PER_CPU)].prev_sem = + task_data[idx].task_sem; + + CPU_ZERO_S(sizeof(cpu_set_t), &cpu_set); + CPU_SET_S(c, sizeof(cpu_set_t), &cpu_set); + + sc = rtems_task_set_affinity(task_data[idx].id, sizeof(cpu_set_t), + &cpu_set); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + } + } + + /* Start the tasks */ + for ( idx = 0; idx < cpu_count * TASKS_PER_CPU; idx++ ) { + sc = rtems_task_start(task_data[idx].id, task, idx); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + } + + /* Start chain */ + sc = rtems_semaphore_release(task_data[0].task_sem); + + /* Wait until chain has completed */ + for ( idx = 0; idx < cpu_count * TASKS_PER_CPU; idx++ ) { + rtems_semaphore_obtain(finished_sem, 0, 0); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + } + +} + +/* Writes an entry in the capture trace for every clock tick */ +static void clock_tick_wrapper(void *arg) +{ + void* rec; + clock_tick_record_t clock_tick_record = {.id = clock_tick}; + Thread_Control* tcb = _Thread_Get_executing(); + + rtems_capture_begin_add_record(tcb, RTEMS_CAPTURE_TIMESTAMP, + sizeof(rtems_capture_record_t) + sizeof(clock_tick_record_t), &rec); + rec = rtems_capture_append_to_record(rec, &clock_tick_record, + sizeof(clock_tick_record)); + rtems_capture_end_add_record(rec); + + org_clock_handler(arg); +} + +/* Tries to locate the clock interrupt handler by looking + * for a handler with the name "Clock" or "clock" */ +static void locate_clock_interrupt_handler( + void *arg, const char *info, rtems_option options, + rtems_interrupt_handler handler, void *handler_arg) +{ + clock_interrupt_handler *cih = (clock_interrupt_handler*)arg; + if ( !strcmp(info, "clock") || !strcmp(info, "Clock") ) { + cih->info = info; + cih->options = options; + cih->handler = handler; + cih->arg = handler_arg; + cih->found = true; + } +} + +static void Init(rtems_task_argument arg) +{ + rtems_status_code sc; + uint32_t i; + uint32_t cpu; + uint32_t read; + uint32_t enter_count; + uint32_t exit_count; + uint32_t clock_tick_count; + uint32_t res_should_be; + rtems_name name; + rtems_capture_record_t *recs; + rtems_capture_record_t *prev_rec; + empty_record_t *record; + enter_add_number_record_t *enter_add_number_rec; + exit_add_number_record_t *exit_add_number_rec; + rtems_vector_number vec; + clock_interrupt_handler cih = {.found = 0}; + + TEST_BEGIN(); + + sc = rtems_capture_open(50000, NULL); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_capture_watch_global(true); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_capture_control(true); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + /* Run main test */ + test(); + + /* Try to find the clock interrupt handler */ + for ( vec=BSP_INTERRUPT_VECTOR_MIN; vectime>=prev_rec->time); + + if ( recs->events & RTEMS_CAPTURE_TIMESTAMP ) { + record = (empty_record_t*)((char*) recs + + sizeof(rtems_capture_record_t)); + + switch ( record->id ) { + case enter_add_number: + rtems_test_assert(enter_count==exit_count); + enter_count++; + enter_add_number_rec = (enter_add_number_record_t*)record; + res_should_be = add_number(enter_add_number_rec->a, + enter_add_number_rec->b); + rtems_object_get_classic_name(recs->task_id, &name); + +#ifdef VERBOSE + /* Print record */ + printf("Time: %"PRIu64"us Task: %4s => Add %"PRIu32" and" + " %"PRIu32"\n", + recs->time/1000, + (char*)&name, + enter_add_number_rec->a, + enter_add_number_rec->b); +#endif + break; + case exit_add_number: + rtems_test_assert(enter_count==exit_count+1); + exit_count++; + exit_add_number_rec = (exit_add_number_record_t*)record; + /* Verify that the result matches the expected result */ + rtems_test_assert(res_should_be == exit_add_number_rec->res); + +#ifdef VERBOSE + /* Print record */ + rtems_object_get_classic_name(recs->task_id, &name); + printf("Time: %"PRIu64"us Task: %4s => Result is %"PRIu32"\n", + recs->time/1000, + (char*)&name, + exit_add_number_rec->res); +#endif + break; + case clock_tick: + clock_tick_count++; +#ifdef VERBOSE + rtems_object_get_classic_name(recs->task_id, &name); + printf("Time: %"PRIu64"us Task: %4s => Clock tick\n", + recs->time/1000, + (char*)&name); +#endif + break; + default: + rtems_test_assert(0); + } + } + + prev_rec = recs; + recs = (rtems_capture_record_t*) ((char*) recs + recs->size); + } + + rtems_test_assert(enter_count == exit_count); + rtems_test_assert(enter_count == TASKS_PER_CPU * ITERATIONS); + + rtems_capture_release(cpu, read); + } + + if( cih.found ) + rtems_test_assert(clock_tick_count == CLOCK_TICKS); + + TEST_END(); + rtems_test_exit(0); +} + +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER + +#define CONFIGURE_SMP_APPLICATION +#define CONFIGURE_SCHEDULER_PRIORITY_AFFINITY_SMP + +#define CONFIGURE_SMP_MAXIMUM_PROCESSORS MAX_CPUS +#define CONFIGURE_MAXIMUM_PROCESSORS MAX_CPUS +#define CONFIGURE_MAXIMUM_SEMAPHORES MAX_CPUS * TASKS_PER_CPU + 1 +#define CONFIGURE_MAXIMUM_TASKS MAX_CPUS * TASKS_PER_CPU + 1 + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE +#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1 +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION +#define CONFIGURE_INIT + +#include diff --git a/testsuites/smptests/smpcapture02/smpcapture02.doc b/testsuites/smptests/smpcapture02/smpcapture02.doc new file mode 100644 index 0000000000..8aa754916b --- /dev/null +++ b/testsuites/smptests/smpcapture02/smpcapture02.doc @@ -0,0 +1,30 @@ +This file describes the directives and concepts tested by this test set. + +test set name: smpcapture02 + +directives: + + rtems_capture_begin_add_record + rtems_capture_append_to_record + rtems_capture_end_add_record + rtems_capture_read + +concepts: + +SMP Capture Test 2 + +This program tests the functionality to add custom entries to +the SMP capture trace. + +A simple function is wrapped inside another function that stores +the input arguments and the function's result in the capture trace +every time it is called. The function is called by a set of tasks +per CPU during the test. The tasks are linked together by semaphores +in such a way that only one task is active at any given time. At the +end of the test the data stored in the capture trace is verified to +make sure that all invocations of the traced function were stored correctly. + +The test also traces clock tick interrupts. If an interrupt handler +with the name "Clock" or "clock" exists, it is assumed to be the main +clock interrupt handler. The test wraps this function with another function +that adds an entry to the trace for every clock tick. diff --git a/testsuites/smptests/smpcapture02/smpcapture02.scn b/testsuites/smptests/smpcapture02/smpcapture02.scn new file mode 100644 index 0000000000..c4435e23a9 --- /dev/null +++ b/testsuites/smptests/smpcapture02/smpcapture02.scn @@ -0,0 +1,2 @@ +*** BEGIN OF TEST SMPCAPTURE 2 *** +*** END OF TEST SMPCAPTURE 2 *** -- cgit v1.2.3