[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