[rtems commit] smpcapture02: Add test of functionality to add custom entries to capture trace
Chris Johns
chrisj at rtems.org
Thu Feb 12 07:29:11 UTC 2015
On 12/02/2015 2:34 am, Daniel Hellstrom wrote:
> Module: rtems
> Branch: master
> Commit: 8d8573acc8f620c93afa8dd30ea8418d25ad2d21
> Changeset: http://git.rtems.org/rtems/commit/?id=8d8573acc8f620c93afa8dd30ea8418d25ad2d21
>
> Author: Daniel Cederman <cederman at gaisler.com>
> Date: Wed Feb 4 10:04:05 2015 +0100
>
> 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(+)
>
> diff --git a/testsuites/smptests/Makefile.am b/testsuites/smptests/Makefile.am
> index 87c3d7f..4b81a20 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 f25c9d7..5aee6ec 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 0000000..d7a172c
> --- /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 at .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 0000000..bd912ee
> --- /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 <rtems.h>
> +#include <bsp/irq.h>
> +#include <rtems/captureimpl.h>
> +#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.
> + */
Daniel(s), could enlighten me why this is needed ? Is this a arch
constraint or something else ?
Chris
> +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; vec<BSP_INTERRUPT_VECTOR_MAX; vec++ ) {
> + rtems_interrupt_handler_iterate(vec, locate_clock_interrupt_handler, &cih);
> + if ( cih.found )
> + break;
> + }
> +
> + /* If we find the clock interrupt handler we replace it with
> + * a wrapper and wait for a fixed number of ticks.
> + */
> + if ( cih.found ) {
> +#ifdef VERBOSE
> + printf("Found a clock handler\n");
> +#endif
> + org_clock_handler = cih.handler;
> + rtems_interrupt_handler_install(vec, cih.info,
> + cih.options | RTEMS_INTERRUPT_REPLACE, clock_tick_wrapper, cih.arg);
> +
> + rtems_task_wake_after(CLOCK_TICKS);
> + }
> +
> + /* Disable capturing */
> + sc = rtems_capture_control(false);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + clock_tick_count = 0;
> +
> + /* Read out the trace from all processors */
> + for ( cpu = 0; cpu < rtems_get_processor_count(); cpu++ ) {
> + sc = rtems_capture_read(cpu, &read, &recs);
> + rtems_test_assert(sc == RTEMS_SUCCESSFUL);
> +
> + prev_rec = recs;
> + enter_count = 0;
> + exit_count = 0;
> + res_should_be = 0;
> +
> + for ( i = 0; i < read; i++ ) {
> +
> + /* Verify that time goes forward */
> + rtems_test_assert(recs->time>=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 <rtems/confdefs.h>
> diff --git a/testsuites/smptests/smpcapture02/smpcapture02.doc b/testsuites/smptests/smpcapture02/smpcapture02.doc
> new file mode 100644
> index 0000000..8aa7549
> --- /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 0000000..c4435e2
> --- /dev/null
> +++ b/testsuites/smptests/smpcapture02/smpcapture02.scn
> @@ -0,0 +1,2 @@
> +*** BEGIN OF TEST SMPCAPTURE 2 ***
> +*** END OF TEST SMPCAPTURE 2 ***
>
> _______________________________________________
> vc mailing list
> vc at rtems.org
> http://lists.rtems.org/mailman/listinfo/vc
>
More information about the devel
mailing list