From a923a82d2d63b29165b249bae6ad46bb045290b2 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Wed, 15 May 2002 16:36:10 +0000 Subject: 2002-05-16 Chris Johns * 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. --- c/src/libmisc/ChangeLog | 9 + c/src/libmisc/Makefile.am | 4 +- c/src/libmisc/capture/.cvsignore | 2 + c/src/libmisc/capture/Makefile.am | 44 + c/src/libmisc/capture/README | 255 ++++++ c/src/libmisc/capture/capture-cli.c | 1406 +++++++++++++++++++++++++++++++ c/src/libmisc/capture/capture-cli.h | 52 ++ c/src/libmisc/capture/capture.c | 1565 +++++++++++++++++++++++++++++++++++ c/src/libmisc/capture/capture.h | 869 +++++++++++++++++++ c/src/libmisc/configure.ac | 1 + c/src/libmisc/wrapup/Makefile.am | 2 +- 11 files changed, 4206 insertions(+), 3 deletions(-) create mode 100644 c/src/libmisc/capture/.cvsignore create mode 100644 c/src/libmisc/capture/Makefile.am create mode 100644 c/src/libmisc/capture/README create mode 100644 c/src/libmisc/capture/capture-cli.c create mode 100644 c/src/libmisc/capture/capture-cli.h create mode 100644 c/src/libmisc/capture/capture.c create mode 100644 c/src/libmisc/capture/capture.h (limited to 'c/src/libmisc') diff --git a/c/src/libmisc/ChangeLog b/c/src/libmisc/ChangeLog index 334a3edfcc..1365bea8e3 100644 --- a/c/src/libmisc/ChangeLog +++ b/c/src/libmisc/ChangeLog @@ -1,3 +1,12 @@ +2002-05-16 Chris Johns + + * 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. + 2001-05-14 Joel Sherrill * dummy/Makefile.am, wrapup/Makefile.am: Fixed to generate diff --git a/c/src/libmisc/Makefile.am b/c/src/libmisc/Makefile.am index cfa832ffba..fa1d2a1b9a 100644 --- a/c/src/libmisc/Makefile.am +++ b/c/src/libmisc/Makefile.am @@ -4,8 +4,8 @@ ACLOCAL_AMFLAGS = -I ../../../aclocal -SUBDIRS = devnull dummy dumpbuf stackchk monitor cpuuse shell rtmonuse rootfs untar \ - mw-fb wrapup +SUBDIRS = capture cpuuse devnull dummy dumpbuf monitor mw-fb shell rootfs \ + rtmonuse stackchk untar wrapup EXTRA_DIST = README diff --git a/c/src/libmisc/capture/.cvsignore b/c/src/libmisc/capture/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/c/src/libmisc/capture/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/c/src/libmisc/capture/Makefile.am b/c/src/libmisc/capture/Makefile.am new file mode 100644 index 0000000000..fe533b265e --- /dev/null +++ b/c/src/libmisc/capture/Makefile.am @@ -0,0 +1,44 @@ +## +## $Id$ +## + +AUTOMAKE_OPTIONS = foreign 1.4 + +include_rtemsdir = $(includedir)/rtems + +LIBNAME = libcapture-tmp +LIB = $(ARCH)/$(LIBNAME).a + +C_FILES = capture.c capture-cli.c +C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o) + +include_rtems_HEADERS = capture.h capture-cli.h + +OBJS = $(C_O_FILES) + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../../../automake/compile.am +include $(top_srcdir)/../../../automake/lib.am + +$(PROJECT_INCLUDE)/rtems: + @$(mkinstalldirs) $@ +$(PROJECT_INCLUDE)/rtems/%.h: %.h + $(INSTALL_DATA) $< $@ + +# +# (OPTIONAL) Add local stuff here using += +# + +$(LIB): $(OBJS) + $(make-library) + +PREINSTALL_FILES = $(PROJECT_INCLUDE)/rtems \ + $(include_rtems_HEADERS:%=$(PROJECT_INCLUDE)/rtems/%) + +all-local: $(ARCH) $(PREINSTALL_FILES) $(OBJS) $(LIB) + +.PRECIOUS: $(LIB) + +EXTRA_DIST = README capture.c capture-cli.c + +include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/libmisc/capture/README b/c/src/libmisc/capture/README new file mode 100644 index 0000000000..2ea76a4cfe --- /dev/null +++ b/c/src/libmisc/capture/README @@ -0,0 +1,255 @@ +# +# $Id$ +# + + RTEMS Performance Monitoring and Measurement Framework + + Copyright 2002 Chris Johns (ccj@acm.org) + 23 April 2002 + +This directory contains the source code for the performance monitoring and +measurement framework. It is more commonly know as the capture engine. + +The capture engine is in an early phase of development. Please review the Status +section of this document for the current status. + +Performance. + +The capture engine is designed to not effect the system it is +monitoring. Resources such as memory are used as well as a small performance +hit in task creation, deletion and context switch. The overhead is small and +will not be noticed unless the system is operating close to the performance +limit of the target. + +Structure. + +The capture engine is implemented in a couple of layers. This lowest layer is +the capture engine. Its interface is in the file 'capture.h'. Typically this +interface is directly used unless you are implementing a target interface. The +user interface is via a target interface. + +Command Line Interface (CLI). + +This is a target interface that provides a number of user commands via the +RTEMS monitor. To use you need to provide the following in your +application initialisation: + + #include + #include + + rtems_monitor_init (0); + rtems_capture_cli_init (0); + +Check the file capture-cli.h for documentation of the interface. The parameter +is a pointer to your board support package's time stamp handler. The time stamp +handler is yet to be tested so it is recommended this is left as 0, unless you +wish to test this part of the engine. + +The commands are: + + copen - Open the capture engine. + cclose - Close the capture engine. + cenable - Enable the capture engine. + cdisable - Disable the capture engine. + ctlist - List the tasks known to the capture engine. + ctload - Display the current load (sort of top). + cwlist - List the watch and trigger controls. + cwadd - Add a watch. + cwdel - Delete a watch. + cwctl - Enable or disable a watch. + cwglob - Enable or disable the global watch. + cwceil - Set the watch ceiling. + cwfloor - Set the watch floor. + ctrace - Dump the trace records. + ctrig - Define a trigger. + +Open + + usage: copen [-i] size + +Open the capture engine. The size parameter is the size of the capture engine +trace buffer. A single record hold a single event, for example a task create or +a context in or out. The option '-i' will enable the capture engine after it is +opened. + +Close + + usage: cclose + +Close the capture engine and release all resources held by the capture engine. + +Enable + + usage: cenable + +Enable the capture engine if it has been opened. + +Disable + + usage: cdisable + +Disable the capture engine. The enable and disable commands provide a means of +removing the overhead of the capture engine from the context switch. This may +be needed when testing if it is felt the capture engines overhead is effecting +the system. + +Task List + + usage: ctlist + +List the tasks the capture engine knows about. This may contain tasks that have +been deleted. + +Task Load + + usage: ctload + +List the tasks in the order of load in a similar way top does on Unix. The +command sends ANSI terminal codes. You press enter to stop the update. The +update period is fixed at 5 seconds. The output looks like: + + Press ENTER to exit. + + PID NAME RPRI CPRI STATE %CPU %STK FLGS EXEC TIME +04010001 IDLE 255 255 READY 96.012% 0% a-----g 1 +08010009 CPlt 1 1 READY 3.815% 15% a------ 0 +08010003 ntwk 20 20 Wevnt 0.072% 0% at----g 0 +08010004 CSr0 20 20 Wevnt 0.041% 0% at----g 0 +08010001 main 250 250 DELAY 0.041% 0% a-----g 0 +08010008 test 100 100 Wevnt 0.000% 20% at-T-+g 0 +08010007 test 100 100 Wevnt 0.000% 0% at-T-+g 0 +08010005 CSt0 20 20 Wevnt 0.000% 0% at----g 0 +08010006 RMON 1 1 Wsem 0.000% 0% a------ 0 + +There are 7 flags and from left to right are: + +1) 'a' the task is active, and 'd' the task has been deleted. +2) 't' the task has been traced. +3) 'F' the task has a from (TO_ANY) trigger. +4) 'T' the task has a to (FROM_ANY) trigger. +5) 'E' the task has an edge (FROM_TO) trigger. +6) '+' the task as a watch control attached, 'w' a watch is enabled. +7) 'g' the task is part of a global trigger. + +The %STK is the percentage of stack used by a task. Currently only tasks +created while the capture engine is enabled can be monitored. + +The RPRI is the real priority. This is the priority set for the task. The +current priority is the executing priority that may reflect a level set as a +result of priority inversion. + +Watch List + + usage: cwlist + +This command lists the watch and trigger controls the capture engine has. A +control is a structure used by the capture engine to determine if a task is +watched or triggers capturing. + +Watch Add + + usage: cwadd [task name] [id] + +Add a watch for a task. You can provide a name or id or both. A name will cause +all tasks with that name to have the watch added. An id results in a watch +being for a specific task. + +Using a name is useful when the task is not yet created. + +Watch Delete + + usage: cwdel [task name] [id] + +Delete a watch that has been added. + +Watch Control + + usage: cwctl [task name] [id] on/off + +Enable or disable a watch. The name and id parameters are the same as the watch +add command. + +Global Watch + + usage: cwglob on/off + +Enable or disable the global watch. A global watch is an easy way to enable +watches for all tasks with real priorities between the watch ceiling and floor +priorities. + +Watch Priority Ceiling + + usage: cwceil priority + +Set the watch priority ceiling. All tasks with a priority less than the ceiling +priority are not watched. This allow you to ignore high priority system and +driver tasks. + +Watch Priority Floor + + usage: cwfloor priority + +Set the watch priority floor. All tasks with a priority greater than the floor +priority level are not watched. This allows you to remove tasks such as IDLE +from being monitored. + +Trace + + usage: ctrace [-c] [-r records] + +Dump the trace record. The option '-c' will output the records in comma +separated variables (CSV). The '-r' option controls the number of records +dumped. This can help stop the command looping for-ever. + +Trigger + + usage: ctrig type [from name] [from id] [to name] [to id] + +Set a trigger. The types of triggers are : + + from : trigger on a context switch from a task + to : trigger on a context switch to a task + edge : trigger on a context switch from a task to a task + +The from and to trigger types requires a task name or task id or both be +provided. The edge requires a from name and/or id and a to name and/or id be +provided. + +Flush + + usage: cflush [-n] + +Flush the trace record. The option '-n' stops the capture engine be +primed. This means an exising trigger state will not be cleared and tracing +will continue. + +Status. + +The following is a list of outstanding issues or bugs. + +1) The capture engine does not scan the existing list of tasks in the kernel + when initialised. This means tasks that exist but are not active are not + seen. Not sure how to implement this one. + +2) The blocking read of trace records has not been completely implemented or + tested. This will wait until I complete the csv support for the cli for a + serial UI or the tcp server is implemented. + +3) Task control block clean up is not implemented. The control block should be + dumped to the trace buffer. This requires extended record formats. This can + be implemented using an event flag to indicate an extended record follows + the trace record. This would allow a task delete record to be directly + followed by the task information. + +4) Complete csv (comma separated variable) support for the CLI. + +5) Implement a tcp server interface. + +6) Complete the capture engine API documentation. + +7) Test the user supplied time stamp handler. + +8) Task name support is only for the rtems_name type. This means the only the + classic API tasks are currently supported. Partial support for the different + task names is provided how-ever this is not clean and does not support the + variable length task name such as found in the POSIX tasks. diff --git a/c/src/libmisc/capture/capture-cli.c b/c/src/libmisc/capture/capture-cli.c new file mode 100644 index 0000000000..0ed145fafc --- /dev/null +++ b/c/src/libmisc/capture/capture-cli.c @@ -0,0 +1,1406 @@ +/* + ------------------------------------------------------------------------ + $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 Target Interface Command Line Interface. You need + start the RTEMS monitor. + +*/ + +#include +#include +#include + +#include +#include +#include + +#define RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS (32) + +/* + * The user capture timestamper. + */ +static rtems_capture_timestamp capture_timestamp; + +/* + * Common variable to sync the load monitor task. + */ +static volatile int cli_load_thread_active; + +/* + * rtems_capture_cli_open + * + * DESCRIPTION: + * + * This function opens the capture engine. We need the size of the + * capture buffer. + * + */ + +static const char* open_usage = "usage: copen [-i] size\n"; + +static void +rtems_capture_cli_open (int argc, char **argv) +{ + rtems_unsigned32 size = 0; + rtems_boolean enable = 0; + rtems_status_code sc; + int arg; + + if (argc <= 1) + { + printf (open_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'i') + enable = 1; + else + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + else + { + size = strtoul (argv[arg], 0, 0); + + if (size < 100) + { + printf ("error: size must be greater than or equal to 100\n"); + return; + } + } + } + + sc = rtems_capture_open (size, capture_timestamp); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: open failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("capture engine opened.\n"); + + if (!enable) + return; + + sc = rtems_capture_control (enable); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: open enable failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("capture engine enabled.\n"); +} + +/* + * rtems_capture_cli_close + * + * DESCRIPTION: + * + * This function closes the capture engine. + * + */ + +static void +rtems_capture_cli_close (int argc, char **argv) +{ + rtems_status_code sc; + + sc = rtems_capture_close (); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: close failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("capture engine closed.\n"); +} + +/* + * rtems_capture_cli_enable + * + * DESCRIPTION: + * + * This function enables the capture engine. + * + */ + +static void +rtems_capture_cli_enable (int argc, char **argv) +{ + rtems_status_code sc; + + sc = rtems_capture_control (1); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: enable failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("capture engine enabled.\n"); +} + +/* + * rtems_capture_cli_disable + * + * DESCRIPTION: + * + * This function disables the capture engine. + * + */ + +static void +rtems_capture_cli_disable (int argc, char **argv) +{ + rtems_status_code sc; + + sc = rtems_capture_control (0); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: disable failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("capture engine disabled.\n"); +} + +/* + * rtems_capture_cli_task_list + * + * DESCRIPTION: + * + * This function lists the tasks the capture engine knows about. + * + */ + +static void +rtems_capture_cli_task_list (int argc, char **argv) +{ + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + rtems_capture_task_t* task = rtems_capture_get_task_list (); + rtems_unsigned32 ticks; + rtems_unsigned32 tick_offset; + unsigned long long total_time; + int count = rtems_capture_task_count (); + + if (capture_timestamp) + capture_timestamp (&ticks, &tick_offset); + else + { + ticks = _Watchdog_Ticks_since_boot; + tick_offset = 0; + } + + total_time = (ticks * rtems_capture_task_time (task)) + tick_offset; + + printf ("total %i\n", count); + + while (task) + { + rtems_task_priority priority; + int stack_used; + int time_used; + + stack_used = rtems_capture_task_stack_usage (task) * 100; + stack_used /= rtems_capture_task_stack_size (task); + + if (stack_used > 100) + stack_used = 100; + + time_used = (rtems_capture_task_time (task) * 100) / total_time; + + if (time_used > 100) + time_used = 100; + + priority = rtems_capture_task_real_priority (task); + + printf (" "); + rtems_monitor_dump_id (rtems_capture_task_id (task)); + printf (" "); + rtems_monitor_dump_name (rtems_capture_task_name (task)); + printf (" "); + rtems_monitor_dump_priority (rtems_capture_task_start_priority (task)); + printf (" "); + rtems_monitor_dump_priority (rtems_capture_task_real_priority (task)); + printf (" "); + rtems_monitor_dump_priority (rtems_capture_task_curr_priority (task)); + printf (" "); + rtems_monitor_dump_state (rtems_capture_task_state (task)); + printf (" %c%c%c%c%c", + rtems_capture_task_valid (task) ? 'a' : 'd', + rtems_capture_task_flags (task) & RTEMS_CAPTURE_TRACED ? 't' : '-', + rtems_capture_task_control_flags (task) & RTEMS_CAPTURE_TO_ANY ? 'F' : '-', + rtems_capture_task_control_flags (task) & RTEMS_CAPTURE_FROM_ANY ? 'T' : '-', + rtems_capture_task_control_flags (task) & RTEMS_CAPTURE_FROM_TO ? 'E' : '-'); + if ((floor > ceiling) && (ceiling > priority)) + printf ("--"); + else + printf ("%c%c", + rtems_capture_task_control (task) ? + (rtems_capture_task_control_flags (task) & RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', + rtems_capture_watch_global_on () ? 'g' : '-'); + printf (" %3i%% %3i%% (%i)\n", + stack_used, time_used, rtems_capture_task_ticks (task)); + + task = rtems_capture_next_task (task); + } +} + +/* + * rtems_capture_cli_task_load_thread + * + * DESCRIPTION: + * + * This function displays the load of the tasks on an ANSI terminal. + * + */ + +static void +rtems_capture_cli_task_load_thread (rtems_task_argument arg) +{ + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + int last_count = 0; + + printf ("\x1b[2J Press ENTER to exit.\n\n"); + printf (" PID NAME RPRI CPRI STATE %%CPU %%STK FLGS EXEC TIME\n"); + + for (;;) + { + rtems_capture_task_t* tasks[RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS + 1]; + unsigned long long load[RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS + 1]; + rtems_capture_task_t* task; + unsigned long long total_time; + int count = 0; + int i; + int j; + + cli_load_thread_active = 1; + + /* + * Iterate over the tasks and sort the highest load tasks + * into our local arrays. We only handle a limited number of + * tasks. + */ + + memset (tasks, 0, sizeof (tasks)); + memset (load, 0, sizeof (load)); + + task = rtems_capture_get_task_list (); + + while (task) + { + if (rtems_capture_task_valid (task)) + { + unsigned long long l = rtems_capture_task_delta_time (task); + + count++; + + for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) + { + if (tasks[i]) + { + if ((l == 0) || (l < load[i])) + continue; + + for (j = (RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS - 1); j >= i; j--) + { + tasks[j + 1] = tasks[j]; + load[j + 1] = load[j]; + } + } + + tasks[i] = task; + load[i] = l; + break; + } + } + task = rtems_capture_next_task (task); + } + + printf ("\x1b[4;0H"); + + total_time = 0; + + for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) + total_time += load[i]; + + if (count > last_count) + j = count; + else + j = last_count; + + for (i = 0; i < RTEMS_CAPTURE_CLI_MAX_LOAD_TASKS; i++) + { + rtems_task_priority priority; + int stack_used; + int task_load; + int k; + + if (!tasks[i]) + break; + + j--; + + stack_used = rtems_capture_task_stack_usage (tasks[i]) * 100; + stack_used /= rtems_capture_task_stack_size (tasks[i]); + + if (stack_used > 100) + stack_used = 100; + + task_load = (int) ((load[i] * 100000) / total_time); + + priority = rtems_capture_task_real_priority (tasks[i]); + + printf ("\x1b[K"); + rtems_monitor_dump_id (rtems_capture_task_id (tasks[i])); + printf (" "); + rtems_monitor_dump_name (rtems_capture_task_name (tasks[i])); + printf (" "); + rtems_monitor_dump_priority (priority); + printf (" "); + rtems_monitor_dump_priority (rtems_capture_task_curr_priority (tasks[i])); + printf (" "); + k = rtems_monitor_dump_state (rtems_capture_task_state (tasks[i])); + printf ("%*c %3i.%03i%% ", 6 - k, ' ', task_load / 1000, task_load % 1000); + printf ("%3i%% %c%c%c%c%c", stack_used, + rtems_capture_task_valid (tasks[i]) ? 'a' : 'd', + rtems_capture_task_flags (tasks[i]) & RTEMS_CAPTURE_TRACED ? 't' : '-', + rtems_capture_task_control_flags (tasks[i]) & RTEMS_CAPTURE_TO_ANY ? 'F' : '-', + rtems_capture_task_control_flags (tasks[i]) & RTEMS_CAPTURE_FROM_ANY ? 'T' : '-', + rtems_capture_task_control_flags (tasks[i]) & RTEMS_CAPTURE_FROM_TO ? 'E' : '-'); + if ((floor > ceiling) && (ceiling > priority)) + printf ("--"); + else + printf ("%c%c", + rtems_capture_task_control (tasks[i]) ? + (rtems_capture_task_control_flags (tasks[i]) & + RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', + rtems_capture_watch_global_on () ? 'g' : '-'); + + printf (" %qi\n", rtems_capture_task_time (tasks[i])); + } + + while (j) + { + printf ("\x1b[K\n"); + j--; + } + + last_count = count; + + cli_load_thread_active = 0; + + rtems_task_wake_after (TOD_MICROSECONDS_TO_TICKS (5000000)); + } +} + +/* + * rtems_capture_cli_task_load + * + * DESCRIPTION: + * + * This function is a monitor command. + * + */ + +static void +rtems_capture_cli_task_load (int argc, char **argv) +{ + rtems_status_code sc; + rtems_task_priority priority; + rtems_name name; + rtems_id id; + + sc = rtems_task_set_priority (RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: cannot obtain the current priority: %s\n", rtems_status_text (sc)); + return; + } + + memcpy (&name, "CPlt", 4); + + sc = rtems_task_create (name, priority, 1024, + RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL, + RTEMS_PREEMPT | RTEMS_TIMESLICE | RTEMS_NO_ASR, + &id); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: cannot create helper thread: %s\n", rtems_status_text (sc)); + return; + } + + sc = rtems_task_start (id, rtems_capture_cli_task_load_thread, 0); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: cannot start helper thread: %s\n", rtems_status_text (sc)); + rtems_task_delete (id); + return; + } + + for (;;) + { + char c = getchar (); + + if ((c == '\r') || (c == '\n')) + { + int loops = 20; + + while (loops && cli_load_thread_active) + rtems_task_wake_after (TOD_MICROSECONDS_TO_TICKS (100000)); + + rtems_task_delete (id); + + printf ("load monitoring stopped.\n"); + + return; + } + } +} + +/* + * rtems_capture_cli_watch_list + * + * DESCRIPTION: + * + * This function lists the controls in the capture engine. + * + */ + +static void +rtems_capture_cli_watch_list (int argc, char **argv) +{ + rtems_capture_control_t* control = rtems_capture_get_control_list (); + rtems_task_priority ceiling = rtems_capture_watch_get_ceiling (); + rtems_task_priority floor = rtems_capture_watch_get_floor (); + + printf ("watch priority ceiling is %i\n", ceiling); + printf ("watch priority floor is %i\n", floor); + printf ("global watch is %s\n", + rtems_capture_watch_global_on () ? "enabled" : "disabled"); + printf ("total %d\n", rtems_capture_control_count ()); + + while (control) + { + int f; + int fshowed; + int lf; + + printf (" "); + rtems_monitor_dump_id (rtems_capture_control_id (control)); + printf (" "); + rtems_monitor_dump_name (rtems_capture_control_name (control)); + printf (" %c%c%c%c%c", + rtems_capture_control_flags (control) & RTEMS_CAPTURE_WATCH ? 'w' : '-', + rtems_capture_watch_global_on () ? 'g' : '-', + rtems_capture_control_flags (control) & RTEMS_CAPTURE_TO_ANY ? 'F' : '-', + rtems_capture_control_flags (control) & RTEMS_CAPTURE_FROM_ANY ? 'T' : '-', + rtems_capture_control_flags (control) & RTEMS_CAPTURE_FROM_TO ? 'E' : '-'); + + for (f = 0, fshowed = 0, lf = 1; f < RTEMS_CAPTURE_TRIGGER_TASKS; f++) + { + if (lf && ((fshowed % 16) == 0)) + { + printf ("\n"); + lf = 0; + } + + /* + * FIXME: name test. + */ + if (rtems_capture_control_from_name (control, f)) + { + printf (" %2i:", f); + rtems_monitor_dump_name (rtems_capture_control_from_name (control, f)); + printf ("/"); + rtems_monitor_dump_id (rtems_capture_control_from_id (control, f)); + fshowed++; + lf = 1; + } + } + + if (lf) + printf ("\n"); + + control = rtems_capture_next_control (control); + } +} + +/* + * rtems_capture_cli_get_name_id + * + * DESCRIPTION: + * + * This function checks arguments for a name or an id. + * + */ + +static rtems_boolean +rtems_capture_cli_get_name_id (char* arg, + rtems_boolean* valid_name, + rtems_boolean* valid_id, + rtems_name* name, + rtems_id* id) +{ + Objects_Classes objclass; + int l; + int i; + + if (*valid_name && *valid_id) + { + printf ("error: too many arguments\n"); + return 0; + } + + /* + * See if the arg is all hex digits. + */ + + l = strlen (arg); + + for (i = 0; i < l; i++) + if (!isxdigit (arg[i])) + break; + + *id = strtoul (arg, 0, 16); + + objclass = _Objects_Get_class (*id); + + if ((i == l) && (l > 4) && + ((objclass == OBJECTS_INTERNAL_THREADS) || + (objclass == OBJECTS_RTEMS_TASKS) || + (objclass == OBJECTS_POSIX_THREADS) || + (objclass == OBJECTS_ITRON_TASKS))) + *valid_id = 1; + else + { + memcpy (name, arg, sizeof (rtems_name)); + *valid_name = 1; + } + + return 1; +} + +/* + * rtems_capture_cli_watch_add + * + * DESCRIPTION: + * + * This function is a monitor command that add a watch to the capture + * engine. + * + */ + +static char const * watch_add_usage = "usage: cwadd [task name] [id]\n"; + +static void +rtems_capture_cli_watch_add (int argc, char **argv) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + rtems_boolean valid_name = 0; + rtems_boolean valid_id = 0; + + if (argc <= 1) + { + printf (watch_add_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + printf("error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_add (name, id); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: watch add failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("watch added.\n"); +} + +/* + * rtems_capture_cli_watch_del + * + * DESCRIPTION: + * + * This function is a monitor command that deletes a watch from the capture + * engine. + * + */ + +static char const * watch_del_usage = "usage: cwdel [task name] [id]\n"; + +static void +rtems_capture_cli_watch_del (int argc, char **argv) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + rtems_boolean valid_name = 0; + rtems_boolean valid_id = 0; + + if (argc <= 1) + { + printf (watch_del_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + printf("error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_del (name, id); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: watch delete failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("watch delete.\n"); +} + +/* + * rtems_capture_cli_watch_control + * + * DESCRIPTION: + * + * This function is a monitor command that controls a watch. + * + */ + +static char const * watch_control_usage = "usage: cwctl [task name] [id] on/off\n"; + +static void +rtems_capture_cli_watch_control (int argc, char **argv) +{ + rtems_status_code sc; + int arg; + rtems_name name = 0; + rtems_id id = 0; + rtems_boolean valid_name = 0; + rtems_boolean valid_id = 0; + rtems_boolean enable = 0; + + if (argc <= 2) + { + printf (watch_control_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (strcmp (argv[arg], "on") == 0) + enable = 1; + else if (strcmp (argv[arg], "off") == 0) + enable = 0; + else if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, &name, &id)) + return; + } + } + + if (!valid_name && !valid_id) + { + printf("error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_ctrl (name, id, enable); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: watch control failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("watch %s.\n", enable ? "enabled" : "disabled"); +} + +/* + * rtems_capture_cli_watch_global + * + * DESCRIPTION: + * + * This function is a monitor command that sets a global watch. + * + */ + +static char const * watch_global_usage = "usage: cwglob on/off\n"; + +static void +rtems_capture_cli_watch_global (int argc, char **argv) +{ + rtems_status_code sc; + int arg; + rtems_boolean enable = 0; + + if (argc <= 1) + { + printf (watch_global_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (strcmp (argv[arg], "on") == 0) + enable = 1; + else if (strcmp (argv[arg], "off") == 0) + enable = 0; + } + } + + sc = rtems_capture_watch_global (enable); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: global watch failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("global watch %s.\n", enable ? "enabled" : "disabled"); +} + +/* + * rtems_capture_cli_watch_ceiling + * + * DESCRIPTION: + * + * This function is a monitor command that sets watch ceiling. + * + */ + +static char const * watch_ceiling_usage = "usage: cwceil priority\n"; + +static void +rtems_capture_cli_watch_ceiling (int argc, char **argv) +{ + rtems_status_code sc; + int arg; + rtems_task_priority priority = 0; + + if (argc <= 1) + { + printf (watch_ceiling_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + else + { + priority = strtoul (argv[arg], 0, 0); + } + } + + sc = rtems_capture_watch_ceiling (priority); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: watch ceiling failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("watch ceiling is %i.\n", priority); +} + +/* + * rtems_capture_cli_watch_floor + * + * DESCRIPTION: + * + * This function is a monitor command that sets watch floor. + * + */ + +static char const * watch_floor_usage = "usage: cwfloor priority\n"; + +static void +rtems_capture_cli_watch_floor (int argc, char **argv) +{ + rtems_status_code sc; + int arg; + rtems_task_priority priority = 0; + + if (argc <= 1) + { + printf (watch_floor_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + else + { + priority = strtoul (argv[arg], 0, 0); + } + } + + sc = rtems_capture_watch_floor (priority); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: watch floor failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("watch floor is %i.\n", priority); +} + +/* + * rtems_capture_cli_trigger_set + * + * DESCRIPTION: + * + * This function is a monitor command that sets a trigger. + * + */ + +static char const *trigger_set_usage = "usage: ctrig type [from] [fromid] [to] [to id]\n"; + +static void +rtems_capture_cli_trigger_set (int argc, char **argv) +{ + rtems_status_code sc; + int arg; + rtems_capture_trigger_t trigger = rtems_capture_from_to; + rtems_boolean trigger_set = 0; + rtems_name name = 0; + rtems_id id = 0; + rtems_boolean valid_name = 0; + rtems_boolean valid_id = 0; + rtems_name from_name = 0; + rtems_id from_id = 0; + rtems_boolean from_valid_name = 0; + rtems_boolean from_valid_id = 0; + rtems_name to_name = 0; + rtems_id to_id = 0; + rtems_boolean to_valid_name = 0; + rtems_boolean to_valid_id = 0; + + if (argc <= 2) + { + printf (trigger_set_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + else + { + if (!trigger_set) + { + if (strcmp (argv[arg], "from") == 0) + trigger = rtems_capture_to_any; + else if (strcmp (argv[arg], "to") == 0) + trigger = rtems_capture_from_any; + else if (strcmp (argv[arg], "edge") == 0) + trigger = rtems_capture_from_any; + else + { + printf ("error: the first argument is the trigger type (from/to/edge)\n"); + return; + } + trigger_set = 1; + } + else + { + if (trigger == rtems_capture_to_any) + { + if (from_valid_name && from_valid_id) + printf ("warning: extra arguments ignored\n"); + else if (!rtems_capture_cli_get_name_id (argv[arg], &from_valid_name, &from_valid_id, + &from_name, &from_id)) + return; + } + else if (trigger == rtems_capture_from_any) + { + if (to_valid_name && to_valid_id) + printf ("warning: extra arguments ignored\n"); + else if (!rtems_capture_cli_get_name_id (argv[arg], &to_valid_name, &to_valid_id, + &to_name, &to_id)) + return; + } + else if (trigger == rtems_capture_from_to) + { + if (from_valid_name && from_valid_id && to_valid_name && to_valid_id) + printf ("warning: extra arguments ignored\n"); + else + { + if (!rtems_capture_cli_get_name_id (argv[arg], &valid_name, &valid_id, + &name, &id)) + return; + + if (valid_name) + { + if (!from_valid_name && !from_valid_id) + { + from_valid_name = 1; + from_name = name; + } + else if (to_valid_name) + printf ("warning: extra arguments ignored\n"); + else + { + to_valid_name = 1; + to_name = name; + } + } + if (valid_id) + { + if (!from_valid_id && !to_valid_name) + { + from_valid_id = 1; + from_id = id; + } + else if (to_valid_id) + printf ("warning: extra arguments ignored\n"); + else + { + to_valid_id = 1; + to_id = id; + } + } + } + } + } + } + } + + if ((trigger == rtems_capture_to_any) && !from_valid_name && !from_valid_id) + { + printf ("error: a from trigger need a to name or id\n"); + return; + } + + if ((trigger == rtems_capture_from_any) && !to_valid_name && !to_valid_id) + { + printf ("error: a to trigger need a from name or id\n"); + return; + } + + if ((trigger == rtems_capture_from_to) && + ((!from_valid_name && !from_valid_id) || (!to_valid_name && !to_valid_id))) + { + printf ("error: an edge trigger need a from and to name or id\n"); + return; + } + + sc = rtems_capture_set_trigger (from_name, from_id, to_name, to_id, trigger); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: setting the trigger failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("trigger set.\n"); +} + +/* + * rtems_capture_cli_trace_records + * + * DESCRIPTION: + * + * This function is a monitor command that dumps trace records. + * + */ + +static void +rtems_capture_cli_trace_records (int argc, char **argv) +{ + rtems_status_code sc; + rtems_boolean csv = 0; + static int dump_total = 32; + int total; + int count; + rtems_unsigned32 read; + rtems_capture_record_t* rec; + int arg; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'c') + csv = 1; + else if (argv[arg][1] == 'r') + { + int i; + int l; + + arg++; + if (arg == argc) + { + printf ("error: option -r requires number\n"); + return; + } + + l = strlen (argv[arg]); + + for (i = 0; i < l; i++) + if (!isdigit (argv[arg][i])) + { + printf ("error: option -r requires number and currently it is not\n"); + return; + } + + dump_total = strtoul (argv[arg], 0, 0); + } + else + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + } + + total = dump_total; + + while (total) + { + sc = rtems_capture_read (0, 0, &read, &rec); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: trace read failed: %s\n", rtems_status_text (sc)); + rtems_capture_flush (0); + return; + } + + if (read == 0) + break; + + for (count = 0; count < read; count++, rec++) + { + if (csv) + printf ("%08x,%03d,%03d,%04x,%d,%d\n", + (rtems_unsigned32) rec->task, + (rec->events >> RTEMS_CAPTURE_REAL_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_CURR_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_EVENT_START), + rec->ticks, rec->tick_offset); + else + { + unsigned long long t; + rtems_unsigned32 event; + int e; + + event = rec->events >> RTEMS_CAPTURE_EVENT_START; + + t = rec->ticks; + t *= rtems_capture_tick_time (); + t += rec->tick_offset; + + for (e = RTEMS_CAPTURE_EVENT_START; e < RTEMS_CAPTURE_EVENT_END; e++) + { + if (event & 1) + { + printf ("%9li.%06li ", (unsigned long) (t / 1000000), + (unsigned long) (t % 1000000)); + rtems_monitor_dump_id (rtems_capture_task_id (rec->task)); + printf (" "); + rtems_monitor_dump_name (rtems_capture_task_name (rec->task)); + printf (" %3i %3i %s\n", + (rec->events >> RTEMS_CAPTURE_REAL_PRIORITY_EVENT) & 0xff, + (rec->events >> RTEMS_CAPTURE_CURR_PRIORITY_EVENT) & 0xff, + rtems_capture_event_text (e)); + } + event >>= 1; + } + } + } + + if (read < total) + total -= read; + else + total = 0; + + rtems_capture_release (read); + } +} + +/* + * rtems_capture_cli_flush + * + * DESCRIPTION: + * + * This function is a monitor command that flushes and primes the capture + * engine. + * + */ + +static void +rtems_capture_cli_flush (int argc, char **argv) +{ + rtems_status_code sc; + rtems_boolean prime = 1; + int arg; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'n') + prime = 0; + else + printf ("warning: option -%c ignored\n", argv[arg][1]); + } + } + + sc = rtems_capture_flush (prime); + + if (sc != RTEMS_SUCCESSFUL) + { + printf ("error: flush failed: %s\n", rtems_status_text (sc)); + return; + } + + printf ("trace buffer flushed and %s.\n", prime ? "primed" : "not primed"); +} + +static rtems_monitor_command_entry_t rtems_capture_cli_cmds[] = +{ + { + "copen", + "usage: copen [-i] size\n", + 0, + (void*) rtems_capture_cli_open, + 0, + 0 + }, + { + "cclose", + "usage: cclose\n", + 0, + (void*) rtems_capture_cli_close, + 0, + 0 + }, + { + "cenable", + "usage: cenable\n", + 0, + (void*) rtems_capture_cli_enable, + 0, + 0 + }, + { + "cdisable", + "usage: cdisable\n", + 0, + (void*) rtems_capture_cli_disable, + 0, + 0 + }, + { + "ctlist", + "usage: ctlist \n", + 0, + (void*) rtems_capture_cli_task_list, + 0, + 0 + }, + { + "ctload", + "usage: ctload \n", + 0, + (void*) rtems_capture_cli_task_load, + 0, + 0 + }, + { + "cwlist", + "usage: cwlist\n", + 0, + (void*) rtems_capture_cli_watch_list, + 0, + 0 + }, + { + "cwadd", + "usage: cwadd [task name] [id]\n", + 0, + (void*) rtems_capture_cli_watch_add, + 0, + 0 + }, + { + "cwdel", + "usage: cwdel [task name] [id]\n", + 0, + (void*) rtems_capture_cli_watch_del, + 0, + 0 + }, + { + "cwctl", + "usage: cwctl [task name] [id] on/off\n", + 0, + (void*) rtems_capture_cli_watch_control, + 0, + 0 + }, + { + "cwglob", + "usage: cwglob on/off\n", + 0, + (void*) rtems_capture_cli_watch_global, + 0, + 0 + }, + { + "cwceil", + "usage: cwceil priority\n", + 0, + (void*) rtems_capture_cli_watch_ceiling, + 0, + 0 + }, + { + "cwfloor", + "usage: cwfloor priority\n", + 0, + (void*) rtems_capture_cli_watch_floor, + 0, + 0 + }, + { + "ctrace", + "usage: ctrace [-c] [-r records]\n", + 0, + (void*) rtems_capture_cli_trace_records, + 0, + 0 + }, + { + "ctrig", + "usage: ctrig type [from name] [from id] [to name] [to id]\n", + 0, + (void*) rtems_capture_cli_trigger_set, + 0, + 0 + }, + { + "cflush", + "usage: cflush [-n]\n", + 0, + (void*) rtems_capture_cli_flush, + 0, + 0 + } +}; + +/* + * rtems_capture_cli_init + * + * DESCRIPTION: + * + * This function initialises the command line interface to the capture + * engine. + * + */ + +rtems_status_code +rtems_capture_cli_init (rtems_capture_timestamp timestamp) +{ + int cmd; + + capture_timestamp = timestamp; + + for (cmd = 0; + cmd < sizeof (rtems_capture_cli_cmds) / sizeof (rtems_monitor_command_entry_t); + cmd++) + rtems_monitor_insert_cmd (&rtems_capture_cli_cmds[cmd]); + + return RTEMS_SUCCESSFUL; +} + diff --git a/c/src/libmisc/capture/capture-cli.h b/c/src/libmisc/capture/capture-cli.h new file mode 100644 index 0000000000..968e3217f9 --- /dev/null +++ b/c/src/libmisc/capture/capture-cli.h @@ -0,0 +1,52 @@ +/* + ------------------------------------------------------------------------ + $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 Target Interface Command Line Interface. You need + start the RTEMS monitor. + +*/ + +#ifndef __CAPTURE_CLI_H_ +#define __CAPTURE_CLI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * rtems_capture_cli_init + * + * DESCRIPTION: + * + * This function initialises the command line interface to the capture + * engine. + * + */ +rtems_status_code +rtems_capture_cli_init (rtems_capture_timestamp timestamp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/libmisc/capture/capture.c b/c/src/libmisc/capture/capture.c new file mode 100644 index 0000000000..b58d065d91 --- /dev/null +++ b/c/src/libmisc/capture/capture.c @@ -0,0 +1,1565 @@ +/* + ------------------------------------------------------------------------ + $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. + +*/ + +#include + +#include "capture.h" +#include +#include +#include + +/* + * 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 rtems_unsigned32 capture_size; +static rtems_unsigned32 capture_count; +static rtems_capture_record_t* capture_in; +static rtems_unsigned32 capture_out; +static rtems_unsigned32 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_unsigned32 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 (rtems_unsigned32* ticks, + rtems_unsigned32* 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) + { + rtems_unsigned32* s; + rtems_unsigned32 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, + rtems_unsigned32 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) + { + rtems_unsigned32 ticks; + rtems_unsigned32 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 (rtems_unsigned32 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 (rtems_unsigned32 threshold, + rtems_unsigned32 timeout, + rtems_unsigned32* read, + rtems_capture_record_t** recs) +{ + rtems_interrupt_level level; + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_unsigned32 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 (rtems_unsigned32 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. + */ +rtems_unsigned32 +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. + */ +rtems_unsigned32 +rtems_capture_task_stack_usage (rtems_capture_task_t* task) +{ + if (task->tcb) + { + rtems_unsigned32* st; + rtems_unsigned32* 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 - (rtems_unsigned32*) 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; +} + diff --git a/c/src/libmisc/capture/capture.h b/c/src/libmisc/capture/capture.h new file mode 100644 index 0000000000..7e5bfea9a8 --- /dev/null +++ b/c/src/libmisc/capture/capture.h @@ -0,0 +1,869 @@ +/* + ------------------------------------------------------------------------ + $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. + +*/ + +#ifndef __CAPTURE_H_ +#define __CAPTURE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * The number of tasks in a trigger group. + */ +#define RTEMS_CAPTURE_TRIGGER_TASKS (32) + +/* + * rtems_capture_control_t + * + * DESCRIPTION: + * + * RTEMS control holds the trigger and watch configuration for a group of + * tasks with the same name. + */ +typedef struct rtems_capture_control_s +{ + rtems_name name; + rtems_id id; + rtems_unsigned32 flags; + rtems_name from[RTEMS_CAPTURE_TRIGGER_TASKS]; + rtems_id from_id[RTEMS_CAPTURE_TRIGGER_TASKS]; + struct rtems_capture_control_s* next; +} rtems_capture_control_t; + +/* + * Control flags. + */ +#define RTEMS_CAPTURE_WATCH (1 << 0) +#define RTEMS_CAPTURE_FROM_ANY (1 << 1) +#define RTEMS_CAPTURE_TO_ANY (1 << 2) +#define RTEMS_CAPTURE_FROM_TO (1 << 3) + +/* + * rtems_capture_control_t + * + * DESCRIPTION: + * + * RTEMS capture control provdes the information about a task, along + * with its trigger state. The control is referenced by each + * capture record. This is* information neeed by the decoder. The + * capture record cannot assume the task will exist when the record is + * dumped via the target interface so task info needed for tracing is + * copied and held here. + * + * The inline heper functions provide more details about the info + * contained in this structure. + * + * Note, the tracer code exploits the fact an rtems_name is a + * 32bit value. + */ +typedef struct rtems_capture_task_s +{ + rtems_name name; + rtems_id id; + rtems_unsigned32 flags; + rtems_tcb* tcb; + rtems_unsigned32 in; + rtems_unsigned32 out; + rtems_task_priority start_priority; + rtems_unsigned32 stack_size; + rtems_unsigned32 stack_clean; + rtems_unsigned32 ticks; + rtems_unsigned32 tick_offset; + rtems_unsigned32 ticks_in; + rtems_unsigned32 tick_offset_in; + rtems_unsigned32 last_ticks; + rtems_unsigned32 last_tick_offset; + rtems_capture_control_t* control; + struct rtems_capture_task_s* next; +} rtems_capture_task_t; + +/* + * Task flags. + */ +#define RTEMS_CAPTURE_TRACED (1 << 0) + +/* + * rtems_capture_record_t + * + * DESCRIPTION: + * + * RTEMS capture record. This is a record that is written into + * the buffer. The events includes the priority of the task + * at the time of the context switch. + */ +typedef struct rtems_capture_record_s +{ + rtems_capture_task_t* task; + rtems_unsigned32 events; + rtems_unsigned32 ticks; + rtems_unsigned32 tick_offset; +} rtems_capture_record_t; + +/* + * The capture record event flags. + */ +#define RTEMS_CAPTURE_REAL_PRI_EVENT_MASK (0x000000ff) +#define RTEMS_CAPTURE_CURR_PRI_EVENT_MASK (0x0000ff00) +#define RTEMS_CAPTURE_REAL_PRIORITY_EVENT (0) +#define RTEMS_CAPTURE_CURR_PRIORITY_EVENT (8) +#define RTEMS_CAPTURE_EVENT_START (16) +#define RTEMS_CAPTURE_CREATED_BY_EVENT (1 << 16) +#define RTEMS_CAPTURE_CREATED_EVENT (1 << 17) +#define RTEMS_CAPTURE_STARTED_BY_EVENT (1 << 18) +#define RTEMS_CAPTURE_STARTED_EVENT (1 << 19) +#define RTEMS_CAPTURE_RESTARTED_BY_EVENT (1 << 20) +#define RTEMS_CAPTURE_RESTARTED_EVENT (1 << 21) +#define RTEMS_CAPTURE_DELETED_BY_EVENT (1 << 22) +#define RTEMS_CAPTURE_DELETED_EVENT (1 << 23) +#define RTEMS_CAPTURE_BEGIN_EVENT (1 << 24) +#define RTEMS_CAPTURE_EXITTED_EVENT (1 << 25) +#define RTEMS_CAPTURE_SWITCHED_OUT_EVENT (1 << 26) +#define RTEMS_CAPTURE_SWITCHED_IN_EVENT (1 << 27) +#define RTEMS_CAPTURE_TIMESTAMP (1 << 28) +#define RTEMS_CAPTURE_EVENT_END (28) + +/* + * rtems_capture_trigger_t + * + * DESCRIPTION: + * + * The types of triggers that exist. FIXME: add more here. + */ +typedef enum rtems_capture_trigger_t +{ + rtems_capture_to_any, + rtems_capture_from_any, + rtems_capture_from_to +} rtems_capture_trigger_t; + +/* + * rtems_capture_timestamp + * + * DESCRIPTION: + * + * This defines the callout handler to obtain a time stamp. The + * value returned is time count since the last read. + * + */ + +typedef void (*rtems_capture_timestamp) + (rtems_unsigned32* ticks, rtems_unsigned32* micro); + +/* + * rtems_capture_open + * + * DESCRIPTION: + * + * This function initialises the realtime trace manager allocating the capture + * buffer. It is assumed we have a working heap at stage of initialisation. + * + */ +rtems_status_code +rtems_capture_open (rtems_unsigned32 size, + rtems_capture_timestamp timestamp); + +/* + * rtems_capture_close + * + * DESCRIPTION: + * + * This function shutdowns the tracer and release any claimed + * resources. + */ +rtems_status_code +rtems_capture_close (); + +/* + * rtems_capture_control + * + * DESCRIPTION: + * + * This function allows control of tracing at a global level. + */ +rtems_status_code +rtems_capture_control (rtems_boolean enable); + +/* + * rtems_capture_flush + * + * DESCRIPTION: + * + * This function flushes the trace buffer. The prime parameter allows the + * capture engine to also be primed again. + */ +rtems_status_code +rtems_capture_flush (rtems_boolean prime); + +/* + * 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_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_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_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_capture_watch_global_on + * + * DESCRIPTION: + * + * This function returns the global watch state. + */ +rtems_boolean +rtems_capture_watch_global_on (); + +/* + * 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); + +/* + * rtems_capture_watch_get_ceiling + * + * DESCRIPTION: + * + * This function gets the watch ceiling. + */ +rtems_task_priority +rtems_capture_watch_get_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); + +/* + * rtems_capture_watch_get_floor + * + * DESCRIPTION: + * + * This function gets the watch floor. + */ +rtems_task_priority +rtems_capture_watch_get_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 trace 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_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 (rtems_unsigned32 threshold, + rtems_unsigned32 timeout, + rtems_unsigned32* read, + rtems_capture_record_t** recs); + +/* + * 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 (rtems_unsigned32 count); + +/* + * rtems_capture_tick_time + * + * DESCRIPTION: + * + * This function returns the tick period in micro-seconds. + */ +rtems_unsigned32 +rtems_capture_tick_time (); + +/* + * rtems_capture_tick_time + * + * DESCRIPTION: + * + * This function returns the tick period in micro-seconds. + */ +rtems_unsigned32 +rtems_capture_tick_time (); + +/* + * 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); + +/* + * 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 (); + +/* + * rtems_capture_next_task + * + * DESCRIPTION: + * + * This function returns the pointer to the next task in the list. The + * pointer NULL terminates the list. + */ +static inline rtems_capture_task_t* +rtems_capture_next_task (rtems_capture_task_t* task) +{ + return task->next; +} + +/* + * rtems_capture_task_valid + * + * DESCRIPTION: + * + * This function returns true if the task control block points to + * a valid task. + */ +static inline rtems_boolean +rtems_capture_task_valid (rtems_capture_task_t* task) +{ + return task->tcb != NULL; +} + +/* + * rtems_capture_task_id + * + * DESCRIPTION: + * + * This function returns the task id. + */ +static inline rtems_id +rtems_capture_task_id (rtems_capture_task_t* task) +{ + return task->id; +} + +/* + * rtems_capture_task_state + * + * DESCRIPTION: + * + * This function returns the task state. + */ +static inline States_Control +rtems_capture_task_state (rtems_capture_task_t* task) +{ + if (rtems_capture_task_valid (task)) + return task->tcb->current_state; + return 0; +} + +/* + * rtems_capture_task_name + * + * DESCRIPTION: + * + * This function returns the task name. + */ +static inline rtems_name +rtems_capture_task_name (rtems_capture_task_t* task) +{ + return task->name; +} + +/* + * rtems_capture_task_flags + * + * DESCRIPTION: + * + * This function returns the task flags. + */ +static inline rtems_unsigned32 +rtems_capture_task_flags (rtems_capture_task_t* task) +{ + return task->flags; +} + +/* + * rtems_capture_task_control + * + * DESCRIPTION: + * + * This function returns the task control if present. + */ +static inline rtems_capture_control_t* +rtems_capture_task_control (rtems_capture_task_t* task) +{ + return task->control; +} + +/* + * rtems_capture_task_control_flags + * + * DESCRIPTION: + * + * This function returns the task control flags if a control is present. + */ +static inline rtems_unsigned32 +rtems_capture_task_control_flags (rtems_capture_task_t* task) +{ + if (!task->control) + return 0; + return task->control->flags; +} + +/* + * rtems_capture_task_switched_in + * + * DESCRIPTION: + * + * This function returns the number of times the task has + * been switched into context. + */ +static inline rtems_unsigned32 +rtems_capture_task_switched_in (rtems_capture_task_t* task) +{ + return task->in; +} + +/* + * rtems_capture_task_switched_out + * + * DESCRIPTION: + * + * This function returns the number of times the task has + * been switched out of context. + */ +static inline rtems_unsigned32 +rtems_capture_task_switched_out (rtems_capture_task_t* task) +{ + return task->out; +} + +/* + * rtems_capture_task_curr_priority + * + * DESCRIPTION: + * + * This function returns the tasks start priority. The tracer needs this + * to track where the task's priority goes. + */ +static inline rtems_task_priority +rtems_capture_task_start_priority (rtems_capture_task_t* task) +{ + return task->start_priority; +} + +/* + * rtems_capture_task_real_priority + * + * DESCRIPTION: + * + * This function returns the tasks real priority. + */ +static inline rtems_task_priority +rtems_capture_task_real_priority (rtems_capture_task_t* task) +{ + if (rtems_capture_task_valid (task)) + return task->tcb->real_priority; + return 0; +} + +/* + * rtems_capture_task_curr_priority + * + * DESCRIPTION: + * + * This function returns the tasks current priority. + */ +static inline rtems_task_priority +rtems_capture_task_curr_priority (rtems_capture_task_t* task) +{ + if (rtems_capture_task_valid (task)) + return task->tcb->current_priority; + return 0; +} + +/* + * rtems_capture_task_stack_usage + * + * DESCRIPTION: + * + * This function updates the stack usage. The task control block + * is updated. + */ +rtems_unsigned32 +rtems_capture_task_stack_usage (rtems_capture_task_t* task); + +/* + * rtems_capture_task_stack_size + * + * DESCRIPTION: + * + * This function returns the task's stack size. + */ +static inline rtems_unsigned32 +rtems_capture_task_stack_size (rtems_capture_task_t* task) +{ + return task->stack_size; +} + +/* + * rtems_capture_task_stack_used + * + * DESCRIPTION: + * + * This function returns the amount of stack used. + */ +static inline rtems_unsigned32 +rtems_capture_task_stack_used (rtems_capture_task_t* task) +{ + return task->stack_size - task->stack_clean; +} + +/* + * rtems_capture_task_ticks + * + * DESCRIPTION: + * + * This function returns the current execution time as ticks. + */ +static inline rtems_unsigned32 +rtems_capture_task_ticks (rtems_capture_task_t* task) +{ + return task->ticks; +} + +/* + * rtems_capture_task_tick_offset + * + * DESCRIPTION: + * + * This function returns the current execution time tick offset. + */ +static inline rtems_unsigned32 +rtems_capture_task_tick_offset (rtems_capture_task_t* task) +{ + return task->tick_offset; +} + +/* + * rtems_capture_task_time + * + * DESCRIPTION: + * + * This function returns the current execution time. + */ +static inline unsigned long long +rtems_capture_task_time (rtems_capture_task_t* task) +{ + unsigned long long t = task->ticks; + return (t * rtems_capture_tick_time ()) + task->tick_offset;; +} + +/* + * rtems_capture_task_delta_time + * + * DESCRIPTION: + * + * This function returns the execution time as a different between the + * last time the detla time was and now. + */ +static inline unsigned long long +rtems_capture_task_delta_time (rtems_capture_task_t* task) +{ + unsigned long long t = task->ticks - task->last_ticks; + rtems_unsigned32 o = task->tick_offset - task->last_tick_offset; + + task->last_ticks = task->ticks; + task->last_tick_offset = task->tick_offset; + + return (t * rtems_capture_tick_time ()) + o; +} + +/* + * rtems_capture_task_count + * + * DESCRIPTION: + * + * This function returns the number of tasks the capture + * engine knows about. + */ +static inline rtems_unsigned32 +rtems_capture_task_count () +{ + rtems_capture_task_t* task = rtems_capture_get_task_list (); + rtems_unsigned32 count = 0; + + while (task) + { + count++; + task = rtems_capture_next_task (task); + } + + return count; +} + +/* + * rtems_capture_get_control_list + * + * DESCRIPTION: + * + * This function returns the head of the list of controls in the + * capture engine. + */ +rtems_capture_control_t* +rtems_capture_get_control_list (); + +/* + * rtems_capture_next_control + * + * DESCRIPTION: + * + * This function returns the pointer to the next control in the list. The + * pointer NULL terminates the list. + */ +static inline rtems_capture_control_t* +rtems_capture_next_control (rtems_capture_control_t* control) +{ + return control->next; +} + +/* + * rtems_capture_control_id + * + * DESCRIPTION: + * + * This function returns the control id. + */ +static inline rtems_id +rtems_capture_control_id (rtems_capture_control_t* control) +{ + return control->id; +} + +/* + * rtems_capture_control_name + * + * DESCRIPTION: + * + * This function returns the control name. + */ +static inline rtems_name +rtems_capture_control_name (rtems_capture_control_t* control) +{ + return control->name; +} + +/* + * rtems_capture_control_flags + * + * DESCRIPTION: + * + * This function returns the control flags. + */ +static inline rtems_unsigned32 +rtems_capture_control_flags (rtems_capture_control_t* control) +{ + return control->flags; +} + +/* + * rtems_capture_control_from_name + * + * DESCRIPTION: + * + * This function returns the control from task name. + */ +static inline rtems_name +rtems_capture_control_from_name (rtems_capture_control_t* control, int from) +{ + if (from < RTEMS_CAPTURE_TRIGGER_TASKS) + return control->from[from]; + return control->from[0]; +} + +/* + * rtems_capture_control_from_id + * + * DESCRIPTION: + * + * This function returns the control from task id. + */ +static inline rtems_id +rtems_capture_control_from_id (rtems_capture_control_t* control, int from) +{ + if (from < RTEMS_CAPTURE_TRIGGER_TASKS) + return control->from_id[from]; + return control->from_id[0]; +} + +/* + * rtems_capture_control_count + * + * DESCRIPTION: + * + * This function returns the number of controls the capture + * engine has. + */ +static inline rtems_unsigned32 +rtems_capture_control_count () +{ + rtems_capture_control_t* control = rtems_capture_get_control_list (); + rtems_unsigned32 count = 0; + + while (control) + { + count++; + control = rtems_capture_next_control (control); + } + + return count; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/libmisc/configure.ac b/c/src/libmisc/configure.ac index 8ffcc748cc..ee93af7192 100644 --- a/c/src/libmisc/configure.ac +++ b/c/src/libmisc/configure.ac @@ -43,6 +43,7 @@ monitor/Makefile rtmonuse/Makefile rootfs/Makefile stackchk/Makefile +capture/Makefile untar/Makefile mw-fb/Makefile wrapup/Makefile diff --git a/c/src/libmisc/wrapup/Makefile.am b/c/src/libmisc/wrapup/Makefile.am index c0313b140c..c0fe6d5363 100644 --- a/c/src/libmisc/wrapup/Makefile.am +++ b/c/src/libmisc/wrapup/Makefile.am @@ -19,7 +19,7 @@ TMP_LIBS = ../monitor/$(ARCH)/libmonitor-tmp.a \ ../cpuuse/$(ARCH)/libcpuuse-tmp.a ../rtmonuse/$(ARCH)/librtmonuse-tmp.a \ ../shell/$(ARCH)/libshell-tmp.a ../dumpbuf/$(ARCH)/libdumpbuf-tmp.a \ ../devnull/$(ARCH)/libdevnull-tmp.a ../dummy/$(ARCH)/libdummy-tmp.a \ - ../mw-fb/$(ARCH)/libmw-fb-tmp.a $(NETLIBS) + ../mw-fb/$(ARCH)/libmw-fb-tmp.a ../capture/$(ARCH)/libcapture-tmp.a $(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a: $(LIB) $(INSTALL_DATA) $< $@ -- cgit v1.2.3