/*
------------------------------------------------------------------------
$Id$
------------------------------------------------------------------------
Copyright Objective Design Systems Pty Ltd, 2002
All rights reserved Objective Design Systems Pty Ltd, 2002
Chris Johns (ccj@acm.org)
COPYRIGHT (c) 1989-1998.
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 "capture.h"
#include <rtems/score/states.inl>
#include <rtems/score/wkspace.h>
#include <rtems/score/wkspace.inl>
/*
* These events are always recorded and are not part of the
* watch filters.
*/
#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)
/*
* Global capture flags.
*/
#define RTEMS_CAPTURE_ON (1 << 0)
#define RTEMS_CAPTURE_NO_MEMORY (1 << 1)
#define RTEMS_CAPTURE_OVERFLOW (1 << 2)
#define RTEMS_CAPTURE_TRIGGERED (1 << 3)
#define RTEMS_CAPTURE_READER_ACTIVE (1 << 4)
#define RTEMS_CAPTURE_READER_WAITING (1 << 5)
#define RTEMS_CAPTURE_GLOBAL_WATCH (1 << 6)
/*
* 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 uint32_t capture_tick_period;
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 (uint32_t * ticks,
uint32_t * tick_offset)
{
if (capture_timestamp)
capture_timestamp (ticks, tick_offset);
else
{
*ticks = _Watchdog_Ticks_since_boot;
*tick_offset = 0;
}
}
/*
* 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 rtems_boolean
rtems_capture_match_names (rtems_name lhs, rtems_name rhs)
{
return lhs == rhs;
}
/*
* rtems_capture_dup_name
*
* DESCRIPTION:
*
* This function duplicates an rtems_names. It protects the
* cpature 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_name_in_group
*
* DESCRIPTION:
*
* This function sees if a name is in a group of names.
*
*/
static inline rtems_boolean
rtems_capture_name_in_group (rtems_name task, rtems_name* tasks)
{
if (tasks)
{
int i;
for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
if (rtems_capture_match_names (task, *tasks++))
return 1;
}
return 0;
}
/*
* rtems_capture_match_name_id
*
* DESCRIPTION:
*
* This function matches a name and/or id.
*/
static inline rtems_boolean
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_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)
{
control = _Workspace_Allocate (sizeof (rtems_capture_control_t));
if (control == NULL)
{
capture_flags |= RTEMS_CAPTURE_NO_MEMORY;
return NULL;
}
control->name = name;
control->id = id;
control->flags = 0;
memset (control->from, 0, sizeof (control->from));
memset (control->from_id, 0, sizeof (control->from_id));
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->next)
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;
task = _Workspace_Allocate (sizeof (rtems_capture_task_t));
if (task == NULL)
{
capture_flags |= RTEMS_CAPTURE_NO_MEMORY;
return NULL;
}
rtems_capture_dup_name (&task->name, ((rtems_name) new_task->Object.name));
task->id = new_task->Object.id;
task->flags = 0;
task->in = 0;
task->out = 0;
task->tcb = new_task;
task->ticks = 0;
task->tick_offset = 0;
task->ticks_in = 0;
task->tick_offset_in = 0;
task->control = 0;
task->last_ticks = 0;
task->last_tick_offset = 0;
task->tcb->extensions[capture_extension_index] = task;
task->start_priority = 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->next = capture_tasks;
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_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)
{
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->ticks, &capture_in->tick_offset);
if (capture_in == &capture_records[capture_size - 1])
capture_in = capture_records;
else
capture_in++;
}
else
capture_flags |= RTEMS_CAPTURE_OVERFLOW;
rtems_interrupt_enable (level);
}
}
}
/*
* rtems_capture_create_task
*
* DESCRIPTION:
*
* This function is called when a task is created.
*
*/
static rtems_boolean
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 ponters 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 we are logging then record this fact.
*/
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 rtems_extension
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 ponters 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);
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 rtems_extension
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 ponters 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);
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 rtems_extension
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 ponters 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);
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.
*/
dt->tcb = 0;
}
/*
* rtems_capture_begin_task
*
* DESCRIPTION:
*
* This function is called when a task is begun.
*
*/
static rtems_extension
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 ponters 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);
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 rtems_extension
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 ponters 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);
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 rtems_extension
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)
{
uint32_t ticks;
uint32_t tick_offset;
/*
* Get the cpature task control block so we can update the
* reference anbd perform any watch or trigger functions.
* The task ponters 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))
{
rtems_id ct_id = current_task->Object.id;
for (ct = capture_tasks; ct; ct = ct->next)
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 tick will not overflow
* for now. This may need to change.
*/
rtems_capture_get_time (&ticks, &tick_offset);
/*
* We could end up with null pointers for both the current task
* and the heir task.
*/
if (ht)
{
ht->in++;
ht->ticks_in = ticks;
ht->tick_offset_in = tick_offset;
}
if (ct)
{
ct->out++;
ct->ticks += ticks - ct->ticks_in;
if (capture_timestamp)
{
tick_offset += capture_tick_period - ct->tick_offset_in;
if (tick_offset < capture_tick_period)
ct->tick_offset = tick_offset;
else
{
ct->ticks++;
ct->tick_offset = tick_offset - capture_tick_period;
}
}
else
{
ct->tick_offset += 100;
}
}
/*
* If we have not triggered then see if this is a trigger condition.
*/
if (!(capture_flags & RTEMS_CAPTURE_TRIGGERED))
{
rtems_capture_control_t* cc = NULL;
rtems_capture_control_t* hc = NULL;
if (ct)
{
cc = ct->control;
/*
* Check the current task for a TO_ANY trigger.
*/
if (cc && (cc->flags & RTEMS_CAPTURE_TO_ANY))
{
capture_flags |= RTEMS_CAPTURE_TRIGGERED;
goto triggered;
}
}
if (ht)
{
hc = ht->control;
/*
* Check the next task for a FROM_ANY.
*/
if (hc && (hc->flags & RTEMS_CAPTURE_FROM_ANY))
{
capture_flags |= RTEMS_CAPTURE_TRIGGERED;
goto triggered;
}
}
/*
* Check is the trigger is from the current task
* to the next task.
*/
if (cc && hc && (hc->flags & RTEMS_CAPTURE_FROM_TO))
if (rtems_capture_name_in_group (cc->name, hc->from))
{
capture_flags |= RTEMS_CAPTURE_TRIGGERED;
goto triggered;
}
}
else
{
triggered:
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)
{
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;
/*
* Get the tick period from the BSP Configuration Table.
*/
capture_tick_period = _Configuration_Table->microseconds_per_tick;
/*
* 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_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 ()
{
rtems_interrupt_level level;
rtems_capture_task_t* task;
rtems_capture_control_t* control;
rtems_capture_record_t* records;
rtems_status_code sc;
rtems_interrupt_disable (level);
if (!capture_records)
{
rtems_interrupt_enable (level);
return RTEMS_SUCCESSFUL;
}
capture_flags &= ~RTEMS_CAPTURE_ON;
records = capture_records;
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->next;
_Workspace_Free (delete);
}
capture_tasks = NULL;
control = capture_controls;
while (control)
{
rtems_capture_control_t* delete = control;
control = control->next;
_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.
*/
rtems_status_code
rtems_capture_control (rtems_boolean 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_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 (rtems_boolean prime)
{
rtems_interrupt_level level;
rtems_capture_task_t* task;
rtems_interrupt_disable (level);
for (task = capture_tasks; task != NULL; task = task->next)
task->flags &= ~RTEMS_CAPTURE_TRACED;
if (prime)
capture_flags &= ~(RTEMS_CAPTURE_TRIGGERED | RTEMS_CAPTURE_OVERFLOW);
else
capture_flags &= ~RTEMS_CAPTURE_OVERFLOW;
capture_in = capture_records;
capture_out = 0;
rtems_interrupt_enable (level);
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;
rtems_boolean found = 0;
/*
* Should this test be for wildcards ?
*/
for (prev_control = &capture_controls, control = capture_controls;
control != NULL; )
{
if (rtems_capture_match_name_id (name, id, control->name, control->id))
{
rtems_interrupt_disable (level);
for (task = capture_tasks; task != NULL; task = task->next)
if (task->control == control)
task->control = 0;
*prev_control = control->next;
rtems_interrupt_enable (level);
_Workspace_Free (control);
control = *prev_control;
found = 1;
}
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, rtems_boolean enable)
{
rtems_interrupt_level level;
rtems_capture_control_t* control;
rtems_boolean found = 0;
/*
* 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 (name, id, control->name, control->id))
{
rtems_interrupt_disable (level);
if (enable)
control->flags |= RTEMS_CAPTURE_WATCH;
else
control->flags &= ~RTEMS_CAPTURE_WATCH;
rtems_interrupt_enable (level);
found = 1;
}
}
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 (rtems_boolean 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.
*/
rtems_boolean
rtems_capture_watch_global_on ()
{
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 ()
{
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 ()
{
return capture_floor;
}
/*
* rtems_capture_set_trigger
*
* DESCRIPTION:
*
* This function sets an edge trigger. Left is the left side of
* the edge and right is right side of the edge. The trigger type
* can be -
*
* FROM_ANY : a switch from any task to the right side of the edge.
* TO_ANY : a switch from the left side of the edge to any task.
* FROM_TO : a switch from the left side of the edge to the right
* side of the edge.
*
* 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,
rtems_id from_id,
rtems_name to,
rtems_id to_id,
rtems_capture_trigger_t trigger)
{
rtems_capture_control_t* control;
int i;
/*
* Find the capture control blocks for the from and to
* tasks.
*/
if (trigger == rtems_capture_to_any)
{
control = rtems_capture_create_control (from, from_id);
if (control == NULL)
return RTEMS_NO_MEMORY;
control->flags |= RTEMS_CAPTURE_TO_ANY;
}
if ((trigger == rtems_capture_from_to) ||
(trigger == rtems_capture_from_any))
{
control = rtems_capture_create_control (to, to_id);
if (control == NULL)
return RTEMS_NO_MEMORY;
if (trigger == rtems_capture_from_any)
control->flags |= RTEMS_CAPTURE_FROM_ANY;
else
{
control->flags |= RTEMS_CAPTURE_FROM_TO;
for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
{
if (control->from[i] == 0)
{
control->from[i] = from;
control->from_id[i] = from_id;
break;
}
}
}
}
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,
TOD_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;
}
rtems_interrupt_disable (level);
capture_flags &= ~RTEMS_CAPTURE_READER_ACTIVE;
rtems_interrupt_enable (level);
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_interrupt_level level;
rtems_interrupt_disable (level);
if (count > capture_count)
count = capture_count;
capture_count -= count;
capture_out = (capture_count + count) % capture_size;
rtems_interrupt_enable (level);
return RTEMS_SUCCESSFUL;
}
/*
* rtems_capture_tick_time
*
* DESCRIPTION:
*
* This function returns the tick period in nano-seconds.
*/
uint32_t
rtems_capture_tick_time ()
{
return capture_tick_period;
}
/*
* 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 ()
{
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 ()
{
return capture_controls;
}