source: rtems/cpukit/libmisc/capture/capture.c @ a923a82

4.104.114.84.95
Last change on this file since a923a82 was a923a82, checked in by Joel Sherrill <joel.sherrill@…>, on 05/15/02 at 16:36:10

2002-05-16 Chris Johns <ccj@…>

  • Per PR194, added the Capture engine.
  • capture/Makefile.am, capture/README, capture/capture-cli.c, capture/capture-cli.h, capture/capture.c, capture/capture.h, capture/.cvsignore: New files.
  • Makefile.am, configure.ac, wrapup/Makefile.am: Modified to reflect addition.
  • Property mode set to 100644
File size: 36.3 KB
Line 
1/*
2  ------------------------------------------------------------------------
3  $Id$
4  ------------------------------------------------------------------------
5 
6  Copyright Objective Design Systems Pty Ltd, 2002
7  All rights reserved Objective Design Systems Pty Ltd, 2002
8  Chris Johns (ccj@acm.org)
9
10  COPYRIGHT (c) 1989-1998.
11  On-Line Applications Research Corporation (OAR).
12
13  The license and distribution terms for this file may be
14  found in the file LICENSE in this distribution.
15
16  This software with is provided ``as is'' and with NO WARRANTY.
17 
18  ------------------------------------------------------------------------
19
20  RTEMS Performance Monitoring and Measurement Framework.
21
22  This is the Capture Engine component.
23
24*/
25
26#include <stdlib.h>
27
28#include "capture.h"
29#include <rtems/score/states.inl>
30#include <rtems/score/wkspace.h>
31#include <rtems/score/wkspace.inl>
32
33/*
34 * These events are always recorded and are not part of the
35 * watch filters.
36 */
37#define RTEMS_CAPTURE_RECORD_EVENTS  (RTEMS_CAPTURE_CREATED_BY_EVENT | \
38                                      RTEMS_CAPTURE_CREATED_EVENT | \
39                                      RTEMS_CAPTURE_STARTED_BY_EVENT | \
40                                      RTEMS_CAPTURE_STARTED_EVENT | \
41                                      RTEMS_CAPTURE_RESTARTED_BY_EVENT | \
42                                      RTEMS_CAPTURE_RESTARTED_EVENT | \
43                                      RTEMS_CAPTURE_DELETED_BY_EVENT | \
44                                      RTEMS_CAPTURE_DELETED_EVENT | \
45                                      RTEMS_CAPTURE_BEGIN_EVENT | \
46                                      RTEMS_CAPTURE_EXITTED_EVENT)
47
48/*
49 * Global capture flags.
50 */
51#define RTEMS_CAPTURE_ON             (1 << 0)
52#define RTEMS_CAPTURE_NO_MEMORY      (1 << 1)
53#define RTEMS_CAPTURE_OVERFLOW       (1 << 2)
54#define RTEMS_CAPTURE_TRIGGERED      (1 << 3)
55#define RTEMS_CAPTURE_READER_ACTIVE  (1 << 4)
56#define RTEMS_CAPTURE_READER_WAITING (1 << 5)
57#define RTEMS_CAPTURE_GLOBAL_WATCH   (1 << 6)
58
59/*
60 * RTEMS Capture Data.
61 */
62static rtems_capture_record_t*  capture_records;
63static rtems_unsigned32         capture_size;
64static rtems_unsigned32         capture_count;
65static rtems_capture_record_t*  capture_in;
66static rtems_unsigned32         capture_out;
67static rtems_unsigned32         capture_flags;
68static rtems_capture_task_t*    capture_tasks;
69static rtems_capture_control_t* capture_controls;
70static int                      capture_extension_index;
71static rtems_id                 capture_id;
72static rtems_capture_timestamp  capture_timestamp;
73static rtems_task_priority      capture_ceiling;
74static rtems_task_priority      capture_floor;
75static rtems_unsigned32         capture_tick_period;
76static rtems_id                 capture_reader;
77
78/*
79 * RTEMS Event text.
80 */
81static const char* capture_event_text[] =
82{
83  "CREATED_BY",
84  "CREATED",
85  "STARTED_BY",
86  "STARTED",
87  "RESTARTED_BY",
88  "RESTARTED",
89  "DELETED_BY",
90  "DELETED",
91  "BEGIN",
92  "EXITTED",
93  "SWITCHED_OUT",
94  "SWITCHED_IN",
95  "TIMESTAMP"
96};
97
98/*
99 * rtems_capture_get_time
100 *
101 *  DESCRIPTION:
102 *
103 * This function returns the current time. If a handler is provided
104 * by the user get the time from that.
105 */
106static inline void rtems_capture_get_time (rtems_unsigned32* ticks,
107                                           rtems_unsigned32* tick_offset)
108{
109  if (capture_timestamp)
110    capture_timestamp (ticks, tick_offset);
111  else
112  {
113    *ticks       = _Watchdog_Ticks_since_boot;
114    *tick_offset = 0;
115  }
116}
117
118/*
119 * rtems_capture_match_names
120 *
121 *  DESCRIPTION:
122 *
123 * This function compares rtems_names. It protects the
124 * capture engine from a change to the way names are supported
125 * in RTEMS.
126 *
127 */
128static inline rtems_boolean
129rtems_capture_match_names (rtems_name lhs, rtems_name rhs)
130{
131  return lhs == rhs;
132}
133
134/*
135 * rtems_capture_dup_name
136 *
137 *  DESCRIPTION:
138 *
139 * This function duplicates an rtems_names. It protects the
140 * cpature engine from a change to the way names are supported
141 * in RTEMS.
142 *
143 */
144static inline void
145rtems_capture_dup_name (rtems_name* dst, rtems_name src)
146{
147  *dst = src;
148}
149
150/*
151 * rtems_capture_name_in_group
152 *
153 *  DESCRIPTION:
154 *
155 * This function sees if a name is in a group of names.
156 *
157 */
158static inline rtems_boolean
159rtems_capture_name_in_group (rtems_name task, rtems_name* tasks)
160{
161  if (tasks)
162  {
163    int i;
164    for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
165      if (rtems_capture_match_names (task, *tasks++))
166        return 1;
167  }
168  return 0;
169}
170
171/*
172 * rtems_capture_match_name_id
173 *
174 *  DESCRIPTION:
175 *
176 * This function matches a name and/or id.
177 */
178static inline rtems_boolean
179rtems_capture_match_name_id (rtems_name lhs_name,
180                             rtems_id   lhs_id,
181                             rtems_name rhs_name,
182                             rtems_id   rhs_id)
183{
184  /*
185   * The left hand side name or id could be 0 which means a wildcard.
186   */
187  if ((lhs_name == 0) && (lhs_id == rhs_id))
188    return 1;
189  else if ((lhs_id == 0) || (lhs_id == rhs_id))
190  {
191    if (rtems_capture_match_names (lhs_name, rhs_name))
192      return 1;
193  }
194  return 0;
195}
196
197/*
198 * rtems_capture_init_stack_usage
199 *
200 *  DESCRIPTION:
201 *
202 * This function setups a stack so its usage can be monitored.
203 */
204static inline void
205rtems_capture_init_stack_usage (rtems_capture_task_t* task)
206{
207  if (task->tcb)
208  {
209    rtems_unsigned32* s;
210    rtems_unsigned32  i;
211
212    task->stack_size  = task->tcb->Start.Initial_stack.size;
213    task->stack_clean = task->stack_size;
214
215    s = task->tcb->Start.Initial_stack.area;
216
217    for (i = 0; i < (task->stack_size - 128); i += 4)
218      *(s++) = 0xdeaddead;
219  }
220}
221
222/*
223 * rtems_capture_find_control
224 *
225 *  DESCRIPTION:
226 *
227 * This function searches for a trigger given a name.
228 *
229 */
230static inline rtems_capture_control_t*
231rtems_capture_find_control (rtems_name name, rtems_id id)
232{
233  rtems_capture_control_t* control;
234 
235  for (control = capture_controls; control != NULL; control = control->next)
236    if (rtems_capture_match_name_id (name, id, control->name, control->id))
237      break;
238  return control;
239}
240
241/*
242 * rtems_capture_create_control
243 *
244 *  DESCRIPTION:
245 *
246 * This function creates a capture control for the capture engine.
247 *
248 */
249static inline rtems_capture_control_t*
250rtems_capture_create_control (rtems_name name, rtems_id id)
251{
252  rtems_interrupt_level    level;
253  rtems_capture_control_t* control;
254  rtems_capture_task_t*    task;
255
256  if ((name == 0) && (id == 0))
257    return NULL;
258
259  control = rtems_capture_find_control (name, id);
260 
261  if (control == NULL)
262  {
263    control = _Workspace_Allocate (sizeof (rtems_capture_control_t));
264
265    if (control == NULL)
266    {
267      capture_flags |= RTEMS_CAPTURE_NO_MEMORY;
268      return NULL;
269    }
270   
271    control->name  = name;
272    control->id    = id;
273    control->flags = 0;
274
275    memset (control->from,    0, sizeof (control->from));
276    memset (control->from_id, 0, sizeof (control->from_id));
277   
278    rtems_interrupt_disable (level);
279
280    control->next    = capture_controls;
281    capture_controls = control;
282
283    /*
284     * We need to scan the task list as set the control to the
285     * tasks.
286     */
287    for (task = capture_tasks; task != NULL; task = task->next)
288      if (rtems_capture_match_name_id (name, id, task->name, task->id))
289        task->control = control;
290
291    rtems_interrupt_enable (level);
292  }
293
294  return control;
295}
296
297/*
298 * rtems_capture_create_capture_task
299 *
300 *  DESCRIPTION:
301 *
302 * This function create the task control.
303 *
304 */
305static inline rtems_capture_task_t*
306rtems_capture_create_capture_task (rtems_tcb* new_task)
307{
308  rtems_interrupt_level    level;
309  rtems_capture_task_t*    task;
310  rtems_capture_control_t* control;
311
312  task = _Workspace_Allocate (sizeof (rtems_capture_task_t));
313
314  if (task == NULL)
315  {
316    capture_flags |= RTEMS_CAPTURE_NO_MEMORY;
317    return NULL;
318  }
319
320  rtems_capture_dup_name (&task->name, *((rtems_name*) new_task->Object.name));
321 
322  task->id               = new_task->Object.id;
323  task->flags            = 0;
324  task->in               = 0;
325  task->out              = 0;
326  task->tcb              = new_task;
327  task->ticks            = 0;
328  task->tick_offset      = 0;
329  task->ticks_in         = 0;
330  task->tick_offset_in   = 0;
331  task->control          = 0;
332  task->last_ticks       = 0;
333  task->last_tick_offset = 0;
334
335  task->tcb->extensions[capture_extension_index] = task;
336
337  task->start_priority = new_task->Start.initial_priority;
338  task->stack_size     = new_task->Start.Initial_stack.size;
339  task->stack_clean    = task->stack_size;
340
341  rtems_interrupt_disable (level);
342
343  task->next    = capture_tasks;
344  capture_tasks = task;
345
346  rtems_interrupt_enable (level);
347
348  /*
349   * We need to scan the default control list to initialise
350   * this control.
351   */
352
353  for (control = capture_controls; control != NULL; control = control->next)
354    if (rtems_capture_match_name_id (control->name, control->id,
355                                     task->name, task->id))
356      task->control = control;
357 
358  return task;
359}
360
361/*
362 * rtems_capture_record
363 *
364 *  DESCRIPTION:
365 *
366 * This function records a capture record into the capture buffer.
367 *
368 */
369static inline void
370rtems_capture_record (rtems_capture_task_t* task,
371                      rtems_unsigned32      events)
372{
373  /*
374   * Check the watch state if we have a task control, and
375   * the task's real priority is lower or equal to the ceiling.
376   */
377  if (task)
378  {
379    rtems_capture_control_t* control;
380
381    control = task->control;
382
383    /*
384     * Capure the record if we have an event that is always
385     * captured, or the task's real priority is greater than the
386     * watch ceiling, and the global watch or task watch is enabled.
387     */
388
389    if ((events & RTEMS_CAPTURE_RECORD_EVENTS) ||
390        ((task->tcb->real_priority >= capture_ceiling) &&
391         (task->tcb->real_priority <= capture_floor) &&
392         ((capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH) ||
393          (control && (control->flags & RTEMS_CAPTURE_WATCH)))))
394    {
395      rtems_interrupt_level level;
396     
397      rtems_interrupt_disable (level);
398   
399      if (capture_count < capture_size)
400      {
401        capture_count++;
402        capture_in->task   = task;
403        capture_in->events = (events |
404                              (task->tcb->real_priority) |
405                              (task->tcb->current_priority << 8));
406
407        if ((events & RTEMS_CAPTURE_RECORD_EVENTS) == 0)
408          task->flags |= RTEMS_CAPTURE_TRACED;
409
410        rtems_capture_get_time (&capture_in->ticks, &capture_in->tick_offset);
411
412        if (capture_in == &capture_records[capture_size - 1])
413          capture_in = capture_records;
414        else
415          capture_in++;         
416      }
417      else
418        capture_flags |= RTEMS_CAPTURE_OVERFLOW;
419      rtems_interrupt_enable (level);
420    }
421  }
422}
423
424/*
425 * rtems_capture_create_task
426 *
427 *  DESCRIPTION:
428 *
429 * This function is called when a task is created.
430 *
431 */
432static rtems_boolean
433rtems_capture_create_task (rtems_tcb* current_task,
434                           rtems_tcb* new_task)
435{
436  rtems_capture_task_t* ct;
437  rtems_capture_task_t* nt;
438
439  ct = current_task->extensions[capture_extension_index];
440
441  /*
442   * The task ponters may not be known as the task may have
443   * been created before the capture engine was open. Add them.
444   */
445
446  if (ct == NULL)
447    ct = rtems_capture_create_capture_task (current_task);
448
449  /*
450   * Create the new task's capture control block.
451   */
452  nt = rtems_capture_create_capture_task (new_task);
453
454  /*
455   * If we are logging then record this fact.
456   */
457  rtems_capture_record (ct, RTEMS_CAPTURE_CREATED_BY_EVENT);
458  rtems_capture_record (nt, RTEMS_CAPTURE_CREATED_EVENT);
459
460  return 1 == 1;
461}
462
463/*
464 * rtems_capture_start_task
465 *
466 *  DESCRIPTION:
467 *
468 * This function is called when a task is started.
469 *
470 */
471static rtems_extension
472rtems_capture_start_task (rtems_tcb* current_task,
473                          rtems_tcb* started_task)
474{
475  /*
476   * Get the capture task control block so we can trace this
477   * event.
478   */
479  rtems_capture_task_t* ct;
480  rtems_capture_task_t* st;
481
482  ct = current_task->extensions[capture_extension_index];
483  st = started_task->extensions[capture_extension_index];
484
485  /*
486   * The task ponters may not be known as the task may have
487   * been created before the capture engine was open. Add them.
488   */
489
490  if (ct == NULL)
491    ct = rtems_capture_create_capture_task (current_task);
492
493  if (st == NULL)
494    st = rtems_capture_create_capture_task (started_task);
495
496  rtems_capture_record (ct, RTEMS_CAPTURE_STARTED_BY_EVENT);
497  rtems_capture_record (st, RTEMS_CAPTURE_STARTED_EVENT);
498 
499  rtems_capture_init_stack_usage (st);
500}
501
502/*
503 * rtems_capture_restart_task
504 *
505 *  DESCRIPTION:
506 *
507 * This function is called when a task is restarted.
508 *
509 */
510static rtems_extension
511rtems_capture_restart_task (rtems_tcb* current_task,
512                            rtems_tcb* restarted_task)
513{
514  /*
515   * Get the capture task control block so we can trace this
516   * event.
517   */
518  rtems_capture_task_t* ct;
519  rtems_capture_task_t* rt;
520
521  ct = current_task->extensions[capture_extension_index];
522  rt = restarted_task->extensions[capture_extension_index];
523
524  /*
525   * The task ponters may not be known as the task may have
526   * been created before the capture engine was open. Add them.
527   */
528
529  if (ct == NULL)
530    ct = rtems_capture_create_capture_task (current_task);
531
532  if (rt == NULL)
533    rt = rtems_capture_create_capture_task (restarted_task);
534
535  rtems_capture_record (ct, RTEMS_CAPTURE_RESTARTED_BY_EVENT);
536  rtems_capture_record (rt, RTEMS_CAPTURE_RESTARTED_EVENT);
537
538  rtems_capture_task_stack_usage (rt);
539  rtems_capture_init_stack_usage (rt);
540}
541
542/*
543 * rtems_capture_delete_task
544 *
545 *  DESCRIPTION:
546 *
547 * This function is called when a task is deleted.
548 *
549 */
550static rtems_extension
551rtems_capture_delete_task (rtems_tcb* current_task,
552                           rtems_tcb* deleted_task)
553{
554  /*
555   * Get the capture task control block so we can trace this
556   * event.
557   */
558  rtems_capture_task_t* ct;
559  rtems_capture_task_t* dt;
560
561  /*
562   * The task ponters may not be known as the task may have
563   * been created before the capture engine was open. Add them.
564   */
565
566  ct = current_task->extensions[capture_extension_index];
567  dt = deleted_task->extensions[capture_extension_index];
568
569  if (ct == NULL)
570    ct = rtems_capture_create_capture_task (current_task);
571
572  if (dt == NULL)
573    dt = rtems_capture_create_capture_task (deleted_task);
574
575  rtems_capture_record (ct, RTEMS_CAPTURE_DELETED_BY_EVENT);
576  rtems_capture_record (dt, RTEMS_CAPTURE_DELETED_EVENT);
577
578  rtems_capture_task_stack_usage (dt);
579
580  /*
581   * This task's tcb will be invalid.
582   */
583  dt->tcb = 0;
584}
585
586/*
587 * rtems_capture_begin_task
588 *
589 *  DESCRIPTION:
590 *
591 * This function is called when a task is begun.
592 *
593 */
594static rtems_extension
595rtems_capture_begin_task (rtems_tcb* begin_task)
596{
597  /*
598   * Get the capture task control block so we can trace this
599   * event.
600   */
601  rtems_capture_task_t* bt;
602
603  bt = begin_task->extensions[capture_extension_index];
604
605  /*
606   * The task ponters may not be known as the task may have
607   * been created before the capture engine was open. Add them.
608   */
609
610  if (bt == NULL)
611    bt = rtems_capture_create_capture_task (begin_task);
612
613  rtems_capture_record (bt, RTEMS_CAPTURE_BEGIN_EVENT);
614}
615
616/*
617 * rtems_capture_exitted_task
618 *
619 *  DESCRIPTION:
620 *
621 * This function is called when a task is exitted. That is
622 * returned rather than was deleted.
623 *
624 */
625static rtems_extension
626rtems_capture_exitted_task (rtems_tcb* exitted_task)
627{
628  /*
629   * Get the capture task control block so we can trace this
630   * event.
631   */
632  rtems_capture_task_t* et;
633
634  et = exitted_task->extensions[capture_extension_index];
635
636  /*
637   * The task ponters may not be known as the task may have
638   * been created before the capture engine was open. Add them.
639   */
640
641  if (et == NULL)
642    et = rtems_capture_create_capture_task (exitted_task);
643
644  rtems_capture_record (et, RTEMS_CAPTURE_EXITTED_EVENT);
645
646  rtems_capture_task_stack_usage (et);
647}
648
649/*
650 * rtems_capture_switch_task
651 *
652 *  DESCRIPTION:
653 *
654 * This function is called when a context is switched.
655 *
656 */
657static rtems_extension
658rtems_capture_switch_task (rtems_tcb* current_task,
659                           rtems_tcb* heir_task)
660{
661  /*
662   * Only perform context switch trace processing if tracing is
663   * enabled.
664   */
665  if (capture_flags & RTEMS_CAPTURE_ON)
666  {
667    rtems_unsigned32 ticks;
668    rtems_unsigned32 tick_offset;
669     
670    /*
671     * Get the cpature task control block so we can update the
672     * reference anbd perform any watch or trigger functions.
673     * The task ponters may not be known as the task may have
674     * been created before the capture engine was open. Add them.
675     */
676    rtems_capture_task_t* ct;
677    rtems_capture_task_t* ht;
678
679    if (_States_Is_transient (current_task->current_state))
680    {
681      rtems_id ct_id = current_task->Object.id;
682
683      for (ct = capture_tasks; ct; ct = ct->next)
684        if (ct->id == ct_id)
685          break;
686    }
687    else
688    {
689      ct = current_task->extensions[capture_extension_index];
690
691      if (ct == NULL)
692        ct = rtems_capture_create_capture_task (current_task);
693    }
694
695    ht = heir_task->extensions[capture_extension_index];
696
697    if (ht == NULL)
698      ht = rtems_capture_create_capture_task (heir_task);
699
700    /*
701     * Update the execution time. Assume the tick will not overflow
702     * for now. This may need to change.
703     */
704    rtems_capture_get_time (&ticks, &tick_offset);
705
706    /*
707     * We could end up with null pointers for both the current task
708     * and the heir task.
709     */
710
711    if (ht)
712    {
713      ht->in++;
714      ht->ticks_in       = ticks;
715      ht->tick_offset_in = tick_offset;
716    }
717 
718    if (ct)
719    {
720      ct->out++;
721      ct->ticks += ticks - ct->ticks_in;
722
723      if (capture_timestamp)
724      {
725        tick_offset += capture_tick_period - ct->tick_offset_in;
726 
727        if (tick_offset < capture_tick_period)
728          ct->tick_offset = tick_offset;
729        else
730        {
731          ct->ticks++;
732          ct->tick_offset = tick_offset - capture_tick_period;
733        }
734      }
735      else
736      {
737        ct->tick_offset += 100;
738      }
739    }
740
741    /*
742     * If we have not triggered then see if this is a trigger condition.
743     */
744    if (!(capture_flags & RTEMS_CAPTURE_TRIGGERED))
745    {
746      rtems_capture_control_t* cc = NULL;
747      rtems_capture_control_t* hc = NULL;
748
749      if (ct)
750      {
751        cc = ct->control;
752     
753        /*
754         * Check the current task for a TO_ANY trigger.
755         */
756        if (cc && (cc->flags & RTEMS_CAPTURE_TO_ANY))
757        {
758          capture_flags |= RTEMS_CAPTURE_TRIGGERED;
759          goto triggered;
760        }
761      }
762
763      if (ht)
764      {
765        hc = ht->control;
766
767        /*
768         * Check the next task for a FROM_ANY.
769         */
770        if (hc && (hc->flags & RTEMS_CAPTURE_FROM_ANY))
771        {
772          capture_flags |= RTEMS_CAPTURE_TRIGGERED;
773          goto triggered;
774        }
775      }
776
777      /*
778       * Check is the trigger is from the current task
779       * to the next task.
780       */
781      if (cc && hc && (hc->flags & RTEMS_CAPTURE_FROM_TO))
782        if (rtems_capture_name_in_group (cc->name, hc->from))
783        {
784          capture_flags |= RTEMS_CAPTURE_TRIGGERED;
785          goto triggered;
786        }
787    }
788    else
789    {
790triggered:
791
792      rtems_capture_record (ct, RTEMS_CAPTURE_SWITCHED_OUT_EVENT);
793      rtems_capture_record (ht, RTEMS_CAPTURE_SWITCHED_IN_EVENT);
794    }
795  }
796}
797
798/*
799 * rtems_capture_open
800 *
801 *  DESCRIPTION:
802 *
803 * This function initialises the realtime capture engine allocating the trace
804 * buffer. It is assumed we have a working heap at stage of initialisation.
805 *
806 */
807rtems_status_code
808rtems_capture_open (rtems_unsigned32 size, rtems_capture_timestamp timestamp)
809{
810  rtems_extensions_table capture_extensions;
811  rtems_name             name;
812  rtems_status_code      sc;
813
814  /*
815   * See if the capture engine is already open.
816   */
817
818  if (capture_records)
819    return RTEMS_RESOURCE_IN_USE;
820
821  capture_records = malloc (size * sizeof (rtems_capture_record_t));
822
823  if (capture_records == NULL)
824    return RTEMS_NO_MEMORY;
825
826  capture_size    = size;
827  capture_count   = 0;
828  capture_in      = capture_records;
829  capture_out     = 0;
830  capture_flags   = 0;
831  capture_tasks   = NULL;
832  capture_ceiling = 0;
833  capture_floor   = 255;
834
835  /*
836   * Create the extension table. This is copied so we
837   * can create it as a local.
838   */
839  capture_extensions.thread_create  = rtems_capture_create_task;
840  capture_extensions.thread_start   = rtems_capture_start_task;
841  capture_extensions.thread_restart = rtems_capture_restart_task;
842  capture_extensions.thread_delete  = rtems_capture_delete_task;
843  capture_extensions.thread_switch  = rtems_capture_switch_task;
844  capture_extensions.thread_begin   = rtems_capture_begin_task;
845  capture_extensions.thread_exitted = rtems_capture_exitted_task;
846  capture_extensions.fatal          = NULL;
847
848  /*
849   * Get the tick period from the BSP Configuration Table.
850   */
851  capture_tick_period = _Configuration_Table->microseconds_per_tick;
852 
853  /*
854   * Register the user extension handlers for the CAPture Engine.
855   */
856  name = rtems_build_name ('C', 'A', 'P', 'E');
857  sc   = rtems_extension_create (name, &capture_extensions, &capture_id);
858
859  if (sc != RTEMS_SUCCESSFUL)
860  {
861    capture_id = 0;
862    free (capture_records);
863    capture_records = NULL;
864  }
865  else
866  {
867    capture_extension_index = rtems_get_index (capture_id);;
868  }
869
870  /*
871   * Iterate over the list of existing tasks.
872   */
873
874  return sc;
875}
876
877/*
878 * rtems_capture_close
879 *
880 *  DESCRIPTION:
881 *
882 * This function shutdowns the capture engine and release any claimed
883 * resources.
884 */
885rtems_status_code
886rtems_capture_close ()
887{
888  rtems_interrupt_level    level;
889  rtems_capture_task_t*    task;
890  rtems_capture_control_t* control;
891  rtems_capture_record_t*  records;
892  rtems_status_code        sc;
893
894  rtems_interrupt_disable (level);
895
896  if (!capture_records)
897  {
898    rtems_interrupt_enable (level);
899    return RTEMS_SUCCESSFUL;
900  }
901
902  capture_flags &= ~RTEMS_CAPTURE_ON;
903
904  records = capture_records;
905  capture_records = NULL;
906
907  rtems_interrupt_enable (level);
908
909  /*
910   * Delete the extension first. This means we are now able to
911   * release the resources we have without them being used.
912   */
913
914  sc = rtems_extension_delete (capture_id);
915
916  if (sc != RTEMS_SUCCESSFUL)
917    return sc;
918
919  task = capture_tasks;
920 
921  while (task)
922  {
923    rtems_capture_task_t* delete = task;
924    task = task->next;
925    _Workspace_Free (delete);
926  }
927
928  capture_tasks = NULL;
929
930  control = capture_controls;
931 
932  while (control)
933  {
934    rtems_capture_control_t* delete = control;
935    control = control->next;
936    _Workspace_Free (delete);
937  }
938
939  capture_controls = NULL;
940
941  if (capture_records)
942  {
943    free (capture_records);
944    capture_records = NULL;
945  }
946
947  return RTEMS_SUCCESSFUL;
948}
949
950/*
951 * rtems_capture_control
952 *
953 *  DESCRIPTION:
954 *
955 * This function allows control of tracing at a global level.
956 */
957rtems_status_code
958rtems_capture_control (rtems_boolean enable)
959{
960  rtems_interrupt_level level;
961
962  rtems_interrupt_disable (level);
963
964  if (!capture_records)
965  {
966    rtems_interrupt_enable (level);
967    return RTEMS_UNSATISFIED;
968  }
969
970  if (enable)
971    capture_flags |= RTEMS_CAPTURE_ON;
972  else
973    capture_flags &= ~RTEMS_CAPTURE_ON;
974
975  rtems_interrupt_enable (level);
976
977  return RTEMS_SUCCESSFUL;
978}
979
980/*
981 * rtems_capture_flush
982 *
983 *  DESCRIPTION:
984 *
985 * This function flushes the capture buffer. The prime parameter allows the
986 * capture engine to also be primed again.
987 */
988rtems_status_code
989rtems_capture_flush (rtems_boolean prime)
990{
991  rtems_interrupt_level level;
992  rtems_capture_task_t* task;
993
994  rtems_interrupt_disable (level);
995
996  for (task = capture_tasks; task != NULL; task = task->next)
997    task->flags &= ~RTEMS_CAPTURE_TRACED;
998
999  if (prime)
1000    capture_flags &= ~(RTEMS_CAPTURE_TRIGGERED | RTEMS_CAPTURE_OVERFLOW);
1001  else
1002    capture_flags &= ~RTEMS_CAPTURE_OVERFLOW;
1003
1004  capture_in     = capture_records;
1005  capture_out    = 0;
1006
1007  rtems_interrupt_enable (level);
1008
1009  return RTEMS_SUCCESSFUL;
1010}
1011
1012/*
1013 * rtems_capture_watch_add
1014 *
1015 *  DESCRIPTION:
1016 *
1017 * This function defines a watch for a specific task given a name. A watch
1018 * causes it to be traced either in or out of context. The watch can be
1019 * optionally enabled or disabled with the set routine. It is disabled by
1020 * default.
1021 */
1022rtems_status_code
1023rtems_capture_watch_add (rtems_name name, rtems_id id)
1024{
1025  rtems_capture_control_t* control;
1026
1027  if ((name == 0) && (id == 0))
1028    return RTEMS_UNSATISFIED;
1029
1030  control = rtems_capture_find_control (name, id);
1031
1032  if (control && !id)
1033    return RTEMS_TOO_MANY;
1034
1035  if (!control)
1036    control = rtems_capture_create_control (name, id);
1037
1038  if (!control)
1039    return RTEMS_NO_MEMORY;
1040
1041  return RTEMS_SUCCESSFUL;
1042}
1043
1044/*
1045 * rtems_capture_watch_del
1046 *
1047 *  DESCRIPTION:
1048 *
1049 * This function removes a watch for a specific task given a name. The task
1050 * description will still exist if referenced by a trace record in the trace
1051 * buffer or a global watch is defined.
1052 */
1053rtems_status_code
1054rtems_capture_watch_del (rtems_name name, rtems_id id)
1055{
1056  rtems_interrupt_level     level;
1057  rtems_capture_control_t*  control;
1058  rtems_capture_control_t** prev_control;
1059  rtems_capture_task_t*     task;
1060  rtems_boolean             found = 0;
1061
1062  /*
1063   * Should this test be for wildcards ?
1064   */
1065
1066  for (prev_control = &capture_controls, control = capture_controls;
1067       control != NULL; )
1068  {
1069    if (rtems_capture_match_name_id (name, id, control->name, control->id))
1070    {
1071      rtems_interrupt_disable (level);
1072     
1073      for (task = capture_tasks; task != NULL; task = task->next)
1074        if (task->control == control)
1075          task->control = 0;
1076
1077      *prev_control = control->next;
1078
1079      rtems_interrupt_enable (level);
1080
1081      _Workspace_Free (control);
1082
1083      control = *prev_control;
1084
1085      found = 1;
1086    }
1087    else
1088    {
1089      prev_control = &control->next;
1090      control      = control->next;
1091      }
1092  }
1093
1094  if (found)
1095    return RTEMS_SUCCESSFUL;
1096
1097  return RTEMS_INVALID_NAME;
1098}
1099
1100/*
1101 * rtems_capture_watch_set
1102 *
1103 *  DESCRIPTION:
1104 *
1105 * This function allows control of a watch. The watch can be enabled or
1106 * disabled.
1107 */
1108rtems_status_code
1109rtems_capture_watch_ctrl (rtems_name name, rtems_id id, rtems_boolean enable)
1110{
1111  rtems_interrupt_level    level;
1112  rtems_capture_control_t* control;
1113  rtems_boolean            found = 0;
1114 
1115  /*
1116   * Find the control and then set the watch. It must exist before it can
1117   * be controlled.
1118   */
1119  for (control = capture_controls; control != NULL; control = control->next)
1120  {
1121    if (rtems_capture_match_name_id (name, id, control->name, control->id))
1122    {
1123      rtems_interrupt_disable (level);
1124
1125      if (enable)
1126        control->flags |= RTEMS_CAPTURE_WATCH;
1127      else
1128        control->flags &= ~RTEMS_CAPTURE_WATCH;
1129
1130      rtems_interrupt_enable (level);
1131
1132      found = 1;
1133    }   
1134  }
1135
1136  if (found)
1137    return RTEMS_SUCCESSFUL;
1138
1139  return RTEMS_INVALID_NAME;
1140}
1141
1142/*
1143 * rtems_capture_watch_global
1144 *
1145 *  DESCRIPTION:
1146 *
1147 * This function allows control of a global watch. The watch can be enabled or
1148 * disabled. A global watch configures all tasks below the ceiling and above
1149 * the floor to be traced.
1150 */
1151rtems_status_code
1152rtems_capture_watch_global (rtems_boolean enable)
1153{
1154  rtems_interrupt_level level;
1155 
1156  rtems_interrupt_disable (level);
1157
1158  /*
1159   * We need to keep specific and global watches separate so
1160   * a global enable/disable does not lose a specific watch.
1161   */
1162  if (enable)
1163    capture_flags |= RTEMS_CAPTURE_GLOBAL_WATCH;
1164  else
1165    capture_flags &= ~RTEMS_CAPTURE_GLOBAL_WATCH;
1166
1167  rtems_interrupt_enable (level);
1168
1169  return RTEMS_SUCCESSFUL;
1170}
1171
1172/*
1173 * rtems_capture_watch_global_on
1174 *
1175 *  DESCRIPTION:
1176 *
1177 * This function returns the global watch state.
1178 */
1179rtems_boolean
1180rtems_capture_watch_global_on ()
1181{
1182  return capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH ? 1 : 0;
1183}
1184
1185/*
1186 * rtems_capture_watch_ceiling
1187 *
1188 *  DESCRIPTION:
1189 *
1190 * This function sets a watch ceiling. Tasks at or greating that the
1191 * ceiling priority are not watched. This is a simple way to monitor
1192 * an application and exclude system tasks running at a higher
1193 * priority level.
1194 */
1195rtems_status_code
1196rtems_capture_watch_ceiling (rtems_task_priority ceiling)
1197{
1198  capture_ceiling = ceiling;
1199  return RTEMS_SUCCESSFUL;
1200}
1201
1202/*
1203 * rtems_capture_watch_get_ceiling
1204 *
1205 *  DESCRIPTION:
1206 *
1207 * This function gets the watch ceiling.
1208 */
1209rtems_task_priority
1210rtems_capture_watch_get_ceiling ()
1211{
1212  return capture_ceiling;
1213}
1214
1215/*
1216 * rtems_capture_watch_floor
1217 *
1218 *  DESCRIPTION:
1219 *
1220 * This function sets a watch floor. Tasks at or less that the
1221 * floor priority are not watched. This is a simple way to monitor
1222 * an application and exclude system tasks running at a lower
1223 * priority level.
1224 */
1225rtems_status_code
1226rtems_capture_watch_floor (rtems_task_priority floor)
1227{
1228  capture_floor = floor;
1229  return RTEMS_SUCCESSFUL;
1230}
1231
1232/*
1233 * rtems_capture_watch_get_floor
1234 *
1235 *  DESCRIPTION:
1236 *
1237 * This function gets the watch floor.
1238 */
1239rtems_task_priority
1240rtems_capture_watch_get_floor ()
1241{
1242  return capture_floor;
1243}
1244
1245/*
1246 * rtems_capture_set_trigger
1247 *
1248 *  DESCRIPTION:
1249 *
1250 * This function sets an edge trigger. Left is the left side of
1251 * the edge and right is right side of the edge. The trigger type
1252 * can be -
1253 *
1254 *  FROM_ANY : a switch from any task to the right side of the edge.
1255 *  TO_ANY   : a switch from the left side of the edge to any task.
1256 *  FROM_TO  : a switch from the left side of the edge to the right
1257 *             side of the edge.
1258 *
1259 * This set trigger routine will create a capture control for the
1260 * target task. The task list is searched and any existing tasks
1261 * are linked to the new control.
1262 *
1263 * We can have a number of tasks that have the same name so we
1264 * search using names. This means a number of tasks can be
1265 * linked to single control.
1266 */
1267rtems_status_code
1268rtems_capture_set_trigger (rtems_name              from,
1269                           rtems_id                from_id,
1270                           rtems_name              to,
1271                           rtems_id                to_id,
1272                           rtems_capture_trigger_t trigger)
1273{
1274  rtems_capture_control_t* control;
1275  int                      i;
1276 
1277  /*
1278   * Find the capture control blocks for the from and to
1279   * tasks.
1280   */
1281  if (trigger == rtems_capture_to_any)
1282  {
1283    control = rtems_capture_create_control (from, from_id);
1284    if (control == NULL)
1285      return RTEMS_NO_MEMORY;
1286    control->flags |= RTEMS_CAPTURE_TO_ANY;
1287  }
1288
1289  if ((trigger == rtems_capture_from_to) ||
1290      (trigger == rtems_capture_from_any))
1291  {
1292    control = rtems_capture_create_control (to, to_id);
1293    if (control == NULL)
1294      return RTEMS_NO_MEMORY;
1295   
1296    if (trigger == rtems_capture_from_any)
1297      control->flags |= RTEMS_CAPTURE_FROM_ANY;
1298    else
1299    {
1300      control->flags |= RTEMS_CAPTURE_FROM_TO;
1301      for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
1302      {
1303        if (control->from[i] == 0)
1304        {
1305          control->from[i]    = from;
1306          control->from_id[i] = from_id;
1307          break;
1308        }
1309      }
1310    }
1311  }
1312  return RTEMS_SUCCESSFUL;
1313}
1314
1315/*
1316 * rtems_capture_read
1317 *
1318 *  DESCRIPTION:
1319 *
1320 * This function reads a number of records from the capture buffer.
1321 * The user can optionally block and wait until the buffer as a
1322 * specific number of records available or a specific time has
1323 * elasped.
1324 *
1325 * The function returns the number of record that is has that are
1326 * in a continous block of memory. If the number of available records
1327 * wrap then only those records are provided. This removes the need for
1328 * caller to be concerned about buffer wrappings. If the number of
1329 * requested records cannot be met due to the wrapping of the records
1330 * less than the specified number will be returned.
1331 *
1332 * The user must release the records. This is achieved with a call to
1333 * rtems_capture_release. Calls this function without a release will
1334 * result in at least the same number of records being released.
1335 *
1336 * The 'threshold' parameter is the number of records that must be
1337 * captured before returning. If a timeout period is specified (non-0)
1338 * any captured records will be returned. These parameters stop
1339 * thrashing occuring for a small number of records, yet allows
1340 * a user configured latiency to be applied for single events.
1341 *
1342 * The 'timeout' parameter is in micro-seconds. A value of 0 will disable
1343 * the timeout.
1344 *
1345 */
1346rtems_status_code
1347rtems_capture_read (rtems_unsigned32         threshold,
1348                    rtems_unsigned32         timeout,
1349                    rtems_unsigned32*        read,
1350                    rtems_capture_record_t** recs)
1351{
1352  rtems_interrupt_level level;
1353  rtems_status_code     sc = RTEMS_SUCCESSFUL;
1354  rtems_unsigned32      count;
1355
1356  *read = 0;
1357  *recs = NULL;
1358
1359  rtems_interrupt_disable (level);
1360
1361  /*
1362   * Only one reader is allowed.
1363   */
1364
1365  if (capture_flags & RTEMS_CAPTURE_READER_ACTIVE)
1366  {
1367    rtems_interrupt_enable (level);
1368    return RTEMS_RESOURCE_IN_USE;
1369  }
1370
1371  capture_flags |= RTEMS_CAPTURE_READER_ACTIVE;
1372  *read = count = capture_count;
1373
1374  rtems_interrupt_enable (level);
1375
1376  *recs = &capture_records[capture_out];
1377
1378  for (;;)
1379  {
1380    /*
1381     * See if the count wraps the end of the record buffer.
1382     */
1383    if (count && ((capture_out + count) >= capture_size))
1384      *read = capture_size - capture_out;
1385     
1386    /*
1387     * Do we have a threshold and the current count has not wrapped
1388     * around the end of the capture record buffer ?
1389     */
1390    if ((*read == count) && threshold)
1391    {
1392      /*
1393       * Do we have enough records ?
1394       */
1395      if (*read < threshold)
1396      {
1397        rtems_event_set event_out;
1398
1399        rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, &capture_reader);
1400
1401        rtems_interrupt_disable (level);
1402         
1403        capture_flags |= RTEMS_CAPTURE_READER_WAITING;
1404
1405        rtems_interrupt_enable (level);
1406         
1407        sc = rtems_event_receive (RTEMS_EVENT_0,
1408                                  RTEMS_WAIT | RTEMS_EVENT_ANY,
1409                                  TOD_MICROSECONDS_TO_TICKS (timeout),
1410                                  &event_out);
1411
1412        /*
1413         * Let the user handle all other sorts of errors. This may
1414         * not be the best solution, but oh well, it will do for
1415         * now.
1416         */
1417        if ((sc != RTEMS_SUCCESSFUL) && (sc != RTEMS_TIMEOUT))
1418          break;
1419
1420        rtems_interrupt_disable (level);
1421
1422        *read = count = capture_count;
1423
1424        rtems_interrupt_enable (level);
1425
1426        continue;
1427      }
1428    }
1429
1430    /*
1431     * Always out if we reach here. To loop use continue.
1432     */
1433    break;
1434  }
1435
1436  rtems_interrupt_disable (level);
1437
1438  capture_flags &= ~RTEMS_CAPTURE_READER_ACTIVE;
1439
1440  rtems_interrupt_enable (level);
1441
1442  return sc;
1443}
1444
1445/*
1446 * rtems_capture_release
1447 *
1448 *  DESCRIPTION:
1449 *
1450 * This function releases the requested number of record slots back
1451 * to the capture engine. The count must match the number read.
1452 */
1453rtems_status_code
1454rtems_capture_release (rtems_unsigned32 count)
1455{
1456  rtems_interrupt_level level;
1457
1458  rtems_interrupt_disable (level);
1459
1460  if (count > capture_count)
1461    count = capture_count;
1462
1463  capture_count -= count;
1464
1465  capture_out = (capture_count + count) % capture_size;
1466
1467  rtems_interrupt_enable (level);
1468
1469  return RTEMS_SUCCESSFUL;
1470}
1471
1472/*
1473 * rtems_capture_tick_time
1474 *
1475 *  DESCRIPTION:
1476 *
1477 * This function returns the tick period in nano-seconds.
1478 */
1479rtems_unsigned32
1480rtems_capture_tick_time ()
1481{
1482  return capture_tick_period;
1483}
1484
1485/*
1486 * rtems_capture_event_text
1487 *
1488 *  DESCRIPTION:
1489 *
1490 * This function returns a string for an event based on the bit in the
1491 * event. The functions takes the bit offset as a number not the bit
1492 * set in a bit map.
1493 */
1494const char*
1495rtems_capture_event_text (int event)
1496{
1497  if ((event < RTEMS_CAPTURE_EVENT_START) || (event > RTEMS_CAPTURE_EVENT_END))
1498    return "invalid event id";
1499  return capture_event_text[event - RTEMS_CAPTURE_EVENT_START];
1500}
1501
1502/*
1503 * rtems_capture_get_task_list
1504 *
1505 *  DESCRIPTION:
1506 *
1507 * This function returns the head of the list of tasks that the
1508 * capture engine has detected.
1509 */
1510rtems_capture_task_t*
1511rtems_capture_get_task_list ()
1512{
1513  return capture_tasks;
1514}
1515
1516/*
1517 * rtems_capture_task_stack_usage
1518 *
1519 *  DESCRIPTION:
1520 *
1521 * This function updates the stack usage. The task control block
1522 * is updated.
1523 */
1524rtems_unsigned32
1525rtems_capture_task_stack_usage (rtems_capture_task_t* task)
1526{
1527  if (task->tcb)
1528  {
1529    rtems_unsigned32* st;
1530    rtems_unsigned32* s;
1531
1532    /*
1533     * @todo: Assumes all stacks move the same way.
1534     */
1535    st = task->tcb->Start.Initial_stack.area + task->stack_size;
1536    s  = task->tcb->Start.Initial_stack.area;
1537
1538    while (s < st)
1539    {
1540      if (*s != 0xdeaddead)
1541        break;
1542      s++;
1543    }
1544
1545    task->stack_clean =
1546      s - (rtems_unsigned32*) task->tcb->Start.Initial_stack.area;
1547  }
1548
1549  return task->stack_clean;
1550}
1551
1552/*
1553 * rtems_capture_get_control_list
1554 *
1555 *  DESCRIPTION:
1556 *
1557 * This function returns the head of the list of control in the
1558 * capture engine.
1559 */
1560rtems_capture_control_t*
1561rtems_capture_get_control_list ()
1562{
1563  return capture_controls;
1564}
1565
Note: See TracBrowser for help on using the repository browser.