source: rtems/testsuites/smptests/smpcapture02/init.c @ 54835ae

5
Last change on this file since 54835ae was 54835ae, checked in by Sebastian Huber <sebastian.huber@…>, on 02/01/17 at 13:10:18

Rename CONFIGURE_SMP_MAXIMUM_PROCESSORS

Rename CONFIGURE_SMP_MAXIMUM_PROCESSORS to CONFIGURE_MAXIMUM_PROCESSORS
since the SMP part is superfluous.

Update #2894.

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/*
2 *  COPYRIGHT (c) 2015
3 *  Cobham Gaisler
4 *
5 *  The license and distribution terms for this file may be
6 *  found in the file LICENSE in this distribution or at
7 *  http://www.rtems.org/license/LICENSE.
8 */
9
10/*
11 * SMP Capture Test 2
12 *
13 * This program tests the functionality to add custom entries to
14 * the SMP capture trace.
15 *
16 */
17
18/* #define VERBOSE 1 */
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24#include <rtems.h>
25#include <bsp/irq.h>
26#include <rtems/captureimpl.h>
27#include "tmacros.h"
28
29const char rtems_test_name[] = "SMPCAPTURE 2";
30
31#define MAX_CPUS       4
32#define TASKS_PER_CPU  5
33#define ITERATIONS     3
34#define TASK_PRIO      30
35#define CLOCK_TICKS    100
36
37typedef struct  {
38  rtems_id id;
39  rtems_id task_sem;
40  rtems_id prev_sem;
41} per_cpu_task_data;
42
43typedef struct {
44  bool found;
45  const char *info;
46  rtems_option options;
47  rtems_interrupt_handler handler;
48  void *arg;
49} clock_interrupt_handler;
50
51static rtems_id finished_sem;
52static per_cpu_task_data task_data[ TASKS_PER_CPU * TASKS_PER_CPU ];
53static rtems_interrupt_handler org_clock_handler;
54
55typedef enum {
56  enter_add_number,
57  exit_add_number,
58  clock_tick
59} cap_rec_type;
60
61/*
62 * These records define the data stored in the capture trace.
63 * The records must be packed so alignment issues are not a factor.
64 */
65typedef struct {
66  uint32_t a;
67  uint32_t b;
68} RTEMS_PACKED enter_add_number_record;
69
70typedef struct {
71  uint32_t res;
72} RTEMS_PACKED exit_add_number_record;
73
74typedef struct {
75  void *arg;
76} RTEMS_PACKED clock_tick_record;
77
78/*
79 * Create a printable set of char's from a name.
80 */
81#define PNAME(n) \
82  (char) (n >> 24) & 0xff, (char) (n >> 16) & 0xff, \
83  (char) (n >> 8) & 0xff, (char) (n >> 0) & 0xff
84
85/*
86 * The function that we want to trace
87 */
88static uint32_t add_number(uint32_t a, uint32_t b)
89{
90  return a + b;
91}
92
93/*
94 * The wrapper for the function we want to trace. Records
95 * input arguments and the result to the capture trace.
96 */
97static uint32_t add_number_wrapper(uint32_t a, uint32_t b)
98{
99  rtems_capture_record_lock_context lock;
100  enter_add_number_record enter_rec;
101  exit_add_number_record exit_rec;
102  cap_rec_type id;
103  uint32_t res;
104  void* rec;
105
106  id = enter_add_number;
107  enter_rec.a = a;
108  enter_rec.b = b;
109
110  rec = rtems_capture_record_open(_Thread_Get_executing(),
111                                  RTEMS_CAPTURE_TIMESTAMP,
112                                  sizeof(id) + sizeof(enter_rec),
113                                  &lock);
114  rtems_test_assert(rec != NULL);
115  rec = rtems_capture_record_append(rec, &id, sizeof(id));
116  rtems_test_assert(rec != NULL);
117  rec = rtems_capture_record_append(rec, &enter_rec, sizeof(enter_rec));
118  rtems_test_assert(rec != NULL);
119  rtems_capture_record_close(&lock);
120
121  res = add_number(a, b);
122
123  id = exit_add_number;
124  exit_rec.res = res;
125
126  rec = rtems_capture_record_open(_Thread_Get_executing(),
127                                  RTEMS_CAPTURE_TIMESTAMP,
128                                  sizeof(id) + sizeof(exit_rec),
129                                  &lock);
130  rtems_test_assert(rec != NULL);
131  rec = rtems_capture_record_append(rec, &id, sizeof(id));
132  rtems_test_assert(rec != NULL);
133  rec = rtems_capture_record_append(rec, &exit_rec, sizeof(exit_rec));
134  rtems_test_assert(rec != NULL);
135  rtems_capture_record_close(&lock);
136
137  return res;
138}
139
140/*
141 * Task that calls the function we want to trace
142 */
143static void task(rtems_task_argument arg)
144{
145  rtems_status_code sc;
146  uint32_t i;
147
148  for ( i = 0; i < ITERATIONS; i++ ) {
149    /*
150     * Wait until the previous task in the task chain
151     * has completed its operation.
152     */
153    sc = rtems_semaphore_obtain(task_data[arg].prev_sem, 0, 0);
154    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
155
156    add_number_wrapper(arg, i);
157
158    /*
159     * Signal the next task in the chain to continue
160     */
161    sc = rtems_semaphore_release(task_data[arg].task_sem);
162    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
163  }
164
165  /* Signal the main task that this task has finished */
166  sc = rtems_semaphore_release(finished_sem);
167  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
168  rtems_task_suspend(rtems_task_self());
169}
170
171static void test(uint32_t cpu_count)
172{
173  rtems_status_code sc;
174  uint32_t t;
175  uint32_t c;
176  rtems_task_argument idx;
177  cpu_set_t cpu_set;
178
179  /* Semaphore to signal end of test */
180  sc = rtems_semaphore_create(rtems_build_name('D', 'o', 'n', 'e'), 0,
181      RTEMS_LOCAL |
182      RTEMS_NO_INHERIT_PRIORITY |
183      RTEMS_NO_PRIORITY_CEILING |
184      RTEMS_FIFO, 0, &finished_sem);
185
186  /*
187   * Create a set of tasks per CPU. Chain them together using
188   * semaphores so that only one task can be active at any given
189   * time.
190   */
191  for ( c = 0; c < cpu_count; c++ ) {
192    for ( t = 0; t < TASKS_PER_CPU; t++ ) {
193      idx = c * TASKS_PER_CPU + t;
194
195      sc = rtems_task_create(rtems_build_name('T', 'A', '0' + c, '0' + t),
196          TASK_PRIO,
197          RTEMS_MINIMUM_STACK_SIZE,
198          RTEMS_DEFAULT_MODES,
199          RTEMS_DEFAULT_ATTRIBUTES,
200          &task_data[idx].id);
201      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
202
203      sc = rtems_semaphore_create(rtems_build_name('S', 'E', '0' + c, '0' + t),
204          0,
205          RTEMS_LOCAL |
206          RTEMS_SIMPLE_BINARY_SEMAPHORE |
207          RTEMS_NO_INHERIT_PRIORITY |
208          RTEMS_NO_PRIORITY_CEILING |
209          RTEMS_FIFO, 0, &task_data[idx].task_sem);
210      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
211
212      task_data[(idx + 1) % (cpu_count * TASKS_PER_CPU)].prev_sem =
213          task_data[idx].task_sem;
214
215      CPU_ZERO_S(sizeof(cpu_set_t), &cpu_set);
216      CPU_SET_S(c, sizeof(cpu_set_t), &cpu_set);
217
218      sc = rtems_task_set_affinity(task_data[idx].id, sizeof(cpu_set_t),
219          &cpu_set);
220      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
221    }
222  }
223
224  /* Start the tasks */
225  for ( idx = 0; idx < cpu_count * TASKS_PER_CPU; idx++ ) {
226    sc = rtems_task_start(task_data[idx].id, task, idx);
227    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
228  }
229
230  /* Start chain */
231  sc = rtems_semaphore_release(task_data[0].task_sem);
232
233  /* Wait until chain has completed */
234  for ( idx = 0; idx < cpu_count * TASKS_PER_CPU; idx++ ) {
235    rtems_semaphore_obtain(finished_sem, 0, 0);
236    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
237  }
238
239}
240
241/* Writes an entry in the capture trace for every clock tick */
242static void clock_tick_wrapper(void *arg)
243{
244  rtems_capture_record_lock_context lock;
245  cap_rec_type id  = clock_tick;
246  Thread_Control* tcb = _Thread_Get_executing();
247  void* rec;
248
249  rec = rtems_capture_record_open(tcb,
250                                  RTEMS_CAPTURE_TIMESTAMP,
251                                  sizeof(id),
252                                  &lock);
253  rtems_test_assert(rec != NULL);
254  rec = rtems_capture_record_append(rec, &id, sizeof(id));
255  rtems_test_assert(rec != NULL);
256  rtems_capture_record_close(&lock);
257
258  org_clock_handler(arg);
259}
260
261/* Tries to locate the clock interrupt handler by looking
262 * for a handler with the name "Clock" or "clock" */
263static void locate_clock_interrupt_handler(
264    void *arg, const char *info, rtems_option options,
265    rtems_interrupt_handler handler, void *handler_arg)
266{
267  clock_interrupt_handler *cih = (clock_interrupt_handler*)arg;
268  if ( !strcmp(info, "clock") || !strcmp(info, "Clock") ) {
269    cih->info = info;
270    cih->options = options;
271    cih->handler = handler;
272    cih->arg = handler_arg;
273    cih->found = true;
274  }
275}
276
277static void Init(rtems_task_argument arg)
278{
279  rtems_status_code sc;
280  uint32_t i;
281  uint32_t cpu;
282  uint32_t cpu_count;
283  uint32_t enter_count;
284  uint32_t exit_count;
285  uint32_t clock_tick_count;
286  uint32_t res_should_be;
287  rtems_vector_number vec;
288  size_t read;
289  const void *recs;
290  cap_rec_type id;
291  rtems_capture_record rec;
292  rtems_capture_record prev_rec;
293  enter_add_number_record enter_rec;
294  exit_add_number_record exit_rec;
295  clock_interrupt_handler cih = {.found = 0};
296
297#ifdef VERBOSE
298  rtems_name name;
299#endif
300
301  TEST_BEGIN();
302
303  /* Get the number of processors that we are using. */
304  cpu_count = rtems_get_processor_count();
305
306  sc = rtems_capture_open(50000, NULL);
307  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
308
309  sc = rtems_capture_watch_global(true);
310  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
311
312  sc = rtems_capture_set_control(true);
313  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
314
315  /* Run main test */
316  test(cpu_count);
317
318  /* Try to find the clock interrupt handler */
319  for ( vec=BSP_INTERRUPT_VECTOR_MIN; vec<BSP_INTERRUPT_VECTOR_MAX; vec++ ) {
320    rtems_interrupt_handler_iterate(vec, locate_clock_interrupt_handler, &cih);
321    if ( cih.found )
322      break;
323  }
324
325  /* If we find the clock interrupt handler we replace it with
326   * a wrapper and wait for a fixed number of ticks.
327   */
328  if ( cih.found ) {
329#ifdef VERBOSE
330    printf("Found a clock handler\n");
331#endif
332    org_clock_handler = cih.handler;
333    rtems_interrupt_handler_install(vec, cih.info,
334        cih.options | RTEMS_INTERRUPT_REPLACE, clock_tick_wrapper, cih.arg);
335
336    rtems_task_wake_after(CLOCK_TICKS);
337  }
338
339  /* Disable capturing */
340  sc = rtems_capture_set_control(false);
341  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
342
343  clock_tick_count = 0;
344
345  /* Read out the trace from all processors */
346  for ( cpu = 0; cpu < cpu_count; cpu++ ) {
347    sc = rtems_capture_read(cpu, &read, &recs);
348    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
349    rtems_test_assert(recs != NULL);
350
351    memset(&rec, 0, sizeof(rec));
352    prev_rec = rec;
353    enter_count = 0;
354    exit_count = 0;
355    res_should_be = 0;
356
357    for ( i = 0; i < read; i++ ) {
358
359      recs = rtems_capture_record_extract(recs, &rec, sizeof(rec));
360      rtems_test_assert(recs != NULL);
361
362      /* Verify that time goes forward */
363      rtems_test_assert(rec.time >= prev_rec.time);
364
365      if ((rec.events & RTEMS_CAPTURE_TIMESTAMP) != 0) {
366        recs = rtems_capture_record_extract(recs, &id, sizeof(id));
367        rtems_test_assert(recs != NULL);
368
369        switch (id) {
370        case enter_add_number:
371          rtems_test_assert(enter_count == exit_count);
372          enter_count++;
373          recs = rtems_capture_record_extract(recs,
374                                              &enter_rec,
375                                              sizeof(enter_rec));
376          rtems_test_assert(recs != NULL);
377          res_should_be = add_number(enter_rec.a, enter_rec.b);
378#ifdef VERBOSE
379          /* Print record */
380          rtems_object_get_classic_name(rec.task_id, &name);
381          printf("Time: %"PRIu64"us Task: %c%c%c%c => Add %"PRIu32" and %"PRIu32" is %"PRIu32"\n",
382                 rec.time / 1000, PNAME(name), enter_rec.a, enter_rec.b, res_should_be);
383#endif
384          break;
385        case exit_add_number:
386          rtems_test_assert(enter_count == exit_count+1);
387          exit_count++;
388          recs = rtems_capture_record_extract(recs,
389                                              &exit_rec,
390                                              sizeof(exit_rec));
391          rtems_test_assert(recs != NULL);
392#ifdef VERBOSE
393          /* Print record */
394          rtems_object_get_classic_name(rec.task_id, &name);
395          printf("Time: %"PRIu64"us Task: %c%c%c%c => Result is %"PRIu32"\n",
396                 rec.time / 1000, PNAME(name), exit_rec.res);
397#endif
398          /* Verify that the result matches the expected result */
399          rtems_test_assert(res_should_be == exit_rec.res);
400          break;
401        case clock_tick:
402          clock_tick_count++;
403#ifdef VERBOSE
404          rtems_object_get_classic_name(rec.task_id, &name);
405          printf("Time: %"PRIu64"us Task: %c%c%c%c => Clock tick\n",
406                 rec.time/1000, PNAME(name));
407#endif
408          break;
409        default:
410          rtems_test_assert(0);
411        }
412      }
413
414      prev_rec = rec;
415    }
416
417    rtems_test_assert(enter_count == exit_count);
418    rtems_test_assert(enter_count == TASKS_PER_CPU * ITERATIONS);
419
420    rtems_capture_release(cpu, read);
421  }
422
423  TEST_END();
424  rtems_test_exit(0);
425}
426
427#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
428#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
429
430#define CONFIGURE_SCHEDULER_PRIORITY_AFFINITY_SMP
431
432#define CONFIGURE_MAXIMUM_PROCESSORS MAX_CPUS
433#define CONFIGURE_MAXIMUM_PROCESSORS MAX_CPUS
434#define CONFIGURE_MAXIMUM_SEMAPHORES MAX_CPUS * TASKS_PER_CPU + 1
435#define CONFIGURE_MAXIMUM_TASKS MAX_CPUS * TASKS_PER_CPU + 1
436
437#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
438#define CONFIGURE_MAXIMUM_USER_EXTENSIONS  1
439#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
440#define CONFIGURE_INIT
441
442#include <rtems/confdefs.h>
Note: See TracBrowser for help on using the repository browser.