source: rtems/testsuites/smptests/smpcapture02/init.c @ 8d8573ac

4.115
Last change on this file since 8d8573ac was 8d8573ac, checked in by Daniel Cederman <cederman@…>, on 02/04/15 at 09:04:05

smpcapture02: Add test of functionality to add custom entries to capture trace

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