forked from Imagelibrary/rtems
1926 lines
44 KiB
C
1926 lines
44 KiB
C
/*
|
|
------------------------------------------------------------------------
|
|
|
|
Copyright Objective Design Systems Pty Ltd, 2002
|
|
All rights reserved Objective Design Systems Pty Ltd, 2002
|
|
Chris Johns (ccj@acm.org)
|
|
|
|
COPYRIGHT (c) 1989-2009.
|
|
On-Line Applications Research Corporation (OAR).
|
|
|
|
The license and distribution terms for this file may be
|
|
found in the file LICENSE in this distribution.
|
|
|
|
This software with is provided ``as is'' and with NO WARRANTY.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
RTEMS Performance Monitoring and Measurement Framework.
|
|
|
|
This is the Capture Engine component.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <rtems/rtems/tasksimpl.h>
|
|
|
|
#include "capture.h"
|
|
|
|
#include <rtems/score/statesimpl.h>
|
|
#include <rtems/score/todimpl.h>
|
|
|
|
/*
|
|
* These events are always recorded and are not part of the
|
|
* watch filters.
|
|
*
|
|
* This feature has been disabled as it becomes confusing when
|
|
* setting up filters and some event leak.
|
|
*/
|
|
#if defined (RTEMS_CAPTURE_ENGINE_ALLOW_RELATED_EVENTS)
|
|
#define RTEMS_CAPTURE_RECORD_EVENTS (RTEMS_CAPTURE_CREATED_BY_EVENT | \
|
|
RTEMS_CAPTURE_CREATED_EVENT | \
|
|
RTEMS_CAPTURE_STARTED_BY_EVENT | \
|
|
RTEMS_CAPTURE_STARTED_EVENT | \
|
|
RTEMS_CAPTURE_RESTARTED_BY_EVENT | \
|
|
RTEMS_CAPTURE_RESTARTED_EVENT | \
|
|
RTEMS_CAPTURE_DELETED_BY_EVENT | \
|
|
RTEMS_CAPTURE_DELETED_EVENT | \
|
|
RTEMS_CAPTURE_BEGIN_EVENT | \
|
|
RTEMS_CAPTURE_EXITTED_EVENT)
|
|
#else
|
|
#define RTEMS_CAPTURE_RECORD_EVENTS (0)
|
|
#endif
|
|
|
|
/*
|
|
* Global capture flags.
|
|
*/
|
|
#define RTEMS_CAPTURE_ON (1U << 0)
|
|
#define RTEMS_CAPTURE_NO_MEMORY (1U << 1)
|
|
#define RTEMS_CAPTURE_OVERFLOW (1U << 2)
|
|
#define RTEMS_CAPTURE_TRIGGERED (1U << 3)
|
|
#define RTEMS_CAPTURE_READER_ACTIVE (1U << 4)
|
|
#define RTEMS_CAPTURE_READER_WAITING (1U << 5)
|
|
#define RTEMS_CAPTURE_GLOBAL_WATCH (1U << 6)
|
|
#define RTEMS_CAPTURE_ONLY_MONITOR (1U << 7)
|
|
|
|
/*
|
|
* RTEMS Capture Data.
|
|
*/
|
|
static rtems_capture_record_t* capture_records;
|
|
static uint32_t capture_size;
|
|
static uint32_t capture_count;
|
|
static rtems_capture_record_t* capture_in;
|
|
static uint32_t capture_out;
|
|
static uint32_t capture_flags;
|
|
static rtems_capture_task_t* capture_tasks;
|
|
static rtems_capture_control_t* capture_controls;
|
|
static int capture_extension_index;
|
|
static rtems_id capture_id;
|
|
static rtems_capture_timestamp capture_timestamp;
|
|
static rtems_task_priority capture_ceiling;
|
|
static rtems_task_priority capture_floor;
|
|
static rtems_id capture_reader;
|
|
|
|
/*
|
|
* RTEMS Event text.
|
|
*/
|
|
static const char* capture_event_text[] =
|
|
{
|
|
"CREATED_BY",
|
|
"CREATED",
|
|
"STARTED_BY",
|
|
"STARTED",
|
|
"RESTARTED_BY",
|
|
"RESTARTED",
|
|
"DELETED_BY",
|
|
"DELETED",
|
|
"BEGIN",
|
|
"EXITTED",
|
|
"SWITCHED_OUT",
|
|
"SWITCHED_IN",
|
|
"TIMESTAMP"
|
|
};
|
|
|
|
/*
|
|
* rtems_capture_get_time
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function returns the current time. If a handler is provided
|
|
* by the user get the time from that.
|
|
*/
|
|
static inline void
|
|
rtems_capture_get_time (rtems_capture_time_t* time)
|
|
{
|
|
if (capture_timestamp)
|
|
capture_timestamp (time);
|
|
else
|
|
{
|
|
*time = rtems_clock_get_uptime_nanoseconds ();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_match_names
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function compares rtems_names. It protects the
|
|
* capture engine from a change to the way names are supported
|
|
* in RTEMS.
|
|
*
|
|
*/
|
|
static inline bool
|
|
rtems_capture_match_names (rtems_name lhs, rtems_name rhs)
|
|
{
|
|
return lhs == rhs;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_match_id
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function compares rtems_ids. It protects the
|
|
* capture engine from a change to the way id are supported
|
|
* in RTEMS.
|
|
*
|
|
*/
|
|
static inline bool
|
|
rtems_capture_match_ids (rtems_id lhs, rtems_id rhs)
|
|
{
|
|
return lhs == rhs;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_match_name_id
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function matches a name and/or id.
|
|
*/
|
|
static inline bool
|
|
rtems_capture_match_name_id (rtems_name lhs_name,
|
|
rtems_id lhs_id,
|
|
rtems_name rhs_name,
|
|
rtems_id rhs_id)
|
|
{
|
|
/*
|
|
* The left hand side name or id could be 0 which means a wildcard.
|
|
*/
|
|
if ((lhs_name == 0) && (lhs_id == rhs_id))
|
|
return 1;
|
|
else if ((lhs_id == 0) || (lhs_id == rhs_id))
|
|
{
|
|
if (rtems_capture_match_names (lhs_name, rhs_name))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_dup_name
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function duplicates an rtems_names. It protects the
|
|
* capture engine from a change to the way names are supported
|
|
* in RTEMS.
|
|
*
|
|
*/
|
|
static inline void
|
|
rtems_capture_dup_name (rtems_name* dst, rtems_name src)
|
|
{
|
|
*dst = src;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_by_in_to
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function sees if a BY control is in the BY names. The use
|
|
* of the valid_mask in this way assumes the number of trigger
|
|
* tasks is the number of bits in uint32_t.
|
|
*
|
|
*/
|
|
static inline bool
|
|
rtems_capture_by_in_to (uint32_t events,
|
|
rtems_capture_task_t* by,
|
|
rtems_capture_control_t* to)
|
|
{
|
|
uint32_t valid_mask = RTEMS_CAPTURE_CONTROL_FROM_MASK (0);
|
|
uint32_t valid_remainder = 0xffffffff;
|
|
int i;
|
|
|
|
for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
|
|
{
|
|
/*
|
|
* If there are no more valid BY entries then
|
|
* we are finished.
|
|
*/
|
|
if ((valid_remainder & to->by_valid) == 0)
|
|
break;
|
|
|
|
/*
|
|
* Is the froby entry valid and does its name or id match.
|
|
*/
|
|
if ((valid_mask & to->by_valid) &&
|
|
(to->by[i].trigger & events))
|
|
{
|
|
/*
|
|
* We have the BY task on the right hand side so we
|
|
* match with id's first then labels if the id's are
|
|
* not set.
|
|
*/
|
|
if (rtems_capture_match_name_id (to->by[i].name, to->by[i].id,
|
|
by->name, by->id))
|
|
return 1;
|
|
}
|
|
|
|
valid_mask >>= 1;
|
|
valid_remainder >>= 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_refcount_up
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function raises the reference count.
|
|
*
|
|
*/
|
|
static inline void
|
|
rtems_capture_refcount_up (rtems_capture_task_t* task)
|
|
{
|
|
task->refcount++;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_refcount_down
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function lowers the reference count and if the count
|
|
* reaches 0 the task control block is returned to the heap.
|
|
*
|
|
*/
|
|
static inline void
|
|
rtems_capture_refcount_down (rtems_capture_task_t* task)
|
|
{
|
|
if (task->refcount)
|
|
task->refcount--;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_init_stack_usage
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function setups a stack so its usage can be monitored.
|
|
*/
|
|
static inline void
|
|
rtems_capture_init_stack_usage (rtems_capture_task_t* task)
|
|
{
|
|
if (task->tcb)
|
|
{
|
|
uint32_t* s;
|
|
uint32_t i;
|
|
|
|
task->stack_size = task->tcb->Start.Initial_stack.size;
|
|
task->stack_clean = task->stack_size;
|
|
|
|
s = task->tcb->Start.Initial_stack.area;
|
|
|
|
for (i = 0; i < (task->stack_size - 128); i += 4)
|
|
*(s++) = 0xdeaddead;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_find_control
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function searches for a trigger given a name.
|
|
*
|
|
*/
|
|
static inline rtems_capture_control_t*
|
|
rtems_capture_find_control (rtems_name name, rtems_id id)
|
|
{
|
|
rtems_capture_control_t* control;
|
|
|
|
for (control = capture_controls; control != NULL; control = control->next)
|
|
if (rtems_capture_match_name_id (name, id, control->name, control->id))
|
|
break;
|
|
return control;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_create_control
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function creates a capture control for the capture engine.
|
|
*
|
|
*/
|
|
static inline rtems_capture_control_t*
|
|
rtems_capture_create_control (rtems_name name, rtems_id id)
|
|
{
|
|
rtems_interrupt_level level;
|
|
rtems_capture_control_t* control;
|
|
rtems_capture_task_t* task;
|
|
|
|
if ((name == 0) && (id == 0))
|
|
return NULL;
|
|
|
|
control = rtems_capture_find_control (name, id);
|
|
|
|
if (control == NULL)
|
|
{
|
|
bool ok = rtems_workspace_allocate (sizeof (*control), (void **) &control);
|
|
|
|
if (!ok)
|
|
{
|
|
capture_flags |= RTEMS_CAPTURE_NO_MEMORY;
|
|
return NULL;
|
|
}
|
|
|
|
control->name = name;
|
|
control->id = id;
|
|
control->flags = 0;
|
|
control->to_triggers = 0;
|
|
control->from_triggers = 0;
|
|
control->by_valid = 0;
|
|
|
|
memset (control->by, 0, sizeof (control->by));
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
control->next = capture_controls;
|
|
capture_controls = control;
|
|
|
|
/*
|
|
* We need to scan the task list as set the control to the
|
|
* tasks.
|
|
*/
|
|
for (task = capture_tasks; task != NULL; task = task->forw)
|
|
if (rtems_capture_match_name_id (name, id, task->name, task->id))
|
|
task->control = control;
|
|
|
|
rtems_interrupt_enable (level);
|
|
}
|
|
|
|
return control;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_create_capture_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function create the task control.
|
|
*
|
|
*/
|
|
static inline rtems_capture_task_t*
|
|
rtems_capture_create_capture_task (rtems_tcb* new_task)
|
|
{
|
|
rtems_interrupt_level level;
|
|
rtems_capture_task_t* task;
|
|
rtems_capture_control_t* control;
|
|
rtems_name name;
|
|
rtems_capture_time_t time;
|
|
bool ok;
|
|
|
|
ok = rtems_workspace_allocate (sizeof (*task), (void **) &task);
|
|
|
|
if (!ok)
|
|
{
|
|
capture_flags |= RTEMS_CAPTURE_NO_MEMORY;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Get the current time.
|
|
*/
|
|
rtems_capture_get_time (&time);
|
|
|
|
/*
|
|
* Check the type of name the object has.
|
|
*/
|
|
|
|
rtems_object_get_classic_name( new_task->Object.id, &name );
|
|
|
|
rtems_capture_dup_name (&task->name, name);
|
|
|
|
task->id = new_task->Object.id;
|
|
task->flags = 0;
|
|
task->in = 0;
|
|
task->refcount = 0;
|
|
task->out = 0;
|
|
task->tcb = new_task;
|
|
task->time = 0;
|
|
task->time_in = time;
|
|
task->control = 0;
|
|
task->last_time = 0;
|
|
|
|
task->tcb->extensions[capture_extension_index] = task;
|
|
|
|
task->start_priority = _RTEMS_tasks_Priority_from_Core(
|
|
new_task->Start.initial_priority
|
|
);
|
|
task->stack_size = new_task->Start.Initial_stack.size;
|
|
task->stack_clean = task->stack_size;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
task->forw = capture_tasks;
|
|
if (task->forw)
|
|
task->forw->back = task;
|
|
task->back = NULL;
|
|
capture_tasks = task;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
/*
|
|
* We need to scan the default control list to initialise
|
|
* this control.
|
|
*/
|
|
|
|
for (control = capture_controls; control != NULL; control = control->next)
|
|
if (rtems_capture_match_name_id (control->name, control->id,
|
|
task->name, task->id))
|
|
task->control = control;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_destroy_capture_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function destroy the task structure if the reference count
|
|
* is 0 and the tcb has been cleared signalling the task has been
|
|
* deleted.
|
|
*
|
|
*/
|
|
static inline void
|
|
rtems_capture_destroy_capture_task (rtems_capture_task_t* task)
|
|
{
|
|
if (task)
|
|
{
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
if (task->tcb || task->refcount)
|
|
task = 0;
|
|
|
|
if (task)
|
|
{
|
|
if (task->forw)
|
|
task->forw->back = task->back;
|
|
if (task->back)
|
|
task->back->forw = task->forw;
|
|
else
|
|
capture_tasks = task->forw;
|
|
}
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
rtems_workspace_free (task);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_record
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function records a capture record into the capture buffer.
|
|
*
|
|
*/
|
|
static inline void
|
|
rtems_capture_record (rtems_capture_task_t* task,
|
|
uint32_t events)
|
|
{
|
|
/*
|
|
* Check the watch state if we have a task control, and
|
|
* the task's real priority is lower or equal to the ceiling.
|
|
*/
|
|
if (task &&
|
|
((capture_flags &
|
|
(RTEMS_CAPTURE_TRIGGERED | RTEMS_CAPTURE_ONLY_MONITOR)) ==
|
|
RTEMS_CAPTURE_TRIGGERED))
|
|
{
|
|
rtems_capture_control_t* control;
|
|
|
|
control = task->control;
|
|
|
|
/*
|
|
* Capure the record if we have an event that is always
|
|
* captured, or the task's real priority is greater than the
|
|
* watch ceiling, and the global watch or task watch is enabled.
|
|
*/
|
|
|
|
if ((events & RTEMS_CAPTURE_RECORD_EVENTS) ||
|
|
((task->tcb->real_priority >= capture_ceiling) &&
|
|
(task->tcb->real_priority <= capture_floor) &&
|
|
((capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH) ||
|
|
(control && (control->flags & RTEMS_CAPTURE_WATCH)))))
|
|
{
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
if (capture_count < capture_size)
|
|
{
|
|
capture_count++;
|
|
capture_in->task = task;
|
|
capture_in->events = (events |
|
|
(task->tcb->real_priority) |
|
|
(task->tcb->current_priority << 8));
|
|
|
|
if ((events & RTEMS_CAPTURE_RECORD_EVENTS) == 0)
|
|
task->flags |= RTEMS_CAPTURE_TRACED;
|
|
|
|
rtems_capture_get_time (&capture_in->time);
|
|
|
|
if (capture_in == &capture_records[capture_size - 1])
|
|
capture_in = capture_records;
|
|
else
|
|
capture_in++;
|
|
|
|
rtems_capture_refcount_up (task);
|
|
}
|
|
else
|
|
capture_flags |= RTEMS_CAPTURE_OVERFLOW;
|
|
rtems_interrupt_enable (level);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_trigger
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* See if we have triggered and if not see if this event is a
|
|
* cause of a trigger.
|
|
*/
|
|
static bool
|
|
rtems_capture_trigger (rtems_capture_task_t* ft,
|
|
rtems_capture_task_t* tt,
|
|
uint32_t events)
|
|
{
|
|
/*
|
|
* If we have not triggered then see if this is a trigger condition.
|
|
*/
|
|
if (!(capture_flags & RTEMS_CAPTURE_TRIGGERED))
|
|
{
|
|
rtems_capture_control_t* fc = NULL;
|
|
rtems_capture_control_t* tc = NULL;
|
|
uint32_t from_events = 0;
|
|
uint32_t to_events = 0;
|
|
uint32_t from_to_events = 0;
|
|
|
|
if (ft)
|
|
{
|
|
fc = ft->control;
|
|
if (fc)
|
|
from_events = fc->from_triggers & events;
|
|
}
|
|
|
|
if (tt)
|
|
{
|
|
tc = tt->control;
|
|
if (tc)
|
|
{
|
|
to_events = tc->to_triggers & events;
|
|
if (ft && tc->by_valid)
|
|
from_to_events = tc->by_triggers & events;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if we have any from or to events. These are the
|
|
* from any or to any type triggers. All from/to triggers are
|
|
* listed in the to's control with the from in the from list.
|
|
*
|
|
* The masking above means any flag set is a trigger.
|
|
*/
|
|
if (from_events || to_events)
|
|
{
|
|
capture_flags |= RTEMS_CAPTURE_TRIGGERED;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Check the from->to events.
|
|
*/
|
|
if (from_to_events)
|
|
{
|
|
if (rtems_capture_by_in_to (events, ft, tc))
|
|
{
|
|
capture_flags |= RTEMS_CAPTURE_TRIGGERED;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_create_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is called when a task is created.
|
|
*
|
|
*/
|
|
static bool
|
|
rtems_capture_create_task (rtems_tcb* current_task,
|
|
rtems_tcb* new_task)
|
|
{
|
|
rtems_capture_task_t* ct;
|
|
rtems_capture_task_t* nt;
|
|
|
|
ct = current_task->extensions[capture_extension_index];
|
|
|
|
/*
|
|
* The task pointers may not be known as the task may have
|
|
* been created before the capture engine was open. Add them.
|
|
*/
|
|
|
|
if (ct == NULL)
|
|
ct = rtems_capture_create_capture_task (current_task);
|
|
|
|
/*
|
|
* Create the new task's capture control block.
|
|
*/
|
|
nt = rtems_capture_create_capture_task (new_task);
|
|
|
|
if (rtems_capture_trigger (ct, nt, RTEMS_CAPTURE_CREATE))
|
|
{
|
|
rtems_capture_record (ct, RTEMS_CAPTURE_CREATED_BY_EVENT);
|
|
rtems_capture_record (nt, RTEMS_CAPTURE_CREATED_EVENT);
|
|
}
|
|
|
|
return 1 == 1;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_start_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is called when a task is started.
|
|
*
|
|
*/
|
|
static void
|
|
rtems_capture_start_task (rtems_tcb* current_task,
|
|
rtems_tcb* started_task)
|
|
{
|
|
/*
|
|
* Get the capture task control block so we can trace this
|
|
* event.
|
|
*/
|
|
rtems_capture_task_t* ct;
|
|
rtems_capture_task_t* st;
|
|
|
|
ct = current_task->extensions[capture_extension_index];
|
|
st = started_task->extensions[capture_extension_index];
|
|
|
|
/*
|
|
* The task pointers may not be known as the task may have
|
|
* been created before the capture engine was open. Add them.
|
|
*/
|
|
|
|
if (ct == NULL)
|
|
ct = rtems_capture_create_capture_task (current_task);
|
|
|
|
if (st == NULL)
|
|
st = rtems_capture_create_capture_task (started_task);
|
|
|
|
if (rtems_capture_trigger (ct, st, RTEMS_CAPTURE_START))
|
|
{
|
|
rtems_capture_record (ct, RTEMS_CAPTURE_STARTED_BY_EVENT);
|
|
rtems_capture_record (st, RTEMS_CAPTURE_STARTED_EVENT);
|
|
}
|
|
|
|
rtems_capture_init_stack_usage (st);
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_restart_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is called when a task is restarted.
|
|
*
|
|
*/
|
|
static void
|
|
rtems_capture_restart_task (rtems_tcb* current_task,
|
|
rtems_tcb* restarted_task)
|
|
{
|
|
/*
|
|
* Get the capture task control block so we can trace this
|
|
* event.
|
|
*/
|
|
rtems_capture_task_t* ct;
|
|
rtems_capture_task_t* rt;
|
|
|
|
ct = current_task->extensions[capture_extension_index];
|
|
rt = restarted_task->extensions[capture_extension_index];
|
|
|
|
/*
|
|
* The task pointers may not be known as the task may have
|
|
* been created before the capture engine was open. Add them.
|
|
*/
|
|
|
|
if (ct == NULL)
|
|
ct = rtems_capture_create_capture_task (current_task);
|
|
|
|
if (rt == NULL)
|
|
rt = rtems_capture_create_capture_task (restarted_task);
|
|
|
|
if (rtems_capture_trigger (ct, rt, RTEMS_CAPTURE_RESTART))
|
|
{
|
|
rtems_capture_record (ct, RTEMS_CAPTURE_RESTARTED_BY_EVENT);
|
|
rtems_capture_record (rt, RTEMS_CAPTURE_RESTARTED_EVENT);
|
|
}
|
|
|
|
rtems_capture_task_stack_usage (rt);
|
|
rtems_capture_init_stack_usage (rt);
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_delete_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is called when a task is deleted.
|
|
*
|
|
*/
|
|
static void
|
|
rtems_capture_delete_task (rtems_tcb* current_task,
|
|
rtems_tcb* deleted_task)
|
|
{
|
|
/*
|
|
* Get the capture task control block so we can trace this
|
|
* event.
|
|
*/
|
|
rtems_capture_task_t* ct;
|
|
rtems_capture_task_t* dt;
|
|
|
|
/*
|
|
* The task pointers may not be known as the task may have
|
|
* been created before the capture engine was open. Add them.
|
|
*/
|
|
|
|
ct = current_task->extensions[capture_extension_index];
|
|
dt = deleted_task->extensions[capture_extension_index];
|
|
|
|
if (ct == NULL)
|
|
ct = rtems_capture_create_capture_task (current_task);
|
|
|
|
if (dt == NULL)
|
|
dt = rtems_capture_create_capture_task (deleted_task);
|
|
|
|
if (rtems_capture_trigger (ct, dt, RTEMS_CAPTURE_DELETE))
|
|
{
|
|
rtems_capture_record (ct, RTEMS_CAPTURE_DELETED_BY_EVENT);
|
|
rtems_capture_record (dt, RTEMS_CAPTURE_DELETED_EVENT);
|
|
}
|
|
|
|
rtems_capture_task_stack_usage (dt);
|
|
|
|
/*
|
|
* This task's tcb will be invalid. This signals the
|
|
* task has been deleted.
|
|
*/
|
|
dt->tcb = 0;
|
|
|
|
rtems_capture_destroy_capture_task (dt);
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_begin_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is called when a task is begun.
|
|
*
|
|
*/
|
|
static void
|
|
rtems_capture_begin_task (rtems_tcb* begin_task)
|
|
{
|
|
/*
|
|
* Get the capture task control block so we can trace this
|
|
* event.
|
|
*/
|
|
rtems_capture_task_t* bt;
|
|
|
|
bt = begin_task->extensions[capture_extension_index];
|
|
|
|
/*
|
|
* The task pointers may not be known as the task may have
|
|
* been created before the capture engine was open. Add them.
|
|
*/
|
|
|
|
if (bt == NULL)
|
|
bt = rtems_capture_create_capture_task (begin_task);
|
|
|
|
if (rtems_capture_trigger (NULL, bt, RTEMS_CAPTURE_BEGIN))
|
|
rtems_capture_record (bt, RTEMS_CAPTURE_BEGIN_EVENT);
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_exitted_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is called when a task is exitted. That is
|
|
* returned rather than was deleted.
|
|
*
|
|
*/
|
|
static void
|
|
rtems_capture_exitted_task (rtems_tcb* exitted_task)
|
|
{
|
|
/*
|
|
* Get the capture task control block so we can trace this
|
|
* event.
|
|
*/
|
|
rtems_capture_task_t* et;
|
|
|
|
et = exitted_task->extensions[capture_extension_index];
|
|
|
|
/*
|
|
* The task pointers may not be known as the task may have
|
|
* been created before the capture engine was open. Add them.
|
|
*/
|
|
|
|
if (et == NULL)
|
|
et = rtems_capture_create_capture_task (exitted_task);
|
|
|
|
if (rtems_capture_trigger (NULL, et, RTEMS_CAPTURE_EXITTED))
|
|
rtems_capture_record (et, RTEMS_CAPTURE_EXITTED_EVENT);
|
|
|
|
rtems_capture_task_stack_usage (et);
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_switch_task
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is called when a context is switched.
|
|
*
|
|
*/
|
|
static void
|
|
rtems_capture_switch_task (rtems_tcb* current_task,
|
|
rtems_tcb* heir_task)
|
|
{
|
|
/*
|
|
* Only perform context switch trace processing if tracing is
|
|
* enabled.
|
|
*/
|
|
if (capture_flags & RTEMS_CAPTURE_ON)
|
|
{
|
|
rtems_capture_time_t time;
|
|
|
|
/*
|
|
* Get the cpature task control block so we can update the
|
|
* reference and perform any watch or trigger functions.
|
|
* The task pointers may not be known as the task may have
|
|
* been created before the capture engine was open. Add them.
|
|
*/
|
|
rtems_capture_task_t* ct;
|
|
rtems_capture_task_t* ht;
|
|
|
|
|
|
if (_States_Is_transient (current_task->current_state)
|
|
|| _States_Is_dormant (current_task->current_state))
|
|
{
|
|
rtems_id ct_id = current_task->Object.id;
|
|
|
|
for (ct = capture_tasks; ct; ct = ct->forw)
|
|
if (ct->id == ct_id)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ct = current_task->extensions[capture_extension_index];
|
|
|
|
if (ct == NULL)
|
|
ct = rtems_capture_create_capture_task (current_task);
|
|
}
|
|
|
|
ht = heir_task->extensions[capture_extension_index];
|
|
|
|
if (ht == NULL)
|
|
ht = rtems_capture_create_capture_task (heir_task);
|
|
|
|
/*
|
|
* Update the execution time. Assume the time will not overflow
|
|
* for now. This may need to change.
|
|
*/
|
|
rtems_capture_get_time (&time);
|
|
|
|
/*
|
|
* We could end up with null pointers for both the current task
|
|
* and the heir task.
|
|
*/
|
|
|
|
if (ht)
|
|
{
|
|
ht->in++;
|
|
ht->time_in = time;
|
|
}
|
|
|
|
if (ct)
|
|
{
|
|
ct->out++;
|
|
if (ct->time_in)
|
|
ct->time += time - ct->time_in;
|
|
}
|
|
|
|
if (rtems_capture_trigger (ct, ht, RTEMS_CAPTURE_SWITCH))
|
|
{
|
|
rtems_capture_record (ct, RTEMS_CAPTURE_SWITCHED_OUT_EVENT);
|
|
rtems_capture_record (ht, RTEMS_CAPTURE_SWITCHED_IN_EVENT);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_open
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function initialises the realtime capture engine allocating the trace
|
|
* buffer. It is assumed we have a working heap at stage of initialisation.
|
|
*
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_open (uint32_t size, rtems_capture_timestamp timestamp __attribute__((unused)))
|
|
{
|
|
rtems_extensions_table capture_extensions;
|
|
rtems_name name;
|
|
rtems_status_code sc;
|
|
|
|
/*
|
|
* See if the capture engine is already open.
|
|
*/
|
|
|
|
if (capture_records)
|
|
return RTEMS_RESOURCE_IN_USE;
|
|
|
|
capture_records = malloc (size * sizeof (rtems_capture_record_t));
|
|
|
|
if (capture_records == NULL)
|
|
return RTEMS_NO_MEMORY;
|
|
|
|
capture_size = size;
|
|
capture_count = 0;
|
|
capture_in = capture_records;
|
|
capture_out = 0;
|
|
capture_flags = 0;
|
|
capture_tasks = NULL;
|
|
capture_ceiling = 0;
|
|
capture_floor = 255;
|
|
|
|
/*
|
|
* Create the extension table. This is copied so we
|
|
* can create it as a local.
|
|
*/
|
|
capture_extensions.thread_create = rtems_capture_create_task;
|
|
capture_extensions.thread_start = rtems_capture_start_task;
|
|
capture_extensions.thread_restart = rtems_capture_restart_task;
|
|
capture_extensions.thread_delete = rtems_capture_delete_task;
|
|
capture_extensions.thread_switch = rtems_capture_switch_task;
|
|
capture_extensions.thread_begin = rtems_capture_begin_task;
|
|
capture_extensions.thread_exitted = rtems_capture_exitted_task;
|
|
capture_extensions.fatal = NULL;
|
|
|
|
/*
|
|
* Register the user extension handlers for the CAPture Engine.
|
|
*/
|
|
name = rtems_build_name ('C', 'A', 'P', 'E');
|
|
sc = rtems_extension_create (name, &capture_extensions, &capture_id);
|
|
|
|
if (sc != RTEMS_SUCCESSFUL)
|
|
{
|
|
capture_id = 0;
|
|
free (capture_records);
|
|
capture_records = NULL;
|
|
}
|
|
else
|
|
{
|
|
capture_extension_index = rtems_object_id_get_index (capture_id);
|
|
}
|
|
|
|
/*
|
|
* Iterate over the list of existing tasks.
|
|
*/
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_close
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function shutdowns the capture engine and release any claimed
|
|
* resources.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_close (void)
|
|
{
|
|
rtems_interrupt_level level;
|
|
rtems_capture_task_t* task;
|
|
rtems_capture_control_t* control;
|
|
rtems_status_code sc;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
if (!capture_records)
|
|
{
|
|
rtems_interrupt_enable (level);
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
capture_flags &= ~(RTEMS_CAPTURE_ON | RTEMS_CAPTURE_ONLY_MONITOR);
|
|
|
|
capture_records = NULL;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
/*
|
|
* Delete the extension first. This means we are now able to
|
|
* release the resources we have without them being used.
|
|
*/
|
|
|
|
sc = rtems_extension_delete (capture_id);
|
|
|
|
if (sc != RTEMS_SUCCESSFUL)
|
|
return sc;
|
|
|
|
task = capture_tasks;
|
|
|
|
while (task)
|
|
{
|
|
rtems_capture_task_t* delete = task;
|
|
task = task->forw;
|
|
rtems_workspace_free (delete);
|
|
}
|
|
|
|
capture_tasks = NULL;
|
|
|
|
control = capture_controls;
|
|
|
|
while (control)
|
|
{
|
|
rtems_capture_control_t* delete = control;
|
|
control = control->next;
|
|
rtems_workspace_free (delete);
|
|
}
|
|
|
|
capture_controls = NULL;
|
|
|
|
if (capture_records)
|
|
{
|
|
free (capture_records);
|
|
capture_records = NULL;
|
|
}
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_control
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function allows control of tracing at a global level.
|
|
*/
|
|
static void
|
|
rtems_capture_task_setup (Thread_Control *tcb)
|
|
{
|
|
rtems_capture_create_capture_task (tcb);
|
|
}
|
|
|
|
rtems_status_code
|
|
rtems_capture_control (bool enable)
|
|
{
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
if (!capture_records)
|
|
{
|
|
rtems_interrupt_enable (level);
|
|
return RTEMS_UNSATISFIED;
|
|
}
|
|
|
|
if (enable)
|
|
capture_flags |= RTEMS_CAPTURE_ON;
|
|
else
|
|
capture_flags &= ~RTEMS_CAPTURE_ON;
|
|
|
|
rtems_iterate_over_all_threads (rtems_capture_task_setup);
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_monitor
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function enable the monitor mode. When in the monitor mode
|
|
* the tasks are monitored but no data is saved. This can be used
|
|
* to profile the load on a system.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_monitor (bool enable)
|
|
{
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
if (!capture_records)
|
|
{
|
|
rtems_interrupt_enable (level);
|
|
return RTEMS_UNSATISFIED;
|
|
}
|
|
|
|
if (enable)
|
|
capture_flags |= RTEMS_CAPTURE_ONLY_MONITOR;
|
|
else
|
|
capture_flags &= ~RTEMS_CAPTURE_ONLY_MONITOR;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_flush
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function flushes the capture buffer. The prime parameter allows the
|
|
* capture engine to also be primed again.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_flush (bool prime)
|
|
{
|
|
rtems_interrupt_level level;
|
|
rtems_capture_task_t* task;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
for (task = capture_tasks; task != NULL; task = task->forw)
|
|
{
|
|
task->flags &= ~RTEMS_CAPTURE_TRACED;
|
|
task->refcount = 0;
|
|
}
|
|
|
|
if (prime)
|
|
capture_flags &= ~(RTEMS_CAPTURE_TRIGGERED | RTEMS_CAPTURE_OVERFLOW);
|
|
else
|
|
capture_flags &= ~RTEMS_CAPTURE_OVERFLOW;
|
|
|
|
capture_count = 0;
|
|
capture_in = capture_records;
|
|
capture_out = 0;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
task = capture_tasks;
|
|
|
|
while (task)
|
|
{
|
|
rtems_capture_task_t* check = task;
|
|
task = task->forw;
|
|
rtems_capture_destroy_capture_task (check);
|
|
}
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_add
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function defines a watch for a specific task given a name. A watch
|
|
* causes it to be traced either in or out of context. The watch can be
|
|
* optionally enabled or disabled with the set routine. It is disabled by
|
|
* default.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_watch_add (rtems_name name, rtems_id id)
|
|
{
|
|
rtems_capture_control_t* control;
|
|
|
|
if ((name == 0) && (id == 0))
|
|
return RTEMS_UNSATISFIED;
|
|
|
|
control = rtems_capture_find_control (name, id);
|
|
|
|
if (control && !id)
|
|
return RTEMS_TOO_MANY;
|
|
|
|
if (!control)
|
|
control = rtems_capture_create_control (name, id);
|
|
|
|
if (!control)
|
|
return RTEMS_NO_MEMORY;
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_del
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function removes a watch for a specific task given a name. The task
|
|
* description will still exist if referenced by a trace record in the trace
|
|
* buffer or a global watch is defined.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_watch_del (rtems_name name, rtems_id id)
|
|
{
|
|
rtems_interrupt_level level;
|
|
rtems_capture_control_t* control;
|
|
rtems_capture_control_t** prev_control;
|
|
rtems_capture_task_t* task;
|
|
bool found = false;
|
|
|
|
/*
|
|
* Should this test be for wildcards ?
|
|
*/
|
|
|
|
for (prev_control = &capture_controls, control = capture_controls;
|
|
control != NULL; )
|
|
{
|
|
if (rtems_capture_match_name_id (control->name, control->id, name, id))
|
|
{
|
|
rtems_interrupt_disable (level);
|
|
|
|
for (task = capture_tasks; task != NULL; task = task->forw)
|
|
if (task->control == control)
|
|
task->control = 0;
|
|
|
|
*prev_control = control->next;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
rtems_workspace_free (control);
|
|
|
|
control = *prev_control;
|
|
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
prev_control = &control->next;
|
|
control = control->next;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
return RTEMS_SUCCESSFUL;
|
|
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_set
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function allows control of a watch. The watch can be enabled or
|
|
* disabled.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_watch_ctrl (rtems_name name, rtems_id id, bool enable)
|
|
{
|
|
rtems_interrupt_level level;
|
|
rtems_capture_control_t* control;
|
|
bool found = false;
|
|
|
|
/*
|
|
* Find the control and then set the watch. It must exist before it can
|
|
* be controlled.
|
|
*/
|
|
for (control = capture_controls; control != NULL; control = control->next)
|
|
{
|
|
if (rtems_capture_match_name_id (control->name, control->id, name, id))
|
|
{
|
|
rtems_interrupt_disable (level);
|
|
|
|
if (enable)
|
|
control->flags |= RTEMS_CAPTURE_WATCH;
|
|
else
|
|
control->flags &= ~RTEMS_CAPTURE_WATCH;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
return RTEMS_SUCCESSFUL;
|
|
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_global
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function allows control of a global watch. The watch can be enabled or
|
|
* disabled. A global watch configures all tasks below the ceiling and above
|
|
* the floor to be traced.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_watch_global (bool enable)
|
|
{
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
/*
|
|
* We need to keep specific and global watches separate so
|
|
* a global enable/disable does not lose a specific watch.
|
|
*/
|
|
if (enable)
|
|
capture_flags |= RTEMS_CAPTURE_GLOBAL_WATCH;
|
|
else
|
|
capture_flags &= ~RTEMS_CAPTURE_GLOBAL_WATCH;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_global_on
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function returns the global watch state.
|
|
*/
|
|
bool
|
|
rtems_capture_watch_global_on (void)
|
|
{
|
|
return capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH ? 1 : 0;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_ceiling
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function sets a watch ceiling. Tasks at or greating that the
|
|
* ceiling priority are not watched. This is a simple way to monitor
|
|
* an application and exclude system tasks running at a higher
|
|
* priority level.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_watch_ceiling (rtems_task_priority ceiling)
|
|
{
|
|
capture_ceiling = ceiling;
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_get_ceiling
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function gets the watch ceiling.
|
|
*/
|
|
rtems_task_priority
|
|
rtems_capture_watch_get_ceiling (void)
|
|
{
|
|
return capture_ceiling;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_floor
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function sets a watch floor. Tasks at or less that the
|
|
* floor priority are not watched. This is a simple way to monitor
|
|
* an application and exclude system tasks running at a lower
|
|
* priority level.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_watch_floor (rtems_task_priority floor)
|
|
{
|
|
capture_floor = floor;
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_watch_get_floor
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function gets the watch floor.
|
|
*/
|
|
rtems_task_priority
|
|
rtems_capture_watch_get_floor (void)
|
|
{
|
|
return capture_floor;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_map_trigger
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Map the trigger to a bit mask.
|
|
*
|
|
*/
|
|
static uint32_t
|
|
rtems_capture_map_trigger (rtems_capture_trigger_t trigger)
|
|
{
|
|
/*
|
|
* Transform the mode and trigger to a bit map.
|
|
*/
|
|
switch (trigger)
|
|
{
|
|
case rtems_capture_switch:
|
|
return RTEMS_CAPTURE_SWITCH;
|
|
case rtems_capture_create:
|
|
return RTEMS_CAPTURE_CREATE;
|
|
case rtems_capture_start:
|
|
return RTEMS_CAPTURE_START;
|
|
case rtems_capture_restart:
|
|
return RTEMS_CAPTURE_RESTART;
|
|
case rtems_capture_delete:
|
|
return RTEMS_CAPTURE_DELETE;
|
|
case rtems_capture_begin:
|
|
return RTEMS_CAPTURE_BEGIN;
|
|
case rtems_capture_exitted:
|
|
return RTEMS_CAPTURE_EXITTED;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_set_trigger
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function sets a trigger.
|
|
*
|
|
* This set trigger routine will create a capture control for the
|
|
* target task. The task list is searched and any existing tasks
|
|
* are linked to the new control.
|
|
*
|
|
* We can have a number of tasks that have the same name so we
|
|
* search using names. This means a number of tasks can be
|
|
* linked to single control.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_set_trigger (rtems_name from_name,
|
|
rtems_id from_id,
|
|
rtems_name to_name,
|
|
rtems_id to_id,
|
|
rtems_capture_trigger_mode_t mode,
|
|
rtems_capture_trigger_t trigger)
|
|
{
|
|
rtems_capture_control_t* control;
|
|
uint32_t flags;
|
|
|
|
flags = rtems_capture_map_trigger (trigger);
|
|
|
|
/*
|
|
* The mode sets the opposite type of trigger. For example
|
|
* FROM ANY means trigger when the event happens TO this
|
|
* task. TO ANY means FROM this task.
|
|
*/
|
|
|
|
if (mode == rtems_capture_to_any)
|
|
{
|
|
control = rtems_capture_create_control (from_name, from_id);
|
|
if (control == NULL)
|
|
return RTEMS_NO_MEMORY;
|
|
control->from_triggers |= flags & RTEMS_CAPTURE_FROM_TRIGS;
|
|
}
|
|
else
|
|
{
|
|
control = rtems_capture_create_control (to_name, to_id);
|
|
if (control == NULL)
|
|
return RTEMS_NO_MEMORY;
|
|
if (mode == rtems_capture_from_any)
|
|
control->to_triggers |= flags;
|
|
else
|
|
{
|
|
bool done = false;
|
|
int i;
|
|
|
|
control->by_triggers |= flags;
|
|
|
|
for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
|
|
{
|
|
if (rtems_capture_control_by_valid (control, i) &&
|
|
((control->by[i].name == from_name) ||
|
|
(from_id && (control->by[i].id == from_id))))
|
|
{
|
|
control->by[i].trigger |= flags;
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!done)
|
|
{
|
|
for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
|
|
{
|
|
if (!rtems_capture_control_by_valid (control, i))
|
|
{
|
|
control->by_valid |= RTEMS_CAPTURE_CONTROL_FROM_MASK (i);
|
|
control->by[i].name = from_name;
|
|
control->by[i].id = from_id;
|
|
control->by[i].trigger = flags;
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!done)
|
|
return RTEMS_TOO_MANY;
|
|
}
|
|
}
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_clear_trigger
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function clear a trigger.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_clear_trigger (rtems_name from_name,
|
|
rtems_id from_id,
|
|
rtems_name to_name,
|
|
rtems_id to_id,
|
|
rtems_capture_trigger_mode_t mode,
|
|
rtems_capture_trigger_t trigger)
|
|
{
|
|
rtems_capture_control_t* control;
|
|
uint32_t flags;
|
|
|
|
flags = rtems_capture_map_trigger (trigger);
|
|
|
|
if (mode == rtems_capture_to_any)
|
|
{
|
|
control = rtems_capture_find_control (from_name, from_id);
|
|
if (control == NULL)
|
|
{
|
|
if (from_id)
|
|
return RTEMS_INVALID_ID;
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
control->from_triggers &= ~flags;
|
|
}
|
|
else
|
|
{
|
|
control = rtems_capture_find_control (to_name, to_id);
|
|
if (control == NULL)
|
|
{
|
|
if (to_id)
|
|
return RTEMS_INVALID_ID;
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
if (mode == rtems_capture_from_any)
|
|
control->to_triggers &= ~flags;
|
|
else
|
|
{
|
|
bool done = false;
|
|
int i;
|
|
|
|
control->by_triggers &= ~flags;
|
|
|
|
for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
|
|
{
|
|
if (rtems_capture_control_by_valid (control, i) &&
|
|
((control->by[i].name == from_name) ||
|
|
(control->by[i].id == from_id)))
|
|
{
|
|
control->by[i].trigger &= ~trigger;
|
|
if (control->by[i].trigger == 0)
|
|
control->by_valid &= ~RTEMS_CAPTURE_CONTROL_FROM_MASK (i);
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!done)
|
|
{
|
|
if (from_id)
|
|
return RTEMS_INVALID_ID;
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
}
|
|
}
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_read
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function reads a number of records from the capture buffer.
|
|
* The user can optionally block and wait until the buffer as a
|
|
* specific number of records available or a specific time has
|
|
* elasped.
|
|
*
|
|
* The function returns the number of record that is has that are
|
|
* in a continous block of memory. If the number of available records
|
|
* wrap then only those records are provided. This removes the need for
|
|
* caller to be concerned about buffer wrappings. If the number of
|
|
* requested records cannot be met due to the wrapping of the records
|
|
* less than the specified number will be returned.
|
|
*
|
|
* The user must release the records. This is achieved with a call to
|
|
* rtems_capture_release. Calls this function without a release will
|
|
* result in at least the same number of records being released.
|
|
*
|
|
* The 'threshold' parameter is the number of records that must be
|
|
* captured before returning. If a timeout period is specified (non-0)
|
|
* any captured records will be returned. These parameters stop
|
|
* thrashing occuring for a small number of records, yet allows
|
|
* a user configured latiency to be applied for single events.
|
|
*
|
|
* The 'timeout' parameter is in micro-seconds. A value of 0 will disable
|
|
* the timeout.
|
|
*
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_read (uint32_t threshold,
|
|
uint32_t timeout,
|
|
uint32_t* read,
|
|
rtems_capture_record_t** recs)
|
|
{
|
|
rtems_interrupt_level level;
|
|
rtems_status_code sc = RTEMS_SUCCESSFUL;
|
|
uint32_t count;
|
|
|
|
*read = 0;
|
|
*recs = NULL;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
/*
|
|
* Only one reader is allowed.
|
|
*/
|
|
|
|
if (capture_flags & RTEMS_CAPTURE_READER_ACTIVE)
|
|
{
|
|
rtems_interrupt_enable (level);
|
|
return RTEMS_RESOURCE_IN_USE;
|
|
}
|
|
|
|
capture_flags |= RTEMS_CAPTURE_READER_ACTIVE;
|
|
*read = count = capture_count;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
*recs = &capture_records[capture_out];
|
|
|
|
for (;;)
|
|
{
|
|
/*
|
|
* See if the count wraps the end of the record buffer.
|
|
*/
|
|
if (count && ((capture_out + count) >= capture_size))
|
|
*read = capture_size - capture_out;
|
|
|
|
/*
|
|
* Do we have a threshold and the current count has not wrapped
|
|
* around the end of the capture record buffer ?
|
|
*/
|
|
if ((*read == count) && threshold)
|
|
{
|
|
/*
|
|
* Do we have enough records ?
|
|
*/
|
|
if (*read < threshold)
|
|
{
|
|
rtems_event_set event_out;
|
|
|
|
rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, &capture_reader);
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
capture_flags |= RTEMS_CAPTURE_READER_WAITING;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
sc = rtems_event_receive (RTEMS_EVENT_0,
|
|
RTEMS_WAIT | RTEMS_EVENT_ANY,
|
|
RTEMS_MICROSECONDS_TO_TICKS (timeout),
|
|
&event_out);
|
|
|
|
/*
|
|
* Let the user handle all other sorts of errors. This may
|
|
* not be the best solution, but oh well, it will do for
|
|
* now.
|
|
*/
|
|
if ((sc != RTEMS_SUCCESSFUL) && (sc != RTEMS_TIMEOUT))
|
|
break;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
*read = count = capture_count;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Always out if we reach here. To loop use continue.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_release
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function releases the requested number of record slots back
|
|
* to the capture engine. The count must match the number read.
|
|
*/
|
|
rtems_status_code
|
|
rtems_capture_release (uint32_t count)
|
|
{
|
|
rtems_capture_record_t* rec;
|
|
uint32_t counted;
|
|
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
if (count > capture_count)
|
|
count = capture_count;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
counted = count;
|
|
|
|
rec = &capture_records[capture_out];
|
|
|
|
while (counted--)
|
|
{
|
|
rtems_capture_refcount_down (rec->task);
|
|
rtems_capture_destroy_capture_task (rec->task);
|
|
rec++;
|
|
}
|
|
|
|
rtems_interrupt_disable (level);
|
|
|
|
capture_count -= count;
|
|
|
|
capture_out = (capture_out + count) % capture_size;
|
|
|
|
capture_flags &= ~RTEMS_CAPTURE_READER_ACTIVE;
|
|
|
|
rtems_interrupt_enable (level);
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_time
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function returns the current time. If a handler is provided
|
|
* by the user get the time from that.
|
|
*/
|
|
void
|
|
rtems_capture_time (rtems_capture_time_t* uptime)
|
|
{
|
|
rtems_capture_get_time(uptime);
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_event_text
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function returns a string for an event based on the bit in the
|
|
* event. The functions takes the bit offset as a number not the bit
|
|
* set in a bit map.
|
|
*/
|
|
const char*
|
|
rtems_capture_event_text (int event)
|
|
{
|
|
if ((event < RTEMS_CAPTURE_EVENT_START) || (event > RTEMS_CAPTURE_EVENT_END))
|
|
return "invalid event id";
|
|
return capture_event_text[event - RTEMS_CAPTURE_EVENT_START];
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_get_task_list
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function returns the head of the list of tasks that the
|
|
* capture engine has detected.
|
|
*/
|
|
rtems_capture_task_t*
|
|
rtems_capture_get_task_list (void)
|
|
{
|
|
return capture_tasks;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_task_stack_usage
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function updates the stack usage. The task control block
|
|
* is updated.
|
|
*/
|
|
uint32_t
|
|
rtems_capture_task_stack_usage (rtems_capture_task_t* task)
|
|
{
|
|
if (task->tcb)
|
|
{
|
|
uint32_t* st;
|
|
uint32_t* s;
|
|
|
|
/*
|
|
* @todo: Assumes all stacks move the same way.
|
|
*/
|
|
st = task->tcb->Start.Initial_stack.area + task->stack_size;
|
|
s = task->tcb->Start.Initial_stack.area;
|
|
|
|
while (s < st)
|
|
{
|
|
if (*s != 0xdeaddead)
|
|
break;
|
|
s++;
|
|
}
|
|
|
|
task->stack_clean =
|
|
s - (uint32_t*) task->tcb->Start.Initial_stack.area;
|
|
}
|
|
|
|
return task->stack_clean;
|
|
}
|
|
|
|
/*
|
|
* rtems_capture_get_control_list
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function returns the head of the list of control in the
|
|
* capture engine.
|
|
*/
|
|
rtems_capture_control_t*
|
|
rtems_capture_get_control_list (void)
|
|
{
|
|
return capture_controls;
|
|
}
|