summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Cederman <cederman@gaisler.com>2015-02-04 10:04:05 +0100
committerDaniel Hellstrom <daniel@gaisler.com>2015-02-11 15:35:26 +0100
commit8d8573acc8f620c93afa8dd30ea8418d25ad2d21 (patch)
tree41051541bd765a98f16d8e996fdf2f1742d91c03
parent0c94a46fa3938922481f06e8c64d8caba6d753b3 (diff)
downloadrtems-8d8573acc8f620c93afa8dd30ea8418d25ad2d21.tar.bz2
smpcapture02: Add test of functionality to add custom entries to capture trace
-rw-r--r--testsuites/smptests/Makefile.am1
-rw-r--r--testsuites/smptests/configure.ac1
-rw-r--r--testsuites/smptests/smpcapture02/Makefile.am19
-rw-r--r--testsuites/smptests/smpcapture02/init.c425
-rw-r--r--testsuites/smptests/smpcapture02/smpcapture02.doc30
-rw-r--r--testsuites/smptests/smpcapture02/smpcapture02.scn2
6 files changed, 478 insertions, 0 deletions
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 <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.
+ */
+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 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 ***