Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:42

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  *  COPYRIGHT (c) 2015
0005  *  Cobham Gaisler
0006  *
0007  * Redistribution and use in source and binary forms, with or without
0008  * modification, are permitted provided that the following conditions
0009  * are met:
0010  * 1. Redistributions of source code must retain the above copyright
0011  *    notice, this list of conditions and the following disclaimer.
0012  * 2. Redistributions in binary form must reproduce the above copyright
0013  *    notice, this list of conditions and the following disclaimer in the
0014  *    documentation and/or other materials provided with the distribution.
0015  *
0016  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0017  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0019  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0020  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0021  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0022  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0023  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0024  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0025  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0026  * POSSIBILITY OF SUCH DAMAGE.
0027  */
0028 
0029 /*
0030  * SMP Capture Test 2
0031  *
0032  * This program tests the functionality to add custom entries to
0033  * the SMP capture trace.
0034  *
0035  */
0036 
0037 /* #define VERBOSE 1 */
0038 
0039 #ifdef HAVE_CONFIG_H
0040 #include "config.h"
0041 #endif
0042 
0043 #include <rtems.h>
0044 #include <bsp/irq.h>
0045 #include <rtems/captureimpl.h>
0046 #include "tmacros.h"
0047 
0048 const char rtems_test_name[] = "SMPCAPTURE 2";
0049 
0050 #define MAX_CPUS       4
0051 #define TASKS_PER_CPU  5
0052 #define ITERATIONS     3
0053 #define TASK_PRIO      30
0054 #define CLOCK_TICKS    100
0055 
0056 typedef struct  {
0057   rtems_id id;
0058   rtems_id task_sem;
0059   rtems_id prev_sem;
0060 } per_cpu_task_data;
0061 
0062 typedef struct {
0063   bool found;
0064   const char *info;
0065   rtems_option options;
0066   rtems_interrupt_handler handler;
0067   void *arg;
0068 } clock_interrupt_handler;
0069 
0070 static rtems_id finished_sem;
0071 static per_cpu_task_data task_data[ TASKS_PER_CPU * TASKS_PER_CPU ];
0072 static rtems_interrupt_handler org_clock_handler;
0073 
0074 typedef enum {
0075   enter_add_number,
0076   exit_add_number,
0077   clock_tick
0078 } cap_rec_type;
0079 
0080 /*
0081  * These records define the data stored in the capture trace.
0082  * The records must be packed so alignment issues are not a factor.
0083  */
0084 typedef struct {
0085   uint32_t a;
0086   uint32_t b;
0087 } RTEMS_PACKED enter_add_number_record;
0088 
0089 typedef struct {
0090   uint32_t res;
0091 } RTEMS_PACKED exit_add_number_record;
0092 
0093 typedef struct {
0094   void *arg;
0095 } RTEMS_PACKED clock_tick_record;
0096 
0097 /*
0098  * Create a printable set of char's from a name.
0099  */
0100 #define PNAME(n) \
0101   (char) (n >> 24) & 0xff, (char) (n >> 16) & 0xff, \
0102   (char) (n >> 8) & 0xff, (char) (n >> 0) & 0xff
0103 
0104 /*
0105  * The function that we want to trace
0106  */
0107 static uint32_t add_number(uint32_t a, uint32_t b)
0108 {
0109   return a + b;
0110 }
0111 
0112 /*
0113  * The wrapper for the function we want to trace. Records
0114  * input arguments and the result to the capture trace.
0115  */
0116 static uint32_t add_number_wrapper(uint32_t a, uint32_t b)
0117 {
0118   rtems_capture_record_lock_context lock;
0119   enter_add_number_record enter_rec;
0120   exit_add_number_record exit_rec;
0121   cap_rec_type id;
0122   uint32_t res;
0123   void* rec;
0124 
0125   id = enter_add_number;
0126   enter_rec.a = a;
0127   enter_rec.b = b;
0128 
0129   rec = rtems_capture_record_open(_Thread_Get_executing(),
0130                                   RTEMS_CAPTURE_TIMESTAMP,
0131                                   sizeof(id) + sizeof(enter_rec),
0132                                   &lock);
0133   rtems_test_assert(rec != NULL);
0134   rec = rtems_capture_record_append(rec, &id, sizeof(id));
0135   rtems_test_assert(rec != NULL);
0136   rec = rtems_capture_record_append(rec, &enter_rec, sizeof(enter_rec));
0137   rtems_test_assert(rec != NULL);
0138   rtems_capture_record_close(&lock);
0139 
0140   res = add_number(a, b);
0141 
0142   id = exit_add_number;
0143   exit_rec.res = res;
0144 
0145   rec = rtems_capture_record_open(_Thread_Get_executing(),
0146                                   RTEMS_CAPTURE_TIMESTAMP,
0147                                   sizeof(id) + sizeof(exit_rec),
0148                                   &lock);
0149   rtems_test_assert(rec != NULL);
0150   rec = rtems_capture_record_append(rec, &id, sizeof(id));
0151   rtems_test_assert(rec != NULL);
0152   rec = rtems_capture_record_append(rec, &exit_rec, sizeof(exit_rec));
0153   rtems_test_assert(rec != NULL);
0154   rtems_capture_record_close(&lock);
0155 
0156   return res;
0157 }
0158 
0159 /*
0160  * Task that calls the function we want to trace
0161  */
0162 static void task(rtems_task_argument arg)
0163 {
0164   rtems_status_code sc;
0165   uint32_t i;
0166 
0167   for ( i = 0; i < ITERATIONS; i++ ) {
0168     /*
0169      * Wait until the previous task in the task chain
0170      * has completed its operation.
0171      */
0172     sc = rtems_semaphore_obtain(task_data[arg].prev_sem, 0, 0);
0173     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0174 
0175     add_number_wrapper(arg, i);
0176 
0177     /*
0178      * Signal the next task in the chain to continue
0179      */
0180     sc = rtems_semaphore_release(task_data[arg].task_sem);
0181     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0182   }
0183 
0184   /* Signal the main task that this task has finished */
0185   sc = rtems_semaphore_release(finished_sem);
0186   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0187   rtems_task_suspend(rtems_task_self());
0188 }
0189 
0190 static void test(uint32_t cpu_count)
0191 {
0192   rtems_status_code sc;
0193   uint32_t t;
0194   uint32_t c;
0195   rtems_task_argument idx;
0196   cpu_set_t cpu_set;
0197 
0198   /* Semaphore to signal end of test */
0199   sc = rtems_semaphore_create(rtems_build_name('D', 'o', 'n', 'e'), 0,
0200       RTEMS_LOCAL |
0201       RTEMS_NO_INHERIT_PRIORITY |
0202       RTEMS_NO_PRIORITY_CEILING |
0203       RTEMS_FIFO, 0, &finished_sem);
0204 
0205   /*
0206    * Create a set of tasks per CPU. Chain them together using
0207    * semaphores so that only one task can be active at any given
0208    * time.
0209    */
0210   for ( c = 0; c < cpu_count; c++ ) {
0211     for ( t = 0; t < TASKS_PER_CPU; t++ ) {
0212       idx = c * TASKS_PER_CPU + t;
0213 
0214       sc = rtems_task_create(rtems_build_name('T', 'A', '0' + c, '0' + t),
0215           TASK_PRIO,
0216           RTEMS_MINIMUM_STACK_SIZE,
0217           RTEMS_DEFAULT_MODES,
0218           RTEMS_DEFAULT_ATTRIBUTES,
0219           &task_data[idx].id);
0220       rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0221 
0222       sc = rtems_semaphore_create(rtems_build_name('S', 'E', '0' + c, '0' + t),
0223           0,
0224           RTEMS_LOCAL |
0225           RTEMS_SIMPLE_BINARY_SEMAPHORE |
0226           RTEMS_NO_INHERIT_PRIORITY |
0227           RTEMS_NO_PRIORITY_CEILING |
0228           RTEMS_FIFO, 0, &task_data[idx].task_sem);
0229       rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0230 
0231       task_data[(idx + 1) % (cpu_count * TASKS_PER_CPU)].prev_sem =
0232           task_data[idx].task_sem;
0233 
0234       CPU_ZERO_S(sizeof(cpu_set_t), &cpu_set);
0235       CPU_SET_S(c, sizeof(cpu_set_t), &cpu_set);
0236 
0237       sc = rtems_task_set_affinity(task_data[idx].id, sizeof(cpu_set_t),
0238           &cpu_set);
0239       rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0240     }
0241   }
0242 
0243   /* Start the tasks */
0244   for ( idx = 0; idx < cpu_count * TASKS_PER_CPU; idx++ ) {
0245     sc = rtems_task_start(task_data[idx].id, task, idx);
0246     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0247   }
0248 
0249   /* Start chain */
0250   sc = rtems_semaphore_release(task_data[0].task_sem);
0251 
0252   /* Wait until chain has completed */
0253   for ( idx = 0; idx < cpu_count * TASKS_PER_CPU; idx++ ) {
0254     rtems_semaphore_obtain(finished_sem, 0, 0);
0255     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0256   }
0257 
0258 }
0259 
0260 /* Writes an entry in the capture trace for every clock tick */
0261 static void clock_tick_wrapper(void *arg)
0262 {
0263   rtems_capture_record_lock_context lock;
0264   cap_rec_type id  = clock_tick;
0265   Thread_Control* tcb = _Thread_Get_executing();
0266   void* rec;
0267 
0268   rec = rtems_capture_record_open(tcb,
0269                                   RTEMS_CAPTURE_TIMESTAMP,
0270                                   sizeof(id),
0271                                   &lock);
0272   rtems_test_assert(rec != NULL);
0273   rec = rtems_capture_record_append(rec, &id, sizeof(id));
0274   rtems_test_assert(rec != NULL);
0275   rtems_capture_record_close(&lock);
0276 
0277   org_clock_handler(arg);
0278 }
0279 
0280 /* Tries to locate the clock interrupt handler by looking
0281  * for a handler with the name "Clock" or "clock" */
0282 static void locate_clock_interrupt_handler(
0283     void *arg, const char *info, rtems_option options,
0284     rtems_interrupt_handler handler, void *handler_arg)
0285 {
0286   clock_interrupt_handler *cih = (clock_interrupt_handler*)arg;
0287   if ( !strcmp(info, "clock") || !strcmp(info, "Clock") ) {
0288     cih->info = info;
0289     cih->options = options;
0290     cih->handler = handler;
0291     cih->arg = handler_arg;
0292     cih->found = true;
0293   }
0294 }
0295 
0296 static void Init(rtems_task_argument arg)
0297 {
0298   rtems_status_code sc;
0299   uint32_t i;
0300   uint32_t cpu;
0301   uint32_t cpu_count;
0302   uint32_t enter_count;
0303   uint32_t exit_count;
0304   uint32_t clock_tick_count;
0305   uint32_t res_should_be;
0306   rtems_vector_number vec;
0307   size_t read;
0308   const void *recs;
0309   cap_rec_type id;
0310   rtems_capture_record rec;
0311   rtems_capture_record prev_rec;
0312   enter_add_number_record enter_rec;
0313   exit_add_number_record exit_rec;
0314   clock_interrupt_handler cih = {.found = 0};
0315 
0316 #ifdef VERBOSE
0317   rtems_name name;
0318 #endif
0319 
0320   TEST_BEGIN();
0321 
0322   /* Get the number of processors that we are using. */
0323   cpu_count = rtems_scheduler_get_processor_maximum();
0324 
0325   sc = rtems_capture_open(50000, NULL);
0326   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0327 
0328   sc = rtems_capture_watch_global(true);
0329   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0330 
0331   sc = rtems_capture_set_control(true);
0332   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0333 
0334   /* Run main test */
0335   test(cpu_count);
0336 
0337   /* Try to find the clock interrupt handler */
0338   for ( vec = 0; vec < BSP_INTERRUPT_VECTOR_COUNT; vec++ ) {
0339     rtems_interrupt_handler_iterate(vec, locate_clock_interrupt_handler, &cih);
0340     if ( cih.found )
0341       break;
0342   }
0343 
0344   /* If we find the clock interrupt handler we replace it with
0345    * a wrapper and wait for a fixed number of ticks.
0346    */
0347   if ( cih.found ) {
0348 #ifdef VERBOSE
0349     printf("Found a clock handler\n");
0350 #endif
0351     org_clock_handler = cih.handler;
0352     rtems_interrupt_handler_install(vec, cih.info,
0353         cih.options | RTEMS_INTERRUPT_REPLACE, clock_tick_wrapper, cih.arg);
0354 
0355     rtems_task_wake_after(CLOCK_TICKS);
0356   }
0357 
0358   /* Disable capturing */
0359   sc = rtems_capture_set_control(false);
0360   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0361 
0362   clock_tick_count = 0;
0363 
0364   /* Read out the trace from all processors */
0365   for ( cpu = 0; cpu < cpu_count; cpu++ ) {
0366     sc = rtems_capture_read(cpu, &read, &recs);
0367     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0368     rtems_test_assert(recs != NULL);
0369 
0370     memset(&rec, 0, sizeof(rec));
0371     prev_rec = rec;
0372     enter_count = 0;
0373     exit_count = 0;
0374     res_should_be = 0;
0375 
0376     for ( i = 0; i < read; i++ ) {
0377 
0378       recs = rtems_capture_record_extract(recs, &rec, sizeof(rec));
0379       rtems_test_assert(recs != NULL);
0380 
0381       /* Verify that time goes forward */
0382       rtems_test_assert(rec.time >= prev_rec.time);
0383 
0384       if ((rec.events & RTEMS_CAPTURE_TIMESTAMP) != 0) {
0385         recs = rtems_capture_record_extract(recs, &id, sizeof(id));
0386         rtems_test_assert(recs != NULL);
0387 
0388         switch (id) {
0389         case enter_add_number:
0390           rtems_test_assert(enter_count == exit_count);
0391           enter_count++;
0392           recs = rtems_capture_record_extract(recs,
0393                                               &enter_rec,
0394                                               sizeof(enter_rec));
0395           rtems_test_assert(recs != NULL);
0396           res_should_be = add_number(enter_rec.a, enter_rec.b);
0397 #ifdef VERBOSE
0398           /* Print record */
0399           rtems_object_get_classic_name(rec.task_id, &name);
0400           printf("Time: %"PRIu64"us Task: %c%c%c%c => Add %"PRIu32" and %"PRIu32" is %"PRIu32"\n",
0401                  rec.time / 1000, PNAME(name), enter_rec.a, enter_rec.b, res_should_be);
0402 #endif
0403           break;
0404         case exit_add_number:
0405           rtems_test_assert(enter_count == exit_count+1);
0406           exit_count++;
0407           recs = rtems_capture_record_extract(recs,
0408                                               &exit_rec,
0409                                               sizeof(exit_rec));
0410           rtems_test_assert(recs != NULL);
0411 #ifdef VERBOSE
0412           /* Print record */
0413           rtems_object_get_classic_name(rec.task_id, &name);
0414           printf("Time: %"PRIu64"us Task: %c%c%c%c => Result is %"PRIu32"\n",
0415                  rec.time / 1000, PNAME(name), exit_rec.res);
0416 #endif
0417           /* Verify that the result matches the expected result */
0418           rtems_test_assert(res_should_be == exit_rec.res);
0419           break;
0420         case clock_tick:
0421           clock_tick_count++;
0422 #ifdef VERBOSE
0423           rtems_object_get_classic_name(rec.task_id, &name);
0424           printf("Time: %"PRIu64"us Task: %c%c%c%c => Clock tick\n",
0425                  rec.time/1000, PNAME(name));
0426 #endif
0427           break;
0428         default:
0429           rtems_test_assert(0);
0430         }
0431       }
0432 
0433       prev_rec = rec;
0434     }
0435 
0436     rtems_test_assert(enter_count == exit_count);
0437     rtems_test_assert(enter_count == TASKS_PER_CPU * ITERATIONS);
0438 
0439     rtems_capture_release(cpu, read);
0440   }
0441 
0442   TEST_END();
0443   rtems_test_exit(0);
0444 }
0445 
0446 #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
0447 #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
0448 
0449 #define CONFIGURE_SCHEDULER_PRIORITY_AFFINITY_SMP
0450 
0451 #define CONFIGURE_MAXIMUM_PROCESSORS MAX_CPUS
0452 #define CONFIGURE_MAXIMUM_PROCESSORS MAX_CPUS
0453 #define CONFIGURE_MAXIMUM_SEMAPHORES MAX_CPUS * TASKS_PER_CPU + 1
0454 #define CONFIGURE_MAXIMUM_TASKS MAX_CPUS * TASKS_PER_CPU + 1
0455 
0456 #define CONFIGURE_RTEMS_INIT_TASKS_TABLE
0457 #define CONFIGURE_MAXIMUM_USER_EXTENSIONS  1
0458 #define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
0459 #define CONFIGURE_INIT
0460 
0461 #include <rtems/confdefs.h>