/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @brief RTEMS Performance Monitoring and Measurement Framework.
*
* This is the Capture Engine component.
*/
/*
* Copyright 2002, 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
*
* COPYRIGHT (c) 1989-2014.
* On-Line Applications Research Corporation (OAR).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <rtems/captureimpl.h>
#include <rtems/score/statesimpl.h>
#include <rtems/score/todimpl.h>
#include <rtems/rtems/tasksimpl.h>
/*
* RTEMS Capture User Extension Data.
*/
static rtems_id capture_id;
static bool
rtems_capture_create_task (rtems_tcb* current_task,
rtems_tcb* new_task);
static void
rtems_capture_start_task (rtems_tcb* current_task,
rtems_tcb* started_task);
static void
rtems_capture_restart_task (rtems_tcb* current_task,
rtems_tcb* restarted_task);
static void
rtems_capture_delete_task (rtems_tcb* current_task,
rtems_tcb* deleted_task);
static void
rtems_capture_switch_task (rtems_tcb* current_task,
rtems_tcb* heir_task);
static void
rtems_capture_begin_task (rtems_tcb* begin_task);
static void
rtems_capture_exitted_task (rtems_tcb* exitted_task);
static void
rtems_capture_terminated_task (rtems_tcb* terminated_task);
static const rtems_extensions_table capture_extensions = {
.thread_create = rtems_capture_create_task,
.thread_start = rtems_capture_start_task,
.thread_restart = rtems_capture_restart_task,
.thread_delete = rtems_capture_delete_task,
.thread_switch = rtems_capture_switch_task,
.thread_begin = rtems_capture_begin_task,
.thread_exitted = rtems_capture_exitted_task,
.fatal = NULL,
.thread_terminate = rtems_capture_terminated_task
};
static inline void capture_record (rtems_tcb* tcb, uint32_t events)
{
rtems_capture_record_lock_context rec_context;
if (rtems_capture_filter (tcb, events))
return;
if (!rtems_capture_task_recorded (tcb))
rtems_capture_record_task (tcb);
rtems_capture_record_open (tcb, events, 0, &rec_context);
rtems_capture_record_close (&rec_context);
}
rtems_status_code rtems_capture_user_extension_open(void)
{
rtems_status_code sc;
rtems_name name;
int index;
/*
* 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;
else {
index = rtems_object_id_get_index (capture_id);
rtems_capture_set_extension_index (index);
}
return sc;
}
rtems_status_code rtems_capture_user_extension_close(void)
{
rtems_status_code sc;
sc = rtems_extension_delete (capture_id);
return sc;
}
/*
* This function is called when a task is created.
*/
static bool
rtems_capture_create_task (rtems_tcb* ct,
rtems_tcb* nt)
{
/*
* The task pointers may not be known as the task may have
* been created before the capture engine was open. Add them.
*/
if (!rtems_capture_task_initialized (ct))
rtems_capture_initialize_task (ct);
/*
* Create the new task's capture control block.
*/
rtems_capture_initialize_task (nt);
if (rtems_capture_trigger_fired (ct, nt, RTEMS_CAPTURE_CREATE))
{
capture_record (ct, RTEMS_CAPTURE_CREATED_BY_EVENT);
capture_record (nt, RTEMS_CAPTURE_CREATED_EVENT);
}
return 1 == 1;
}
/*
* This function is called when a task is started.
*/
static void
rtems_capture_start_task (rtems_tcb* ct,
rtems_tcb* st)
{
/*
* The task pointers may not be known as the task may have
* been created before the capture engine was open. Add them.
*/
if (!rtems_capture_task_initialized (ct))
rtems_capture_initialize_task (ct);
if (st != NULL)
rtems_capture_initialize_task (st);
if (rtems_capture_trigger_fired (ct, st, RTEMS_CAPTURE_START))
{
capture_record (ct, RTEMS_CAPTURE_STARTED_BY_EVENT);
capture_record (st, RTEMS_CAPTURE_STARTED_EVENT);
}
}
/*
* This function is called when a task is restarted.
*/
static void
rtems_capture_restart_task (rtems_tcb* ct,
rtems_tcb* rt)
{
/*
* The task pointers may not be known as the task may have
* been created before the capture engine was open. Add them.
*/
if (!rtems_capture_task_initialized (ct))
rtems_capture_initialize_task (ct);
if (!rtems_capture_task_initialized (rt))
rtems_capture_initialize_task (rt);
if (rtems_capture_trigger_fired (ct, rt, RTEMS_CAPTURE_RESTART))
{
capture_record (ct, RTEMS_CAPTURE_RESTARTED_BY_EVENT);
capture_record (rt, RTEMS_CAPTURE_RESTARTED_EVENT);
}
}
/*
* This function is called when a task is deleted.
*/
static void
rtems_capture_delete_task (rtems_tcb* ct,
rtems_tcb* dt)
{
if (!rtems_capture_task_initialized (ct))
rtems_capture_initialize_task (ct);
if (!rtems_capture_task_initialized (dt))
rtems_capture_initialize_task (dt);
if (rtems_capture_trigger_fired (ct, dt, RTEMS_CAPTURE_DELETE))
{
capture_record (ct, RTEMS_CAPTURE_DELETED_BY_EVENT);
capture_record (dt, RTEMS_CAPTURE_DELETED_EVENT);
}
}
/*
* This function is called when a task is begun.
*/
static void
rtems_capture_begin_task (rtems_tcb* bt)
{
/*
* The task pointers may not be known as the task may have
* been created before the capture engine was open. Add them.
*/
if (!rtems_capture_task_initialized (bt))
rtems_capture_initialize_task (bt);
if (rtems_capture_trigger_fired (NULL, bt, RTEMS_CAPTURE_BEGIN))
capture_record (bt, RTEMS_CAPTURE_BEGIN_EVENT);
}
/*
* This function is called when a task is exitted. That is
* returned rather than was deleted.
*/
static void
rtems_capture_exitted_task (rtems_tcb* et)
{
/*
* The task pointers may not be known as the task may have
* been created before the capture engine was open. Add them.
*/
if (!rtems_capture_task_initialized (et))
rtems_capture_initialize_task (et);
if (rtems_capture_trigger_fired (NULL, et, RTEMS_CAPTURE_EXITTED))
capture_record (et, RTEMS_CAPTURE_EXITTED_EVENT);
}
/*
* This function is called when a termination request is identified.
*/
static void
rtems_capture_terminated_task (rtems_tcb* tt)
{
/*
* The task pointers may not be known as the task may have
* been created before the capture engine was open. Add them.
*/
if (!rtems_capture_task_initialized (tt))
rtems_capture_initialize_task (tt);
if (rtems_capture_trigger_fired (NULL, tt, RTEMS_CAPTURE_TERMINATED))
capture_record (tt, RTEMS_CAPTURE_TERMINATED_EVENT);
}
/*
* This function is called when a context is switched.
*/
static void
rtems_capture_switch_task (rtems_tcb* ct,
rtems_tcb* ht)
{
uint32_t flags = rtems_capture_get_flags();
/*
* Only perform context switch trace processing if tracing is
* enabled.
*/
if (flags & RTEMS_CAPTURE_ON)
{
rtems_capture_time time;
if (!rtems_capture_task_initialized (ct))
rtems_capture_initialize_task (ct);
if (!rtems_capture_task_initialized (ht))
rtems_capture_initialize_task (ht);
/*
* Update the execution time. Assume the time will not overflow
* for now. This may need to change.
*/
rtems_capture_get_time (&time);
if (rtems_capture_trigger_fired (ct, ht, RTEMS_CAPTURE_SWITCH))
{
capture_record (ct, RTEMS_CAPTURE_SWITCHED_OUT_EVENT);
capture_record (ht, RTEMS_CAPTURE_SWITCHED_IN_EVENT);
}
}
}