diff options
Diffstat (limited to 'cpukit/libmisc')
63 files changed, 14596 insertions, 0 deletions
diff --git a/cpukit/libmisc/.cvsignore b/cpukit/libmisc/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/cpukit/libmisc/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/cpukit/libmisc/Makefile.am b/cpukit/libmisc/Makefile.am new file mode 100644 index 0000000000..1d1cd18209 --- /dev/null +++ b/cpukit/libmisc/Makefile.am @@ -0,0 +1,331 @@ +## +## $Id$ +## + +include $(top_srcdir)/automake/compile.am + +EXTRA_DIST = README + +AM_CPPFLAGS += -I$(top_builddir) + +include_rtemsdir = $(includedir)/rtems +include_rtems_HEADERS = +EXTRA_LIBRARIES = +TMP_LIBS = +CLEANFILES = + +## capture +EXTRA_DIST += capture/README +include_rtems_HEADERS += capture/capture.h capture/capture-cli.h + +EXTRA_LIBRARIES += libcapture.a +CLEANFILES += libcapture.a +libcapture_a_SOURCES = capture/capture.c capture/capture-cli.c +libcapture_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libcapture_g.a +CLEANFILES += libcapture_g.a +libcapture_g_a_SOURCES = $(libcapture_a_SOURCES) +libcapture_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libcapture$(LIB_VARIANT).a + +## cpuuse +EXTRA_DIST += cpuuse/README +include_rtems_HEADERS += cpuuse/cpuuse.h + +EXTRA_LIBRARIES += libcpuuse.a +CLEANFILES += libcpuuse.a +libcpuuse_a_SOURCES = cpuuse/cpuuse.c +libcpuuse_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libcpuuse_g.a +CLEANFILES += libcpuuse_g.a +libcpuuse_g_a_SOURCES = $(libcpuuse_a_SOURCES) +libcpuuse_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libcpuuse$(LIB_VARIANT).a + +## devnull +include_rtems_HEADERS += devnull/devnull.h + +EXTRA_LIBRARIES += libdevnull.a +CLEANFILES += libdevnull.a +libdevnull_a_SOURCES = devnull/devnull.c +libdevnull_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libdevnull_g.a +CLEANFILES += libdevnull_g.a +libdevnull_g_a_SOURCES = $(libdevnull_a_SOURCES) +libdevnull_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libdevnull$(LIB_VARIANT).a + +## dummy +EXTRA_DIST += dummy/README + +EXTRA_LIBRARIES += libdummy.a +CLEANFILES += libdummy.a +libdummy_a_SOURCES = dummy/dummy.c +libdummy_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libdummy_g.a +CLEANFILES += libdummy_g.a +libdummy_g_a_SOURCES = $(libdummy_a_SOURCES) +libdummy_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libdummy$(LIB_VARIANT).a + +## dumpbuf +include_rtems_HEADERS += dumpbuf/dumpbuf.h + +EXTRA_LIBRARIES += libdumpbuf.a +CLEANFILES += libdumpbuf.a +libdumpbuf_a_SOURCES = dumpbuf/dumpbuf.c +libdumpbuf_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libdumpbuf_g.a +CLEANFILES += libdumpbuf_g.a +libdumpbuf_g_a_SOURCES = $(libdumpbuf_a_SOURCES) +libdumpbuf_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libdumpbuf$(LIB_VARIANT).a + +## monitor + +include_rtems_HEADERS += monitor/monitor.h +libmonitor_a_SOURCES = monitor/mon-command.c monitor/mon-symbols.c \ + monitor/mon-prmisc.c monitor/mon-monitor.c monitor/mon-object.c \ + monitor/mon-server.c monitor/mon-task.c monitor/mon-queue.c \ + monitor/mon-driver.c monitor/mon-dname.c monitor/mon-itask.c \ + monitor/mon-extension.c monitor/mon-manager.c monitor/mon-config.c \ + monitor/symbols.h +if HAS_MP +libmonitor_a_SOURCES += monitor/mon-mpci.c +endif + +EXTRA_LIBRARIES += libmonitor.a +CLEANFILES += libmonitor.a +libmonitor_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libmonitor_g.a +CLEANFILES += libmonitor_g.a +libmonitor_g_a_SOURCES = $(libmonitor_a_SOURCES) +libmonitor_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +EXTRA_DIST += monitor/README + +TMP_LIBS += libmonitor$(LIB_VARIANT).a + +## mw-fb +include_rtems_HEADERS += mw-fb/mw_fb.h mw-fb/mw_uid.h + +EXTRA_LIBRARIES += libmw-fb.a +CLEANFILES += libmw-fb.a +libmw_fb_a_SOURCES = mw-fb/mw_fb.c mw-fb/mw_uid.c +libmw_fb_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libmw-fb_g.a +CLEANFILES += libmw-fb_g.a +libmw_fb_g_a_SOURCES = $(libmw_fb_a_SOURCES) +libmw_fb_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libmw-fb$(LIB_VARIANT).a + +## shell + +if LIBSHELL +include_rtems_HEADERS += shell/shell.h + +EXTRA_LIBRARIES += libshell.a +CLEANFILES += libshell.a +libshell_a_SOURCES = shell/cmds.c shell/shell.c +libshell_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libshell_g.a +CLEANFILES += libshell_g.a +libshell_g_a_SOURCES = $(libshell_a_SOURCES) +libshell_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libshell$(LIB_VARIANT).a +endif + +EXTRA_DIST += shell/README + +## rtmonuse +include_rtems_HEADERS += rtmonuse/rtmonuse.h + +EXTRA_LIBRARIES += librtmonuse.a +CLEANFILES += librtmonuse.a +librtmonuse_a_SOURCES = rtmonuse/rtmonuse.c +librtmonuse_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += librtmonuse_g.a +CLEANFILES += librtmonuse_g.a +librtmonuse_g_a_SOURCES = $(librtmonuse_a_SOURCES) +librtmonuse_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += librtmonuse$(LIB_VARIANT).a + +## serdbg + +if LIBSERDBG +include_rtems_HEADERS += serdbg/serdbgcnf.h serdbg/serdbg.h \ + serdbg/termios_printk_cnf.h serdbg/termios_printk.h + +EXTRA_LIBRARIES += libserdbg.a +CLEANFILES += libserdbg.a +libserdbg_a_SOURCES = serdbg/serdbg.c serdbg/serdbgio.c \ + serdbg/termios_printk.c +libserdbg_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libserdbg_g.a +CLEANFILES += libserdbg_g.a +libserdbg_g_a_SOURCES = $(libserdbg_a_SOURCES) +libserdbg_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libserdbg$(LIB_VARIANT).a +endif + +EXTRA_DIST += serdbg/README + +## stackchk + +include_rtems_HEADERS += stackchk/stackchk.h + +EXTRA_LIBRARIES += libstackchk.a +CLEANFILES += libstackchk.a +libstackchk_a_SOURCES = stackchk/check.c stackchk/internal.h +libstackchk_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libstackchk_g.a +CLEANFILES += libstackchk_g.a +libstackchk_g_a_SOURCES = $(libstackchk_a_SOURCES) +libstackchk_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libstackchk$(LIB_VARIANT).a +EXTRA_DIST += stackchk/README + +## libuntar + +include_rtems_HEADERS += untar/untar.h + +EXTRA_LIBRARIES += libuntar.a +CLEANFILES += libuntar.a +libuntar_a_SOURCES = untar/untar.c +libuntar_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libuntar_g.a +CLEANFILES += libuntar_g.a +libuntar_g_a_SOURCES = $(libuntar_a_SOURCES) +libuntar_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libuntar$(LIB_VARIANT).a + +EXTRA_DIST += untar/README + +## fsmount +include_rtems_HEADERS += fsmount/fsmount.h + +EXTRA_LIBRARIES += libfsmount.a +CLEANFILES += libfsmount.a +libfsmount_a_SOURCES = fsmount/fsmount.c +libfsmount_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_OPTIMIZE_V) + +EXTRA_LIBRARIES += libfsmount_g.a +CLEANFILES += libfsmount_g.a +libfsmount_g_a_SOURCES = $(libfsmount_a_SOURCES) +libfsmount_g_a_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_DEBUG_V) + +TMP_LIBS += libfsmount$(LIB_VARIANT).a + +EXTRA_DIST += fsmount/README + +## --- + +all-local: $(PREINSTALL_FILES) $(TMP_LIBS) + +PREINSTALL_DIRS = +PREINSTALL_FILES = + +$(PROJECT_INCLUDE)/rtems/$(dirstamp): + @$(mkdir_p) $(PROJECT_INCLUDE)/rtems + @: > $(PROJECT_INCLUDE)/rtems/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/rtems/$(dirstamp) + +$(PROJECT_INCLUDE)/rtems/capture.h: capture/capture.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/capture.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/capture.h + +$(PROJECT_INCLUDE)/rtems/capture-cli.h: capture/capture-cli.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/capture-cli.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/capture-cli.h + +$(PROJECT_INCLUDE)/rtems/cpuuse.h: cpuuse/cpuuse.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/cpuuse.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/cpuuse.h + +$(PROJECT_INCLUDE)/rtems/devnull.h: devnull/devnull.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/devnull.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/devnull.h + +$(PROJECT_INCLUDE)/rtems/dumpbuf.h: dumpbuf/dumpbuf.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/dumpbuf.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/dumpbuf.h + +$(PROJECT_INCLUDE)/rtems/monitor.h: monitor/monitor.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/monitor.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/monitor.h + +$(PROJECT_INCLUDE)/rtems/mw_fb.h: mw-fb/mw_fb.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/mw_fb.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/mw_fb.h + +$(PROJECT_INCLUDE)/rtems/mw_uid.h: mw-fb/mw_uid.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/mw_uid.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/mw_uid.h + +if LIBSHELL +$(PROJECT_INCLUDE)/rtems/shell.h: shell/shell.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/shell.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/shell.h +endif + +$(PROJECT_INCLUDE)/rtems/rtmonuse.h: rtmonuse/rtmonuse.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/rtmonuse.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/rtmonuse.h + +if LIBSERDBG +$(PROJECT_INCLUDE)/rtems/serdbgcnf.h: serdbg/serdbgcnf.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/serdbgcnf.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/serdbgcnf.h + +$(PROJECT_INCLUDE)/rtems/serdbg.h: serdbg/serdbg.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/serdbg.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/serdbg.h + +$(PROJECT_INCLUDE)/rtems/termios_printk_cnf.h: serdbg/termios_printk_cnf.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/termios_printk_cnf.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/termios_printk_cnf.h + +$(PROJECT_INCLUDE)/rtems/termios_printk.h: serdbg/termios_printk.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/termios_printk.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/termios_printk.h +endif + +$(PROJECT_INCLUDE)/rtems/stackchk.h: stackchk/stackchk.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/stackchk.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/stackchk.h + +$(PROJECT_INCLUDE)/rtems/untar.h: untar/untar.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/untar.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/untar.h + +$(PROJECT_INCLUDE)/rtems/fsmount.h: fsmount/fsmount.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/fsmount.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/fsmount.h + +CLEANFILES += $(PREINSTALL_FILES) +DISTCLEANFILES = $(PREINSTALL_DIRS) + +include $(top_srcdir)/automake/local.am diff --git a/cpukit/libmisc/README b/cpukit/libmisc/README new file mode 100644 index 0000000000..bbde4feeae --- /dev/null +++ b/cpukit/libmisc/README @@ -0,0 +1,23 @@ +# +# $Id$ +# + +This directory contains for the "miscellaneous" library. Currently +this library contains a number of useful support libraries: + + + Task Stack Overflow Checker + + Workspace Consistency Checker + + Task Execution Time Monitor + + Period Statistics Monitor + + Debug Monitor + +The following ideas have been mentioned for items which could go +in this library, but this list is not all inclusive: + + + there are no outstanding suggestions. + +The intent of this library is to provide a home for useful utility routines +which are dependent upon RTEMS. + +--joel +16 Sept 97 diff --git a/cpukit/libmisc/capture/README b/cpukit/libmisc/capture/README new file mode 100644 index 0000000000..2ea76a4cfe --- /dev/null +++ b/cpukit/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 <rtems/monitor.h> + #include <rtems/capture-cli.h> + + 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/cpukit/libmisc/capture/capture-cli.c b/cpukit/libmisc/capture/capture-cli.c new file mode 100644 index 0000000000..ca5041d768 --- /dev/null +++ b/cpukit/libmisc/capture/capture-cli.c @@ -0,0 +1,1470 @@ +/* + ------------------------------------------------------------------------ + $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. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <rtems.h> +#include <rtems/capture-cli.h> +#include <rtems/monitor.h> + +#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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + uint32_t size = 0; + rtems_boolean enable = 0; + rtems_status_code sc; + int arg; + + if (argc <= 1) + { + fprintf(stdout,open_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + if (argv[arg][1] == 'i') + enable = 1; + else + fprintf(stdout,"warning: option -%c ignored\n", argv[arg][1]); + } + else + { + size = strtoul (argv[arg], 0, 0); + + if (size < 100) + { + fprintf(stdout,"error: size must be greater than or equal to 100\n"); + return; + } + } + } + + sc = rtems_capture_open (size, capture_timestamp); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: open failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"capture engine opened.\n"); + + if (!enable) + return; + + sc = rtems_capture_control (enable); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: open enable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + rtems_status_code sc; + + sc = rtems_capture_close (); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: close failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + rtems_status_code sc; + + sc = rtems_capture_control (1); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: enable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + rtems_status_code sc; + + sc = rtems_capture_control (0); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: disable failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + 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 (); + uint32_t ticks; + uint32_t 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; + + fprintf(stdout,"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); + + fprintf(stdout," "); + rtems_monitor_dump_id (rtems_capture_task_id (task)); + fprintf(stdout," "); + rtems_monitor_dump_name (rtems_capture_task_name (task)); + fprintf(stdout," "); + rtems_monitor_dump_priority (rtems_capture_task_start_priority (task)); + fprintf(stdout," "); + rtems_monitor_dump_priority (rtems_capture_task_real_priority (task)); + fprintf(stdout," "); + rtems_monitor_dump_priority (rtems_capture_task_curr_priority (task)); + fprintf(stdout," "); + rtems_monitor_dump_state (rtems_capture_task_state (task)); + fprintf(stdout," %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)) + fprintf(stdout,"--"); + else + fprintf(stdout,"%c%c", + rtems_capture_task_control (task) ? + (rtems_capture_task_control_flags (task) & RTEMS_CAPTURE_WATCH ? 'w' : '+') : '-', + rtems_capture_watch_global_on () ? 'g' : '-'); + fprintf(stdout," %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; + + fprintf(stdout,"\x1b[2J Press ENTER to exit.\n\n"); + fprintf(stdout," 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); + } + + fprintf(stdout,"\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]); + + fprintf(stdout,"\x1b[K"); + rtems_monitor_dump_id (rtems_capture_task_id (tasks[i])); + fprintf(stdout," "); + rtems_monitor_dump_name (rtems_capture_task_name (tasks[i])); + fprintf(stdout," "); + rtems_monitor_dump_priority (priority); + fprintf(stdout," "); + rtems_monitor_dump_priority (rtems_capture_task_curr_priority (tasks[i])); + fprintf(stdout," "); + k = rtems_monitor_dump_state (rtems_capture_task_state (tasks[i])); + fprintf(stdout,"%*c %3i.%03i%% ", 6 - k, ' ', task_load / 1000, task_load % 1000); + fprintf(stdout,"%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)) + fprintf(stdout,"--"); + else + fprintf(stdout,"%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' : '-'); + + fprintf(stdout," %qi\n", rtems_capture_task_time (tasks[i])); + } + + while (j) + { + fprintf(stdout,"\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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + 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) + { + fprintf(stdout,"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) + { + fprintf(stdout,"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) + { + fprintf(stdout,"error: cannot start helper thread: %s\n", rtems_status_text (sc)); + rtems_task_delete (id); + return; + } + + for (;;) + { + int 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); + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + 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 (); + + fprintf(stdout,"watch priority ceiling is %i\n", ceiling); + fprintf(stdout,"watch priority floor is %i\n", floor); + fprintf(stdout,"global watch is %s\n", + rtems_capture_watch_global_on () ? "enabled" : "disabled"); + fprintf(stdout,"total %d\n", rtems_capture_control_count ()); + + while (control) + { + int f; + int fshowed; + int lf; + + fprintf(stdout," "); + rtems_monitor_dump_id (rtems_capture_control_id (control)); + fprintf(stdout," "); + rtems_monitor_dump_name (rtems_capture_control_name (control)); + fprintf(stdout," %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)) + { + fprintf(stdout,"\n"); + lf = 0; + } + + /* + * FIXME: name test. + */ + if (rtems_capture_control_from_name (control, f)) + { + fprintf(stdout," %2i:", f); + rtems_monitor_dump_name (rtems_capture_control_from_name (control, f)); + fprintf(stdout,"/"); + rtems_monitor_dump_id (rtems_capture_control_from_id (control, f)); + fshowed++; + lf = 1; + } + } + + if (lf) + fprintf(stdout,"\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) +{ + uint32_t objclass; + int l; + int i; + + if (*valid_name && *valid_id) + { + fprintf(stdout,"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)) + *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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + 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) + { + fprintf(stdout,watch_add_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf(stdout,"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) + { + fprintf(stdout,"error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_add (name, id); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: watch add failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + 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) + { + fprintf(stdout,watch_del_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf(stdout,"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) + { + fprintf(stdout,"error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_del (name, id); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: watch delete failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + 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) + { + fprintf(stdout,watch_control_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf(stdout,"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) + { + fprintf(stdout,"error: no valid name or task id located\n"); + return; + } + + sc = rtems_capture_watch_ctrl (name, id, enable); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: watch control failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + rtems_status_code sc; + int arg; + rtems_boolean enable = 0; + + if (argc <= 1) + { + fprintf(stdout,watch_global_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf(stdout,"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) + { + fprintf(stdout,"error: global watch failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + rtems_status_code sc; + int arg; + rtems_task_priority priority = 0; + + if (argc <= 1) + { + fprintf(stdout,watch_ceiling_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf(stdout,"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) + { + fprintf(stdout,"error: watch ceiling failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + rtems_status_code sc; + int arg; + rtems_task_priority priority = 0; + + if (argc <= 1) + { + fprintf(stdout,watch_floor_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf(stdout,"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) + { + fprintf(stdout,"error: watch floor failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + 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) + { + fprintf(stdout,trigger_set_usage); + return; + } + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] == '-') + { + fprintf(stdout,"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 + { + fprintf(stdout,"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) + fprintf(stdout,"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) + fprintf(stdout,"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) + fprintf(stdout,"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) + fprintf(stdout,"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) + fprintf(stdout,"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) + { + fprintf(stdout,"error: a from trigger need a to name or id\n"); + return; + } + + if ((trigger == rtems_capture_from_any) && !to_valid_name && !to_valid_id) + { + fprintf(stdout,"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))) + { + fprintf(stdout,"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) + { + fprintf(stdout,"error: setting the trigger failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + rtems_status_code sc; + rtems_boolean csv = 0; + static int dump_total = 32; + int total; + int count; + uint32_t 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) + { + fprintf(stdout,"error: option -r requires number\n"); + return; + } + + l = strlen (argv[arg]); + + for (i = 0; i < l; i++) + if (!isdigit (argv[arg][i])) + { + fprintf(stdout,"error: option -r requires number and currently it is not\n"); + return; + } + + dump_total = strtoul (argv[arg], 0, 0); + } + else + fprintf(stdout,"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) + { + fprintf(stdout,"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) + fprintf(stdout,"%08x,%03d,%03d,%04x,%d,%d\n", + (uint32_t ) 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; + uint32_t 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) + { + fprintf(stdout,"%9li.%06li ", (unsigned long) (t / 1000000), + (unsigned long) (t % 1000000)); + rtems_monitor_dump_id (rtems_capture_task_id (rec->task)); + fprintf(stdout," "); + rtems_monitor_dump_name (rtems_capture_task_name (rec->task)); + fprintf(stdout," %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_monitor_command_arg_t *command_arg, + boolean verbose ) +{ + 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 + fprintf(stdout,"warning: option -%c ignored\n", argv[arg][1]); + } + } + + sc = rtems_capture_flush (prime); + + if (sc != RTEMS_SUCCESSFUL) + { + fprintf(stdout,"error: flush failed: %s\n", rtems_status_text (sc)); + return; + } + + fprintf(stdout,"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, + rtems_capture_cli_open, + { 0 }, + 0 + }, + { + "cclose", + "usage: cclose\n", + 0, + rtems_capture_cli_close, + { 0 }, + 0 + }, + { + "cenable", + "usage: cenable\n", + 0, + rtems_capture_cli_enable, + { 0 }, + 0 + }, + { + "cdisable", + "usage: cdisable\n", + 0, + rtems_capture_cli_disable, + { 0 }, + 0 + }, + { + "ctlist", + "usage: ctlist \n", + 0, + rtems_capture_cli_task_list, + { 0 }, + 0 + }, + { + "ctload", + "usage: ctload \n", + 0, + rtems_capture_cli_task_load, + { 0 }, + 0 + }, + { + "cwlist", + "usage: cwlist\n", + 0, + rtems_capture_cli_watch_list, + { 0 }, + 0 + }, + { + "cwadd", + "usage: cwadd [task name] [id]\n", + 0, + rtems_capture_cli_watch_add, + { 0 }, + 0 + }, + { + "cwdel", + "usage: cwdel [task name] [id]\n", + 0, + rtems_capture_cli_watch_del, + { 0 }, + 0 + }, + { + "cwctl", + "usage: cwctl [task name] [id] on/off\n", + 0, + rtems_capture_cli_watch_control, + { 0 }, + 0 + }, + { + "cwglob", + "usage: cwglob on/off\n", + 0, + rtems_capture_cli_watch_global, + { 0 }, + 0 + }, + { + "cwceil", + "usage: cwceil priority\n", + 0, + rtems_capture_cli_watch_ceiling, + { 0 }, + 0 + }, + { + "cwfloor", + "usage: cwfloor priority\n", + 0, + rtems_capture_cli_watch_floor, + { 0 }, + 0 + }, + { + "ctrace", + "usage: ctrace [-c] [-r records]\n", + 0, + rtems_capture_cli_trace_records, + { 0 }, + 0 + }, + { + "ctrig", + "usage: ctrig type [from name] [from id] [to name] [to id]\n", + 0, + rtems_capture_cli_trigger_set, + { 0 }, + 0 + }, + { + "cflush", + "usage: cflush [-n]\n", + 0, + 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/cpukit/libmisc/capture/capture-cli.h b/cpukit/libmisc/capture/capture-cli.h new file mode 100644 index 0000000000..519cad0d56 --- /dev/null +++ b/cpukit/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.h> + +/* + * 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/cpukit/libmisc/capture/capture.c b/cpukit/libmisc/capture/capture.c new file mode 100644 index 0000000000..c8fd751918 --- /dev/null +++ b/cpukit/libmisc/capture/capture.c @@ -0,0 +1,1569 @@ +/* + ------------------------------------------------------------------------ + $Id$ + ------------------------------------------------------------------------ + + Copyright Objective Design Systems Pty Ltd, 2002 + All rights reserved Objective Design Systems Pty Ltd, 2002 + Chris Johns (ccj@acm.org) + + COPYRIGHT (c) 1989-1998. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution. + + This software with is provided ``as is'' and with NO WARRANTY. + + ------------------------------------------------------------------------ + + RTEMS Performance Monitoring and Measurement Framework. + + This is the Capture Engine component. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "capture.h" +#include <rtems/score/states.inl> +#include <rtems/score/wkspace.h> +#include <rtems/score/wkspace.inl> + +/* + * These events are always recorded and are not part of the + * watch filters. + */ +#define RTEMS_CAPTURE_RECORD_EVENTS (RTEMS_CAPTURE_CREATED_BY_EVENT | \ + RTEMS_CAPTURE_CREATED_EVENT | \ + RTEMS_CAPTURE_STARTED_BY_EVENT | \ + RTEMS_CAPTURE_STARTED_EVENT | \ + RTEMS_CAPTURE_RESTARTED_BY_EVENT | \ + RTEMS_CAPTURE_RESTARTED_EVENT | \ + RTEMS_CAPTURE_DELETED_BY_EVENT | \ + RTEMS_CAPTURE_DELETED_EVENT | \ + RTEMS_CAPTURE_BEGIN_EVENT | \ + RTEMS_CAPTURE_EXITTED_EVENT) + +/* + * Global capture flags. + */ +#define RTEMS_CAPTURE_ON (1 << 0) +#define RTEMS_CAPTURE_NO_MEMORY (1 << 1) +#define RTEMS_CAPTURE_OVERFLOW (1 << 2) +#define RTEMS_CAPTURE_TRIGGERED (1 << 3) +#define RTEMS_CAPTURE_READER_ACTIVE (1 << 4) +#define RTEMS_CAPTURE_READER_WAITING (1 << 5) +#define RTEMS_CAPTURE_GLOBAL_WATCH (1 << 6) + +/* + * RTEMS Capture Data. + */ +static rtems_capture_record_t* capture_records; +static uint32_t capture_size; +static uint32_t capture_count; +static rtems_capture_record_t* capture_in; +static uint32_t capture_out; +static uint32_t capture_flags; +static rtems_capture_task_t* capture_tasks; +static rtems_capture_control_t* capture_controls; +static int capture_extension_index; +static rtems_id capture_id; +static rtems_capture_timestamp capture_timestamp; +static rtems_task_priority capture_ceiling; +static rtems_task_priority capture_floor; +static uint32_t capture_tick_period; +static rtems_id capture_reader; + +/* + * RTEMS Event text. + */ +static const char* capture_event_text[] = +{ + "CREATED_BY", + "CREATED", + "STARTED_BY", + "STARTED", + "RESTARTED_BY", + "RESTARTED", + "DELETED_BY", + "DELETED", + "BEGIN", + "EXITTED", + "SWITCHED_OUT", + "SWITCHED_IN", + "TIMESTAMP" +}; + +/* + * rtems_capture_get_time + * + * DESCRIPTION: + * + * This function returns the current time. If a handler is provided + * by the user get the time from that. + */ +static inline void rtems_capture_get_time (uint32_t * ticks, + uint32_t * tick_offset) +{ + if (capture_timestamp) + capture_timestamp (ticks, tick_offset); + else + { + *ticks = _Watchdog_Ticks_since_boot; + *tick_offset = 0; + } +} + +/* + * rtems_capture_match_names + * + * DESCRIPTION: + * + * This function compares rtems_names. It protects the + * capture engine from a change to the way names are supported + * in RTEMS. + * + */ +static inline rtems_boolean +rtems_capture_match_names (rtems_name lhs, rtems_name rhs) +{ + return lhs == rhs; +} + +/* + * rtems_capture_dup_name + * + * DESCRIPTION: + * + * This function duplicates an rtems_names. It protects the + * cpature engine from a change to the way names are supported + * in RTEMS. + * + */ +static inline void +rtems_capture_dup_name (rtems_name* dst, rtems_name src) +{ + *dst = src; +} + +/* + * rtems_capture_name_in_group + * + * DESCRIPTION: + * + * This function sees if a name is in a group of names. + * + */ +static inline rtems_boolean +rtems_capture_name_in_group (rtems_name task, rtems_name* tasks) +{ + if (tasks) + { + int i; + for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++) + if (rtems_capture_match_names (task, *tasks++)) + return 1; + } + return 0; +} + +/* + * rtems_capture_match_name_id + * + * DESCRIPTION: + * + * This function matches a name and/or id. + */ +static inline rtems_boolean +rtems_capture_match_name_id (rtems_name lhs_name, + rtems_id lhs_id, + rtems_name rhs_name, + rtems_id rhs_id) +{ + /* + * The left hand side name or id could be 0 which means a wildcard. + */ + if ((lhs_name == 0) && (lhs_id == rhs_id)) + return 1; + else if ((lhs_id == 0) || (lhs_id == rhs_id)) + { + if (rtems_capture_match_names (lhs_name, rhs_name)) + return 1; + } + return 0; +} + +/* + * rtems_capture_init_stack_usage + * + * DESCRIPTION: + * + * This function setups a stack so its usage can be monitored. + */ +static inline void +rtems_capture_init_stack_usage (rtems_capture_task_t* task) +{ + if (task->tcb) + { + uint32_t * s; + uint32_t i; + + task->stack_size = task->tcb->Start.Initial_stack.size; + task->stack_clean = task->stack_size; + + s = task->tcb->Start.Initial_stack.area; + + for (i = 0; i < (task->stack_size - 128); i += 4) + *(s++) = 0xdeaddead; + } +} + +/* + * rtems_capture_find_control + * + * DESCRIPTION: + * + * This function searches for a trigger given a name. + * + */ +static inline rtems_capture_control_t* +rtems_capture_find_control (rtems_name name, rtems_id id) +{ + rtems_capture_control_t* control; + + for (control = capture_controls; control != NULL; control = control->next) + if (rtems_capture_match_name_id (name, id, control->name, control->id)) + break; + return control; +} + +/* + * rtems_capture_create_control + * + * DESCRIPTION: + * + * This function creates a capture control for the capture engine. + * + */ +static inline rtems_capture_control_t* +rtems_capture_create_control (rtems_name name, rtems_id id) +{ + rtems_interrupt_level level; + rtems_capture_control_t* control; + rtems_capture_task_t* task; + + if ((name == 0) && (id == 0)) + return NULL; + + control = rtems_capture_find_control (name, id); + + if (control == NULL) + { + control = _Workspace_Allocate (sizeof (rtems_capture_control_t)); + + if (control == NULL) + { + capture_flags |= RTEMS_CAPTURE_NO_MEMORY; + return NULL; + } + + control->name = name; + control->id = id; + control->flags = 0; + + memset (control->from, 0, sizeof (control->from)); + memset (control->from_id, 0, sizeof (control->from_id)); + + rtems_interrupt_disable (level); + + control->next = capture_controls; + capture_controls = control; + + /* + * We need to scan the task list as set the control to the + * tasks. + */ + for (task = capture_tasks; task != NULL; task = task->next) + if (rtems_capture_match_name_id (name, id, task->name, task->id)) + task->control = control; + + rtems_interrupt_enable (level); + } + + return control; +} + +/* + * rtems_capture_create_capture_task + * + * DESCRIPTION: + * + * This function create the task control. + * + */ +static inline rtems_capture_task_t* +rtems_capture_create_capture_task (rtems_tcb* new_task) +{ + rtems_interrupt_level level; + rtems_capture_task_t* task; + rtems_capture_control_t* control; + + task = _Workspace_Allocate (sizeof (rtems_capture_task_t)); + + if (task == NULL) + { + capture_flags |= RTEMS_CAPTURE_NO_MEMORY; + return NULL; + } + + rtems_capture_dup_name (&task->name, ((rtems_name) new_task->Object.name)); + + task->id = new_task->Object.id; + task->flags = 0; + task->in = 0; + task->out = 0; + task->tcb = new_task; + task->ticks = 0; + task->tick_offset = 0; + task->ticks_in = 0; + task->tick_offset_in = 0; + task->control = 0; + task->last_ticks = 0; + task->last_tick_offset = 0; + + task->tcb->extensions[capture_extension_index] = task; + + task->start_priority = new_task->Start.initial_priority; + task->stack_size = new_task->Start.Initial_stack.size; + task->stack_clean = task->stack_size; + + rtems_interrupt_disable (level); + + task->next = capture_tasks; + capture_tasks = task; + + rtems_interrupt_enable (level); + + /* + * We need to scan the default control list to initialise + * this control. + */ + + for (control = capture_controls; control != NULL; control = control->next) + if (rtems_capture_match_name_id (control->name, control->id, + task->name, task->id)) + task->control = control; + + return task; +} + +/* + * rtems_capture_record + * + * DESCRIPTION: + * + * This function records a capture record into the capture buffer. + * + */ +static inline void +rtems_capture_record (rtems_capture_task_t* task, + uint32_t events) +{ + /* + * Check the watch state if we have a task control, and + * the task's real priority is lower or equal to the ceiling. + */ + if (task) + { + rtems_capture_control_t* control; + + control = task->control; + + /* + * Capure the record if we have an event that is always + * captured, or the task's real priority is greater than the + * watch ceiling, and the global watch or task watch is enabled. + */ + + if ((events & RTEMS_CAPTURE_RECORD_EVENTS) || + ((task->tcb->real_priority >= capture_ceiling) && + (task->tcb->real_priority <= capture_floor) && + ((capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH) || + (control && (control->flags & RTEMS_CAPTURE_WATCH))))) + { + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (capture_count < capture_size) + { + capture_count++; + capture_in->task = task; + capture_in->events = (events | + (task->tcb->real_priority) | + (task->tcb->current_priority << 8)); + + if ((events & RTEMS_CAPTURE_RECORD_EVENTS) == 0) + task->flags |= RTEMS_CAPTURE_TRACED; + + rtems_capture_get_time (&capture_in->ticks, &capture_in->tick_offset); + + if (capture_in == &capture_records[capture_size - 1]) + capture_in = capture_records; + else + capture_in++; + } + else + capture_flags |= RTEMS_CAPTURE_OVERFLOW; + rtems_interrupt_enable (level); + } + } +} + +/* + * rtems_capture_create_task + * + * DESCRIPTION: + * + * This function is called when a task is created. + * + */ +static rtems_boolean +rtems_capture_create_task (rtems_tcb* current_task, + rtems_tcb* new_task) +{ + rtems_capture_task_t* ct; + rtems_capture_task_t* nt; + + ct = current_task->extensions[capture_extension_index]; + + /* + * The task ponters may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + + /* + * Create the new task's capture control block. + */ + nt = rtems_capture_create_capture_task (new_task); + + /* + * If we are logging then record this fact. + */ + rtems_capture_record (ct, RTEMS_CAPTURE_CREATED_BY_EVENT); + rtems_capture_record (nt, RTEMS_CAPTURE_CREATED_EVENT); + + return 1 == 1; +} + +/* + * rtems_capture_start_task + * + * DESCRIPTION: + * + * This function is called when a task is started. + * + */ +static rtems_extension +rtems_capture_start_task (rtems_tcb* current_task, + rtems_tcb* started_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* ct; + rtems_capture_task_t* st; + + ct = current_task->extensions[capture_extension_index]; + st = started_task->extensions[capture_extension_index]; + + /* + * The task ponters may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + + if (st == NULL) + st = rtems_capture_create_capture_task (started_task); + + rtems_capture_record (ct, RTEMS_CAPTURE_STARTED_BY_EVENT); + rtems_capture_record (st, RTEMS_CAPTURE_STARTED_EVENT); + + rtems_capture_init_stack_usage (st); +} + +/* + * rtems_capture_restart_task + * + * DESCRIPTION: + * + * This function is called when a task is restarted. + * + */ +static rtems_extension +rtems_capture_restart_task (rtems_tcb* current_task, + rtems_tcb* restarted_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* ct; + rtems_capture_task_t* rt; + + ct = current_task->extensions[capture_extension_index]; + rt = restarted_task->extensions[capture_extension_index]; + + /* + * The task ponters may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + + if (rt == NULL) + rt = rtems_capture_create_capture_task (restarted_task); + + rtems_capture_record (ct, RTEMS_CAPTURE_RESTARTED_BY_EVENT); + rtems_capture_record (rt, RTEMS_CAPTURE_RESTARTED_EVENT); + + rtems_capture_task_stack_usage (rt); + rtems_capture_init_stack_usage (rt); +} + +/* + * rtems_capture_delete_task + * + * DESCRIPTION: + * + * This function is called when a task is deleted. + * + */ +static rtems_extension +rtems_capture_delete_task (rtems_tcb* current_task, + rtems_tcb* deleted_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* ct; + rtems_capture_task_t* dt; + + /* + * The task ponters may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + ct = current_task->extensions[capture_extension_index]; + dt = deleted_task->extensions[capture_extension_index]; + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + + if (dt == NULL) + dt = rtems_capture_create_capture_task (deleted_task); + + rtems_capture_record (ct, RTEMS_CAPTURE_DELETED_BY_EVENT); + rtems_capture_record (dt, RTEMS_CAPTURE_DELETED_EVENT); + + rtems_capture_task_stack_usage (dt); + + /* + * This task's tcb will be invalid. + */ + dt->tcb = 0; +} + +/* + * rtems_capture_begin_task + * + * DESCRIPTION: + * + * This function is called when a task is begun. + * + */ +static rtems_extension +rtems_capture_begin_task (rtems_tcb* begin_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* bt; + + bt = begin_task->extensions[capture_extension_index]; + + /* + * The task ponters may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (bt == NULL) + bt = rtems_capture_create_capture_task (begin_task); + + rtems_capture_record (bt, RTEMS_CAPTURE_BEGIN_EVENT); +} + +/* + * rtems_capture_exitted_task + * + * DESCRIPTION: + * + * This function is called when a task is exitted. That is + * returned rather than was deleted. + * + */ +static rtems_extension +rtems_capture_exitted_task (rtems_tcb* exitted_task) +{ + /* + * Get the capture task control block so we can trace this + * event. + */ + rtems_capture_task_t* et; + + et = exitted_task->extensions[capture_extension_index]; + + /* + * The task ponters may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + + if (et == NULL) + et = rtems_capture_create_capture_task (exitted_task); + + rtems_capture_record (et, RTEMS_CAPTURE_EXITTED_EVENT); + + rtems_capture_task_stack_usage (et); +} + +/* + * rtems_capture_switch_task + * + * DESCRIPTION: + * + * This function is called when a context is switched. + * + */ +static rtems_extension +rtems_capture_switch_task (rtems_tcb* current_task, + rtems_tcb* heir_task) +{ + /* + * Only perform context switch trace processing if tracing is + * enabled. + */ + if (capture_flags & RTEMS_CAPTURE_ON) + { + uint32_t ticks; + uint32_t tick_offset; + + /* + * Get the cpature task control block so we can update the + * reference anbd perform any watch or trigger functions. + * The task ponters may not be known as the task may have + * been created before the capture engine was open. Add them. + */ + rtems_capture_task_t* ct; + rtems_capture_task_t* ht; + + if (_States_Is_transient (current_task->current_state)) + { + rtems_id ct_id = current_task->Object.id; + + for (ct = capture_tasks; ct; ct = ct->next) + if (ct->id == ct_id) + break; + } + else + { + ct = current_task->extensions[capture_extension_index]; + + if (ct == NULL) + ct = rtems_capture_create_capture_task (current_task); + } + + ht = heir_task->extensions[capture_extension_index]; + + if (ht == NULL) + ht = rtems_capture_create_capture_task (heir_task); + + /* + * Update the execution time. Assume the tick will not overflow + * for now. This may need to change. + */ + rtems_capture_get_time (&ticks, &tick_offset); + + /* + * We could end up with null pointers for both the current task + * and the heir task. + */ + + if (ht) + { + ht->in++; + ht->ticks_in = ticks; + ht->tick_offset_in = tick_offset; + } + + if (ct) + { + ct->out++; + ct->ticks += ticks - ct->ticks_in; + + if (capture_timestamp) + { + tick_offset += capture_tick_period - ct->tick_offset_in; + + if (tick_offset < capture_tick_period) + ct->tick_offset = tick_offset; + else + { + ct->ticks++; + ct->tick_offset = tick_offset - capture_tick_period; + } + } + else + { + ct->tick_offset += 100; + } + } + + /* + * If we have not triggered then see if this is a trigger condition. + */ + if (!(capture_flags & RTEMS_CAPTURE_TRIGGERED)) + { + rtems_capture_control_t* cc = NULL; + rtems_capture_control_t* hc = NULL; + + if (ct) + { + cc = ct->control; + + /* + * Check the current task for a TO_ANY trigger. + */ + if (cc && (cc->flags & RTEMS_CAPTURE_TO_ANY)) + { + capture_flags |= RTEMS_CAPTURE_TRIGGERED; + goto triggered; + } + } + + if (ht) + { + hc = ht->control; + + /* + * Check the next task for a FROM_ANY. + */ + if (hc && (hc->flags & RTEMS_CAPTURE_FROM_ANY)) + { + capture_flags |= RTEMS_CAPTURE_TRIGGERED; + goto triggered; + } + } + + /* + * Check is the trigger is from the current task + * to the next task. + */ + if (cc && hc && (hc->flags & RTEMS_CAPTURE_FROM_TO)) + if (rtems_capture_name_in_group (cc->name, hc->from)) + { + capture_flags |= RTEMS_CAPTURE_TRIGGERED; + goto triggered; + } + } + else + { +triggered: + + rtems_capture_record (ct, RTEMS_CAPTURE_SWITCHED_OUT_EVENT); + rtems_capture_record (ht, RTEMS_CAPTURE_SWITCHED_IN_EVENT); + } + } +} + +/* + * rtems_capture_open + * + * DESCRIPTION: + * + * This function initialises the realtime capture engine allocating the trace + * buffer. It is assumed we have a working heap at stage of initialisation. + * + */ +rtems_status_code +rtems_capture_open (uint32_t size, rtems_capture_timestamp timestamp) +{ + rtems_extensions_table capture_extensions; + rtems_name name; + rtems_status_code sc; + + /* + * See if the capture engine is already open. + */ + + if (capture_records) + return RTEMS_RESOURCE_IN_USE; + + capture_records = malloc (size * sizeof (rtems_capture_record_t)); + + if (capture_records == NULL) + return RTEMS_NO_MEMORY; + + capture_size = size; + capture_count = 0; + capture_in = capture_records; + capture_out = 0; + capture_flags = 0; + capture_tasks = NULL; + capture_ceiling = 0; + capture_floor = 255; + + /* + * Create the extension table. This is copied so we + * can create it as a local. + */ + capture_extensions.thread_create = rtems_capture_create_task; + capture_extensions.thread_start = rtems_capture_start_task; + capture_extensions.thread_restart = rtems_capture_restart_task; + capture_extensions.thread_delete = rtems_capture_delete_task; + capture_extensions.thread_switch = rtems_capture_switch_task; + capture_extensions.thread_begin = rtems_capture_begin_task; + capture_extensions.thread_exitted = rtems_capture_exitted_task; + capture_extensions.fatal = NULL; + + /* + * Get the tick period from the BSP Configuration Table. + */ + capture_tick_period = _Configuration_Table->microseconds_per_tick; + + /* + * Register the user extension handlers for the CAPture Engine. + */ + name = rtems_build_name ('C', 'A', 'P', 'E'); + sc = rtems_extension_create (name, &capture_extensions, &capture_id); + + if (sc != RTEMS_SUCCESSFUL) + { + capture_id = 0; + free (capture_records); + capture_records = NULL; + } + else + { + capture_extension_index = rtems_get_index (capture_id);; + } + + /* + * Iterate over the list of existing tasks. + */ + + return sc; +} + +/* + * rtems_capture_close + * + * DESCRIPTION: + * + * This function shutdowns the capture engine and release any claimed + * resources. + */ +rtems_status_code +rtems_capture_close () +{ + rtems_interrupt_level level; + rtems_capture_task_t* task; + rtems_capture_control_t* control; + rtems_capture_record_t* records; + rtems_status_code sc; + + rtems_interrupt_disable (level); + + if (!capture_records) + { + rtems_interrupt_enable (level); + return RTEMS_SUCCESSFUL; + } + + capture_flags &= ~RTEMS_CAPTURE_ON; + + records = capture_records; + capture_records = NULL; + + rtems_interrupt_enable (level); + + /* + * Delete the extension first. This means we are now able to + * release the resources we have without them being used. + */ + + sc = rtems_extension_delete (capture_id); + + if (sc != RTEMS_SUCCESSFUL) + return sc; + + task = capture_tasks; + + while (task) + { + rtems_capture_task_t* delete = task; + task = task->next; + _Workspace_Free (delete); + } + + capture_tasks = NULL; + + control = capture_controls; + + while (control) + { + rtems_capture_control_t* delete = control; + control = control->next; + _Workspace_Free (delete); + } + + capture_controls = NULL; + + if (capture_records) + { + free (capture_records); + capture_records = NULL; + } + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_control + * + * DESCRIPTION: + * + * This function allows control of tracing at a global level. + */ +rtems_status_code +rtems_capture_control (rtems_boolean enable) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (!capture_records) + { + rtems_interrupt_enable (level); + return RTEMS_UNSATISFIED; + } + + if (enable) + capture_flags |= RTEMS_CAPTURE_ON; + else + capture_flags &= ~RTEMS_CAPTURE_ON; + + rtems_interrupt_enable (level); + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_flush + * + * DESCRIPTION: + * + * This function flushes the capture buffer. The prime parameter allows the + * capture engine to also be primed again. + */ +rtems_status_code +rtems_capture_flush (rtems_boolean prime) +{ + rtems_interrupt_level level; + rtems_capture_task_t* task; + + rtems_interrupt_disable (level); + + for (task = capture_tasks; task != NULL; task = task->next) + task->flags &= ~RTEMS_CAPTURE_TRACED; + + if (prime) + capture_flags &= ~(RTEMS_CAPTURE_TRIGGERED | RTEMS_CAPTURE_OVERFLOW); + else + capture_flags &= ~RTEMS_CAPTURE_OVERFLOW; + + capture_in = capture_records; + capture_out = 0; + + rtems_interrupt_enable (level); + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_add + * + * DESCRIPTION: + * + * This function defines a watch for a specific task given a name. A watch + * causes it to be traced either in or out of context. The watch can be + * optionally enabled or disabled with the set routine. It is disabled by + * default. + */ +rtems_status_code +rtems_capture_watch_add (rtems_name name, rtems_id id) +{ + rtems_capture_control_t* control; + + if ((name == 0) && (id == 0)) + return RTEMS_UNSATISFIED; + + control = rtems_capture_find_control (name, id); + + if (control && !id) + return RTEMS_TOO_MANY; + + if (!control) + control = rtems_capture_create_control (name, id); + + if (!control) + return RTEMS_NO_MEMORY; + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_del + * + * DESCRIPTION: + * + * This function removes a watch for a specific task given a name. The task + * description will still exist if referenced by a trace record in the trace + * buffer or a global watch is defined. + */ +rtems_status_code +rtems_capture_watch_del (rtems_name name, rtems_id id) +{ + rtems_interrupt_level level; + rtems_capture_control_t* control; + rtems_capture_control_t** prev_control; + rtems_capture_task_t* task; + rtems_boolean found = 0; + + /* + * Should this test be for wildcards ? + */ + + for (prev_control = &capture_controls, control = capture_controls; + control != NULL; ) + { + if (rtems_capture_match_name_id (name, id, control->name, control->id)) + { + rtems_interrupt_disable (level); + + for (task = capture_tasks; task != NULL; task = task->next) + if (task->control == control) + task->control = 0; + + *prev_control = control->next; + + rtems_interrupt_enable (level); + + _Workspace_Free (control); + + control = *prev_control; + + found = 1; + } + else + { + prev_control = &control->next; + control = control->next; + } + } + + if (found) + return RTEMS_SUCCESSFUL; + + return RTEMS_INVALID_NAME; +} + +/* + * rtems_capture_watch_set + * + * DESCRIPTION: + * + * This function allows control of a watch. The watch can be enabled or + * disabled. + */ +rtems_status_code +rtems_capture_watch_ctrl (rtems_name name, rtems_id id, rtems_boolean enable) +{ + rtems_interrupt_level level; + rtems_capture_control_t* control; + rtems_boolean found = 0; + + /* + * Find the control and then set the watch. It must exist before it can + * be controlled. + */ + for (control = capture_controls; control != NULL; control = control->next) + { + if (rtems_capture_match_name_id (name, id, control->name, control->id)) + { + rtems_interrupt_disable (level); + + if (enable) + control->flags |= RTEMS_CAPTURE_WATCH; + else + control->flags &= ~RTEMS_CAPTURE_WATCH; + + rtems_interrupt_enable (level); + + found = 1; + } + } + + if (found) + return RTEMS_SUCCESSFUL; + + return RTEMS_INVALID_NAME; +} + +/* + * rtems_capture_watch_global + * + * DESCRIPTION: + * + * This function allows control of a global watch. The watch can be enabled or + * disabled. A global watch configures all tasks below the ceiling and above + * the floor to be traced. + */ +rtems_status_code +rtems_capture_watch_global (rtems_boolean enable) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + /* + * We need to keep specific and global watches separate so + * a global enable/disable does not lose a specific watch. + */ + if (enable) + capture_flags |= RTEMS_CAPTURE_GLOBAL_WATCH; + else + capture_flags &= ~RTEMS_CAPTURE_GLOBAL_WATCH; + + rtems_interrupt_enable (level); + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_global_on + * + * DESCRIPTION: + * + * This function returns the global watch state. + */ +rtems_boolean +rtems_capture_watch_global_on () +{ + return capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH ? 1 : 0; +} + +/* + * rtems_capture_watch_ceiling + * + * DESCRIPTION: + * + * This function sets a watch ceiling. Tasks at or greating that the + * ceiling priority are not watched. This is a simple way to monitor + * an application and exclude system tasks running at a higher + * priority level. + */ +rtems_status_code +rtems_capture_watch_ceiling (rtems_task_priority ceiling) +{ + capture_ceiling = ceiling; + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_get_ceiling + * + * DESCRIPTION: + * + * This function gets the watch ceiling. + */ +rtems_task_priority +rtems_capture_watch_get_ceiling () +{ + return capture_ceiling; +} + +/* + * rtems_capture_watch_floor + * + * DESCRIPTION: + * + * This function sets a watch floor. Tasks at or less that the + * floor priority are not watched. This is a simple way to monitor + * an application and exclude system tasks running at a lower + * priority level. + */ +rtems_status_code +rtems_capture_watch_floor (rtems_task_priority floor) +{ + capture_floor = floor; + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_watch_get_floor + * + * DESCRIPTION: + * + * This function gets the watch floor. + */ +rtems_task_priority +rtems_capture_watch_get_floor () +{ + return capture_floor; +} + +/* + * rtems_capture_set_trigger + * + * DESCRIPTION: + * + * This function sets an edge trigger. Left is the left side of + * the edge and right is right side of the edge. The trigger type + * can be - + * + * FROM_ANY : a switch from any task to the right side of the edge. + * TO_ANY : a switch from the left side of the edge to any task. + * FROM_TO : a switch from the left side of the edge to the right + * side of the edge. + * + * This set trigger routine will create a capture control for the + * target task. The task list is searched and any existing tasks + * are linked to the new control. + * + * We can have a number of tasks that have the same name so we + * search using names. This means a number of tasks can be + * linked to single control. + */ +rtems_status_code +rtems_capture_set_trigger (rtems_name from, + rtems_id from_id, + rtems_name to, + rtems_id to_id, + rtems_capture_trigger_t trigger) +{ + rtems_capture_control_t* control; + int i; + + /* + * Find the capture control blocks for the from and to + * tasks. + */ + if (trigger == rtems_capture_to_any) + { + control = rtems_capture_create_control (from, from_id); + if (control == NULL) + return RTEMS_NO_MEMORY; + control->flags |= RTEMS_CAPTURE_TO_ANY; + } + + if ((trigger == rtems_capture_from_to) || + (trigger == rtems_capture_from_any)) + { + control = rtems_capture_create_control (to, to_id); + if (control == NULL) + return RTEMS_NO_MEMORY; + + if (trigger == rtems_capture_from_any) + control->flags |= RTEMS_CAPTURE_FROM_ANY; + else + { + control->flags |= RTEMS_CAPTURE_FROM_TO; + for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++) + { + if (control->from[i] == 0) + { + control->from[i] = from; + control->from_id[i] = from_id; + break; + } + } + } + } + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_read + * + * DESCRIPTION: + * + * This function reads a number of records from the capture buffer. + * The user can optionally block and wait until the buffer as a + * specific number of records available or a specific time has + * elasped. + * + * The function returns the number of record that is has that are + * in a continous block of memory. If the number of available records + * wrap then only those records are provided. This removes the need for + * caller to be concerned about buffer wrappings. If the number of + * requested records cannot be met due to the wrapping of the records + * less than the specified number will be returned. + * + * The user must release the records. This is achieved with a call to + * rtems_capture_release. Calls this function without a release will + * result in at least the same number of records being released. + * + * The 'threshold' parameter is the number of records that must be + * captured before returning. If a timeout period is specified (non-0) + * any captured records will be returned. These parameters stop + * thrashing occuring for a small number of records, yet allows + * a user configured latiency to be applied for single events. + * + * The 'timeout' parameter is in micro-seconds. A value of 0 will disable + * the timeout. + * + */ +rtems_status_code +rtems_capture_read (uint32_t threshold, + uint32_t timeout, + uint32_t * read, + rtems_capture_record_t** recs) +{ + rtems_interrupt_level level; + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint32_t count; + + *read = 0; + *recs = NULL; + + rtems_interrupt_disable (level); + + /* + * Only one reader is allowed. + */ + + if (capture_flags & RTEMS_CAPTURE_READER_ACTIVE) + { + rtems_interrupt_enable (level); + return RTEMS_RESOURCE_IN_USE; + } + + capture_flags |= RTEMS_CAPTURE_READER_ACTIVE; + *read = count = capture_count; + + rtems_interrupt_enable (level); + + *recs = &capture_records[capture_out]; + + for (;;) + { + /* + * See if the count wraps the end of the record buffer. + */ + if (count && ((capture_out + count) >= capture_size)) + *read = capture_size - capture_out; + + /* + * Do we have a threshold and the current count has not wrapped + * around the end of the capture record buffer ? + */ + if ((*read == count) && threshold) + { + /* + * Do we have enough records ? + */ + if (*read < threshold) + { + rtems_event_set event_out; + + rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, &capture_reader); + + rtems_interrupt_disable (level); + + capture_flags |= RTEMS_CAPTURE_READER_WAITING; + + rtems_interrupt_enable (level); + + sc = rtems_event_receive (RTEMS_EVENT_0, + RTEMS_WAIT | RTEMS_EVENT_ANY, + TOD_MICROSECONDS_TO_TICKS (timeout), + &event_out); + + /* + * Let the user handle all other sorts of errors. This may + * not be the best solution, but oh well, it will do for + * now. + */ + if ((sc != RTEMS_SUCCESSFUL) && (sc != RTEMS_TIMEOUT)) + break; + + rtems_interrupt_disable (level); + + *read = count = capture_count; + + rtems_interrupt_enable (level); + + continue; + } + } + + /* + * Always out if we reach here. To loop use continue. + */ + break; + } + + rtems_interrupt_disable (level); + + capture_flags &= ~RTEMS_CAPTURE_READER_ACTIVE; + + rtems_interrupt_enable (level); + + return sc; +} + +/* + * rtems_capture_release + * + * DESCRIPTION: + * + * This function releases the requested number of record slots back + * to the capture engine. The count must match the number read. + */ +rtems_status_code +rtems_capture_release (uint32_t count) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + + if (count > capture_count) + count = capture_count; + + capture_count -= count; + + capture_out = (capture_count + count) % capture_size; + + rtems_interrupt_enable (level); + + return RTEMS_SUCCESSFUL; +} + +/* + * rtems_capture_tick_time + * + * DESCRIPTION: + * + * This function returns the tick period in nano-seconds. + */ +uint32_t +rtems_capture_tick_time () +{ + return capture_tick_period; +} + +/* + * rtems_capture_event_text + * + * DESCRIPTION: + * + * This function returns a string for an event based on the bit in the + * event. The functions takes the bit offset as a number not the bit + * set in a bit map. + */ +const char* +rtems_capture_event_text (int event) +{ + if ((event < RTEMS_CAPTURE_EVENT_START) || (event > RTEMS_CAPTURE_EVENT_END)) + return "invalid event id"; + return capture_event_text[event - RTEMS_CAPTURE_EVENT_START]; +} + +/* + * rtems_capture_get_task_list + * + * DESCRIPTION: + * + * This function returns the head of the list of tasks that the + * capture engine has detected. + */ +rtems_capture_task_t* +rtems_capture_get_task_list () +{ + return capture_tasks; +} + +/* + * rtems_capture_task_stack_usage + * + * DESCRIPTION: + * + * This function updates the stack usage. The task control block + * is updated. + */ +uint32_t +rtems_capture_task_stack_usage (rtems_capture_task_t* task) +{ + if (task->tcb) + { + uint32_t * st; + uint32_t * s; + + /* + * @todo: Assumes all stacks move the same way. + */ + st = task->tcb->Start.Initial_stack.area + task->stack_size; + s = task->tcb->Start.Initial_stack.area; + + while (s < st) + { + if (*s != 0xdeaddead) + break; + s++; + } + + task->stack_clean = + s - (uint32_t *) task->tcb->Start.Initial_stack.area; + } + + return task->stack_clean; +} + +/* + * rtems_capture_get_control_list + * + * DESCRIPTION: + * + * This function returns the head of the list of control in the + * capture engine. + */ +rtems_capture_control_t* +rtems_capture_get_control_list () +{ + return capture_controls; +} diff --git a/cpukit/libmisc/capture/capture.h b/cpukit/libmisc/capture/capture.h new file mode 100644 index 0000000000..6e040efa0e --- /dev/null +++ b/cpukit/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 <rtems.h> + +/* + * 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; + uint32_t 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; + uint32_t flags; + rtems_tcb* tcb; + uint32_t in; + uint32_t out; + rtems_task_priority start_priority; + uint32_t stack_size; + uint32_t stack_clean; + uint32_t ticks; + uint32_t tick_offset; + uint32_t ticks_in; + uint32_t tick_offset_in; + uint32_t last_ticks; + uint32_t 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; + uint32_t events; + uint32_t ticks; + uint32_t 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) + (uint32_t * ticks, uint32_t * 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 (uint32_t 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 (uint32_t threshold, + uint32_t timeout, + uint32_t * 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 (uint32_t count); + +/* + * rtems_capture_tick_time + * + * DESCRIPTION: + * + * This function returns the tick period in micro-seconds. + */ +uint32_t +rtems_capture_tick_time (); + +/* + * rtems_capture_tick_time + * + * DESCRIPTION: + * + * This function returns the tick period in micro-seconds. + */ +uint32_t +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 uint32_t +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 uint32_t +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 uint32_t +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 uint32_t +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. + */ +uint32_t +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 uint32_t +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 uint32_t +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 uint32_t +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 uint32_t +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; + uint32_t 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 uint32_t +rtems_capture_task_count () +{ + rtems_capture_task_t* task = rtems_capture_get_task_list (); + uint32_t 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 uint32_t +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 uint32_t +rtems_capture_control_count () +{ + rtems_capture_control_t* control = rtems_capture_get_control_list (); + uint32_t count = 0; + + while (control) + { + count++; + control = rtems_capture_next_control (control); + } + + return count; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libmisc/cpuuse/README b/cpukit/libmisc/cpuuse/README new file mode 100644 index 0000000000..20e76f07bc --- /dev/null +++ b/cpukit/libmisc/cpuuse/README @@ -0,0 +1,41 @@ +# +# $Id$ +# + +This directory contains a stack bounds checker. It provides two +primary features: + + + check for stack overflow at each context switch + + provides an educated guess at each task's stack usage + +The stack overflow check at context switch works by looking for +a 16 byte pattern at the logical end of the stack to be corrupted. +The "guesser" assumes that the entire stack was prefilled with a known +pattern and assumes that the pattern is still in place if the memory +has not been used as a stack. + +Both of these can be fooled by pushing large holes onto the stack +and not writing to them... or (much more unlikely) writing the +magic patterns into memory. + +This code has not been extensively tested. It is provided as a tool +for RTEMS users to catch the most common mistake in multitasking +systems ... too little stack space. Suggestions and comments are appreciated. + +NOTES: + +1. Stack usage information is questionable on CPUs which push + large holes on stack. + +2. The stack checker has a tendency to generate a fault when + trying to print the helpful diagnostic message. If it comes + out, congratulations. If not, then the variable Stack_check_Blown_task + contains a pointer to the TCB of the offending task. This + is usually enough to go on. + +FUTURE: + +1. Determine how/if gcc will generate stack probe calls and support that. + +2. Get accurate stack usage numbers on i960.. it pushes very large + holes on the stack. diff --git a/cpukit/libmisc/cpuuse/cpuuse.c b/cpukit/libmisc/cpuuse/cpuuse.c new file mode 100644 index 0000000000..189b542644 --- /dev/null +++ b/cpukit/libmisc/cpuuse/cpuuse.c @@ -0,0 +1,148 @@ +/* + * CPU Usage Reporter + * + * COPYRIGHT (c) 1989-1999. 1996. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include <rtems/cpuuse.h> + +uint32_t CPU_usage_Ticks_at_last_reset; + +/*PAGE + * + * CPU_usage_Dump + */ + +void CPU_usage_Dump( void ) +{ + uint32_t i; + uint32_t api_index; + Thread_Control *the_thread; + Objects_Information *information; + uint32_t u32_name; + char *cname; + char name[5]; + uint32_t total_units = 0; + + for ( api_index = 1 ; + api_index <= OBJECTS_APIS_LAST ; + api_index++ ) { + if ( !_Objects_Information_table[ api_index ] ) + continue; + information = _Objects_Information_table[ api_index ][ 1 ]; + if ( information ) { + for ( i=1 ; i <= information->maximum ; i++ ) { + the_thread = (Thread_Control *)information->local_table[ i ]; + + if ( the_thread ) + total_units += the_thread->ticks_executed; + } + } + } + + fprintf(stdout,"CPU Usage by thread\n"); +#if defined(unix) || ( CPU_HARDWARE_FP == TRUE ) + fprintf(stdout, " ID NAME TICKS PERCENT\n" ); +#else + fprintf(stdout, " ID NAME TICKS\n" ); +#endif + + for ( api_index = 1 ; + api_index <= OBJECTS_APIS_LAST ; + api_index++ ) { + if ( !_Objects_Information_table[ api_index ] ) + continue; + information = _Objects_Information_table[ api_index ][ 1 ]; + if ( information ) { + for ( i=1 ; i <= information->maximum ; i++ ) { + the_thread = (Thread_Control *)information->local_table[ i ]; + + if ( !the_thread ) + continue; + + if ( information->is_string ) { + cname = the_thread->Object.name; + name[ 0 ] = cname[0]; + name[ 1 ] = cname[1]; + name[ 2 ] = cname[2]; + name[ 3 ] = cname[3]; + name[ 4 ] = '\0'; + } else { + u32_name = (uint32_t )the_thread->Object.name; + name[ 0 ] = (u32_name >> 24) & 0xff; + name[ 1 ] = (u32_name >> 16) & 0xff; + name[ 2 ] = (u32_name >> 8) & 0xff; + name[ 3 ] = (u32_name >> 0) & 0xff; + name[ 4 ] = '\0'; + } + + if ( !isprint(name[0]) ) name[0] = '*'; + if ( !isprint(name[1]) ) name[1] = '*'; + if ( !isprint(name[2]) ) name[2] = '*'; + if ( !isprint(name[3]) ) name[3] = '*'; + +#if defined(unix) || ( CPU_HARDWARE_FP == TRUE ) + fprintf(stdout, "0x%08x %4s %8d %5.3f\n", + the_thread->Object.id, + name, + the_thread->ticks_executed, + (total_units) ? + (double)the_thread->ticks_executed / (double)total_units : + (double)total_units + ); +#else + fprintf(stdout, "0x%08x %4s %8d\n", + the_thread->Object.id, + name, + the_thread->ticks_executed + ); +#endif + } + } + } + + fprintf(stdout, + "\nTicks since last reset = %d\n", + _Watchdog_Ticks_since_boot - CPU_usage_Ticks_at_last_reset + ); + fprintf(stdout, "\nTotal Units = %d\n", total_units ); +} + +/*PAGE + * + * CPU_usage_Reset + */ + +static void CPU_usage_Per_thread_handler( + Thread_Control *the_thread +) +{ + the_thread->ticks_executed = 0; +} + +void CPU_usage_Reset( void ) +{ + CPU_usage_Ticks_at_last_reset = _Watchdog_Ticks_since_boot; + + rtems_iterate_over_all_threads(CPU_usage_Per_thread_handler); +} diff --git a/cpukit/libmisc/cpuuse/cpuuse.h b/cpukit/libmisc/cpuuse/cpuuse.h new file mode 100644 index 0000000000..de874e0fcd --- /dev/null +++ b/cpukit/libmisc/cpuuse/cpuuse.h @@ -0,0 +1,40 @@ +/* cpuuse.h + * + * This include file contains information necessary to utilize + * and install the cpu usage reporting mechanism. + * + * COPYRIGHT (c) 1989-1999. 1996. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __CPU_USE_h +#define __CPU_USE_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * CPU_usage_Dump + */ + +void CPU_usage_Dump( void ); + +/* + * CPU_usage_Reset + */ + +void CPU_usage_Reset( void ); + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/devnull/devnull.c b/cpukit/libmisc/devnull/devnull.c new file mode 100644 index 0000000000..cfc8a750ce --- /dev/null +++ b/cpukit/libmisc/devnull/devnull.c @@ -0,0 +1,184 @@ +/* /dev/null + * + * Derived from rtems' stub driver. + * + * Author: Ralf Corsepius (corsepiu@faw.uni-ulm.de) + * + * COPYRIGHT (c) 1989-2000. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/devnull.h> +#include <rtems/libio.h> + +/* null_initialize + * + * This routine is the null device driver init routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargp - pointer to parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +uint32_t NULL_major; +static char initialized; + +rtems_device_driver null_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + rtems_device_driver status; + + if ( !initialized ) { + initialized = 1; + + status = rtems_io_register_name( + "/dev/null", + major, + (rtems_device_minor_number) 0 + ); + + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(status); + + NULL_major = major; + } + + return RTEMS_SUCCESSFUL; +} + +/* null_open + * + * This routine is the null device driver open routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargb - pointer to open parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + return NULL_SUCCESSFUL; +} + + +/* null_close + * + * This routine is the null device driver close routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargb - pointer to close parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + return NULL_SUCCESSFUL; +} + + +/* null_read + * + * This routine is the null device driver read routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargp - pointer to read parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + return NULL_SUCCESSFUL; +} + + +/* null_write + * + * This routine is the null device driver write routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargp - pointer to write parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) pargp; + + if ( rw_args ) + rw_args->bytes_moved = rw_args->count; + + return NULL_SUCCESSFUL; +} + + +/* null_control + * + * This routine is the null device driver control routine. + * + * Input parameters: + * major - device major number + * minor - device minor number + * pargp - pointer to cntrl parameter block + * + * Output parameters: + * rval - NULL_SUCCESSFUL + */ + +rtems_device_driver null_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + return NULL_SUCCESSFUL; +} diff --git a/cpukit/libmisc/devnull/devnull.h b/cpukit/libmisc/devnull/devnull.h new file mode 100644 index 0000000000..6fdf3b6850 --- /dev/null +++ b/cpukit/libmisc/devnull/devnull.h @@ -0,0 +1,71 @@ +/* devnull.h + * + * Null device driver, derived from rtems' stub driver. + * + * Author: Ralf Corsepius (corsepiu@faw.uni-ulm.de) + * + * COPYRIGHT (c) 1989-2000. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __NULL_DRIVER_h +#define __NULL_DRIVER_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEVNULL_DRIVER_TABLE_ENTRY \ + { null_initialize, null_open, null_close, null_read, \ + null_write, null_control } + +#define NULL_SUCCESSFUL RTEMS_SUCCESSFUL + +rtems_device_driver null_initialize( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_open( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_close( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_read( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_write( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +rtems_device_driver null_control( + rtems_device_major_number, + rtems_device_minor_number, + void * +); + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/dummy/README b/cpukit/libmisc/dummy/README new file mode 100644 index 0000000000..03bb30834d --- /dev/null +++ b/cpukit/libmisc/dummy/README @@ -0,0 +1,23 @@ +dummy.rel +========= + +A relocatible objects which contains a dummy configuration for RTEMS. + +Helps linking standard c-program code with RTEMS, which shall *not* be run +on a target, such as configure script code fragments generated by autoconf's +AC_TRY_LINK. + +Example: + +tar xzvf somepkg.tar.gz +cd somepkg + +LDFLAGS=/usr/local/rtems/<cpu>-rtems/<bsp>/lib/dummy.rel \ +CC="<cpu>-rtems-gcc \ +-B/usr/local/rtems/<cpu>-rtems/<bsp>/lib/ -specs bsp_specs -qrtems" \ +CC_FOR_BUILD="gcc" \ +configure --host=<cpu>-rtems --build=i686-pc-linux-gnu +make + +History: +Starting dummy.c with a copy of rtems-19990528/c/src/tests/samples/minimum/init.c diff --git a/cpukit/libmisc/dummy/dummy.c b/cpukit/libmisc/dummy/dummy.c new file mode 100644 index 0000000000..c59114375c --- /dev/null +++ b/cpukit/libmisc/dummy/dummy.c @@ -0,0 +1,45 @@ +/* Init + * + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +rtems_task Init( + rtems_task_argument ignored +) +{ +} + +int main( int, char **, char **); + +/* configuration information */ + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE +#define CONFIGURE_MAXIMUM_TASKS 10 +#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM +#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 20 +#define CONFIGURE_INIT_TASK_ENTRY_POINT (void *)main + +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +#define CONFIGURE_INIT + +#include <rtems/confdefs.h> + +/* global variables */ diff --git a/cpukit/libmisc/dumpbuf/dumpbuf.c b/cpukit/libmisc/dumpbuf/dumpbuf.c new file mode 100644 index 0000000000..120a464a90 --- /dev/null +++ b/cpukit/libmisc/dumpbuf/dumpbuf.c @@ -0,0 +1,79 @@ +/* + * COPYRIGHT (c) 1997. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may in + * the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <rtems/dumpbuf.h> + +/* + * Put the body below Dump_Buffer so it won't get inlined. + */ + +static inline void Dump_Line( + unsigned char *buffer, + int length +); + +void Dump_Buffer( + unsigned char *buffer, + int length +) +{ + + int i, mod, max; + + if ( !length ) return; + + mod = length % 16; + + max = length - mod; + + for ( i=0 ; i<max ; i+=16 ) + Dump_Line( &buffer[ i ], 16 ); + + if ( mod ) + Dump_Line( &buffer[ max ], mod ); +} + +static inline void Dump_Line( + unsigned char *buffer, + int length +) +{ + + int i; + char line_buffer[120]; + + line_buffer[0] = '\0'; + + for( i=0 ; i<length ; i++ ) + sprintf( line_buffer, "%s%02x ", line_buffer, buffer[ i ] ); + + for( ; i<16 ; i++ ) + strcat( line_buffer, " " ); + + strcat( line_buffer, "|" ); + for( i=0 ; i<length ; i++ ) + sprintf( line_buffer, "%s%c", line_buffer, + isprint( buffer[ i ] ) ? buffer[ i ] : '.' ); + + for( ; i<16 ; i++ ) + strcat( line_buffer, " " ); + + strcat( line_buffer, "|\n" ); + + fprintf(stdout, line_buffer ); +} diff --git a/cpukit/libmisc/dumpbuf/dumpbuf.h b/cpukit/libmisc/dumpbuf/dumpbuf.h new file mode 100644 index 0000000000..bcdf4d56ee --- /dev/null +++ b/cpukit/libmisc/dumpbuf/dumpbuf.h @@ -0,0 +1,21 @@ +/* + * COPYRIGHT (c) 1997. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may in + * the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __DUMP_BUFFER_h +#define __DUMP_BUFFER_h + +void Dump_Buffer( + unsigned char *buffer, + int length +); + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/fsmount/README b/cpukit/libmisc/fsmount/README new file mode 100644 index 0000000000..88bfda62e7 --- /dev/null +++ b/cpukit/libmisc/fsmount/README @@ -0,0 +1,24 @@ +# +# +# fsmount information +# +# Author: Thomas Doerfler 02/07/2003 +# +# README,v 1.1 1999/07/09 17:23:15 joel Exp +# + +fsmount.c contains the function fsmount. It processes the +fs table given as an parameter to create the listed mount points +and mount the corresponding file systems to their mount points. +See "c/src/tests/samples/fileio" for a sample on how to use this +function. + +The field "report_reasons" specifies, what results of the mount point +creation/mount operations should print to the console. + +The field "abort_reasons" specifies, what results of the mount point +creation/mount operations should abort the function. Do not set the +"abort_reasons" bit, unless you want to stop the processing of the +fsmount table after the first successful mount. + + diff --git a/cpukit/libmisc/fsmount/fsmount.c b/cpukit/libmisc/fsmount/fsmount.c new file mode 100644 index 0000000000..0d5e388f62 --- /dev/null +++ b/cpukit/libmisc/fsmount/fsmount.c @@ -0,0 +1,195 @@ +/*===============================================================*\ +| Project: RTEMS fsmount | ++-----------------------------------------------------------------+ +| File: fsmount.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2003 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains the fsmount functions. These functions | +| are used to mount a list of filesystems (and create their mount | +| points before) | +| | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 02.07.03 creation doe | +\*===============================================================*/ +#include <rtems.h> +#include <rtems/fsmount.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <rtems/imfs.h> +#include <sys/stat.h> + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int rtems_fsmount_create_mountpoint +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| This function will create the mount point given | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const char *mount_point + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + int rc = 0; + char *tok_buffer = NULL; + char *token = NULL; + int token_len; + size_t total_len; + IMFS_token_types token_type; + struct stat file_info; + /* + * allocate temp memory to rebuild path name + */ + tok_buffer = calloc(strlen(mount_point)+1,sizeof(char)); + token = tok_buffer; + total_len = 0; + do { + /* + * scan through given string, one segment at a time + */ + token_type = IMFS_get_token(mount_point+total_len,token,&token_len); + total_len += token_len; + strncpy(tok_buffer,mount_point,total_len); + tok_buffer[total_len] = '\0'; + + if ((token_type != IMFS_NO_MORE_PATH) && + (token_type != IMFS_CURRENT_DIR) && + (token_type != IMFS_INVALID_TOKEN)) { + /* + * check, whether segment exists + */ + if (0 != stat(tok_buffer,&file_info)) { + /* + * if not, create directory + */ + rc = mknod(tok_buffer,S_IRWXU | S_IRWXG | S_IRWXO | S_IFDIR,0); + } + } + } while ((rc == 0) && + (token_type != IMFS_NO_MORE_PATH) && + (token_type != IMFS_INVALID_TOKEN)); + + /* + * return token buffer to heap + */ + if (tok_buffer != NULL) { + free(tok_buffer); + } + return rc; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int rtems_fsmount +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| This function will create the mount points listed and mount the file | +| systems listed in the calling parameters | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const fstab_t *fstab_ptr, + int fstab_count, + int *fail_idx + ) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ +{ + int rc = 0; + int tmp_rc; + int fstab_idx = 0; + rtems_filesystem_mount_table_entry_t *tmp_mt_entry; + boolean terminate = FALSE; + + /* + * scan through all fstab entries; + */ + while (!terminate && + (fstab_idx < fstab_count)) { + tmp_rc = 0; + /* + * create mount point + */ + if (tmp_rc == 0) { + tmp_rc = rtems_fsmount_create_mountpoint(fstab_ptr->mount_point); + if (tmp_rc != 0) { + if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNTPNT_CRTERR)) { + fprintf(stdout,"fsmount: creation of mount point \"%s\" failed: %s\n", + fstab_ptr->mount_point, + strerror(errno)); + } + if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNTPNT_CRTERR)) { + terminate = TRUE; + rc = tmp_rc; + } + } + } + /* + * mount device to given mount point + */ + if (tmp_rc == RTEMS_SUCCESSFUL) { + tmp_rc = mount(&tmp_mt_entry, + fstab_ptr->fs_ops, + fstab_ptr->mount_options, + fstab_ptr->dev, + fstab_ptr->mount_point); + if (tmp_rc != RTEMS_SUCCESSFUL) { + if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNT_FAILED)) { + fprintf(stdout,"fsmount: mounting of \"%s\" to" + " \"%s\" failed: %s\n", + fstab_ptr->dev, + fstab_ptr->mount_point, + strerror(errno)); + } + if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNT_FAILED)) { + terminate = TRUE; + rc = tmp_rc; + } + } + else { + if (0 != (fstab_ptr->report_reasons & FSMOUNT_MNT_OK)) { + fprintf(stdout,"fsmount: mounting of \"%s\" to" + " \"%s\" succeeded\n", + fstab_ptr->dev, + fstab_ptr->mount_point); + } + if (0 != (fstab_ptr->abort_reasons & FSMOUNT_MNT_OK)) { + terminate = TRUE; + } + } + } + /* + * proceed to next entry + */ + if (!terminate) { + fstab_ptr++; + fstab_idx++; + } + } + if (fail_idx != NULL) { + *fail_idx = fstab_idx; + } + return rc; +} diff --git a/cpukit/libmisc/fsmount/fsmount.h b/cpukit/libmisc/fsmount/fsmount.h new file mode 100644 index 0000000000..1b876e6f81 --- /dev/null +++ b/cpukit/libmisc/fsmount/fsmount.h @@ -0,0 +1,71 @@ +/*===============================================================*\ +| Project: RTEMS fsmount | ++-----------------------------------------------------------------+ +| File: fsmount.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2003 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains the fsmount functions. These functions | +| are used to mount a list of filesystems (and create their mount | +| points before) | +| | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 02.07.03 creation doe | +\*===============================================================*/ +#ifndef _FSMOUNT_H +#define _FSMOUNT_H +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/libcsupport.h> + +/* + * bits to define, what errors will cause reporting (via printf) and + * abort of mount processing + * Use a combination of these bits + * for the fields "report_reasons" and "abort_reasons" + */ +#define FSMOUNT_MNT_OK 0x0001 /* mounted ok */ +#define FSMOUNT_MNTPNT_CRTERR 0x0002 /* cannot create mount point */ +#define FSMOUNT_MNT_FAILED 0x0004 /* mounting failed */ + +typedef struct { + char *dev; + char *mount_point; + rtems_filesystem_operations_table *fs_ops; + rtems_filesystem_options_t mount_options; + uint16_t report_reasons; + uint16_t abort_reasons; +} fstab_t; + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int rtems_fsmount +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| This function will create the mount points listed and mount the file | +| systems listed in the calling parameters | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + const fstab_t *fstab_ptr, /* Ptr to filesystem mount table */ + int fstab_count, /* number of entries in mount table*/ + int *fail_idx /* return: index of failed entry */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0, if success, -1 and errno if failed | +\*=========================================================================*/ + +#endif /* _FSMOUNT_H */ diff --git a/cpukit/libmisc/monitor/README b/cpukit/libmisc/monitor/README new file mode 100644 index 0000000000..d5a73da140 --- /dev/null +++ b/cpukit/libmisc/monitor/README @@ -0,0 +1,97 @@ +# +# $Id$ +# + +monitor task + +The monitor task is an optional task that knows about RTEMS +data structures and can print out information about them. +It is a work-in-progress and needs many more commands, but +is useful now. + +The monitor works best when it is the highest priority task, +so all your other tasks should ideally be at some priority +greater than 1. + +To use the monitor: +------------------- + + #include <rtems/monitor.h> + + ... + + rtems_monitor_init(0); + + The parameter to rtems_monitor_init() tells the monitor whether + to suspend itself on startup. A value of 0 causes the monitor + to immediately enter command mode; a non-zero value causes the + monitor to suspend itself after creation and wait for explicit + wakeup. + + + rtems_monitor_wakeup(); + + wakes up a suspended monitor and causes it to reenter command mode. + +Monitor commands +---------------- + + The monitor prompt is 'rtems> '. + Can abbreviate commands to "uniquity" + There is a 'help' command. Here is the output from various + help commands: + + Commands (may be abbreviated) + + help -- get this message or command specific help + task -- show task information + queue -- show message queue information + symbol -- show entries from symbol table + pause -- pause monitor for a specified number of ticks + fatal -- invoke a fatal RTEMS error + + task [id [id ...] ] + display information about the specified tasks. + Default is to display information about all tasks on this node + + queue [id [id ... ] ] + display information about the specified message queues + Default is to display information about all queues on this node + + symbol [ symbolname [symbolname ... ] ] + display value associated with specified symbol. + Defaults to displaying all known symbols. + + pause [ticks] + monitor goes to "sleep" for specified ticks (default is 1) + monitor will resume at end of period or if explicitly awakened + + fatal [status] + Invoke 'rtems_fatal_error_occurred' with 'status' + (default is RTEMS_INTERNAL_ERROR) + + continue + put the monitor to sleep waiting for an explicit wakeup from the + program running. + + +Sample output from 'task' command +--------------------------------- + + rtems> task + ID NAME PRIO STAT MODES EVENTS WAITID WAITARG NOTES + ------------------------------------------------------------------------ + 00010001 UI1 2 READY P:T:nA NONE15: 0x40606348 + 00010002 RMON 1 READY nP NONE15: 0x40604110 + + 'RMON' is the monitor itself, so we have 1 "user" task. + Its modes are P:T:nA which translate to: + + preemptable + timesliced + no ASRS + + It has no events. + It has a notepad value for notepad 15 which is 0x40606348 + (this is the libc thread state) + diff --git a/cpukit/libmisc/monitor/mon-command.c b/cpukit/libmisc/monitor/mon-command.c new file mode 100644 index 0000000000..dc20dcb203 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-command.c @@ -0,0 +1,745 @@ +/* + * Command parsing routines for RTEMS monitor + * + * TODO: + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <rtems/monitor.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#ifndef MONITOR_PROMPT +#define MONITOR_PROMPT "rtems" /* will have '> ' appended */ +#endif + +/* + * 2001-01-30 KJO (vac4050@cae597.rsc.raytheon.com): + * Fixed rtems_monitor_command_lookup() to accept partial + * commands to uniqeness. Added support for setting + * the monitor prompt via an environment variable: + * RTEMS_MONITOR_PROMPT + * + * CCJ: 26-3-2000, adding command history and command line + * editing. This code is donated from My Right Boot and not + * covered by GPL, only the RTEMS license. + */ + +/* + * Some key labels to define special keys. + */ + +#define KEYS_EXTENDED (0x8000) +#define KEYS_NORMAL_MASK (0x00ff) +#define KEYS_INS (0) +#define KEYS_DEL (1) +#define KEYS_UARROW (2) +#define KEYS_DARROW (3) +#define KEYS_LARROW (4) +#define KEYS_RARROW (5) +#define KEYS_HOME (6) +#define KEYS_END (7) +#define KEYS_F1 (8) +#define KEYS_F2 (9) +#define KEYS_F3 (10) +#define KEYS_F4 (11) +#define KEYS_F5 (12) +#define KEYS_F6 (13) +#define KEYS_F7 (14) +#define KEYS_F8 (15) +#define KEYS_F9 (16) +#define KEYS_F10 (17) + +#define RTEMS_COMMAND_BUFFER_SIZE (75) + +static char monitor_prompt[32]; +#ifndef RTEMS_UNIX +static char buffer[RTEMS_COMMAND_BUFFER_SIZE]; +static int pos; +static int logged_in; +#endif +/* + * History data. + */ + +#define RTEMS_COMMAND_HISTORIES (20) + +#ifndef RTEMS_UNIX +static char history_buffer[RTEMS_COMMAND_HISTORIES][RTEMS_COMMAND_BUFFER_SIZE]; +static int history_pos[RTEMS_COMMAND_HISTORIES]; +static int history; +static int history_next; +#endif + +/* + * Translation tables. Not sure if this is the best way to + * handle this, how-ever I wish to avoid the overhead of + * including a more complete and standard environment such + * as ncurses. + */ + +struct translation_table +{ + char expecting; + struct translation_table *branch; + unsigned int key; +}; + +#ifndef RTEMS_UNIX +static struct translation_table trans_two[] = +{ + { '~', 0, KEYS_INS }, + { 0, 0, 0 } +}; + +static struct translation_table trans_three[] = +{ + { '~', 0, KEYS_DEL }, + { 0, 0, 0 } +}; + +static struct translation_table trans_tab_csi[] = +{ + { '2', trans_two, 0 }, + { '3', trans_three, 0 }, + { 'A', 0, KEYS_UARROW }, + { 'B', 0, KEYS_DARROW }, + { 'D', 0, KEYS_LARROW }, + { 'C', 0, KEYS_RARROW }, + { 'F', 0, KEYS_END }, + { 'H', 0, KEYS_HOME }, + { 0, 0, 0 } +}; + +static struct translation_table trans_tab_O[] = +{ + { '1', 0, KEYS_F1 }, + { '2', 0, KEYS_F2 }, + { '3', 0, KEYS_F3 }, + { '4', 0, KEYS_F4 }, + { '5', 0, KEYS_F5 }, + { '6', 0, KEYS_F6 }, + { '7', 0, KEYS_F7 }, + { '8', 0, KEYS_F8 }, + { '9', 0, KEYS_F9 }, + { ':', 0, KEYS_F10 }, + { 'P', 0, KEYS_F1 }, + { 'Q', 0, KEYS_F2 }, + { 'R', 0, KEYS_F3 }, + { 'S', 0, KEYS_F4 }, + { 'T', 0, KEYS_F5 }, + { 'U', 0, KEYS_F6 }, + { 'V', 0, KEYS_F7 }, + { 'W', 0, KEYS_F8 }, + { 'X', 0, KEYS_F9 }, + { 'Y', 0, KEYS_F10 }, + { 0, 0, 0 } +}; + +static struct translation_table trans_tab[] = +{ + { '[', trans_tab_csi, 0 }, /* CSI command sequences */ + { 'O', trans_tab_O, 0 }, /* O are the fuction keys */ + { 0, 0, 0 } +}; +#endif + +/* + * Perform a basic tranlation for some ANSI/VT100 key codes. + * This code could do with a timeout on the ESC as it is + * now lost from the input stream. It is not* used by the + * line editor below so considiered not worth the effort. + */ + +#ifndef RTEMS_UNIX +static unsigned int +rtems_monitor_getchar ( +) +{ + struct translation_table *translation = 0; + for (;;) + { + char c = getchar (); + if (c == 27) + translation = trans_tab; + else + { + /* + * If no translation happing just pass through + * and return the key. + */ + if (translation) + { + /* + * Scan the current table for the key, and if found + * see if this key is a fork. If so follow it and + * wait else return the extended key. + */ + int index = 0; + int branched = 0; + while ((translation[index].expecting != '\0') || + (translation[index].key != '\0')) + { + if (translation[index].expecting == c) + { + /* + * A branch is take if more keys are to come. + */ + if (translation[index].branch == 0) + return KEYS_EXTENDED | translation[index].key; + else + { + translation = translation[index].branch; + branched = 1; + break; + } + } + index++; + } + /* + * Who knows what these keys are, just drop them. + */ + if (!branched) + translation = 0; + } + else + return c; + } + } +} +#endif + +#ifndef RTEMS_UNIX +/* + * The line editor with history. + */ + +static int +rtems_monitor_line_editor ( + char *command +) +{ + int repeating = 0; + + memset (buffer, 0, RTEMS_COMMAND_BUFFER_SIZE); + history = history_next; + pos = 0; + + if (!logged_in) + fprintf(stdout,"\nMonitor ready, press enter to login.\n\n"); + else + fprintf(stdout,"%s $ ", monitor_prompt); + + while (1) + { + unsigned int extended_key = rtems_monitor_getchar (); + char c = extended_key & KEYS_NORMAL_MASK; + + /* + * Make the extended_key usable as a boolean. + */ + extended_key &= ~KEYS_NORMAL_MASK; + + if (!extended_key && !logged_in) + { + if (c == '\n') + { + logged_in = 1; + /* + * The prompt has changed from `>' to `$' to help know + * which version of the monitor code people are using. + */ + fprintf(stdout,"%s $ ", monitor_prompt); + } + } + else + { + if (extended_key) + { + switch (c) + { + case KEYS_END: + fprintf(stdout,buffer + pos); + pos = (int) strlen (buffer); + break; + + case KEYS_HOME: + fprintf(stdout,"\r%s $ ", monitor_prompt); + pos = 0; + break; + + case KEYS_LARROW: + if (pos > 0) + { + pos--; + putchar ('\b'); + } + break; + + case KEYS_RARROW: + if ((pos < RTEMS_COMMAND_BUFFER_SIZE) && (buffer[pos] != '\0')) + { + putchar (buffer[pos]); + pos++; + } + break; + + case KEYS_UARROW: + /* + * If we are moving up the histories then we need to save the working + * buffer. + */ + if (history) + { + int end; + int bs; + if (history == history_next) + { + memcpy (history_buffer[history_next], buffer, + RTEMS_COMMAND_BUFFER_SIZE); + history_pos[history_next] = pos; + } + history--; + memcpy (buffer, history_buffer[history], + RTEMS_COMMAND_BUFFER_SIZE); + pos = history_pos[history]; + fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' '); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + + case KEYS_DARROW: + if (history < history_next) + { + int end; + int bs; + history++; + memcpy (buffer, history_buffer[history], + RTEMS_COMMAND_BUFFER_SIZE); + pos = history_pos[history]; + fprintf(stdout,"\r%*c", RTEMS_COMMAND_BUFFER_SIZE, ' '); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + + case KEYS_DEL: + if (buffer[pos] != '\0') + { + int end; + int bs; + strcpy (&buffer[pos], &buffer[pos + 1]); + fprintf(stdout,"\r%s $ %s", monitor_prompt, buffer); + end = (int) strlen (buffer); + for (bs = 0; bs < (end - pos); bs++) + putchar ('\b'); + } + break; + } + } + else + { + switch (c) + { + case '\b': + case '\x7e': + case '\x7f': + if (pos > 0) + { + int bs; + pos--; + strcpy (buffer + pos, buffer + pos + 1); + fprintf(stdout,"\b%s \b", buffer + pos); + for (bs = 0; bs < ((int) strlen (buffer) - pos); bs++) + putchar ('\b'); + } + break; + + case '\n': + /* + * Process the command. + */ + fprintf(stdout,"\n"); + repeating = 1; + /* + * Only process the history if we have a command and + *a history. + */ + if (strlen (buffer)) + { + if (history_next && (history == history_next)) + { + /* + * Do not place the last command into the history + *if the same. + */ + if (strcmp (history_buffer[history_next - 1], buffer)) + repeating = 0; + } + else + repeating = 0; + } + if (!repeating) + { + memcpy (history_buffer[history_next], buffer, + RTEMS_COMMAND_BUFFER_SIZE); + history_pos[history_next] = pos; + if (history_next < (RTEMS_COMMAND_HISTORIES - 1)) + history_next++; + else + { + memmove (history_buffer[0], history_buffer[1], + RTEMS_COMMAND_BUFFER_SIZE * (RTEMS_COMMAND_HISTORIES - 1)); + memmove (&history_pos[0], &history_pos[1], + sizeof (history_pos[0]) * (RTEMS_COMMAND_HISTORIES - 1)); + } + } + else + { +#ifdef ENABLE_ENTER_REPEATS + if (history_next) + memcpy (buffer, history_buffer[history_next - 1], + RTEMS_COMMAND_BUFFER_SIZE); +#endif + } + memmove (command, buffer, RTEMS_COMMAND_BUFFER_SIZE); + return repeating; + break; + + default: + if ((pos < (RTEMS_COMMAND_BUFFER_SIZE - 1)) && + (c >= ' ') && (c <= 'z')) + { + int end; + end = strlen (buffer); + if ((pos < end) && (end < RTEMS_COMMAND_BUFFER_SIZE)) + { + int ch, bs; + for (ch = end + 1; ch > pos; ch--) + buffer[ch] = buffer[ch - 1]; + fprintf(stdout,buffer + pos); + for (bs = 0; bs < (end - pos + 1); bs++) + putchar ('\b'); + } + buffer[pos++] = c; + if (pos > end) + buffer[pos] = '\0'; + putchar (c); + } + break; + } + } + } + } +} +#endif + +/* + * make_argv(cp): token-count + * Break up the command line in 'cp' into global argv[] and argc (return + * value). + */ + +int +rtems_monitor_make_argv( + char *cp, + int *argc_p, + char **argv) +{ + int argc = 0; + + while ((cp = strtok(cp, " \t\n\r"))) + { + argv[argc++] = cp; + cp = (char *) NULL; + } + argv[argc] = (char *) NULL; /* end of argv */ + + return *argc_p = argc; +} + + +/* + * Read and break up a monitor command + * + * We have to loop on the gets call, since it will return NULL under UNIX + * RTEMS when we get a signal (eg: SIGALRM). + */ + +int +rtems_monitor_command_read(char *command, + int *argc, + char **argv) +{ + char *env_prompt; + + env_prompt = getenv("RTEMS_MONITOR_PROMPT"); + + /* + * put node number in the prompt if we are multiprocessing + */ + if (!rtems_configuration_get_user_multiprocessing_table ()) + sprintf (monitor_prompt, "%s", + (env_prompt == NULL) ? MONITOR_PROMPT: env_prompt); + else if (rtems_monitor_default_node != rtems_monitor_node) + sprintf (monitor_prompt, "%d-%s-%d", rtems_monitor_node, + (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt, + rtems_monitor_default_node); + else + sprintf (monitor_prompt, "%d-%s", rtems_monitor_node, + (env_prompt == NULL) ? MONITOR_PROMPT : env_prompt); + +#if defined(RTEMS_UNIX) + /* RTEMS on unix gets so many interrupt system calls this is hosed */ + fprintf(stdout,"%s> ", monitor_prompt); + fflush (stdout); + while (gets(command) == (char *) 0) + ; +#else + rtems_monitor_line_editor (command); +#endif + + return rtems_monitor_make_argv (command, argc, argv); +} + +/* + * Look up a command in a command table + * + */ + +rtems_monitor_command_entry_t * +rtems_monitor_command_lookup( + rtems_monitor_command_entry_t *table, + int argc, + char **argv +) +{ + int command_length; + rtems_monitor_command_entry_t *found_it = NULL; + + command_length = strlen (argv[0]); + + if ((table == 0) || (argv[0] == 0)) + return 0; + + while (table) + { + if (table->command) + { + + /* + * Check for ambiguity + */ + if (!strncmp (table->command, argv[0], command_length)) + { + if (found_it) + { + return 0; + } + + else + found_it = table; + } + } + table = table->next; + } + + /* + * No ambiguity (the possible partial command was unique after all) + */ + if (found_it) + { + if (found_it->command_function == 0) + return 0; + + return found_it; + } + + return 0; +} + +static void +rtems_monitor_show_help ( + rtems_monitor_command_entry_t *help_cmd, + int max_cmd_len +) +{ +#define MAX_HELP_LINE_LENGTH (75 - max_cmd_len - 2) + + if (help_cmd && help_cmd->command) + { + const char *help = help_cmd->usage; + int help_len = strlen (help); + int spaces = max_cmd_len - strlen (help_cmd->command); + int show_this_line = 0; + int line_one = 1; + int c; + + fprintf(stdout,"%s", help_cmd->command); + + if (help_len == 0) + { + fprintf(stdout," - No help associated.\n"); + return; + } + + while (help_len) + { + fprintf(stdout,"%*c", spaces, ' '); + + if (line_one) + fprintf(stdout," - "); + + spaces = max_cmd_len + 2; + line_one = 0; + + /* + * See if greater then the line length if so, work back + * from the end for a space, tab or lf or cr. + */ + + if (help_len > MAX_HELP_LINE_LENGTH) + { + for (show_this_line = MAX_HELP_LINE_LENGTH - 1; + show_this_line; + show_this_line--) + if ((help[show_this_line] == ' ') || + (help[show_this_line] == '\n') || + (help[show_this_line] == '\r')) + break; + + /* + * If show_this_line is 0, it is a very long word !! + */ + + if (show_this_line == 0) + show_this_line = MAX_HELP_LINE_LENGTH - 1; + } + else + show_this_line = help_len; + + for (c = 0; c < show_this_line; c++) + if ((help[c] == '\r') || (help[c] == '\n')) + show_this_line = c; + else + putchar (help[c]); + + fprintf(stdout,"\n"); + + help += show_this_line; + help_len -= show_this_line; + + /* + * Move past the line feeds or what ever else is being skipped. + */ + + while (help_len) + { + if ((*help != '\r') && (*help != '\n')) + break; + + if (*help != ' ') + { + help++; + help_len--; + break; + } + help++; + help_len--; + } + } + } +} + +void +rtems_monitor_command_usage( + rtems_monitor_command_entry_t *table, + char *command_string +) +{ + rtems_monitor_command_entry_t *command = table; + int max_cmd_len = 0; + + /* if first entry in table is a usage, then print it out */ + + if (command_string && (*command_string != '\0')) + { + char *argv[2]; + + argv[0] = command_string; + argv[1] = 0; + + command = rtems_monitor_command_lookup (table, 1, argv); + + if (command) + rtems_monitor_show_help (command, strlen (command_string)); + else + fprintf(stdout,"Unrecognised command; try just 'help'\n"); + return; + } + + /* + * Find the largest command size. + */ + + while (command) + { + int len = command->command ? strlen (command->command) : 0 ; + + if (len > max_cmd_len) + max_cmd_len = len; + + command = command->next; + } + + max_cmd_len++; + + command = table; + + /* + * Now some nice formatting for the help. + */ + + while (command) + { + rtems_monitor_show_help (command, max_cmd_len); + command = command->next; + } +} + + +void +rtems_monitor_help_cmd( + int argc, + char **argv, + rtems_monitor_command_arg_t *command_arg, + boolean verbose +) +{ + int arg; + rtems_monitor_command_entry_t *command; + + command = command_arg->monitor_command_entry; + + if (argc == 1) + rtems_monitor_command_usage(command, 0); + else + { + for (arg = 1; argv[arg]; arg++) + rtems_monitor_command_usage(command, argv[arg]); + } +} diff --git a/cpukit/libmisc/monitor/mon-config.c b/cpukit/libmisc/monitor/mon-config.c new file mode 100644 index 0000000000..0928eca7e1 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-config.c @@ -0,0 +1,134 @@ +/* + * RTEMS Config display support + * + * TODO + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ + +#define DATACOL 15 +#define CONTCOL DATACOL /* continued col */ + +/* + * Fill in entire monitor config table + * for sending to a remote monitor or printing on the local system + */ + +void +rtems_monitor_config_canonical( + rtems_monitor_config_t *canonical_config, + void *config_void +) +{ + rtems_configuration_table *c = (rtems_configuration_table *) config_void; + rtems_api_configuration_table *r = c->RTEMS_api_configuration; + + canonical_config->work_space_start = c->work_space_start; + canonical_config->work_space_size = c->work_space_size; + canonical_config->maximum_tasks = r->maximum_tasks; + canonical_config->maximum_timers = r->maximum_timers; + canonical_config->maximum_semaphores = r->maximum_semaphores; + canonical_config->maximum_message_queues = r->maximum_message_queues; + canonical_config->maximum_partitions = r->maximum_partitions; + canonical_config->maximum_regions = r->maximum_regions; + canonical_config->maximum_ports = r->maximum_ports; + canonical_config->maximum_periods = r->maximum_periods; + canonical_config->maximum_extensions = c->maximum_extensions; + canonical_config->microseconds_per_tick = c->microseconds_per_tick; + canonical_config->ticks_per_timeslice = c->ticks_per_timeslice; + canonical_config->number_of_initialization_tasks = r->number_of_initialization_tasks; +} + +/* + * This is easy, since there is only 1 (altho we could get them from + * other nodes...) + */ + +void * +rtems_monitor_config_next( + void *object_info, + rtems_monitor_config_t *canonical_config, + rtems_id *next_id +) +{ + rtems_configuration_table *c = _Configuration_Table; + int n = rtems_get_index(*next_id); + + if (n >= 1) + goto failed; + + _Thread_Disable_dispatch(); + + *next_id += 1; + return (void *) c; + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + + +void +rtems_monitor_config_dump_header( + boolean verbose +) +{ + fprintf(stdout,"\ +INITIAL (startup) Configuration Info\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + + +void +rtems_monitor_config_dump( + rtems_monitor_config_t *monitor_config, + boolean verbose +) +{ + uint32_t length = 0; + + length = 0; + length += fprintf(stdout,"WORKSPACE"); + length += rtems_monitor_pad(DATACOL, length); + length += fprintf(stdout,"start: %p; size: 0x%x\n", + monitor_config->work_space_start, + monitor_config->work_space_size); + + length = 0; + length += fprintf(stdout,"TIME"); + length += rtems_monitor_pad(DATACOL, length); + length += fprintf(stdout,"usec/tick: %d; tick/timeslice: %d; tick/sec: %d\n", + monitor_config->microseconds_per_tick, + monitor_config->ticks_per_timeslice, + 1000000 / monitor_config->microseconds_per_tick); + + length = 0; + length += fprintf(stdout,"MAXIMUMS"); + length += rtems_monitor_pad(DATACOL, length); + length += fprintf(stdout,"tasks: %d; timers: %d; sems: %d; que's: %d; ext's: %d\n", + monitor_config->maximum_tasks, + monitor_config->maximum_timers, + monitor_config->maximum_semaphores, + monitor_config->maximum_message_queues, + monitor_config->maximum_extensions); + length = 0; + length += rtems_monitor_pad(CONTCOL, length); + length += fprintf(stdout,"partitions: %d; regions: %d; ports: %d; periods: %d\n", + monitor_config->maximum_partitions, + monitor_config->maximum_regions, + monitor_config->maximum_ports, + monitor_config->maximum_periods); +} diff --git a/cpukit/libmisc/monitor/mon-dname.c b/cpukit/libmisc/monitor/mon-dname.c new file mode 100644 index 0000000000..4f9cc1aef2 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-dname.c @@ -0,0 +1,116 @@ +/* + * RTEMS monitor driver names support. + * + * There are 2 "driver" things the monitor knows about. + * + * 1. Regular RTEMS drivers. + * This is a table indexed by major device number and + * containing driver entry points only. + * + * 2. Driver name table. + * A separate table of names for drivers. + * The table converts driver names to a major number + * as index into the driver table and a minor number + * for an argument to driver. + * + * Drivers are displayed with 'driver' command. + * Names are displayed with 'dname' command. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> + +#include <rtems/monitor.h> + +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ +#include <string.h> /* strncpy() */ + +#define DATACOL 15 +#define CONTCOL DATACOL /* continued col */ + +void +rtems_monitor_dname_canonical( + rtems_monitor_dname_t *canonical_dname, + void *dname_void +) +{ + rtems_driver_name_t *np = (rtems_driver_name_t *) dname_void; + + (void) strncpy(canonical_dname->name_string, np->device_name, sizeof(canonical_dname->name_string)); + canonical_dname->major = np->major; + canonical_dname->minor = np->minor; +} + +void * +rtems_monitor_dname_next( + void *object_information, + rtems_monitor_dname_t *canonical_dname, + rtems_id *next_id +) +{ + uint32_t n = rtems_get_index(*next_id); + rtems_driver_name_t *table = _IO_Driver_name_table; + rtems_driver_name_t *np = 0; + +/* XXX should we be using _IO_Number_of_devices */ + for (np = table + n ; n<_IO_Number_of_devices; n++, np++) + if (np->device_name) + goto done; + + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; + +done: + _Thread_Disable_dispatch(); + + /* + * dummy up a fake id and name for this item + */ + + canonical_dname->id = n; + canonical_dname->name = rtems_build_name('-', '-', '-', '-'); + + *next_id += 1; + return np; +} + +void +rtems_monitor_dname_dump_header( + boolean verbose +) +{ + fprintf(stdout,"\ + Major:Minor Name\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + +void +rtems_monitor_dname_dump( + rtems_monitor_dname_t *monitor_dname, + boolean verbose +) +{ + uint32_t length = 0; + + length += rtems_monitor_pad(6, length); + length += rtems_monitor_dump_hex(monitor_dname->major); + length += fprintf(stdout,":"); + length += rtems_monitor_dump_hex(monitor_dname->minor); + + length += rtems_monitor_pad(16, length); + length += fprintf(stdout,"%.*s", + (int) sizeof(monitor_dname->name_string), + (char *) monitor_dname->name_string); + + length += fprintf(stdout,"\n"); + length = 0; +} diff --git a/cpukit/libmisc/monitor/mon-driver.c b/cpukit/libmisc/monitor/mon-driver.c new file mode 100644 index 0000000000..12e84f414e --- /dev/null +++ b/cpukit/libmisc/monitor/mon-driver.c @@ -0,0 +1,140 @@ +/* + * RTEMS monitor IO (device drivers) support + * + * There are 2 "driver" things the monitor knows about. + * + * 1. Regular RTEMS drivers. + * This is a table indexed by major device number and + * containing driver entry points only. + * + * 2. Driver name table. + * A separate table of names for drivers. + * The table converts driver names to a major number + * as index into the driver table and a minor number + * for an argument to driver. + * + * Drivers are displayed with 'driver' command. + * Names are displayed with 'name' command. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> + +#include <rtems/monitor.h> + +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ + +#define DATACOL 15 +#define CONTCOL DATACOL /* continued col */ + + +void +rtems_monitor_driver_canonical( + rtems_monitor_driver_t *canonical_driver, + void *driver_void +) +{ + rtems_driver_address_table *d = (rtems_driver_address_table *) driver_void; + + rtems_monitor_symbol_canonical_by_value(&canonical_driver->initialization, + (void *) d->initialization_entry); + + rtems_monitor_symbol_canonical_by_value(&canonical_driver->open, + (void *) d->open_entry); + rtems_monitor_symbol_canonical_by_value(&canonical_driver->close, + (void *) d->close_entry); + rtems_monitor_symbol_canonical_by_value(&canonical_driver->read, + (void *) d->read_entry); + rtems_monitor_symbol_canonical_by_value(&canonical_driver->write, + (void *) d->write_entry); + rtems_monitor_symbol_canonical_by_value(&canonical_driver->control, + (void *) d->control_entry); +} + + +void * +rtems_monitor_driver_next( + void *object_info, + rtems_monitor_driver_t *canonical_driver, + rtems_id *next_id +) +{ + rtems_configuration_table *c = _Configuration_Table; + uint32_t n = rtems_get_index(*next_id); + + if (n >= c->number_of_device_drivers) + goto failed; + + _Thread_Disable_dispatch(); + + /* + * dummy up a fake id and name for this item + */ + + canonical_driver->id = n; + canonical_driver->name = rtems_build_name('-', '-', '-', '-'); + + *next_id += 1; + return (void *) (c->Device_driver_table + n); + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + + +void +rtems_monitor_driver_dump_header( + boolean verbose +) +{ + fprintf(stdout,"\ + Major Entry points\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + +void +rtems_monitor_driver_dump( + rtems_monitor_driver_t *monitor_driver, + boolean verbose +) +{ + uint32_t length = 0; + + length += fprintf(stdout," %d", monitor_driver->id); + + length += rtems_monitor_pad(13, length); + length += fprintf(stdout,"init: "); + length += rtems_monitor_symbol_dump(&monitor_driver->initialization, verbose); + length += fprintf(stdout,"; control: "); + length += rtems_monitor_symbol_dump(&monitor_driver->control, verbose); + length += fprintf(stdout,"\n"); + length = 0; + + length += rtems_monitor_pad(13, length); + + length += fprintf(stdout,"open: "); + length += rtems_monitor_symbol_dump(&monitor_driver->open, verbose); + length += fprintf(stdout,"; close: "); + length += rtems_monitor_symbol_dump(&monitor_driver->close, verbose); + length += fprintf(stdout,"\n"); + length = 0; + + length += rtems_monitor_pad(13, length); + + length += fprintf(stdout,"read: "); + length += rtems_monitor_symbol_dump(&monitor_driver->read, verbose); + length += fprintf(stdout,"; write: "); + length += rtems_monitor_symbol_dump(&monitor_driver->write, verbose); + length += fprintf(stdout,"\n"); + length = 0; +} diff --git a/cpukit/libmisc/monitor/mon-extension.c b/cpukit/libmisc/monitor/mon-extension.c new file mode 100644 index 0000000000..900fee5b87 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-extension.c @@ -0,0 +1,102 @@ +/* + * RTEMS Monitor extension support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> + +void +rtems_monitor_extension_canonical( + rtems_monitor_extension_t *canonical_extension, + void *extension_void +) +{ + Extension_Control *rtems_extension = (Extension_Control *) extension_void; + rtems_extensions_table *e = &rtems_extension->Extension.Callouts; + + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_create, + (void *) e->thread_create); + + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_start, + (void *) e->thread_start); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_restart, + (void *) e->thread_restart); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_delete, + (void *) e->thread_delete); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_tswitch, + (void *) e->thread_switch); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_begin, + (void *) e->thread_begin); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_exitted, + (void *) e->thread_exitted); + rtems_monitor_symbol_canonical_by_value(&canonical_extension->e_fatal, + (void *) e->fatal); +} + +void +rtems_monitor_extension_dump_header( + boolean verbose +) +{ + fprintf(stdout,"\ + ID NAME\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + + +/* + * Dump out the canonical form + */ + +void +rtems_monitor_extension_dump( + rtems_monitor_extension_t *monitor_extension, + boolean verbose +) +{ + uint32_t length = 0; + + length += rtems_monitor_dump_id(monitor_extension->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_extension->name); + + length += rtems_monitor_pad(18, length); + length += fprintf(stdout,"create: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_create, verbose); + length += fprintf(stdout,"; start: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_start, verbose); + length += fprintf(stdout,"; restart: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_restart, verbose); + length += fprintf(stdout,"\n"); + length = 0; + + length += rtems_monitor_pad(18, length); + length += fprintf(stdout,"delete: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_delete, verbose); + length += fprintf(stdout,"; switch: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_tswitch, verbose); + length += fprintf(stdout,"; begin: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_begin, verbose); + length += fprintf(stdout,"\n"); + length = 0; + + length += rtems_monitor_pad(18, length); + length += fprintf(stdout,"exitted: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_exitted, verbose); + length += fprintf(stdout,"; fatal: "); + length += rtems_monitor_symbol_dump(&monitor_extension->e_fatal, verbose); + length += fprintf(stdout,"\n"); + length = 0; + fprintf(stdout,"\n"); +} diff --git a/cpukit/libmisc/monitor/mon-itask.c b/cpukit/libmisc/monitor/mon-itask.c new file mode 100644 index 0000000000..f83d0e8561 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-itask.c @@ -0,0 +1,119 @@ +/* + * RTEMS Monitor init task support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> + +/* + * As above, but just for init tasks + */ +void +rtems_monitor_init_task_canonical( + rtems_monitor_init_task_t *canonical_itask, + void *itask_void +) +{ + rtems_initialization_tasks_table *rtems_itask = itask_void; + + rtems_monitor_symbol_canonical_by_value(&canonical_itask->entry, + (void *) rtems_itask->entry_point); + + canonical_itask->argument = rtems_itask->argument; + canonical_itask->stack_size = rtems_itask->stack_size; + canonical_itask->priority = rtems_itask->initial_priority; + canonical_itask->modes = rtems_itask->mode_set; + canonical_itask->attributes = rtems_itask->attribute_set; +} + +void * +rtems_monitor_init_task_next( + void *object_info, + rtems_monitor_init_task_t *canonical_init_task, + rtems_id *next_id +) +{ + rtems_configuration_table *c = _Configuration_Table; + rtems_initialization_tasks_table *itask; + uint32_t n = rtems_get_index(*next_id); + + if (n >= c->RTEMS_api_configuration->number_of_initialization_tasks) + goto failed; + + _Thread_Disable_dispatch(); + + itask = c->RTEMS_api_configuration->User_initialization_tasks_table + n; + + /* + * dummy up a fake id and name for this item + */ + + canonical_init_task->id = n; + canonical_init_task->name = itask->name; + + *next_id += 1; + return (void *) itask; + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + + +void +rtems_monitor_init_task_dump_header( + boolean verbose +) +{ + fprintf(stdout,"\ + # NAME ENTRY ARGUMENT PRIO MODES ATTRIBUTES STACK SIZE\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + +/* + */ + +void +rtems_monitor_init_task_dump( + rtems_monitor_init_task_t *monitor_itask, + boolean verbose +) +{ + int length = 0; + + length += rtems_monitor_dump_decimal(monitor_itask->id); + + length += rtems_monitor_pad(7, length); + length += rtems_monitor_dump_name(monitor_itask->name); + + length += rtems_monitor_pad(14, length); + length += rtems_monitor_symbol_dump(&monitor_itask->entry, verbose); + + length += rtems_monitor_pad(25, length); + length += fprintf(stdout,"%d [0x%x]", monitor_itask->argument, monitor_itask->argument); + + length += rtems_monitor_pad(39, length); + length += rtems_monitor_dump_priority(monitor_itask->priority); + + length += rtems_monitor_pad(46, length); + length += rtems_monitor_dump_modes(monitor_itask->modes); + + length += rtems_monitor_pad(54, length); + length += rtems_monitor_dump_attributes(monitor_itask->attributes); + + length += rtems_monitor_pad(66, length); + length += fprintf(stdout,"%d [0x%x]", monitor_itask->stack_size, monitor_itask->stack_size); + + fprintf(stdout,"\n"); +} diff --git a/cpukit/libmisc/monitor/mon-manager.c b/cpukit/libmisc/monitor/mon-manager.c new file mode 100644 index 0000000000..7e7a269cd6 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-manager.c @@ -0,0 +1,54 @@ +/* + * RTEMS Monitor "manager" support. + * Used to traverse object (chain) lists and print them out. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> + +/* + * "next" routine for all objects that are RTEMS manager objects + */ + +void * +rtems_monitor_manager_next( + void *table_void, + void *canonical, + rtems_id *next_id +) +{ + Objects_Information *table = table_void; + rtems_monitor_generic_t *copy; + Objects_Control *object = 0; + Objects_Locations location; + + /* + * When we are called, it must be local + */ + + if ( ! _Objects_Is_local_id(*next_id)) + goto done; + + object = _Objects_Get_next(table, *next_id, &location, next_id); + + if (object) + { + copy = (rtems_monitor_generic_t *) canonical; + copy->id = object->id; + if(table->is_string) + _Objects_Copy_name_raw(object->name, ©->name, sizeof(copy->name)); + else + _Objects_Copy_name_raw(&object->name, ©->name, sizeof(copy->name)); + } + +done: + return object; +} diff --git a/cpukit/libmisc/monitor/mon-monitor.c b/cpukit/libmisc/monitor/mon-monitor.c new file mode 100644 index 0000000000..d45df2dfc0 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-monitor.c @@ -0,0 +1,591 @@ +/* + * RTEMS monitor main body + * + * TODO: + * add stuff to RTEMS api + * rtems_get_name(id) + * rtems_get_type(id) + * rtems_build_id(node, type, num) + * Add a command to dump out info about an arbitrary id when + * types are added to id's + * rtems> id idnum + * idnum: node n, object: whatever, id: whatever + * allow id's to be specified as n:t:id, where 'n:t' is optional + * should have a separate monitor FILE stream (ala the debugger) + * remote request/response stuff should be cleaned up + * maybe we can use real rpc?? + * 'info' command to print out: + * interrupt stack location, direction and size + * floating point config stuff + * interrupt config stuff + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <termios.h> +#include <unistd.h> + +#include <rtems/monitor.h> + +#define STREQ(a,b) (strcmp(a,b) == 0) + +/* set by trap handler */ +extern rtems_tcb *debugger_interrupted_task; +extern rtems_context *debugger_interrupted_task_context; +extern uint32_t debugger_trap; + +/* + * Various id's for the monitor + * They need to be public variables for access by other agencies + * such as debugger and remote servers' + */ + +rtems_id rtems_monitor_task_id; + +uint32_t rtems_monitor_node; /* our node number */ +uint32_t rtems_monitor_default_node; /* current default for commands */ + +/* + * The rtems symbol table + */ + +rtems_symbol_table_t *rtems_monitor_symbols; + +/* + * User registered commands. + */ + +rtems_monitor_command_entry_t rtems_registered_commands; + +/* + * The top-level commands + */ + +rtems_monitor_command_entry_t rtems_monitor_commands[] = { + { "config", + "Show the system configuration.", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_CONFIG }, + &rtems_monitor_commands[1], + }, + { "itask", + "List init tasks for the system", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_INIT_TASK }, + &rtems_monitor_commands[2], + }, + { "mpci", + "Show the MPCI system configuration, if configured.", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_MPCI }, + &rtems_monitor_commands[3], + }, + { "pause", + "Monitor goes to \"sleep\" for specified ticks (default is 1). " + "Monitor will resume at end of period or if explicitly awakened\n" + " pause [ticks]", + 0, + rtems_monitor_pause_cmd, + { 0 }, + &rtems_monitor_commands[4], + }, + { "continue", + "Put the monitor to sleep waiting for an explicit wakeup from the " + "program running.\n", + 0, + rtems_monitor_continue_cmd, + { 0 }, + &rtems_monitor_commands[5], + }, + { "go", + "Alias for 'continue'", + 0, + rtems_monitor_continue_cmd, + { 0 }, + &rtems_monitor_commands[6], + }, + { "node", + "Specify default node number for commands that take id's.\n" + " node [ node number ]", + 0, + rtems_monitor_node_cmd, + { 0 }, + &rtems_monitor_commands[7], + }, + { "symbol", + "Display value associated with specified symbol. " + "Defaults to displaying all known symbols.\n" + " symbol [ symbolname [symbolname ... ] ]", + 0, + rtems_monitor_symbol_cmd, + { .symbol_table = &rtems_monitor_symbols }, + &rtems_monitor_commands[8], + }, + { "extension", + "Display information about specified extensions. " + "Default is to display information about all extensions on this node.\n" + " extension [id [id ...] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_EXTENSION }, + &rtems_monitor_commands[9], + }, + { "task", + "Display information about the specified tasks. " + "Default is to display information about all tasks on this node.\n" + " task [id [id ...] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_TASK }, + &rtems_monitor_commands[10], + }, + { "queue", + "Display information about the specified message queues. " + "Default is to display information about all queues on this node.\n" + " queue [id [id ... ] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_QUEUE }, + &rtems_monitor_commands[11], + }, + { "object", + "Display information about specified RTEMS objects. " + "Object id's must include 'type' information. " + "(which may normally be defaulted)\n" + " object [id [id ...] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_INVALID }, + &rtems_monitor_commands[12], + }, + { "driver", + "Display the RTEMS device driver table.\n" + " driver [ major [ major ... ] ]", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_DRIVER }, + &rtems_monitor_commands[13], + }, + { "dname", + "Displays information about named drivers.\n", + 0, + rtems_monitor_object_cmd, + { RTEMS_MONITOR_OBJECT_DNAME }, + &rtems_monitor_commands[14], + }, + { "exit", + "Invoke 'rtems_fatal_error_occurred' with 'status' " + "(default is RTEMS_SUCCESSFUL)\n" + " exit [status]", + 0, + rtems_monitor_fatal_cmd, + { .status_code = RTEMS_SUCCESSFUL }, + &rtems_monitor_commands[15], + }, + { "fatal", + "'exit' with fatal error; default error is RTEMS_TASK_EXITTED\n" + " fatal [status]", + 0, + rtems_monitor_fatal_cmd, + { .status_code = RTEMS_TASK_EXITTED }, /* exit value */ + &rtems_monitor_commands[16], + }, + { "quit", + "Alias for 'exit'\n", + 0, + rtems_monitor_fatal_cmd, + { .status_code = RTEMS_SUCCESSFUL }, /* exit value */ + &rtems_monitor_commands[17], + }, + { "help", + "Provide information about commands. " + "Default is show basic command summary.\n" + "help [ command [ command ] ]", + 0, + rtems_monitor_help_cmd, + { .monitor_command_entry = rtems_monitor_commands }, + &rtems_monitor_commands[18], + }, +#ifdef CPU_INVOKE_DEBUGGER + { "debugger", + "Enter the debugger, if possible. " + "A continue from the debugger will return to the monitor.\n", + 0, + rtems_monitor_debugger_cmd, + { 0 }, + &rtems_monitor_commands[19], + }, +#endif + { 0, 0, 0, 0, { 0 }, &rtems_registered_commands }, +}; + + +rtems_status_code +rtems_monitor_suspend(rtems_interval timeout) +{ + rtems_event_set event_set; + rtems_status_code status; + + status = rtems_event_receive(MONITOR_WAKEUP_EVENT, + RTEMS_DEFAULT_OPTIONS, + timeout, + &event_set); + return status; +} + +void +rtems_monitor_wakeup(void) +{ + rtems_status_code status; + + status = rtems_event_send(rtems_monitor_task_id, MONITOR_WAKEUP_EVENT); +} + +void +rtems_monitor_debugger_cmd( + int argc, + char **argv, + rtems_monitor_command_arg_t* command_arg, + boolean verbose +) +{ +#ifdef CPU_INVOKE_DEBUGGER + CPU_INVOKE_DEBUGGER; +#endif +} + +void +rtems_monitor_pause_cmd( + int argc, + char **argv, + rtems_monitor_command_arg_t* command_arg, + boolean verbose +) +{ + if (argc == 1) + rtems_monitor_suspend(1); + else + rtems_monitor_suspend(strtoul(argv[1], 0, 0)); +} + +void +rtems_monitor_fatal_cmd( + int argc, + char **argv, + rtems_monitor_command_arg_t* command_arg, + boolean verbose +) +{ + if (argc == 1) + rtems_fatal_error_occurred(command_arg->status_code); + else + rtems_fatal_error_occurred(strtoul(argv[1], 0, 0)); +} + +void +rtems_monitor_continue_cmd( + int argc, + char **argv, + rtems_monitor_command_arg_t* command_arg, + boolean verbose +) +{ + rtems_monitor_suspend(RTEMS_NO_TIMEOUT); +} + +void +rtems_monitor_node_cmd( + int argc, + char **argv, + rtems_monitor_command_arg_t* command_arg, + boolean verbose +) +{ + uint32_t new_node = rtems_monitor_default_node; + + switch (argc) + { + case 1: /* no node, just set back to ours */ + new_node = rtems_monitor_node; + break; + + case 2: + new_node = strtoul(argv[1], 0, 0); + break; + + default: + fprintf(stdout,"invalid syntax, try 'help node'\n"); + break; + } + + if ((new_node >= 1) && + _Configuration_MP_table && + (new_node <= _Configuration_MP_table->maximum_nodes)) + rtems_monitor_default_node = new_node; +} + + +/* + * Function: rtems_monitor_symbols_loadup + * + * Description: + * Create and load the monitor's symbol table. + * We are reading the output format of 'gnm' which looks like this: + * + * 400a7068 ? _Rate_monotonic_Information + * 400a708c ? _Thread_Dispatch_disable_level + * 400a7090 ? _Configuration_Table + * + * We ignore the type field. + * + * Side Effects: + * Creates and fills in 'rtems_monitor_symbols' table + * + * TODO + * there should be a BSP #define or something like that + * to do this; Assuming stdio is crazy. + * Someday this should know BFD + * Maybe we could get objcopy to just copy the symbol areas + * and copy that down. + * + */ + +void +rtems_monitor_symbols_loadup(void) +{ + FILE *fp; + char buffer[128]; + + if (rtems_monitor_symbols) + rtems_symbol_table_destroy(rtems_monitor_symbols); + + rtems_monitor_symbols = rtems_symbol_table_create(10); + if (rtems_monitor_symbols == 0) + return; + + fp = fopen("symbols", "r"); + + if (fp == 0) + return; + + while (fgets(buffer, sizeof(buffer) - 1, fp)) + { + char *symbol; + char *value; + char *ignored_type; + + value = strtok(buffer, " \t\n"); + ignored_type = strtok(0, " \t\n"); + symbol = strtok(0, " \t\n"); + + if (symbol && ignored_type && value) + { + rtems_symbol_t *sp; + sp = rtems_symbol_create(rtems_monitor_symbols, + symbol, + (uint32_t ) strtoul(value, 0, 16)); + if (sp == 0) + { + fprintf(stdout,"could not define symbol '%s'\n", symbol); + goto done; + } + } + else + { + fprintf(stdout,"parsing error on '%s'\n", buffer); + goto done; + } + } + +done: + return; +} + +/* + * User registered commands. + */ + +int +rtems_monitor_insert_cmd ( + rtems_monitor_command_entry_t *command +) +{ + rtems_monitor_command_entry_t **p = &rtems_registered_commands.next; + + command->next = 0; + + while (*p) { + if ( STREQ(command->command, (*p)->command) ) + return 0; + p = & (*p)->next; + } + *p = command; + return 1; +} + +int +rtems_monitor_erase_cmd ( + rtems_monitor_command_entry_t *command +) +{ + rtems_monitor_command_entry_t **p = & rtems_registered_commands.next; + + while (*p) { + if ( STREQ(command->command, (*p)->command) ) { + *p = (*p)->next; + command->next = 0; + return 1; + } + p = & (*p)->next; + } + return 0; + +} + +/* + * Main monitor command loop + */ + +void +rtems_monitor_task( + rtems_task_argument monitor_flags +) +{ + rtems_tcb *debugee = 0; + rtems_context *rp; + rtems_context_fp *fp; + char command_buffer[513]; + int argc; + char *argv[64]; + boolean verbose = FALSE; + struct termios term; + + /* + * Make the stdin stream characte not line based. + */ + + if (tcgetattr (STDIN_FILENO, &term) < 0) + { + fprintf(stdout,"rtems-monitor: cannot get terminal attributes.\n"); + } + else + { + /* + * No echo, no canonical processing. + */ + + term.c_lflag &= ~(ECHO | ICANON | IEXTEN); + + /* + * No sigint on BREAK, CR-to-NL off, input parity off, + * don't strip 8th bit on input, output flow control off + */ + + term.c_lflag &= ~(INPCK | ISTRIP | IXON); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; + + if (tcsetattr (STDIN_FILENO, TCSANOW, &term) < 0) + { + fprintf(stdout,"cannot set terminal attributes\n"); + } + } + + if (monitor_flags & RTEMS_MONITOR_SUSPEND) + (void) rtems_monitor_suspend(RTEMS_NO_TIMEOUT); + + for (;;) + { + extern rtems_tcb * _Thread_Executing; + rtems_monitor_command_entry_t *command; + + debugee = _Thread_Executing; + rp = &debugee->Registers; +#if (CPU_HARDWARE_FP == TRUE) || (CPU_SOFTWARE_FP == TRUE) + fp = debugee->fp_context; /* possibly 0 */ +#else + fp = 0; +#endif + + if (0 == rtems_monitor_command_read(command_buffer, &argc, argv)) + continue; + if ((command = rtems_monitor_command_lookup(rtems_monitor_commands, + argc, + argv)) == 0) + { + /* no command */ + fprintf(stdout,"Unrecognised command; try 'help'\n"); + continue; + } + + command->command_function(argc, argv, &command->command_arg, verbose); + + fflush(stdout); + } +} + + +void +rtems_monitor_kill(void) +{ + if (rtems_monitor_task_id) + rtems_task_delete(rtems_monitor_task_id); + rtems_monitor_task_id = 0; + + rtems_monitor_server_kill(); +} + +void +rtems_monitor_init( + uint32_t monitor_flags +) +{ + rtems_status_code status; + + rtems_monitor_kill(); + + status = rtems_task_create(RTEMS_MONITOR_NAME, + 1, + RTEMS_MINIMUM_STACK_SIZE * 2, + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_DEFAULT_ATTRIBUTES, + &rtems_monitor_task_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor task"); + return; + } + + rtems_monitor_node = rtems_get_node(rtems_monitor_task_id); + rtems_monitor_default_node = rtems_monitor_node; + + rtems_monitor_symbols_loadup(); + + if (monitor_flags & RTEMS_MONITOR_GLOBAL) + rtems_monitor_server_init(monitor_flags); + + /* + * Start the monitor task itself + */ + + status = rtems_task_start(rtems_monitor_task_id, + rtems_monitor_task, + monitor_flags); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not start monitor"); + return; + } +} diff --git a/cpukit/libmisc/monitor/mon-mpci.c b/cpukit/libmisc/monitor/mon-mpci.c new file mode 100644 index 0000000000..2cbd48eb3c --- /dev/null +++ b/cpukit/libmisc/monitor/mon-mpci.c @@ -0,0 +1,162 @@ +/* + * RTEMS MPCI Config display support + * + * TODO + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ + +#define DATACOL 15 + +/* + * Fill in entire monitor config table + * for sending to a remote monitor or printing on the local system + */ + +void +rtems_monitor_mpci_canonical( + rtems_monitor_mpci_t *canonical_mpci, + void *config_void +) +{ + rtems_configuration_table *c = _Configuration_Table; + rtems_multiprocessing_table *m; + rtems_mpci_table *mt; + + m = c->User_multiprocessing_table; + if (m == 0) + return; + mt = m->User_mpci_table; + + canonical_mpci->node = m->node; + canonical_mpci->maximum_nodes = m->maximum_nodes; + canonical_mpci->maximum_global_objects = m->maximum_global_objects; + canonical_mpci->maximum_proxies = m->maximum_proxies; + + canonical_mpci->default_timeout = mt->default_timeout; + canonical_mpci->maximum_packet_size = mt->maximum_packet_size; + + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->initialization, + (void *) mt->initialization); + + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->get_packet, + (void *) mt->get_packet); + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->return_packet, + (void *) mt->return_packet); + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->send_packet, + (void *) mt->send_packet); + rtems_monitor_symbol_canonical_by_value(&canonical_mpci->receive_packet, + (void *) mt->receive_packet); +} + +/* + * This is easy, since there is only 1 (altho we could get them from + * other nodes...) + */ + +void * +rtems_monitor_mpci_next( + void *object_info, + rtems_monitor_mpci_t *canonical_mpci, + rtems_id *next_id +) +{ + rtems_configuration_table *c = _Configuration_Table; + int n = rtems_get_index(*next_id); + + if (n >= 1) + goto failed; + + if ( ! c->User_multiprocessing_table) + goto failed; + + _Thread_Disable_dispatch(); + + *next_id += 1; + return (void *) c; + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + + +void +rtems_monitor_mpci_dump_header( + boolean verbose +) +{ + fprintf(stdout,"\ + max max max default max\n\ + node nodes globals proxies timeout pktsize\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + + +void +rtems_monitor_mpci_dump( + rtems_monitor_mpci_t *monitor_mpci, + boolean verbose +) +{ + uint32_t length = 0; + + length += rtems_monitor_pad(2, length); + length += fprintf(stdout," %d", monitor_mpci->node); + length += rtems_monitor_pad(11, length); + length += fprintf(stdout,"%d", monitor_mpci->maximum_nodes); + + length += rtems_monitor_pad(18, length); + length += rtems_monitor_dump_decimal(monitor_mpci->maximum_global_objects); + + length += rtems_monitor_pad(28, length); + length += rtems_monitor_dump_decimal(monitor_mpci->maximum_proxies); + + length += rtems_monitor_pad(37, length); + length += rtems_monitor_dump_decimal(monitor_mpci->default_timeout); + + length += rtems_monitor_pad(46, length); + length += rtems_monitor_dump_decimal(monitor_mpci->maximum_packet_size); + + fprintf(stdout,"\n"); + length = 0; + length += rtems_monitor_pad(DATACOL, length); + + length += fprintf(stdout,"init: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->initialization, verbose); + + fprintf(stdout,"\n"); + length = 0; + length += rtems_monitor_pad(DATACOL, length); + + length += fprintf(stdout,"get: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->get_packet, verbose); + length += fprintf(stdout,"; return: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->return_packet, verbose); + + fprintf(stdout,"\n"); + length = 0; + length += rtems_monitor_pad(DATACOL, length); + + length += fprintf(stdout,"send: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->send_packet, verbose); + length += fprintf(stdout,"; receive: "); + length += rtems_monitor_symbol_dump(&monitor_mpci->receive_packet, verbose); + + fprintf(stdout,"\n"); + length = 0; +} diff --git a/cpukit/libmisc/monitor/mon-object.c b/cpukit/libmisc/monitor/mon-object.c new file mode 100644 index 0000000000..4428234220 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-object.c @@ -0,0 +1,387 @@ +/* + * RTEMS Monitor "object" support. + * + * Used to traverse object lists and print them out. + * An object can be an RTEMS object (chain based stuff) or + * a "misc" object such as a device driver. + * + * Each object has its own file in this directory (eg: extension.c) + * That file provides routines to convert a "native" structure + * to its canonical form, print a canonical structure, etc. + * + * TODO: + * should allow for non-numeric id's??? + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> +#include <stdlib.h> /* strtoul() */ +#include <string.h> /* memcpy() */ + +#define NUMELEMS(arr) (sizeof(arr) / sizeof(arr[0])) + +/* + * add: + * next + */ + +rtems_monitor_object_info_t rtems_monitor_object_info[] = +{ + { RTEMS_MONITOR_OBJECT_CONFIG, + (void *) 0, + sizeof(rtems_monitor_config_t), + (rtems_monitor_object_next_fn) rtems_monitor_config_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_config_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_config_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_config_dump, + }, + { RTEMS_MONITOR_OBJECT_MPCI, + (void *) 0, +#if defined(RTEMS_MULTIPROCESSING) + sizeof(rtems_monitor_mpci_t), + (rtems_monitor_object_next_fn) rtems_monitor_mpci_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_mpci_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_mpci_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_mpci_dump, +#else + 0, + (rtems_monitor_object_next_fn) 0, + (rtems_monitor_object_canonical_fn) 0, + (rtems_monitor_object_dump_header_fn) 0, + (rtems_monitor_object_dump_fn) 0, +#endif + }, + { RTEMS_MONITOR_OBJECT_INIT_TASK, + (void *) 0, + sizeof(rtems_monitor_init_task_t), + (rtems_monitor_object_next_fn) rtems_monitor_init_task_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_init_task_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_init_task_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_init_task_dump, + }, + { RTEMS_MONITOR_OBJECT_TASK, + (void *) &_RTEMS_tasks_Information, + sizeof(rtems_monitor_task_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_task_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_task_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_task_dump, + }, + { RTEMS_MONITOR_OBJECT_QUEUE, + (void *) &_Message_queue_Information, + sizeof(rtems_monitor_queue_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_queue_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_queue_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_queue_dump, + }, + { RTEMS_MONITOR_OBJECT_EXTENSION, + (void *) &_Extension_Information, + sizeof(rtems_monitor_extension_t), + (rtems_monitor_object_next_fn) rtems_monitor_manager_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_extension_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_extension_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_extension_dump, + }, + { RTEMS_MONITOR_OBJECT_DRIVER, + (void *) 0, + sizeof(rtems_monitor_driver_t), + (rtems_monitor_object_next_fn) rtems_monitor_driver_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_driver_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_driver_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_driver_dump, + }, + { RTEMS_MONITOR_OBJECT_DNAME, + /* XXX now that the driver name table is allocated from the */ + /* XXX Workspace, this does not work */ + (void *) 0, + /* (void *) _IO_Driver_name_table, */ + sizeof(rtems_monitor_dname_t), + (rtems_monitor_object_next_fn) rtems_monitor_dname_next, + (rtems_monitor_object_canonical_fn) rtems_monitor_dname_canonical, + (rtems_monitor_object_dump_header_fn) rtems_monitor_dname_dump_header, + (rtems_monitor_object_dump_fn) rtems_monitor_dname_dump, + }, +}; + +/* + * Allow id's to be specified without the node number or + * type for convenience. + */ + +rtems_id +rtems_monitor_id_fixup( + rtems_id id, + uint32_t default_node, + rtems_monitor_object_type_t type +) +{ + uint32_t node; + + node = rtems_get_node(id); + if (node == 0) + { + if (rtems_get_class(id) != OBJECTS_NO_CLASS) + type = rtems_get_class(id); + + id = _Objects_Build_id( + OBJECTS_CLASSIC_API, type, default_node, rtems_get_index(id)); + } + return id; +} + + +rtems_monitor_object_info_t * +rtems_monitor_object_lookup( + rtems_monitor_object_type_t type +) +{ + rtems_monitor_object_info_t *p; + for (p = &rtems_monitor_object_info[0]; + p < &rtems_monitor_object_info[NUMELEMS(rtems_monitor_object_info)]; + p++) + { + if (p->type == type) + return p; + } + return 0; +} + +rtems_id +rtems_monitor_object_canonical_next_remote( + rtems_monitor_object_type_t type, + rtems_id id, + void *canonical +) +{ + rtems_id next_id; + rtems_status_code status; + rtems_monitor_server_request_t request; + rtems_monitor_server_response_t response; + + /* + * Send request + */ + + request.command = RTEMS_MONITOR_SERVER_CANONICAL; + request.argument0 = (uint32_t ) type; + request.argument1 = (uint32_t ) id; + + status = rtems_monitor_server_request(rtems_get_node(id), &request, &response); + if (status != RTEMS_SUCCESSFUL) + goto failed; + + /* + * process response + */ + + next_id = (rtems_id) response.result0; + if (next_id != RTEMS_OBJECT_ID_FINAL) + (void) memcpy(canonical, &response.payload, response.result1); + + return next_id; + +failed: + return RTEMS_OBJECT_ID_FINAL; + +} + + +rtems_id +rtems_monitor_object_canonical_next( + rtems_monitor_object_info_t *info, + rtems_id id, + void *canonical +) +{ + rtems_id next_id; + void *raw_item; + + if ( ! _Objects_Is_local_id(id)) + next_id = rtems_monitor_object_canonical_next_remote(info->type, + id, + canonical); + else + { + next_id = id; + + raw_item = (void *) info->next(info->object_information, + canonical, + &next_id); + + if (raw_item) + { + info->canonical(canonical, raw_item); + _Thread_Enable_dispatch(); + } + } + return next_id; +} + + +/* + * this is routine server invokes locally to get the type + */ + +rtems_id +rtems_monitor_object_canonical_get( + rtems_monitor_object_type_t type, + rtems_id id, + void *canonical, + uint32_t *size_p +) +{ + rtems_monitor_object_info_t *info; + rtems_id next_id; + + *size_p = 0; + + info = rtems_monitor_object_lookup(type); + + if (info == 0) + return RTEMS_OBJECT_ID_FINAL; + + next_id = rtems_monitor_object_canonical_next(info, id, canonical); + *size_p = info->size; + + return next_id; +} + + +void +rtems_monitor_object_dump_1( + rtems_monitor_object_info_t *info, + rtems_id id, + boolean verbose +) +{ + rtems_id next_id; + rtems_monitor_union_t canonical; + + if ((next_id = rtems_monitor_object_canonical_next( + info, + id, + &canonical)) != RTEMS_OBJECT_ID_FINAL) + { + /* + * If the one we actually got is the one we wanted, then + * print it out. + * For ones that have an id field, this works fine, + * for all others, always dump it out. + * + * HACK: the way we determine whether there is an id is a hack. + * + * by the way: the reason we try to not have an id, is that some + * of the canonical structures are almost too big for shared + * memory driver (eg: mpci) + */ + + if ((info->next != rtems_monitor_manager_next) || + (id == canonical.generic.id)) + info->dump(&canonical, verbose); + } +} + +void +rtems_monitor_object_dump_all( + rtems_monitor_object_info_t *info, + boolean verbose +) +{ + rtems_id next_id; + rtems_monitor_union_t canonical; + + next_id = RTEMS_OBJECT_ID_INITIAL(OBJECTS_CLASSIC_API, info->type, rtems_monitor_default_node); + + while ((next_id = rtems_monitor_object_canonical_next( + info, + next_id, + &canonical)) != RTEMS_OBJECT_ID_FINAL) + { + info->dump(&canonical, verbose); + } +} + +void +rtems_monitor_object_cmd( + int argc, + char **argv, + rtems_monitor_command_arg_t *command_arg, + boolean verbose +) +{ + int arg; + rtems_monitor_object_info_t *info = 0; + rtems_monitor_object_type_t type ; + + /* what is the default type? */ + type = command_arg->monitor_object; + + if (argc == 1) + { + if (type == RTEMS_MONITOR_OBJECT_INVALID) + { + fprintf(stdout,"A type must be specified to \"dump all\"\n"); + goto done; + } + + info = rtems_monitor_object_lookup(type); + if (info == 0) + goto not_found; + + if (info->dump_header) + info->dump_header(verbose); + rtems_monitor_object_dump_all(info, verbose); + } + else + { + uint32_t default_node = rtems_monitor_default_node; + rtems_monitor_object_type_t last_type = RTEMS_MONITOR_OBJECT_INVALID; + rtems_id id; + + for (arg=1; argv[arg]; arg++) + { + id = (rtems_id) strtoul(argv[arg], 0, 16); + id = rtems_monitor_id_fixup(id, default_node, type); + type = (rtems_monitor_object_type_t) rtems_get_class(id); + + /* + * Allow the item type to change in the middle + * of the command. If the type changes, then + * just dump out a new header and keep on going. + */ + if (type != last_type) + { + info = rtems_monitor_object_lookup(type); + if (info == 0) + goto not_found; + + if (info->dump_header) + info->dump_header(verbose); + } + + if (info == 0) + { +not_found: fprintf(stdout,"Invalid or unsupported type %d\n", type); + goto done; + } + + rtems_monitor_object_dump_1(info, id, verbose); + + default_node = rtems_get_node(id); + + last_type = type; + } + } +done: + return; +} diff --git a/cpukit/libmisc/monitor/mon-prmisc.c b/cpukit/libmisc/monitor/mon-prmisc.c new file mode 100644 index 0000000000..5104962650 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-prmisc.c @@ -0,0 +1,268 @@ +/* + * Print misc stuff for the monitor dump routines + * Each routine returns the number of characters it output. + * + * TODO: + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <rtems/assoc.h> + +#include <stdio.h> +#include <ctype.h> + +void +rtems_monitor_separator(void) +{ + fprintf(stdout,"------------------------------------------------------------------------------\n"); +} + +uint32_t +rtems_monitor_pad( + uint32_t destination_column, + uint32_t current_column +) +{ + int pad_length; + + if (destination_column <= current_column) + pad_length = 1; + else + pad_length = destination_column - current_column; + + return fprintf(stdout,"%*s", pad_length, ""); +} + +uint32_t +rtems_monitor_dump_char(uint8_t ch) +{ + if (isprint(ch)) + return fprintf(stdout,"%c", ch); + else + return fprintf(stdout,"%02x", ch); +} + +uint32_t +rtems_monitor_dump_decimal(uint32_t num) +{ + return fprintf(stdout,"%4d", num); +} + +uint32_t +rtems_monitor_dump_hex(uint32_t num) +{ + return fprintf(stdout,"0x%x", num); +} + +uint32_t +rtems_monitor_dump_assoc_bitfield( + rtems_assoc_t *ap, + char *separator, + uint32_t value + ) +{ + uint32_t b; + uint32_t length = 0; + const char *name; + + for (b = 1; b; b <<= 1) + if (b & value) + { + if (length) + length += fprintf(stdout,"%s", separator); + + name = rtems_assoc_name_by_local(ap, b); + + if (name) + length += fprintf(stdout,"%s", name); + else + length += fprintf(stdout,"0x%x", b); + } + + return length; +} + +uint32_t +rtems_monitor_dump_id(rtems_id id) +{ + return fprintf(stdout,"%08x", id); +} + +uint32_t +rtems_monitor_dump_name(rtems_name name) +{ + uint32_t i; + uint32_t length = 0; + union { + uint32_t ui; + char c[4]; + } u; + + u.ui = (uint32_t ) name; + +#if (CPU_BIG_ENDIAN == TRUE) + for (i=0; i<sizeof(u.c); i++) + length += rtems_monitor_dump_char(u.c[i]); +#else + for (i=0; i<sizeof(u.c); i++) + length += rtems_monitor_dump_char(u.c[sizeof(u.c)-1-i]); +#endif + return length; +} + +uint32_t +rtems_monitor_dump_priority(rtems_task_priority priority) +{ + return fprintf(stdout,"%3d", priority); +} + + +rtems_assoc_t rtems_monitor_state_assoc[] = { + { "DORM", STATES_DORMANT }, + { "SUSP", STATES_SUSPENDED }, + { "TRANS", STATES_TRANSIENT }, + { "DELAY", STATES_DELAYING }, + { "Wbuf", STATES_WAITING_FOR_BUFFER }, + { "Wseg", STATES_WAITING_FOR_SEGMENT }, + { "Wmsg" , STATES_WAITING_FOR_MESSAGE }, + { "Wevnt", STATES_WAITING_FOR_EVENT }, + { "Wsem", STATES_WAITING_FOR_SEMAPHORE }, + { "Wtime", STATES_WAITING_FOR_TIME }, + { "Wrpc", STATES_WAITING_FOR_RPC_REPLY }, + { "Wmutex", STATES_WAITING_FOR_MUTEX }, + { "Wcvar", STATES_WAITING_FOR_CONDITION_VARIABLE }, + { "Wjatx", STATES_WAITING_FOR_JOIN_AT_EXIT }, + { "Wsig", STATES_WAITING_FOR_SIGNAL }, + { "WRATE", STATES_WAITING_FOR_PERIOD }, + { "Wisig", STATES_INTERRUPTIBLE_BY_SIGNAL }, + { 0, 0, 0 }, +}; + +uint32_t +rtems_monitor_dump_state(States_Control state) +{ + uint32_t length = 0; + + if (state == STATES_READY) /* assoc doesn't deal with this as it is 0 */ + length += fprintf(stdout,"READY"); + + length += rtems_monitor_dump_assoc_bitfield(rtems_monitor_state_assoc, + ":", + state); + return length; +} + +rtems_assoc_t rtems_monitor_attribute_assoc[] = { + { "FL", RTEMS_FLOATING_POINT }, + { "GL", RTEMS_GLOBAL }, + { "PR", RTEMS_PRIORITY }, + { "BI", RTEMS_BINARY_SEMAPHORE }, + { "IN", RTEMS_INHERIT_PRIORITY }, + { 0, 0, 0 }, +}; + +uint32_t +rtems_monitor_dump_attributes(rtems_attribute attributes) +{ + uint32_t length = 0; + + if (attributes == RTEMS_DEFAULT_ATTRIBUTES) /* value is 0 */ + length += fprintf(stdout,"DEFAULT"); + + length += rtems_monitor_dump_assoc_bitfield(rtems_monitor_attribute_assoc, + ":", + attributes); + return length; +} + +rtems_assoc_t rtems_monitor_modes_assoc[] = { + { "nP", RTEMS_NO_PREEMPT }, + { "T", RTEMS_TIMESLICE }, + { "nA", RTEMS_NO_ASR }, + { 0, 0, 0 }, +}; + +uint32_t +rtems_monitor_dump_modes(rtems_mode modes) +{ + uint32_t length = 0; + + if (modes == RTEMS_DEFAULT_MODES) /* value is 0 */ + length += fprintf(stdout,"P:T:nA"); + + length += rtems_monitor_dump_assoc_bitfield(rtems_monitor_modes_assoc, + ":", + modes); + return length; +} + +rtems_assoc_t rtems_monitor_events_assoc[] = { + { "0", RTEMS_EVENT_0 }, + { "1", RTEMS_EVENT_1 }, + { "2", RTEMS_EVENT_2 }, + { "3", RTEMS_EVENT_3 }, + { "4", RTEMS_EVENT_4 }, + { "5", RTEMS_EVENT_5 }, + { "6", RTEMS_EVENT_6 }, + { "7", RTEMS_EVENT_7 }, + { "8", RTEMS_EVENT_8 }, + { "9", RTEMS_EVENT_9 }, + { "10", RTEMS_EVENT_10 }, + { "11", RTEMS_EVENT_11 }, + { "12", RTEMS_EVENT_12 }, + { "13", RTEMS_EVENT_13 }, + { "14", RTEMS_EVENT_14 }, + { "15", RTEMS_EVENT_15 }, + { "16", RTEMS_EVENT_16 }, + { "17", RTEMS_EVENT_17 }, + { "18", RTEMS_EVENT_18 }, + { "19", RTEMS_EVENT_19 }, + { "20", RTEMS_EVENT_20 }, + { "21", RTEMS_EVENT_21 }, + { "22", RTEMS_EVENT_22 }, + { "23", RTEMS_EVENT_23 }, + { "24", RTEMS_EVENT_24 }, + { "25", RTEMS_EVENT_25 }, + { "26", RTEMS_EVENT_26 }, + { "27", RTEMS_EVENT_27 }, + { "28", RTEMS_EVENT_28 }, + { "29", RTEMS_EVENT_29 }, + { "30", RTEMS_EVENT_30 }, + { "31", RTEMS_EVENT_31 }, + { 0, 0, 0 }, +}; + +uint32_t +rtems_monitor_dump_events(rtems_event_set events) +{ + uint32_t length = 0; + + if (events == EVENT_SETS_NONE_PENDING) /* value is 0 */ + length += fprintf(stdout,"NONE"); + + length += rtems_monitor_dump_assoc_bitfield(rtems_monitor_events_assoc, + ":", + events); + return length; +} + +uint32_t +rtems_monitor_dump_notepad(uint32_t *notepad) +{ + uint32_t length = 0; + int i; + + for (i=0; i < RTEMS_NUMBER_NOTEPADS; i++) + if (notepad[i]) + length += fprintf(stdout,"%d: 0x%x ", i, notepad[i]); + + return length; +} diff --git a/cpukit/libmisc/monitor/mon-queue.c b/cpukit/libmisc/monitor/mon-queue.c new file mode 100644 index 0000000000..1714808fd8 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-queue.c @@ -0,0 +1,68 @@ +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> + +void +rtems_monitor_queue_canonical( + rtems_monitor_queue_t *canonical_queue, + void *queue_void +) +{ + Message_queue_Control *rtems_queue = (Message_queue_Control *) queue_void; + + canonical_queue->attributes = rtems_queue->attribute_set; + canonical_queue->maximum_message_size = rtems_queue->message_queue.maximum_message_size; + canonical_queue->maximum_pending_messages = rtems_queue->message_queue.maximum_pending_messages; + canonical_queue->number_of_pending_messages = rtems_queue->message_queue.number_of_pending_messages; +} + +void +rtems_monitor_queue_dump_header( + boolean verbose +) +{ + fprintf(stdout,"\ + ID NAME ATTRIBUTES PEND MAXPEND MAXSIZE\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + rtems_monitor_separator(); +} + + +/* + * Dump out the "next" queue indicated by 'id'. + * Returns next one to check. + * Returns RTEMS_OBJECT_ID_FINAL when all done + */ + +void +rtems_monitor_queue_dump( + rtems_monitor_queue_t *monitor_queue, + boolean verbose +) +{ + uint32_t length = 0; + + length += rtems_monitor_dump_id(monitor_queue->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_queue->name); + length += rtems_monitor_pad(19, length); + length += rtems_monitor_dump_attributes(monitor_queue->attributes); + length += rtems_monitor_pad(31, length); + length += rtems_monitor_dump_decimal(monitor_queue->number_of_pending_messages); + length += rtems_monitor_pad(39, length); + length += rtems_monitor_dump_decimal(monitor_queue->maximum_pending_messages); + length += rtems_monitor_pad(48, length); + length += rtems_monitor_dump_decimal(monitor_queue->maximum_message_size); + + fprintf(stdout,"\n"); +} diff --git a/cpukit/libmisc/monitor/mon-server.c b/cpukit/libmisc/monitor/mon-server.c new file mode 100644 index 0000000000..b04e5f2390 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-server.c @@ -0,0 +1,307 @@ +/* + * RTEMS monitor server (handles requests for info from RTEMS monitors + * running on other nodes) + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rtems/monitor.h> + +/* + * Various id's for the server + */ + +rtems_id rtems_monitor_server_task_id; +rtems_id rtems_monitor_server_request_queue_id; /* our server */ +rtems_id *rtems_monitor_server_request_queue_ids; /* all servers */ +rtems_id rtems_monitor_server_response_queue_id; /* our server */ + + +/* + * Send a request to a server task + */ + +rtems_status_code +rtems_monitor_server_request( + uint32_t server_node, + rtems_monitor_server_request_t *request, + rtems_monitor_server_response_t *response +) +{ + rtems_id server_id; + rtems_status_code status; + uint32_t size; + + /* + * What is id of monitor on target node? + * Look it up if we don't know it yet. + */ + + server_id = rtems_monitor_server_request_queue_ids[server_node]; + if (server_id == 0) + { + status = rtems_message_queue_ident(RTEMS_MONITOR_QUEUE_NAME, + server_node, + &server_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "ident of remote server failed"); + goto done; + } + + rtems_monitor_server_request_queue_ids[server_node] = server_id; + } + + request->return_id = rtems_monitor_server_response_queue_id; + + status = rtems_message_queue_send(server_id, request, sizeof(*request)); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "monitor server request send failed"); + goto done; + } + + /* + * Await response, if requested + */ + + if (response) + { + status = rtems_message_queue_receive(rtems_monitor_server_response_queue_id, + response, + &size, + RTEMS_WAIT, + 100); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "server did not respond"); + + /* maybe server task was restarted; look it up again next time */ + rtems_monitor_server_request_queue_ids[server_node] = 0; + + goto done; + } + + if (response->command != RTEMS_MONITOR_SERVER_RESPONSE) + { + status = RTEMS_INCORRECT_STATE; + goto done; + } + } + +done: + return status; +} + + + +/* + * monitor server task + */ + +void +rtems_monitor_server_task( + rtems_task_argument monitor_flags +) +{ + rtems_monitor_server_request_t request; + rtems_monitor_server_response_t response; + rtems_status_code status; + uint32_t size; + + for (;;) + { + status = rtems_message_queue_receive( + rtems_monitor_server_request_queue_id, + &request, + &size, + RTEMS_WAIT, + (rtems_interval) 0); + + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "monitor server msg queue receive error"); + goto failed; + } + + if (size != sizeof(request)) + { + rtems_error(0, "monitor server bad size on receive"); + goto failed; + } + + switch (request.command) + { + case RTEMS_MONITOR_SERVER_CANONICAL: + { + rtems_monitor_object_type_t object_type; + rtems_id id; + rtems_id next_id; + + object_type = (rtems_monitor_object_type_t) request.argument0; + id = (rtems_id) request.argument1; + next_id = rtems_monitor_object_canonical_get(object_type, + id, + &response.payload, + &size); + + response.command = RTEMS_MONITOR_SERVER_RESPONSE; + response.result0 = next_id; + response.result1 = size; + +#define SERVER_OVERHEAD (RTEMS_offsetof(rtems_monitor_server_response_t, \ + payload)) + + status = rtems_message_queue_send(request.return_id, + &response, + size + SERVER_OVERHEAD); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "response send failed"); + goto failed; + } + break; + } + + default: + { + rtems_error(0, "invalid command to monitor server: %d", request.command); + goto failed; + } + } + } + +failed: + rtems_task_delete(RTEMS_SELF); +} + + +/* + * Kill off any old server + * Not sure if this is useful, but it doesn't help + */ + +void +rtems_monitor_server_kill(void) +{ + if (rtems_monitor_server_task_id) + rtems_task_delete(rtems_monitor_server_task_id); + rtems_monitor_server_task_id = 0; + + if (rtems_monitor_server_request_queue_id) + rtems_message_queue_delete(rtems_monitor_server_request_queue_id); + rtems_monitor_server_request_queue_ids = 0; + + if (rtems_monitor_server_response_queue_id) + rtems_message_queue_delete(rtems_monitor_server_response_queue_id); + rtems_monitor_server_response_queue_id = 0; + + if (rtems_monitor_server_request_queue_ids) + free(rtems_monitor_server_request_queue_ids); + rtems_monitor_server_request_queue_ids = 0; +} + + +void +rtems_monitor_server_init( + uint32_t monitor_flags +) +{ + rtems_status_code status; + + if (_System_state_Is_multiprocessing && + (_Configuration_MP_table->maximum_nodes > 1)) + { + uint32_t maximum_nodes = _Configuration_MP_table->maximum_nodes; + + /* + * create the msg que our server will listen + * Since we only get msgs from other RTEMS monitors, we just + * need reserve space for 1 msg from each node. + */ + + status = rtems_message_queue_create( + RTEMS_MONITOR_QUEUE_NAME, + maximum_nodes, + sizeof(rtems_monitor_server_request_t), + RTEMS_GLOBAL, + &rtems_monitor_server_request_queue_id); + + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor server message queue"); + goto done; + } + + /* + * create the msg que our responses will come on + * Since monitor just does one thing at a time, we only need 1 item + * message queue. + */ + + status = rtems_message_queue_create( + RTEMS_MONITOR_RESPONSE_QUEUE_NAME, + 1, /* depth */ + sizeof(rtems_monitor_server_response_t), + RTEMS_GLOBAL, + &rtems_monitor_server_response_queue_id); + + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor response message queue"); + goto done; + } + + /* need an id for queue of each other server we might talk to */ + /* indexed by node, so add 1 to maximum_nodes */ + rtems_monitor_server_request_queue_ids = + (rtems_id *) malloc((maximum_nodes + 1) * sizeof(rtems_id)); + (void) memset(rtems_monitor_server_request_queue_ids, + 0, + (maximum_nodes + 1) * sizeof(rtems_id)); + + rtems_monitor_server_request_queue_ids[rtems_monitor_node] = + rtems_monitor_server_request_queue_id; + + /* + * create the server task + */ + status = rtems_task_create(RTEMS_MONITOR_SERVER_NAME, + 1, + 0 /* default stack */, + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_DEFAULT_ATTRIBUTES, + &rtems_monitor_server_task_id); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not create monitor server task"); + goto done; + } + + /* + * Start the server task + */ + status = rtems_task_start(rtems_monitor_server_task_id, + rtems_monitor_server_task, + monitor_flags); + if (status != RTEMS_SUCCESSFUL) + { + rtems_error(status, "could not start monitor server"); + goto done; + } + } + +done: + return; +} diff --git a/cpukit/libmisc/monitor/mon-symbols.c b/cpukit/libmisc/monitor/mon-symbols.c new file mode 100644 index 0000000000..b83d0243e3 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-symbols.c @@ -0,0 +1,486 @@ +/* + * File: symbols.c + * + * Description: + * Symbol table manager for the RTEMS monitor. + * These routines may be used by other system resources also. + * + * + * TODO: + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ +#include <rtems.h> +#include <stdio.h> +#include <stdlib.h> + +#include <rtems/monitor.h> +#include "symbols.h" + + +rtems_symbol_table_t * +rtems_symbol_table_create() +{ + rtems_symbol_table_t *table; + + table = (rtems_symbol_table_t *) malloc(sizeof(rtems_symbol_table_t)); + memset((void *) table, 0, sizeof(*table)); + + table->growth_factor = 30; /* 30 percent */ + + return table; +} + +void +rtems_symbol_table_destroy(rtems_symbol_table_t *table) +{ + rtems_symbol_string_block_t *p, *pnext; + + if (table) + { + if (table->addresses) + (void) free(table->addresses); + table->addresses = 0; + p = table->string_buffer_head; + while (p) + { + pnext = p->next; + free(p); + p = pnext; + } + table->string_buffer_head = 0; + table->string_buffer_current = 0; + + free(table); + } +} + +rtems_symbol_t * +rtems_symbol_create( + rtems_symbol_table_t *table, + char *name, + uint32_t value + ) +{ + int symbol_length; + size_t newsize; + rtems_symbol_t *sp; + + symbol_length = strlen(name) + 1; /* include '\000' in length */ + + /* need to grow the table? */ + if (table->next >= table->size) + { + if (table->size == 0) + newsize = 100; + else + newsize = table->size + (table->size / (100 / table->growth_factor)); + + table->addresses = (rtems_symbol_t *) realloc((void *) table->addresses, newsize * sizeof(rtems_symbol_t)); + if (table->addresses == 0) /* blew it; lost orig */ + goto failed; + table->size = newsize; + } + + sp = &table->addresses[table->next]; + sp->value = value; + + /* Have to add it to string pool */ + /* need to grow pool? */ + + if ((table->string_buffer_head == 0) || + (table->strings_next + symbol_length) >= SYMBOL_STRING_BLOCK_SIZE) + { + rtems_symbol_string_block_t *p; + + p = (rtems_symbol_string_block_t *) malloc(sizeof(rtems_symbol_string_block_t)); + if (p == 0) + goto failed; + p->next = 0; + if (table->string_buffer_head == 0) + table->string_buffer_head = p; + else + table->string_buffer_current->next = p; + table->string_buffer_current = p; + + table->strings_next = 0; + } + + sp->name = table->string_buffer_current->buffer + table->strings_next; + (void) strcpy(sp->name, name); + + table->strings_next += symbol_length; + table->sorted = 0; + table->next++; + + return sp; + +/* XXX Not sure what to do here. We've possibly destroyed the initial + symbol table due to realloc failure */ +failed: + return 0; +} + +/* + * Qsort entry point for compare by address + */ + +static int +rtems_symbol_compare(const void *e1, + const void *e2) +{ + rtems_symbol_t *s1, *s2; + s1 = (rtems_symbol_t *) e1; + s2 = (rtems_symbol_t *) e2; + + if (s1->value < s2->value) + return -1; + if (s1->value > s2->value) + return 1; + return 0; +} + + +/* + * Sort the symbol table using qsort + */ + +static void +rtems_symbol_sort(rtems_symbol_table_t *table) +{ + qsort((void *) table->addresses, (size_t) table->next, + sizeof(rtems_symbol_t), rtems_symbol_compare); + table->sorted = 1; +} + + +/* + * Search the symbol table by address + * This code based on CYGNUS newlib bsearch, but changed + * to allow for finding closest symbol <= key + */ + +rtems_symbol_t * +rtems_symbol_value_lookup( + rtems_symbol_table_t *table, + uint32_t value + ) +{ + rtems_symbol_t *sp; + rtems_symbol_t *base; + rtems_symbol_t *best = 0; + uint32_t distance; + uint32_t best_distance = ~0; + uint32_t elements; + + if (table == 0) + table = rtems_monitor_symbols; + + if ((table == 0) || (table->size == 0)) + return 0; + + if (table->sorted == 0) + rtems_symbol_sort(table); + + base = table->addresses; + elements = table->next; + + while (elements) + { + sp = base + (elements / 2); + if (value < sp->value) + elements /= 2; + else if (value > sp->value) + { + distance = value - sp->value; + if (distance < best_distance) + { + best_distance = distance; + best = sp; + } + base = sp + 1; + elements = (elements / 2) - (elements % 2 ? 0 : 1); + } + else + return sp; + } + + if (value == base->value) + return base; + + return best; +} + +/* + * Search the symbol table for the exact matching address. + * If the symbol table has already been sorted, then + * call the regular symbol value lookup, however, it it + * has not yet been sorted, search it sequentially. + * This routine is primarily used for low level symbol + * lookups (eg. from exception handler and interrupt routines) + * where the penality of sorted is not wanted and where + * an exact match is needed such that symbol table order + * is not important. + */ +const rtems_symbol_t * +rtems_symbol_value_lookup_exact( + rtems_symbol_table_t *table, + uint32_t value + ) +{ + uint32_t s; + rtems_symbol_t *sp; + + if (table == 0) + { + table = rtems_monitor_symbols; + if (table == 0) + return NULL; + } + + if (table->sorted) + { + sp = rtems_symbol_value_lookup(table, value); + if ( rtems_symbol_value(sp) == value ) + return sp; + else + return NULL; /* not an exact match */ + } + + for (s = 0, sp = table->addresses; s < table->next; s++, sp++) + { + if ( sp->value == value ) + return sp; + } + + return NULL; + +} + + +/* + * Search the symbol table by string name (case independent) + */ + +rtems_symbol_t * +rtems_symbol_name_lookup( + rtems_symbol_table_t *table, + char *name + ) +{ + uint32_t s; + rtems_symbol_t *sp; + + if (table == 0) + { + table = rtems_monitor_symbols; + if (table == 0) + return NULL; + } + + for (s = 0, sp = table->addresses; s < table->next; s++, sp++) + { + if ( strcasecmp(sp->name, name) == 0 ) + return sp; + } + + return NULL; +} + +void * +rtems_monitor_symbol_next( + void *object_info, + rtems_monitor_symbol_t *canonical, + rtems_id *next_id +) +{ + rtems_symbol_table_t *table; + uint32_t n = rtems_get_index(*next_id); + + table = *(rtems_symbol_table_t **) object_info; + if (table == 0) + goto failed; + + if (n >= table->next) + goto failed; + + /* NOTE: symbols do not have id and name fields */ + + if (table->sorted == 0) + rtems_symbol_sort(table); + + _Thread_Disable_dispatch(); + + *next_id += 1; + return (void *) (table->addresses + n); + +failed: + *next_id = RTEMS_OBJECT_ID_FINAL; + return 0; +} + +void +rtems_monitor_symbol_canonical( + rtems_monitor_symbol_t *canonical_symbol, + rtems_symbol_t *sp +) +{ + canonical_symbol->value = sp->value; + canonical_symbol->offset = 0; + strncpy(canonical_symbol->name, sp->name, sizeof(canonical_symbol->name)); +} + + +void +rtems_monitor_symbol_canonical_by_name( + rtems_monitor_symbol_t *canonical_symbol, + char *name +) +{ + rtems_symbol_t *sp; + + sp = rtems_symbol_name_lookup(0, name); + + canonical_symbol->value = sp ? sp->value : 0; + + strncpy(canonical_symbol->name, name, sizeof(canonical_symbol->name)); + canonical_symbol->offset = 0; +} + +void +rtems_monitor_symbol_canonical_by_value( + rtems_monitor_symbol_t *canonical_symbol, + void *value_void_p +) +{ + uint32_t value = (uint32_t ) value_void_p; + rtems_symbol_t *sp; + + sp = rtems_symbol_value_lookup(0, value); + if (sp) + { + canonical_symbol->value = sp->value; + canonical_symbol->offset = value - sp->value; + strncpy(canonical_symbol->name, sp->name, sizeof(canonical_symbol->name)); + } + else + { + canonical_symbol->value = value; + canonical_symbol->offset = 0; + canonical_symbol->name[0] = '\0'; + } +} + + +uint32_t +rtems_monitor_symbol_dump( + rtems_monitor_symbol_t *canonical_symbol, + boolean verbose +) +{ + uint32_t length = 0; + + /* + * print the name if it exists AND if value is non-zero + * Ie: don't print some garbage symbol for address 0 + */ + + if (canonical_symbol->name[0] && (canonical_symbol->value != 0)) + { + if (canonical_symbol->offset == 0) + length += fprintf(stdout,"%.*s", + (int) sizeof(canonical_symbol->name), + canonical_symbol->name); + else + length += fprintf(stdout,"<%.*s+0x%x>", + (int) sizeof(canonical_symbol->name), + canonical_symbol->name, + canonical_symbol->offset); + if (verbose) + length += fprintf(stdout," [0x%x]", canonical_symbol->value); + } + else + length += fprintf(stdout,"[0x%x]", canonical_symbol->value); + + return length; +} + + +void +rtems_monitor_symbol_dump_all( + rtems_symbol_table_t *table, + boolean verbose +) +{ + uint32_t s; + rtems_symbol_t *sp; + + if (table == 0) + { + table = rtems_monitor_symbols; + if (table == 0) + return; + } + + if (table->sorted == 0) + rtems_symbol_sort(table); + + for (s = 0, sp = table->addresses; s < table->next; s++, sp++) + { + rtems_monitor_symbol_t canonical_symbol; + + rtems_monitor_symbol_canonical(&canonical_symbol, sp); + rtems_monitor_symbol_dump(&canonical_symbol, TRUE); + fprintf(stdout,"\n"); + } +} + + +/* + * 'symbol' command + */ + +void +rtems_monitor_symbol_cmd( + int argc, + char **argv, + rtems_monitor_command_arg_t* command_arg, + boolean verbose +) +{ + int arg; + rtems_symbol_table_t *table; + + table = *command_arg->symbol_table; + if (table == 0) + { + table = rtems_monitor_symbols; + if (table == 0) + return; + } + + /* + * Use object command to dump out whole symbol table + */ + if (argc == 1) + rtems_monitor_symbol_dump_all(table, verbose); + else + { + rtems_monitor_symbol_t canonical_symbol; + + for (arg=1; argv[arg]; arg++) + { + rtems_monitor_symbol_canonical_by_name(&canonical_symbol, argv[arg]); + rtems_monitor_symbol_dump(&canonical_symbol, verbose); + fprintf(stdout,"\n"); + } + } +} diff --git a/cpukit/libmisc/monitor/mon-task.c b/cpukit/libmisc/monitor/mon-task.c new file mode 100644 index 0000000000..98e097dc71 --- /dev/null +++ b/cpukit/libmisc/monitor/mon-task.c @@ -0,0 +1,96 @@ +/* + * RTEMS Monitor task support + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/monitor.h> + +#include <stdio.h> +#include <string.h> /* memcpy() */ + +void +rtems_monitor_task_canonical( + rtems_monitor_task_t *canonical_task, + void *thread_void +) +{ + Thread_Control *rtems_thread = (Thread_Control *) thread_void; + RTEMS_API_Control *api; + + api = rtems_thread->API_Extensions[ THREAD_API_RTEMS ]; + + canonical_task->entry = rtems_thread->Start.entry_point; + canonical_task->argument = rtems_thread->Start.numeric_argument; + canonical_task->stack = rtems_thread->Start.Initial_stack.area; + canonical_task->stack_size = rtems_thread->Start.Initial_stack.size; + canonical_task->priority = rtems_thread->current_priority; + canonical_task->state = rtems_thread->current_state; + canonical_task->wait_id = rtems_thread->Wait.id; + canonical_task->events = api->pending_events; + +/* XXX modes and attributes only exist in the RTEMS API .. */ +/* XXX not directly in the core thread.. they will have to be derived */ +/* XXX if they are important enough to include anymore. */ + canonical_task->modes = 0; /* XXX FIX ME.... rtems_thread->current_modes; */ + canonical_task->attributes = 0 /* XXX FIX ME rtems_thread->API_Extensions[ THREAD_API_RTEMS ]->attribute_set */; + (void) memcpy(canonical_task->notepad, api ->Notepads, sizeof(canonical_task->notepad)); +/* XXX more to fix */ +/* + (void) memcpy(&canonical_task->wait_args, &rtems_thread->Wait.Extra, sizeof(canonical_task->wait_args)); +*/ +} + + +void +rtems_monitor_task_dump_header( + boolean verbose +) +{ + fprintf(stdout,"\ + ID NAME PRIO STAT MODES EVENTS WAITID WAITARG NOTES\n"); +/*23456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 +0 1 2 3 4 5 6 7 */ + + rtems_monitor_separator(); +} + +/* + */ + +void +rtems_monitor_task_dump( + rtems_monitor_task_t *monitor_task, + boolean verbose +) +{ + int length = 0; + + length += rtems_monitor_dump_id(monitor_task->id); + length += rtems_monitor_pad(11, length); + length += rtems_monitor_dump_name(monitor_task->name); + length += rtems_monitor_pad(18, length); + length += rtems_monitor_dump_priority(monitor_task->priority); + length += rtems_monitor_pad(24, length); + length += rtems_monitor_dump_state(monitor_task->state); + length += rtems_monitor_pad(31, length); + length += rtems_monitor_dump_modes(monitor_task->modes); + length += rtems_monitor_pad(39, length); + length += rtems_monitor_dump_events(monitor_task->events); + if (monitor_task->wait_id) + { + length += rtems_monitor_pad(47, length); + length += rtems_monitor_dump_id(monitor_task->wait_id); + length += rtems_monitor_pad(57, length); + length += rtems_monitor_dump_hex(monitor_task->wait_args); + } + + length += rtems_monitor_pad(65, length); + length += rtems_monitor_dump_notepad(monitor_task->notepad); + fprintf(stdout,"\n"); +} diff --git a/cpukit/libmisc/monitor/monitor.h b/cpukit/libmisc/monitor/monitor.h new file mode 100644 index 0000000000..b12f07a012 --- /dev/null +++ b/cpukit/libmisc/monitor/monitor.h @@ -0,0 +1,459 @@ +/* + * File: monitor.h + * + * Description: + * The RTEMS monitor task include file. + * + * TODO: + * + * $Id$ + */ + +#ifndef __MONITOR_H +#define __MONITOR_H + +#include <rtems/error.h> /* rtems_error() */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward decls from symbols.h */ +typedef struct _rtems_symbol_t rtems_symbol_t ; +typedef struct _rtems_symbol_table_t rtems_symbol_table_t; + +/* + * Monitor types are derived from rtems object classes + */ + +typedef enum { + RTEMS_MONITOR_OBJECT_INVALID = OBJECTS_NO_CLASS, + RTEMS_MONITOR_OBJECT_TASK = OBJECTS_RTEMS_TASKS, + RTEMS_MONITOR_OBJECT_EXTENSION = OBJECTS_RTEMS_EXTENSIONS, + RTEMS_MONITOR_OBJECT_QUEUE = OBJECTS_RTEMS_MESSAGE_QUEUES, + RTEMS_MONITOR_OBJECT_SEMAPHORE = OBJECTS_RTEMS_SEMAPHORES, + RTEMS_MONITOR_OBJECT_PARTITION = OBJECTS_RTEMS_PARTITIONS, + RTEMS_MONITOR_OBJECT_REGION = OBJECTS_RTEMS_REGIONS, + RTEMS_MONITOR_OBJECT_PORT = OBJECTS_RTEMS_PORTS, + + /* following monitor objects are not known to RTEMS, but + * we like to have "types" for them anyway */ + + RTEMS_MONITOR_OBJECT_DRIVER = OBJECTS_RTEMS_CLASSES_LAST+1, + RTEMS_MONITOR_OBJECT_DNAME, + RTEMS_MONITOR_OBJECT_CONFIG, + RTEMS_MONITOR_OBJECT_INIT_TASK, + RTEMS_MONITOR_OBJECT_MPCI, + RTEMS_MONITOR_OBJECT_SYMBOL +} rtems_monitor_object_type_t; + +/* + * rtems_monitor_init() flags + */ + +#define RTEMS_MONITOR_SUSPEND 0x0001 /* suspend monitor on startup */ +#define RTEMS_MONITOR_GLOBAL 0x0002 /* monitor should be global */ + + +/* + * Public interfaces for RTEMS data structures monitor is aware of. + * These are only used by the monitor. + * + * NOTE: + * All the canonical objects that correspond to RTEMS managed "objects" + * must have an identical first portion with 'id' and 'name' fields. + * + * Others do not have that restriction, even tho we would like them to. + * This is because some of the canonical structures are almost too big + * for shared memory driver (eg: mpci) and we are nickel and diming it. + */ + +/* + * Type of a pointer that may be a symbol + */ + +#define MONITOR_SYMBOL_LEN 20 +typedef struct { + char name[MONITOR_SYMBOL_LEN]; + uint32_t value; + uint32_t offset; +} rtems_monitor_symbol_t; + +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ +} rtems_monitor_generic_t; + +/* + * Task + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + Thread_Entry entry; + uint32_t argument; + void *stack; + uint32_t stack_size; + rtems_task_priority priority; + States_Control state; + rtems_event_set events; + rtems_mode modes; + rtems_attribute attributes; + uint32_t notepad[RTEMS_NUMBER_NOTEPADS]; + rtems_id wait_id; + uint32_t wait_args; +} rtems_monitor_task_t; + +/* + * Init task + */ + +typedef struct { + rtems_id id; /* not really an id */ + rtems_name name; + /* end of common portion */ + rtems_monitor_symbol_t entry; + uint32_t argument; + uint32_t stack_size; + rtems_task_priority priority; + rtems_mode modes; + rtems_attribute attributes; +} rtems_monitor_init_task_t; + + +/* + * Message queue + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + rtems_attribute attributes; + uint32_t number_of_pending_messages; + uint32_t maximum_pending_messages; + uint32_t maximum_message_size; +} rtems_monitor_queue_t; + +/* + * Extension + */ +typedef struct { + rtems_id id; + rtems_name name; + /* end of common portion */ + rtems_monitor_symbol_t e_create; + rtems_monitor_symbol_t e_start; + rtems_monitor_symbol_t e_restart; + rtems_monitor_symbol_t e_delete; + rtems_monitor_symbol_t e_tswitch; + rtems_monitor_symbol_t e_begin; + rtems_monitor_symbol_t e_exitted; + rtems_monitor_symbol_t e_fatal; +} rtems_monitor_extension_t; + +/* + * Device driver + */ + +typedef struct { + rtems_id id; /* not really an id (should be tho) */ + rtems_name name; /* ditto */ + /* end of common portion */ + rtems_monitor_symbol_t initialization; /* initialization procedure */ + rtems_monitor_symbol_t open; /* open request procedure */ + rtems_monitor_symbol_t close; /* close request procedure */ + rtems_monitor_symbol_t read; /* read request procedure */ + rtems_monitor_symbol_t write; /* write request procedure */ + rtems_monitor_symbol_t control; /* special functions procedure */ +} rtems_monitor_driver_t; + +typedef struct { + rtems_id id; /* not used for drivers (yet) */ + rtems_name name; /* not used for drivers (yet) */ + /* end of common portion */ + uint32_t major; + uint32_t minor; + char name_string[64]; +} rtems_monitor_dname_t; + +/* + * System config + */ + +typedef struct { + void *work_space_start; + uint32_t work_space_size; + uint32_t maximum_tasks; + uint32_t maximum_timers; + uint32_t maximum_semaphores; + uint32_t maximum_message_queues; + uint32_t maximum_partitions; + uint32_t maximum_regions; + uint32_t maximum_ports; + uint32_t maximum_periods; + uint32_t maximum_extensions; + uint32_t microseconds_per_tick; + uint32_t ticks_per_timeslice; + uint32_t number_of_initialization_tasks; +} rtems_monitor_config_t; + +/* + * MPCI config + */ + +#if defined(RTEMS_MULTIPROCESSING) +typedef struct { + uint32_t node; /* local node number */ + uint32_t maximum_nodes; /* maximum # nodes in system */ + uint32_t maximum_global_objects; /* maximum # global objects */ + uint32_t maximum_proxies; /* maximum # proxies */ + + uint32_t default_timeout; /* in ticks */ + uint32_t maximum_packet_size; + rtems_monitor_symbol_t initialization; + rtems_monitor_symbol_t get_packet; + rtems_monitor_symbol_t return_packet; + rtems_monitor_symbol_t send_packet; + rtems_monitor_symbol_t receive_packet; +} rtems_monitor_mpci_t; +#endif + +/* + * The generic canonical information union + */ + +typedef union { + rtems_monitor_generic_t generic; + rtems_monitor_task_t task; + rtems_monitor_queue_t queue; + rtems_monitor_extension_t extension; + rtems_monitor_driver_t driver; + rtems_monitor_dname_t dname; + rtems_monitor_config_t config; +#if defined(RTEMS_MULTIPROCESSING) + rtems_monitor_mpci_t mpci; +#endif + rtems_monitor_init_task_t itask; +} rtems_monitor_union_t; + +/* + * Support for talking to other monitors + */ + +/* + * Names of other monitors + */ + +#define RTEMS_MONITOR_NAME (rtems_build_name('R', 'M', 'O', 'N')) +#define RTEMS_MONITOR_SERVER_NAME (rtems_build_name('R', 'M', 'S', 'V')) +#define RTEMS_MONITOR_QUEUE_NAME (rtems_build_name('R', 'M', 'S', 'Q')) +#define RTEMS_MONITOR_RESPONSE_QUEUE_NAME (rtems_build_name('R', 'M', 'R', 'Q')) + +#define RTEMS_MONITOR_SERVER_RESPONSE 0x0001 +#define RTEMS_MONITOR_SERVER_CANONICAL 0x0002 + +typedef struct +{ + uint32_t command; + rtems_id return_id; + uint32_t argument0; + uint32_t argument1; + uint32_t argument2; + uint32_t argument3; + uint32_t argument4; + uint32_t argument5; +} rtems_monitor_server_request_t; + +typedef struct +{ + uint32_t command; + uint32_t result0; + uint32_t result1; + rtems_monitor_union_t payload; +} rtems_monitor_server_response_t; + +extern rtems_id rtems_monitor_task_id; + +extern uint32_t rtems_monitor_node; /* our node number */ +extern uint32_t rtems_monitor_default_node; /* current default for commands */ + +/* + * Monitor command function and table entry + */ + +typedef struct rtems_monitor_command_entry_s rtems_monitor_command_entry_t; +typedef union _rtems_monitor_command_arg_t rtems_monitor_command_arg_t; + +typedef void ( *rtems_monitor_command_function_t )( + int argc, + char **argv, + rtems_monitor_command_arg_t *command_arg, + boolean verbose + ); + +union _rtems_monitor_command_arg_t { + rtems_monitor_object_type_t monitor_object ; + rtems_status_code status_code ; + rtems_symbol_table_t **symbol_table ; + rtems_monitor_command_entry_t *monitor_command_entry ; +}; + +struct rtems_monitor_command_entry_s { + char *command; /* command name */ + char *usage; /* usage string for the command */ + uint32_t arguments_required; /* # of required args */ + rtems_monitor_command_function_t command_function; + /* Some argument for the command */ + rtems_monitor_command_arg_t command_arg; + rtems_monitor_command_entry_t *next; +}; + + +typedef void *(*rtems_monitor_object_next_fn)(void *, void *, rtems_id *); +typedef void (*rtems_monitor_object_canonical_fn)(void *, void *); +typedef void (*rtems_monitor_object_dump_header_fn)(boolean); +typedef void (*rtems_monitor_object_dump_fn)(void *, boolean); + +typedef struct { + rtems_monitor_object_type_t type; + void *object_information; + int size; /* of canonical object */ + rtems_monitor_object_next_fn next; + rtems_monitor_object_canonical_fn canonical; + rtems_monitor_object_dump_header_fn dump_header; + rtems_monitor_object_dump_fn dump; +} rtems_monitor_object_info_t; + + +/* monitor.c */ +void rtems_monitor_kill(void); +void rtems_monitor_init(uint32_t ); +void rtems_monitor_wakeup(void); +void rtems_monitor_pause_cmd(int, char **, rtems_monitor_command_arg_t*, boolean); +void rtems_monitor_fatal_cmd(int, char **, rtems_monitor_command_arg_t*, boolean); +void rtems_monitor_continue_cmd(int, char **, rtems_monitor_command_arg_t*, boolean); +void rtems_monitor_debugger_cmd(int, char **, rtems_monitor_command_arg_t*, boolean); +void rtems_monitor_node_cmd(int, char **, rtems_monitor_command_arg_t*, boolean); +void rtems_monitor_symbols_loadup(void); +int rtems_monitor_insert_cmd(rtems_monitor_command_entry_t *); +int rtems_monitor_erase_cmd(rtems_monitor_command_entry_t *); + +void rtems_monitor_task(rtems_task_argument); + +/* server.c */ +void rtems_monitor_server_kill(void); +rtems_status_code rtems_monitor_server_request(uint32_t , rtems_monitor_server_request_t *, rtems_monitor_server_response_t *); +void rtems_monitor_server_task(rtems_task_argument); +void rtems_monitor_server_init(uint32_t ); + +/* command.c */ +int rtems_monitor_make_argv(char *, int *, char **); +int rtems_monitor_command_read(char *, int *, char **); +rtems_monitor_command_entry_t *rtems_monitor_command_lookup( + rtems_monitor_command_entry_t * table, int argc, char **argv); +void rtems_monitor_command_usage(rtems_monitor_command_entry_t *, char *); +void rtems_monitor_help_cmd(int, char **, rtems_monitor_command_arg_t *, boolean); + +/* prmisc.c */ +void rtems_monitor_separator(void); +uint32_t rtems_monitor_pad(uint32_t dest_col, uint32_t curr_col); +uint32_t rtems_monitor_dump_char(uint8_t ch); +uint32_t rtems_monitor_dump_decimal(uint32_t num); +uint32_t rtems_monitor_dump_hex(uint32_t num); +uint32_t rtems_monitor_dump_id(rtems_id id); +uint32_t rtems_monitor_dump_name(rtems_name name); +uint32_t rtems_monitor_dump_priority(rtems_task_priority priority); +uint32_t rtems_monitor_dump_state(States_Control state); +uint32_t rtems_monitor_dump_modes(rtems_mode modes); +uint32_t rtems_monitor_dump_attributes(rtems_attribute attributes); +uint32_t rtems_monitor_dump_events(rtems_event_set events); +uint32_t rtems_monitor_dump_notepad(uint32_t *notepad); + +/* object.c */ +rtems_id rtems_monitor_id_fixup(rtems_id, uint32_t , rtems_monitor_object_type_t); +rtems_id rtems_monitor_object_canonical_get(rtems_monitor_object_type_t, rtems_id, void *, uint32_t *size_p); +rtems_id rtems_monitor_object_canonical_next(rtems_monitor_object_info_t *, rtems_id, void *); +void *rtems_monitor_object_next(void *, void *, rtems_id, rtems_id *); +rtems_id rtems_monitor_object_canonical(rtems_id, void *); +void rtems_monitor_object_cmd(int, char **, rtems_monitor_command_arg_t*, boolean); + +/* manager.c */ +void *rtems_monitor_manager_next(void *, void *, rtems_id *); + +/* config.c */ +void rtems_monitor_config_canonical(rtems_monitor_config_t *, void *); +void *rtems_monitor_config_next(void *, rtems_monitor_config_t *, rtems_id *); +void rtems_monitor_config_dump_header(boolean); +void rtems_monitor_config_dump(rtems_monitor_config_t *, boolean verbose); + +/* mpci.c */ +#if defined(RTEMS_MULTIPROCESSING) +void rtems_monitor_mpci_canonical(rtems_monitor_mpci_t *, void *); +void *rtems_monitor_mpci_next(void *, rtems_monitor_mpci_t *, rtems_id *); +void rtems_monitor_mpci_dump_header(boolean); +void rtems_monitor_mpci_dump(rtems_monitor_mpci_t *, boolean verbose); +#endif + +/* itask.c */ +void rtems_monitor_init_task_canonical(rtems_monitor_init_task_t *, void *); +void *rtems_monitor_init_task_next(void *, rtems_monitor_init_task_t *, rtems_id *); +void rtems_monitor_init_task_dump_header(boolean); +void rtems_monitor_init_task_dump(rtems_monitor_init_task_t *, boolean verbose); + +/* extension.c */ +void rtems_monitor_extension_canonical(rtems_monitor_extension_t *, void *); +void rtems_monitor_extension_dump_header(boolean verbose); +void rtems_monitor_extension_dump(rtems_monitor_extension_t *, boolean); + +/* task.c */ +void rtems_monitor_task_canonical(rtems_monitor_task_t *, void *); +void rtems_monitor_task_dump_header(boolean verbose); +void rtems_monitor_task_dump(rtems_monitor_task_t *, boolean); + +/* queue.c */ +void rtems_monitor_queue_canonical(rtems_monitor_queue_t *, void *); +void rtems_monitor_queue_dump_header(boolean verbose); +void rtems_monitor_queue_dump(rtems_monitor_queue_t *, boolean); + +/* driver.c */ +void *rtems_monitor_driver_next(void *, rtems_monitor_driver_t *, rtems_id *); +void rtems_monitor_driver_canonical(rtems_monitor_driver_t *, void *); +void rtems_monitor_driver_dump_header(boolean); +void rtems_monitor_driver_dump(rtems_monitor_driver_t *, boolean); + +/* dname.c */ +void *rtems_monitor_dname_next(void *, rtems_monitor_dname_t *, rtems_id *); +void rtems_monitor_dname_canonical(rtems_monitor_dname_t *, void *); +void rtems_monitor_dname_dump_header(boolean); +void rtems_monitor_dname_dump(rtems_monitor_dname_t *, boolean); + +/* symbols.c */ +rtems_symbol_table_t *rtems_symbol_table_create(); +void rtems_symbol_table_destroy(rtems_symbol_table_t *table); + +rtems_symbol_t *rtems_symbol_create(rtems_symbol_table_t *, char *, uint32_t ); +rtems_symbol_t *rtems_symbol_value_lookup(rtems_symbol_table_t *, uint32_t ); +const rtems_symbol_t *rtems_symbol_value_lookup_exact(rtems_symbol_table_t *, uint32_t ); +rtems_symbol_t *rtems_symbol_name_lookup(rtems_symbol_table_t *, char *); +void *rtems_monitor_symbol_next(void *object_info, rtems_monitor_symbol_t *, rtems_id *); +void rtems_monitor_symbol_canonical(rtems_monitor_symbol_t *, rtems_symbol_t *); +void rtems_monitor_symbol_canonical_by_name(rtems_monitor_symbol_t *, char *); +void rtems_monitor_symbol_canonical_by_value(rtems_monitor_symbol_t *, void *); +uint32_t rtems_monitor_symbol_dump(rtems_monitor_symbol_t *, boolean); +void rtems_monitor_symbol_cmd(int, char **, rtems_monitor_command_arg_t*, boolean); + + +extern rtems_symbol_table_t *rtems_monitor_symbols; + +/* FIXME: This should not be here */ +extern rtems_monitor_command_entry_t rtems_monitor_commands[]; + +#define MONITOR_WAKEUP_EVENT RTEMS_EVENT_0 + +#ifdef __cplusplus +} +#endif + +#endif /* ! __MONITOR_H */ diff --git a/cpukit/libmisc/monitor/symbols.h b/cpukit/libmisc/monitor/symbols.h new file mode 100644 index 0000000000..6246379a94 --- /dev/null +++ b/cpukit/libmisc/monitor/symbols.h @@ -0,0 +1,64 @@ +/* + * RTEMS monitor symbol table functions + * + * Description: + * Entry points for symbol table routines. + * + * + * + * TODO: + * + * $Id$ + */ + +#ifndef _INCLUDE_SYMBOLS_H +#define _INCLUDE_SYMBOLS_H + +#include <rtems/monitor.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct _rtems_symbol_t { + uint32_t value; + char *name; +} ; + +#define SYMBOL_STRING_BLOCK_SIZE 4080 +typedef struct rtems_symbol_string_block_s { + struct rtems_symbol_string_block_s *next; + char buffer[SYMBOL_STRING_BLOCK_SIZE]; +} rtems_symbol_string_block_t; + +struct _rtems_symbol_table_t { + + uint32_t sorted; /* are symbols sorted right now? */ + uint32_t growth_factor; /* % to grow by when needed */ + uint32_t next; /* next symbol slot to use when adding */ + uint32_t size; /* max # of symbols */ + + /* + * Symbol list -- sorted by address (when we do a lookup) + */ + + rtems_symbol_t *addresses; /* symbol array by address */ + + /* + * String pool, unsorted, a list of blocks of string data + */ + + rtems_symbol_string_block_t *string_buffer_head; + rtems_symbol_string_block_t *string_buffer_current; + uint32_t strings_next; /* next byte to use in this block */ + +} ; + +#define rtems_symbol_name(sp) ((sp)->name) +#define rtems_symbol_value(sp) ((sp)->value) + +#ifdef __cplusplus +} +#endif + +#endif /* ! _INCLUDE_SYMBOLS_H */ diff --git a/cpukit/libmisc/mw-fb/mw_fb.c b/cpukit/libmisc/mw-fb/mw_fb.c new file mode 100644 index 0000000000..7ea715a18f --- /dev/null +++ b/cpukit/libmisc/mw-fb/mw_fb.c @@ -0,0 +1,194 @@ +/* +///////////////////////////////////////////////////////////////////////////// +// $Header$ +// +// Copyright (c) 2000 - Rosimildo da Silva +// +// MODULE DESCRIPTION: +// Wrapper API around the ioctls calls for the Micro FrameBuffer +// interface for Embedded Systems +// +// All functions returns 0 on success. Any other value should be +// decoded as an error. A list of errors will be created over time. +// +// MODIFICATION/HISTORY: +// +// $Log$ +// Revision 1.3 2004/04/15 13:24:46 ralf +// Remove stray white spaces. +// +// Revision 1.2 2003/07/08 08:38:48 ralf +// 2003-07-08 Ralf Corsepius <corsepiu@faw.uni-ulm.de> +// +// * capture/capture-cli.c: Add config-header support. +// * capture/capture.c: Add config-header support. +// * cpuuse/cpuuse.c: Add config-header support. +// * devnull/devnull.c: Add config-header support. +// * dummy/dummy.c: Add config-header support. +// * dumpbuf/dumpbuf.c: Add config-header support. +// * monitor/mon-command.c: Add config-header support. +// * monitor/mon-config.c: Add config-header support. +// * monitor/mon-dname.c: Add config-header support. +// * monitor/mon-driver.c: Add config-header support. +// * monitor/mon-extension.c: Add config-header support. +// * monitor/mon-itask.c: Add config-header support. +// * monitor/mon-manager.c: Add config-header support. +// * monitor/mon-monitor.c: Add config-header support. +// * monitor/mon-mpci.c: Add config-header support. +// * monitor/mon-object.c: Add config-header support. +// * monitor/mon-prmisc.c: Add config-header support. +// * monitor/mon-queue.c: Add config-header support. +// * monitor/mon-server.c: Add config-header support. +// * monitor/mon-symbols.c: Add config-header support. +// * monitor/mon-task.c: Add config-header support. +// * mw-fb/mw_fb.c: Add config-header support. +// * mw-fb/mw_uid.c: Add config-header support. +// * rtmonuse/rtmonuse.c: Add config-header support. +// * serdbg/serdbg.c: Add config-header support. +// * serdbg/serdbgio.c: Add config-header support. +// * serdbg/termios_printk.c: Add config-header support. +// * shell/cmds.c: Add config-header support. +// * stackchk/check.c: Add config-header support. +// * untar/untar.c: Add config-header support. +// +// Revision 1.1 2000/08/30 08:21:24 joel +// 2000-08-26 Rosimildo da Silva <rdasilva@connecttel.com> +// +// * Added generic Micro FrameBuffer interface for MicroWindows. +// This interface allows MicroWindows to under RTEMS. A sample +// driver has been developed for the pc386 BSP. See +// pc386/fb_vga.c as a sample. +// * Added Uniform Input Device interface for MicroWindows. +// See PC386 bsp for sample drivers for mouse and keyboard (console). +// * mw-bf: New directory. +// * Makefile.am, configure.in, wrapup/Makefile.am: Account for mw-fb. +// * mw-fb/Makefile.am: New file. +// * mw-fb/mw_fb.c: New file. +// * mw-fb/mw_fb.h: New file. +// * mw-fb/mw_uid.c: New file. +// * mw-fb/mw_uid.h: New file. +// +// +///////////////////////////////////////////////////////////////////////////// +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/ioctl.h> +#include <rtems/mw_fb.h> + + +/* + * This function returns the information regarding the display. + * It is called just after the driver be opened to get all needed + * information about the driver. No change in the mode of operation + * of the driver is done with this call. + */ + int ufb_get_screen_info( int fd, struct fb_screeninfo *info ) + { + return ioctl( fd, FB_SCREENINFO, ( void *)info); + } + + + +/* + * Returns the mode of the graphics subsystem + */ + int ufb_get_mode( int fd, int *mode ) + { + struct fb_exec_function exec; + exec.func_no = FB_FUNC_GET_MODE; + exec.param = ( void *)mode; + return ioctl( fd, FB_EXEC_FUNCTION , ( void *)&exec ); + } + + +/* + * Returns the current collor pallete + */ + int ufb_get_palette( int fd, struct fb_cmap *color ) + { + return ioctl( fd, FB_GETPALETTE, ( void *)color ); + } + + +/* + * Set the current collor pallete + */ + int ufb_set_palette( int fd, struct fb_cmap *color ) + { + return ioctl( fd, FB_SETPALETTE, ( void *)color ); + } + +/* + * Does all necessary initialization to put the device in + * graphics mode + */ + int ufb_enter_graphics( int fd, int mode ) + { + struct fb_exec_function exec; + exec.func_no = FB_FUNC_ENTER_GRAPHICS; + exec.param = ( void *)mode; + return ioctl( fd, FB_EXEC_FUNCTION , ( void *)&exec ); + } + + +/* + * Switch the device back to the default mode of operation. + * In most cases it put the device back to plain text mode. + */ + int ufb_exit_graphics( int fd ) + { + struct fb_exec_function exec; + exec.func_no = FB_FUNC_EXIT_GRAPHICS; + exec.param = 0; + return ioctl( fd, FB_EXEC_FUNCTION , ( void *)&exec ); + } + +/* + * Tell the driver that the "virtual buffer" is dirty, and an update + * of it to the real device, maybe a serial/parallel LCD or whatever + * is required + */ + int ufb_buffer_is_dirty( int fd ) + { + struct fb_exec_function exec; + exec.func_no = FB_FUNC_IS_DIRTY; + exec.param = 0; + return ioctl( fd, FB_EXEC_FUNCTION , ( void *)&exec ); + } + + + +/* + * This function maps the physical ( kernel mode ) address of the framebuffer device + * and maps it to the user space address. + */ + int ufb_mmap_to_user_space( int fd, void **fb_addr, void *physical_addr, unsigned long size ) + { + #ifdef __rtems__ + /* RTEMS runs in ring 0, and there is no distinction between + user space and kernel space, so we just return the same + pointer to the caller. + */ + *fb_addr = physical_addr; + return 0; + #else + /* other kernels might want to map it to the user space, + maybe using mmap() + */ + return 0; + #endif + + } + + +/* + * This function unmaps memory of the FB from the user's space + */ + int ufb_unmmap_from_user_space( int fd, void *addr ) + { + return 0; + } diff --git a/cpukit/libmisc/mw-fb/mw_fb.h b/cpukit/libmisc/mw-fb/mw_fb.h new file mode 100644 index 0000000000..fb7b6f6170 --- /dev/null +++ b/cpukit/libmisc/mw-fb/mw_fb.h @@ -0,0 +1,170 @@ +/* +///////////////////////////////////////////////////////////////////////////// +// $Header$ +// +// Copyright (c) 2000 - Rosimildo da Silva +// +// MODULE DESCRIPTION: +// Micro FrameBuffer interface for Embedded Systems. +// +// MODIFICATION/HISTORY: +// +// $Log$ +// Revision 1.1 2000/08/30 08:21:24 joel +// 2000-08-26 Rosimildo da Silva <rdasilva@connecttel.com> +// +// * Added generic Micro FrameBuffer interface for MicroWindows. +// This interface allows MicroWindows to under RTEMS. A sample +// driver has been developed for the pc386 BSP. See +// pc386/fb_vga.c as a sample. +// * Added Uniform Input Device interface for MicroWindows. +// See PC386 bsp for sample drivers for mouse and keyboard (console). +// * mw-bf: New directory. +// * Makefile.am, configure.in, wrapup/Makefile.am: Account for mw-fb. +// * mw-fb/Makefile.am: New file. +// * mw-fb/mw_fb.c: New file. +// * mw-fb/mw_fb.h: New file. +// * mw-fb/mw_uid.c: New file. +// * mw-fb/mw_uid.h: New file. +// +// +///////////////////////////////////////////////////////////////////////////// +*/ +#ifndef _MW_FB_H +#define _MW_FB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ioctls + 0x46 is 'F' */ +#define FB_SCREENINFO 0x4601 +#define FB_GETPALETTE 0x4602 +#define FB_SETPALETTE 0x4603 +#define FB_EXEC_FUNCTION 0x4604 + + +#define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ +#define FB_TYPE_PLANES 1 /* Non interleaved planes */ +#define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */ +#define FB_TYPE_TEXT 3 /* Text/attributes */ +#define FB_TYPE_VGA_PLANES 4 /* EGA/VGA planes */ +#define FB_TYPE_VIRTUAL_BUFFER 5 /* Virtual Buffer */ + + +#define FB_VISUAL_MONO01 0 /* Monochr. 1=Black 0=White */ +#define FB_VISUAL_MONO10 1 /* Monochr. 1=White 0=Black */ +#define FB_VISUAL_TRUECOLOR 2 /* True color */ +#define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) */ +#define FB_VISUAL_DIRECTCOLOR 4 /* Direct color */ +#define FB_VISUAL_STATIC_PSEUDOCOLOR 5 /* Pseudo color readonly */ + +#define FB_ACCEL_NONE 0 /* no hardware accelerator */ + +/* no dependency on any other header file */ +typedef unsigned long __u32; +typedef unsigned short __u16; + +struct fb_screeninfo { + __u32 xres; /* visible resolution */ + __u32 yres; + __u32 bits_per_pixel; /* guess what */ + __u32 line_length; /* number of chars per line */ + volatile char *smem_start; /* Start of frame buffer mem */ + /* (physical address) */ + __u32 smem_len; /* Length of frame buffer mem */ + __u32 type; /* see FB_TYPE_* */ + __u32 visual; /* see FB_VISUAL_* */ + +}; + +struct fb_cmap { + __u32 start; /* First entry */ + __u32 len; /* Number of entries */ + __u16 *red; /* Red values */ + __u16 *green; + __u16 *blue; + __u16 *transp; /* transparency, can be NULL */ +}; + +/* type of function to be executed at the driver level */ +#define FB_FUNC_ENTER_GRAPHICS 0 +#define FB_FUNC_EXIT_GRAPHICS 1 +#define FB_FUNC_IS_DIRTY 2 +#define FB_FUNC_GET_MODE 3 + +struct fb_exec_function +{ + int func_no; + void *param; +}; + + +/* Micro Framebuffer API Wrapper */ + +/* + * This function returns the information regarding the display. + * It is called just after the driver be opened to get all needed + * information about the driver. No change in the mode of operation + * of the driver is done with this call. + */ +extern int ufb_get_screen_info( int fd, struct fb_screeninfo *info ); + + +/* + * Returns the mode of the graphics subsystem + */ +extern int ufb_get_mode( int fd, int *mode ); + + +/* + * Returns the current collor pallete + */ +extern int ufb_get_palette( int fd, struct fb_cmap *color ); + +/* + * Set the current collor pallete + */ +extern int ufb_set_palette( int fd, struct fb_cmap *color ); + +/* + * Does all necessary initialization to put the device in + * graphics mode + */ +extern int ufb_enter_graphics( int fd, int mode ); + + +/* + * Switch the device back to the default mode of operation. + * In most cases it put the device back to plain text mode. + */ +extern int ufb_exit_graphics( int fd ); + + +/* + * Tell the driver that the "virtual buffer" is dirty, and an update + * of it to the real device, maybe a serial/parallel LCD or whatever + * is required + */ +extern int ufb_buffer_is_dirty( int fd ); + + +/* + * This function maps the physical ( kernel mode ) address of the framebuffer device + * and maps it to the user space address. + */ + int ufb_mmap_to_user_space( int fd, void **fb_addr, void *physical_addr, unsigned long size ); + + + +/* + * This function unmaps memory of the FB from the user's space + */ + int ufb_unmmap_from_user_space( int fd, void *addr ); + +#ifdef __cplusplus +} +#endif + +#endif /* _MW_FB_H */ diff --git a/cpukit/libmisc/mw-fb/mw_uid.c b/cpukit/libmisc/mw-fb/mw_uid.c new file mode 100644 index 0000000000..07cfca5571 --- /dev/null +++ b/cpukit/libmisc/mw-fb/mw_uid.c @@ -0,0 +1,248 @@ +/* +///////////////////////////////////////////////////////////////////////////// +// $Header$ +// +// Copyright (c) 2000 - Rosimildo da Silva +// +// MODULE DESCRIPTION: +// This module implements the input devices interface used by MicroWindows +// in an embedded system environment. +// It uses the RTEMS message queue as the repository for the messages posted +// by the devices registered. +// +// MODIFICATION/HISTORY: +// +// $Log$ +// Revision 1.6 2004/03/26 06:59:18 ralf +// 2004-03-26 Ralf Corsepius <ralf_corsepius@rtems.org> +// +// * libmisc/capture/capture-cli.c, libmisc/capture/capture.c, +// libmisc/capture/capture.h, libmisc/cpuuse/cpuuse.c, +// libmisc/devnull/devnull.c, libmisc/fsmount/fsmount.h, +// libmisc/monitor/mon-config.c, libmisc/monitor/mon-dname.c, +// libmisc/monitor/mon-driver.c, libmisc/monitor/mon-extension.c, +// libmisc/monitor/mon-itask.c, libmisc/monitor/mon-monitor.c, +// libmisc/monitor/mon-mpci.c, libmisc/monitor/mon-object.c, +// libmisc/monitor/mon-prmisc.c, libmisc/monitor/mon-queue.c, +// libmisc/monitor/mon-server.c, libmisc/monitor/mon-symbols.c, +// libmisc/monitor/monitor.h, libmisc/monitor/symbols.h, +// libmisc/mw-fb/mw_uid.c, libmisc/rtmonuse/rtmonuse.c, +// libmisc/serdbg/serdbg.h, libmisc/serdbg/serdbgio.c, +// libmisc/serdbg/termios_printk.c, libmisc/serdbg/termios_printk.h, +// libmisc/shell/shell.c, libmisc/shell/shell.h, libmisc/stackchk/check.c, +// libmisc/stackchk/internal.h: Convert to using c99 fixed size types. +// +// Revision 1.5 2003/07/08 08:38:48 ralf +// 2003-07-08 Ralf Corsepius <corsepiu@faw.uni-ulm.de> +// +// * capture/capture-cli.c: Add config-header support. +// * capture/capture.c: Add config-header support. +// * cpuuse/cpuuse.c: Add config-header support. +// * devnull/devnull.c: Add config-header support. +// * dummy/dummy.c: Add config-header support. +// * dumpbuf/dumpbuf.c: Add config-header support. +// * monitor/mon-command.c: Add config-header support. +// * monitor/mon-config.c: Add config-header support. +// * monitor/mon-dname.c: Add config-header support. +// * monitor/mon-driver.c: Add config-header support. +// * monitor/mon-extension.c: Add config-header support. +// * monitor/mon-itask.c: Add config-header support. +// * monitor/mon-manager.c: Add config-header support. +// * monitor/mon-monitor.c: Add config-header support. +// * monitor/mon-mpci.c: Add config-header support. +// * monitor/mon-object.c: Add config-header support. +// * monitor/mon-prmisc.c: Add config-header support. +// * monitor/mon-queue.c: Add config-header support. +// * monitor/mon-server.c: Add config-header support. +// * monitor/mon-symbols.c: Add config-header support. +// * monitor/mon-task.c: Add config-header support. +// * mw-fb/mw_fb.c: Add config-header support. +// * mw-fb/mw_uid.c: Add config-header support. +// * rtmonuse/rtmonuse.c: Add config-header support. +// * serdbg/serdbg.c: Add config-header support. +// * serdbg/serdbgio.c: Add config-header support. +// * serdbg/termios_printk.c: Add config-header support. +// * shell/cmds.c: Add config-header support. +// * stackchk/check.c: Add config-header support. +// * untar/untar.c: Add config-header support. +// +// Revision 1.4 2002/01/04 18:32:48 joel +// 2002-01-04 Ralf Corsepius <corsepiu@faw.uni-ulm.de> +// +// * mw-fb/mw_uid.c: Apply rtems_set_errno_and_return_minus_one. +// +// Revision 1.3 2000/11/30 14:36:46 joel +// 2000-11-30 Joel Sherrill <joel@OARcorp.com> +// +// * mw-fb/mw_uid.c: Removed unnecessary dependency on <bsp.h>. +// +// Revision 1.2 2000/08/30 17:12:55 joel +// 2000-08-30 Joel Sherrill <joel@OARcorp.com> +// +// * Many files: Moved posix/include/rtems/posix/seterr.h to +// score/include/rtems/seterr.h so it would be available within +// all APIs. +// +// Revision 1.1 2000/08/30 08:21:24 joel +// 2000-08-26 Rosimildo da Silva <rdasilva@connecttel.com> +// +// * Added generic Micro FrameBuffer interface for MicroWindows. +// This interface allows MicroWindows to under RTEMS. A sample +// driver has been developed for the pc386 BSP. See +// pc386/fb_vga.c as a sample. +// * Added Uniform Input Device interface for MicroWindows. +// See PC386 bsp for sample drivers for mouse and keyboard (console). +// * mw-bf: New directory. +// * Makefile.am, configure.in, wrapup/Makefile.am: Account for mw-fb. +// * mw-fb/Makefile.am: New file. +// * mw-fb/mw_fb.c: New file. +// * mw-fb/mw_fb.h: New file. +// * mw-fb/mw_uid.c: New file. +// * mw-fb/mw_uid.h: New file. +// +// +///////////////////////////////////////////////////////////////////////////// +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <rtems.h> + +#include <rtems/mw_uid.h> +#include <rtems/seterr.h> + +static rtems_id queue_id = 0; +static int open_count = 0; + +/* +#define MW_DEBUG_ON 1 +*/ + +/* open a message queue with the kernel */ +int uid_open_queue( const char *q_name, int flags, size_t max_msgs ) +{ + static rtems_name queue_name; + + /* + * For the first device calling this function we would create the queue. + * It is assumed that this call is done at initialization, and no concerns + * regarding multi-threading is taken in consideration here. + */ + if( !open_count ) + { + rtems_status_code status; + queue_name = rtems_build_name( q_name[0], + q_name[1], + q_name[2], + q_name[3] ); + status = rtems_message_queue_create( queue_name, + max_msgs, + sizeof( struct MW_UID_MESSAGE ), + RTEMS_FIFO | RTEMS_LOCAL, + &queue_id ); + if( status != RTEMS_SUCCESSFUL ) + { +#ifdef MW_DEBUG_ON + printk( "UID_Queue: error creating queue: %d\n", status ); +#endif + return -1; + } +#ifdef MW_DEBUG_ON + printk( "UID_Queue: id=%X\n", queue_id ); +#endif + } + open_count++; + return 0; +} + + +/* close message queue */ +int uid_close_queue( void ) +{ + if( open_count == 1 ) + { + rtems_message_queue_delete( queue_id ); + queue_id = 0; + } + open_count--; + return 0; +} + +/* reads for a message from the device */ +int uid_read_message( struct MW_UID_MESSAGE *m, unsigned long timeout ) +{ + rtems_status_code status; + uint32_t size = 0; + unsigned long micro_secs = timeout*1000; + int wait = ( timeout != 0 ); + + status = rtems_message_queue_receive( queue_id, + (void*)m, + &size, + wait ? RTEMS_WAIT : RTEMS_NO_WAIT, + TOD_MICROSECONDS_TO_TICKS(micro_secs ) ); + + if( status == RTEMS_SUCCESSFUL ) + { + return size; + } + else if( ( status == RTEMS_UNSATISFIED ) || ( status == RTEMS_TIMEOUT ) ) + { + /* this macro returns -1 */ + rtems_set_errno_and_return_minus_one( ETIMEDOUT ); + } + /* Here we have one error condition */ +#ifdef MW_DEBUG_ON + printk( "UID_Queue: error reading queue: %d\n", status ); +#endif + return -1; +} + + +/* + * add a message to the queue of events. This method cna be used to + * simulate hardware events, and it can be very handy during development + * a new interface. + */ +int uid_send_message( struct MW_UID_MESSAGE *m ) +{ + rtems_status_code status; + status = rtems_message_queue_send( queue_id, ( void * )m, + sizeof( struct MW_UID_MESSAGE ) ); + return status == RTEMS_SUCCESSFUL ? 0 : -1; +} + +/* + * register the device to insert events to the message + * queue named as the value passed in q_name + */ +int uid_register_device( int fd, const char *q_name ) +{ + return ioctl( fd, MW_UID_REGISTER_DEVICE, q_name ); +} + +/* tell this device to stop adding events to the queue */ +int uid_unregister_device( int fd ) +{ + return ioctl( fd, MW_UID_UNREGISTER_DEVICE, NULL ); +} + +/* set the keyboard */ +int uid_set_kbd_mode( int fd, int mode, int *old_mode ) +{ + if (ioctl( fd, MV_KDGKBMODE, old_mode) < 0) + { + return -1; + } + if (ioctl(fd, MV_KDSKBMODE, mode ) < 0 ) + { + return -1; + } + return 0; +} diff --git a/cpukit/libmisc/mw-fb/mw_uid.h b/cpukit/libmisc/mw-fb/mw_uid.h new file mode 100644 index 0000000000..2bfc258057 --- /dev/null +++ b/cpukit/libmisc/mw-fb/mw_uid.h @@ -0,0 +1,160 @@ +/* +///////////////////////////////////////////////////////////////////////////// +// $Header$ +// +// Copyright (c) 2000 - Rosimildo da Silva +// +// MODULE DESCRIPTION: +// This module defines the interface for input devices used by MicroWindows +// in an embedded system environment. +// +// MODIFICATION/HISTORY: +// +// $Log$ +// Revision 1.1 2000/08/30 08:21:24 joel +// 2000-08-26 Rosimildo da Silva <rdasilva@connecttel.com> +// +// * Added generic Micro FrameBuffer interface for MicroWindows. +// This interface allows MicroWindows to under RTEMS. A sample +// driver has been developed for the pc386 BSP. See +// pc386/fb_vga.c as a sample. +// * Added Uniform Input Device interface for MicroWindows. +// See PC386 bsp for sample drivers for mouse and keyboard (console). +// * mw-bf: New directory. +// * Makefile.am, configure.in, wrapup/Makefile.am: Account for mw-fb. +// * mw-fb/Makefile.am: New file. +// * mw-fb/mw_fb.c: New file. +// * mw-fb/mw_fb.h: New file. +// * mw-fb/mw_uid.c: New file. +// * mw-fb/mw_uid.h: New file. +// +// +///////////////////////////////////////////////////////////////////////////// +*/ +#ifndef _MW_UID_H +#define _MW_UID_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* 0x41XX -- IOCLT functions for the Micro Input Devices commands */ +#define MW_UID_REGISTER_DEVICE 0x4100 +#define MW_UID_UNREGISTER_DEVICE 0x4101 + + +/* devices supported by MicroWindows */ +enum MW_INPUT_DEVICE_TYPE +{ + MV_UID_INVALID = 0, + MV_UID_REL_POS = 1, /* mouse */ + MV_UID_ABS_POS = 2, /* touch-screen */ + MV_UID_KBD = 3, /* keyboard */ + MV_UID_TIMER = 4 /* timer -- not used */ +}; + + +/* matching MicroWindows */ +#define MV_BUTTON_RIGHT 0x01 +#define MV_BUTTON_CENTER 0x02 +#define MV_BUTTON_LEFT 0x04 + +/* modifiers of the keyboard type devices */ +#define MV_KEY_MODIFIER_SHIFT_DOWN 0x10 +#define MV_KEY_MODIFIER_ALT_DOWN 0x20 + +/* indication of the LEDS */ +#define MV_KEY_MODIFIER_CAPS_ON 0x04 +#define MV_KEY_MODIFIER_NUN_LOCK_ON 0x02 +#define MV_KEY_SCROLL_LOCK_ON 0x01 + +/* keyboard modes -- default ASCII */ +#define MV_KEY_MODE_ASCII 0x01 +/* + * This mode one event is sent when a key is pressed, + * and another one is send when a key is released. + */ +#define MV_KEY_MODE_SCANCODE 0x00 + + +/* these defines match with the linux keyboard range + for ioctls functions for the keyboard interface. + 0x4BXX --- keyboard related functions + */ +#define MV_KDGKBMODE 0x4B44 /* gets current keyboard mode */ +#define MV_KDSKBMODE 0x4B45 /* sets current keyboard mode */ + +/* + * Message generated by input devices controlled by MicroWindows. + */ +struct MW_UID_MESSAGE +{ + enum MW_INPUT_DEVICE_TYPE type; /* device type */ + union + { + /* fired when keyboard events are raised */ + struct kbd_t { + unsigned short code; /* keycode or scancode */ + unsigned char modifiers; /* key modifiers */ + unsigned char mode; /* current Kbd mode */ + } kbd; + + /* fired when position events are raised, mouse, touch screen, etc */ + struct pos_t { + unsigned short btns; /* indicates which buttons are pressed */ + short x; /* x location */ + short y; /* y location */ + short z; /* z location, 0 for 2D */ + } pos; + + /* fired by a timer device periodically */ + struct timer_t { + unsigned long frt; /* free running timer */ + unsigned long seq; /* sequence number */ + } tmr; + + } m; +}; + + +/* + * API for creating/closing/accessing the message queue used by the micro + * input device interface. All functions in this interface returns a + * zero ( 0 ) on success. One exception for that is the "read" routine + * that returns the number of bytes read. Negaive numbers indicate errors + * + * The implementation of the message queue for RTEMS uses a POSIX message + * queue interface. It should be very portable among systems with a POSIX + * support. + */ + +/* creates the message queue that holds events from the input devices */ +extern int uid_open_queue( const char *q_name, int flags, size_t max_msgs ); + +/* closes message queue */ +extern int uid_close_queue( void ); + +/* + * reads a message from the queue. It waits up to the specified + * timeout in mili-seconds. + */ +extern int uid_read_message( struct MW_UID_MESSAGE *m, unsigned long timeout ); + +/* write a message to the queue */ +extern int uid_write_message( struct MW_UID_MESSAGE *m ); + + +/* register device to insert data to the queue */ +extern int uid_register_device( int fd, const char *q_name ); + +/* unregister device to stop adding messages to the queue */ +extern int uid_unregister_device( int fd ); + +/* set the keyboard */ +extern int uid_set_kbd_mode( int fd, int mode, int *old_mode ); + +#ifdef __cplusplus +} +#endif + +#endif /* _MW_UID_H */ diff --git a/cpukit/libmisc/rtmonuse/rtmonuse.c b/cpukit/libmisc/rtmonuse/rtmonuse.c new file mode 100644 index 0000000000..14fca3b0eb --- /dev/null +++ b/cpukit/libmisc/rtmonuse/rtmonuse.c @@ -0,0 +1,208 @@ +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <ctype.h> + +#include <rtems/rtmonuse.h> + +typedef struct { + rtems_id id; + uint32_t count; + uint32_t missed_count; + uint32_t min_cpu_time; + uint32_t max_cpu_time; + uint32_t total_cpu_time; + uint32_t min_wall_time; + uint32_t max_wall_time; + uint32_t total_wall_time; +} Period_usage_t; + +Period_usage_t *Period_usage_Information; + +/*PAGE + * + * Period_usage_Initialize + */ + +void Period_usage_Initialize( void ) +{ + int maximum; + + maximum = _Configuration_Table->RTEMS_api_configuration->maximum_periods; + + Period_usage_Information = malloc( sizeof(Period_usage_t) * (maximum+1) ); + + Period_usage_Reset(); +} + +/*PAGE + * + * Period_usage_Reset + */ + +void Period_usage_Reset( void ) +{ + uint32_t i; + Period_usage_t *the_usage; + + for ( i=0 ; + i<_Configuration_Table->RTEMS_api_configuration->maximum_periods ; + i++ ) { + the_usage = &Period_usage_Information[ i ]; + + the_usage->count = 0; + the_usage->missed_count = 0; + the_usage->min_cpu_time = 0xFFFFFFFF; + the_usage->max_cpu_time = 0; + the_usage->total_cpu_time = 0; + the_usage->min_wall_time = 0xFFFFFFFF; + the_usage->max_wall_time = 0; + the_usage->total_wall_time = 0; + + } +} + +/*PAGE + * + * Period_usage_Update + */ + +void Period_usage_Update( + rtems_id id +) +{ + rtems_rate_monotonic_period_status rm_status; + rtems_status_code status; + Period_usage_t *the_usage; + + assert( Period_usage_Information ); + + status = rtems_rate_monotonic_get_status( id, &rm_status ); + assert( status == RTEMS_SUCCESSFUL ); + + the_usage = &Period_usage_Information[ rtems_get_index( id ) ]; + + the_usage->id = id; + the_usage->count++; + if ( rm_status.state == RATE_MONOTONIC_EXPIRED ) + the_usage->missed_count++; + the_usage->total_cpu_time += rm_status.ticks_executed_since_last_period; + the_usage->total_wall_time += rm_status.ticks_since_last_period; + + /* + * Update CPU time + */ + + if ( rm_status.ticks_executed_since_last_period < the_usage->min_cpu_time ) + the_usage->min_cpu_time = rm_status.ticks_executed_since_last_period; + + if ( rm_status.ticks_executed_since_last_period > the_usage->max_cpu_time ) + the_usage->max_cpu_time = rm_status.ticks_executed_since_last_period; + + /* + * Update Wall time + */ + + if ( rm_status.ticks_since_last_period < the_usage->min_wall_time ) + the_usage->min_wall_time = rm_status.ticks_since_last_period; + + if ( rm_status.ticks_since_last_period > the_usage->max_wall_time ) + the_usage->max_wall_time = rm_status.ticks_since_last_period; + +} + +/*PAGE + * + * Period_usage_Dump + */ + +void Period_usage_Dump( void ) +{ + uint32_t i; + Period_usage_t *the_usage; + Rate_monotonic_Control *the_period; + uint32_t u32_name; + char *cname; + char name[5]; + uint32_t api_index; + Objects_Information *information; + + if ( !Period_usage_Information ) { + fprintf(stdout, "Period statistics library is not initialized\n" ); + return; + } + + fprintf(stdout, "Period information by period\n" ); + fprintf(stdout, " ID OWNER PERIODS MISSED CPU TIME WALL TIME\n" ); + + /* + * RTEMS does not use an index of zero for object ids. + */ + + for ( i=1 ; + i<=_Configuration_Table->RTEMS_api_configuration->maximum_periods ; + i++ ) { + the_usage = &Period_usage_Information[ i ]; + if ( the_usage->count == 0 ) + continue; + + the_period = + (Rate_monotonic_Control *)_Rate_monotonic_Information.local_table[ i ]; + + name[ 0 ] = ' '; + name[ 1 ] = ' '; + name[ 2 ] = ' '; + name[ 3 ] = ' '; + name[ 4 ] = '\0'; + + if ( the_period->owner ) { + api_index = _Objects_Get_API(the_period->owner->Object.id); + information = _Objects_Information_table[ api_index ][ 1 ]; + + if ( information->is_string ) { + cname = the_period->owner->Object.name; + name[ 0 ] = cname[0]; + name[ 1 ] = cname[1]; + name[ 2 ] = cname[2]; + name[ 3 ] = cname[3]; + name[ 4 ] = '\0'; + } else { + u32_name = (uint32_t )the_period->owner->Object.name; + name[ 0 ] = (u32_name >> 24) & 0xff; + name[ 1 ] = (u32_name >> 16) & 0xff; + name[ 2 ] = (u32_name >> 8) & 0xff; + name[ 3 ] = (u32_name >> 0) & 0xff; + name[ 4 ] = '\0'; + } + } + + if ( !isprint(name[0]) ) name[0] = '*'; + if ( !isprint(name[1]) ) name[1] = '*'; + if ( !isprint(name[2]) ) name[2] = '*'; + if ( !isprint(name[3]) ) name[3] = '*'; + + + fprintf(stdout, + "0x%08x %4s %6d %3d %d/%d/%5.2f %d/%d/%3.2f\n", + the_usage->id, + name, + the_usage->count, + the_usage->missed_count, + the_usage->min_cpu_time, + the_usage->max_cpu_time, + (double) the_usage->total_cpu_time / (double) the_usage->count, + the_usage->min_wall_time, + the_usage->max_wall_time, + (double) the_usage->total_wall_time / (double) the_usage->count + ); + } +} diff --git a/cpukit/libmisc/rtmonuse/rtmonuse.h b/cpukit/libmisc/rtmonuse/rtmonuse.h new file mode 100644 index 0000000000..2741698738 --- /dev/null +++ b/cpukit/libmisc/rtmonuse/rtmonuse.h @@ -0,0 +1,18 @@ +/* + * $Id$ + */ + +#ifndef __RATE_MONOTONIC_USAGE_h +#define __RATE_MONOTONIC_USAGE_h + +void Period_usage_Initialize( void ); + +void Period_usage_Reset( void ); + +void Period_usage_Update( + rtems_id id +); + +void Period_usage_Dump( void ); + +#endif diff --git a/cpukit/libmisc/serdbg/README b/cpukit/libmisc/serdbg/README new file mode 100644 index 0000000000..338249c928 --- /dev/null +++ b/cpukit/libmisc/serdbg/README @@ -0,0 +1,138 @@ +# +# $Id$ +# + +This directory contains three useful packages related to the termios I/O +system: + +PACKAGE SERDBGIO +================ +"serdbgio" provides the "serial gdb" standard I/O functions "getDebugChar" +and "putDebugChar" for any device driver supporting polled termios mode. + +The initialization function "serdbg_open" opens the v.24 port intended +for the serial debug connection, and sets the desired baud rate. The +"getDebugChar" and "putDebugChar" functions then interact with the +corresponding driver using the calls intended for polled termios +operation. + +Specification for the debug device, baud rate and other parameters is +done in a global structure of type "serdbg_conf_t". A configuration +mechanism quite similar to the overall RTEMS configuration is available. + +PACKAGE SERDBG +============== +"serdbg" provides a means to optionally initialize and/or start a +serial gdb session as soon as possible, this means as soon as all +drivers have been initialized. The serial debug I/O functions can +either be integrated as special routines of the BSP drivers, or using +the package "serdbgio" + +PACKAGE TERMIOS_PRINTK +====================== +"termios_printk" provides a standard output function suitable to use +with "printk". It uses the same technique as serdbgio, hooking the +interface between a polled device driver and the termios system. + + +REQUIREMENTS +============ + +- These two packages can be used with any polled termios device +driver. +- For standard initialization, they need a modified "bsppost.c" +to perform the initialization calls. + +USAGE +===== + +For using these packages add the following to your "init" module or +your "system.h" file (Note: most macro settings fall back to a +default, if not set.): + +/* + * CONFIGURE_USE_SERDBG + * set this macro, if you want to connect gdb over a serial line + * when set, the debug stub will be connected after driver + * initialization in "bsppost.c" + */ +#define CONFIGURE_USE_SERDBG + + +/* + * CONFIGURE_SERDBG_SKIP_INIT_BKPT + * set this macro, if you do not want the gdb interface to wait for a + * debugger connection directly after initialization + * If you set this macro, the gdb stub will only hook various + * exception vectors when called from "bsppost.c". + */ +/* #define CONFIGURE_SERDBG_SKIP_INIT_BKPT */ + +/* + * CONFIGURE_SERDBG_USE_POLLED_TERMIOS + * set this macro, if you want "serdbgio" to provide the I/O + * functions for the serial gdb connection + */ +#define CONFIGURE_SERDBG_USE_POLLED_TERMIOS + +/* + * CONFIGURE_SERDBG_DEVNAME + * use this macro to specify the serial device to use + * for "serdbgio". + * Only used, when CONFIGURE_SERDBG_USE_POLLED_TERMIOS is set + */ +#define CONFIGURE_SERDBG_DEVNAME "/dev/tty03" + +/* + * CONFIGURE_SERDBG_BAUDRATE + * use this macro to specify the baud rate to use + * for "serdbgio". + * Only used, when CONFIGURE_SERDBG_USE_POLLED_TERMIOS is set + */ +#define CONFIGURE_SERDBG_BAUDRATE 57600 + +/* + * CONFIGURE_SERDBG_CALLOUT + * use this macro to specify a routine that will called during I/O polling + * Only used, when CONFIGURE_SERDBG_USE_POLLED_TERMIOS is set + * This function of type "void pollfnc(void)" can be used for e.g. + * tickling a watchdog + */ +/* #define CONFIGURE_SERDBG_CALLOUT tickle_my_watchdog_fnc */ + +#include <serdbgcnf.h> + +/* + * CONFIGURE_USE_TERMIOS_PRINTK + * set this macro, if you want printk output to be sent to a serial + * driver using the polled termios interface + * when set, the printk output function will be connected after driver + * initialization in "bsppost.c" + */ +#define CONFIGURE_USE_TERMIOS_PRINTK + +/* + * CONFIGURE_TERMIOS_PRINTK_DEVNAME + * use this macro to specify the serial device to use + * for printk output. + * Only used, when CONFIGURE_USE_TERMIOS_PRINTK is set + */ +#define CONFIGURE_TERMIOS_PRINTK_DEVNAME "/dev/console" + +/* + * CONFIGURE_TERMIOS_PRINTK_BAUDRATE + * use this macro to specify the baudrate to use + * for printk output. + * Only used, when CONFIGURE_USE_TERMIOS_PRINTK is set + */ +#define CONFIGURE_TERMIOS_PRINTK_BAUDRATE 9600 + +/* + * CONFIGURE_TERMIOS_PRINTK_CALLOUT + * use this macro to specify a routine that will called during I/O polling + * This function of type "void pollfnc(void)" can be used for e.g. + * tickling a watchdog + */ +/* #define CONFIGURE_TERMIOS_PRINTK_CALLOUT tickle_my_watchdog_fnc */ + +#include <termios_printk_cnf.h> diff --git a/cpukit/libmisc/serdbg/serdbg.c b/cpukit/libmisc/serdbg/serdbg.c new file mode 100644 index 0000000000..4bca324934 --- /dev/null +++ b/cpukit/libmisc/serdbg/serdbg.c @@ -0,0 +1,94 @@ +/*===============================================================*\ +| Project: RTEMS remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: serdbg.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file contains intialization and utility functions to add | +| a gdb remote debug stub to an RTEMS system | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 04.04.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <rtems/serdbg.h> + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int serdbg_init_dbg +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize remote gdb session over serial line | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| rtems_status_code | +\*=========================================================================*/ +{ + static boolean is_initialized = FALSE; + + rtems_status_code rc = RTEMS_SUCCESSFUL; + extern void set_debug_traps(void); + extern void breakpoint(void); + + if (is_initialized) { + return RTEMS_SUCCESSFUL; + } + is_initialized = TRUE; + /* + * try to open serial device + */ + if (rc == RTEMS_SUCCESSFUL) { + if ((serdbg_conf.open_io != NULL) && + (0 > serdbg_conf.open_io(serdbg_conf.devname,serdbg_conf.baudrate))) { + fprintf(stderr, + "remote_gdb_init: cannot open device %s " + "for gdb connection:%s\n",serdbg_conf.devname,strerror(errno)); + rc = RTEMS_IO_ERROR; + } + } + /* + * initialize gdb stub + */ + if (rc == RTEMS_SUCCESSFUL) { + set_debug_traps(); + } + /* + * now activate gdb stub + */ + if ((rc == RTEMS_SUCCESSFUL) && + !serdbg_conf.skip_init_bkpt) { + breakpoint(); + } + + /* + * return to original function + * this may be already unter gdb control + */ + return rc; +} diff --git a/cpukit/libmisc/serdbg/serdbg.h b/cpukit/libmisc/serdbg/serdbg.h new file mode 100644 index 0000000000..00d5d827e1 --- /dev/null +++ b/cpukit/libmisc/serdbg/serdbg.h @@ -0,0 +1,174 @@ +/*===============================================================*\ +| Project: RTEMS remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: serdbg.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file declares intialization functions to add | +| a gdb remote debug stub to an RTEMS system | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 04.04.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ +#ifndef _SERDBG_H +#define _SERDBG_H + +#include <rtems.h> +#include <termios.h> + +typedef struct { + uint32_t baudrate; /* debug baud rate, e.g. 57600 */ + void (*callout)(void); /* callout pointer during polling */ + int (*open_io)(const char *dev_name,uint32_t baudrate); /* I/O open fnc */ + const char *devname; /* debug device, e.g. "/dev/tty01" */ + uint8_t skip_init_bkpt; /* if TRUE, do not stop when initializing */ +} serdbg_conf_t; + +/* + * must be defined in init module... + */ +extern serdbg_conf_t serdbg_conf; + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void putDebugChar +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send character to remote debugger | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + char c /* char to send */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int getDebugChar +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| get character from remote debugger | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void /* <none> */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void serdbg_exceptionHandler +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| hook directly to an exception vector | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int vecnum, /* vector index to hook at */ + void *vector /* address of handler function */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int serdbg_init +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| initialize remote gdb session over serial line | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + void + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| rtems_status_code | +\*=========================================================================*/ + +/* + * stuff from serdbgio.c + */ +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int serdbg_open + +/*-------------------------------------------------------------------------*\ +| Purpose: | +| try to open given serial debug port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + const char *dev_name, /* name of device to open */ + uint32_t baudrate /* baud rate to use */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0 on success, -1 and errno otherwise | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void putDebugChar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send one character to serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + char c /* character to print */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int getDebugChar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for one character from serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + void /* none */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| received character | +\*=========================================================================*/ + +#endif /* _SERDBG_H */ diff --git a/cpukit/libmisc/serdbg/serdbgcnf.h b/cpukit/libmisc/serdbg/serdbgcnf.h new file mode 100644 index 0000000000..024e545d5c --- /dev/null +++ b/cpukit/libmisc/serdbg/serdbgcnf.h @@ -0,0 +1,81 @@ +/*===============================================================*\ +| Project: RTEMS configure remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: serdbgcnf.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file declares intialization functions to add | +| a gdb remote debug stub to an RTEMS system | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 13.05.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ +#ifndef _SERDBGCNF_H +#define _SERDBGCNF_H + +#include <rtems/serdbg.h> + +#ifdef CONFIGURE_INIT + +/* + * fallback for baud rate to use + */ +#ifndef CONFIGURE_SERDBG_BAUDRATE +#define CONFIGURE_SERDBG_BAUDRATE 9600 +#endif + +/* + * fallback for device name to use + */ +#ifndef CONFIGURE_SERDBG_DEVNAME +#define CONFIGURE_SERDBG_DEVNAME "/dev/tty01" +#endif + +/* + * fill in serdbg_conf structure + */ +serdbg_conf_t serdbg_conf = { + CONFIGURE_SERDBG_BAUDRATE, + +#ifdef CONFIGURE_SERDBG_CALLOUT + CONFIGURE_SERDBG_CALLOUT, +#else + NULL, +#endif + +#ifdef CONFIGURE_SERDBG_USE_POLLED_TERMIOS + serdbg_open, +#else + NULL, +#endif + + CONFIGURE_SERDBG_DEVNAME, + +#ifdef CONFIGURE_SERDBG_SKIP_INIT_BKPT + TRUE, +#else + FALSE, +#endif +}; + +int serdbg_init(void) { +#ifdef CONFIGURE_USE_SERDBG + extern int serdbg_init_dbg(void); + return serdbg_init_dbg(); +#else + return 0; +#endif +} + +#endif /* CONFIGURE_INIT */ + +#endif /* _SERDBGCNF_H */ diff --git a/cpukit/libmisc/serdbg/serdbgio.c b/cpukit/libmisc/serdbg/serdbgio.c new file mode 100644 index 0000000000..494a2fab0b --- /dev/null +++ b/cpukit/libmisc/serdbg/serdbgio.c @@ -0,0 +1,256 @@ +/*===============================================================*\ +| File: serdbgio.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| Hebststr. 8, 82178 Puchheim, Germany | +| <Thomas.Doerfler@imd-systems.de> | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| all rights reserved | ++-----------------------------------------------------------------+ +| TERMIOS serial gdb interface support | +| the functions in this file allow the standard gdb stubs like | +| "m68k-stub.c" to access any serial interfaces that work with | +| RTEMS termios in polled mode | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 10.05.02 creation doe | +|*****************************************************************| +|* $Id$ + * +|*****************************************************************| +\*===============================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/libio_.h> +#include <errno.h> +#include <unistd.h> /* close */ +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> + +#include <rtems/termiostypes.h> +#include <rtems/serdbg.h> + + +/* + * internal variables + */ +int serdbg_fd = -1; +struct rtems_termios_tty *serdbg_tty; + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int serdbg_open + +/*-------------------------------------------------------------------------*\ +| Purpose: | +| try to open given serial debug port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + const char *dev_name, /* name of device to open */ + uint32_t baudrate /* baud rate to use */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0 on success, -1 and errno otherwise | +\*=========================================================================*/ +{ + boolean err_occurred = FALSE; + rtems_libio_t *iop = NULL; + struct termios act_termios; + tcflag_t baudcode = B0; + +#define FD_STORE_CNT 3 + int fd_store[FD_STORE_CNT]; + int fd_store_used = 0; + + /* + * translate baudrate into baud code + */ + switch(baudrate) { + case 50: baudcode = B50; break; + case 75: baudcode = B75; break; + case 110: baudcode = B110; break; + case 134: baudcode = B134; break; + case 150: baudcode = B150; break; + case 200: baudcode = B200; break; + case 300: baudcode = B300; break; + case 600: baudcode = B600; break; + case 1200: baudcode = B1200; break; + case 1800: baudcode = B1800; break; + case 2400: baudcode = B2400; break; + case 4800: baudcode = B4800; break; + case 9600: baudcode = B9600; break; + case 19200: baudcode = B19200; break; + case 38400: baudcode = B38400; break; + case 57600: baudcode = B57600; break; + case 115200: baudcode = B115200; break; + case 230400: baudcode = B230400; break; + case 460800: baudcode = B460800; break; + default : err_occurred = TRUE; errno = EINVAL; break; + } + + /* + * open device for serdbg operation + * skip any fds that are between 0..2, because they are + * reserved for stdin/out/err + */ + if (!err_occurred && + (dev_name != NULL) && + (dev_name[0] != '\0')) { + do { + serdbg_fd = open(dev_name,O_RDWR); + if (serdbg_fd < 0) { + err_occurred = TRUE; + } + else { + if (serdbg_fd < 3) { + if (fd_store_used >= FD_STORE_CNT) { + err_occurred = TRUE; + } + else { + fd_store[fd_store_used++] = serdbg_fd; + } + } + } + } while (!err_occurred && + (serdbg_fd < 3)); + } + /* + * close any fds, that have been placed in fd_store + * so fd 0..2 are reusable again + */ + while (--fd_store_used >= 0) { + close(fd_store[fd_store_used]); + } + + /* + * capture tty structure + */ + if (!err_occurred) { + iop = &rtems_libio_iops[serdbg_fd]; + serdbg_tty = iop->data1; + } + /* + * set device baudrate + * (and transp mode, this is not really needed) + * ... + */ + /* + * ... get fd settings + */ + if (!err_occurred && + (0 != tcgetattr(serdbg_fd,&act_termios))) { + err_occurred = TRUE; + } + if (!err_occurred) { + act_termios.c_iflag + &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + act_termios.c_oflag + &= ~OPOST; + + act_termios.c_lflag + &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + + cfsetospeed(&act_termios,baudcode); + cfsetispeed(&act_termios,baudcode); + + if (0 != tcsetattr(serdbg_fd,TCSANOW,&act_termios)) { + err_occurred = TRUE; + } + } + return (err_occurred + ? -1 + : 0); +} + +void putDebugChar(char c) __attribute__ ((__weak__)); +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void putDebugChar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send one character to serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + char c /* character to print */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + /* + * call serdbg polling callout, if available + */ + if (serdbg_conf.callout != NULL) { + serdbg_conf.callout(); + } + /* + * check, whether debug serial port is available + */ + if ((serdbg_tty != NULL) && + (serdbg_tty->device.write != NULL)) { + /* + * send character to debug serial port + */ + serdbg_tty->device.write(serdbg_tty->minor,&c,1); + } +} + +int getDebugChar(void) __attribute__ ((__weak__)); +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int getDebugChar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for one character from serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + void /* none */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| received character | +\*=========================================================================*/ +{ + int c = -1; + /* + * check, whether debug serial port is available + */ + if ((serdbg_tty != NULL) && + (serdbg_tty->device.pollRead != NULL)) { + do { + /* + * call serdbg polling callout, if available + */ + if (serdbg_conf.callout != NULL) { + serdbg_conf.callout(); + } + /* + * get character from debug serial port + */ + c = serdbg_tty->device.pollRead(serdbg_tty->minor); + } while (c < 0); + } + return c; +} diff --git a/cpukit/libmisc/serdbg/termios_printk.c b/cpukit/libmisc/serdbg/termios_printk.c new file mode 100644 index 0000000000..6e31bbcfd0 --- /dev/null +++ b/cpukit/libmisc/serdbg/termios_printk.c @@ -0,0 +1,237 @@ +/*===============================================================*\ +| File: termios_printk.c | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| Hebststr. 8, 82178 Puchheim, Germany | +| <Thomas.Doerfler@imd-systems.de> | +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| http://www.rtems.com/license/LICENSE. | +| all rights reserved | ++-----------------------------------------------------------------+ +| TERMIOS printk support | +| this module performs low-level printk output using | +| a polled termios driver | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 13.05.02 creation doe | +|*****************************************************************| +|* $Id$ + * +|*****************************************************************| +\*===============================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/libio_.h> +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> + +#include <rtems/termiostypes.h> +#include <rtems/bspIo.h> +#include <rtems/termios_printk.h> + +/* + * internal variables + */ +int termios_printk_fd = -1; +struct rtems_termios_tty *termios_printk_tty; + +static void _termios_printk_null_char( char c ) {return;} + +BSP_output_char_function_type BSP_output_char = _termios_printk_null_char; +BSP_polling_getchar_function_type BSP_poll_char; + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void termios_printk_outputchar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send one character to serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + char c /* character to print */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ +{ + static const char cr = '\r'; + /* + * check, whether printk serial port is available + */ + + if ((termios_printk_tty != NULL) && + (termios_printk_tty->device.write != NULL)) { + /* + * call termios_printk polling callout, if available + */ + if (termios_printk_conf.callout != NULL) { + termios_printk_conf.callout(); + } + /* + * send character to debug serial port + */ + if (c == '\n') { + termios_printk_tty->device.write(termios_printk_tty->minor,&cr,1); + } + termios_printk_tty->device.write(termios_printk_tty->minor,&c,1); + } +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +char termios_printk_inputchar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for one character from serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + void /* none */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| received character | +\*=========================================================================*/ +{ + int c = -1; + /* + * check, whether debug serial port is available + */ + if ((termios_printk_tty != NULL) && + (termios_printk_tty->device.pollRead != NULL)) { + do { + /* + * call termios_printk polling callout, if available + */ + if (termios_printk_conf.callout != NULL) { + termios_printk_conf.callout(); + } + /* + * get character from debug serial port + */ + c = termios_printk_tty->device.pollRead(termios_printk_tty->minor); + } while (c < 0); + } + return c; +} + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int termios_printk_open + +/*-------------------------------------------------------------------------*\ +| Purpose: | +| try to open given serial debug port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + const char *dev_name, /* name of device to open */ + uint32_t baudrate /* baud rate to use */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0 on success, -1 and errno otherwise | +\*=========================================================================*/ +{ + boolean err_occurred = FALSE; + rtems_libio_t *iop = NULL; + struct termios act_termios; + tcflag_t baudcode = B0; + + if (termios_printk_fd >= 0) { + /* + * already initialized + */ + return 0; + } + /* + * translate baudrate into baud code + */ + switch(baudrate) { + case 50: baudcode = B50; break; + case 75: baudcode = B75; break; + case 110: baudcode = B110; break; + case 134: baudcode = B134; break; + case 150: baudcode = B150; break; + case 200: baudcode = B200; break; + case 300: baudcode = B300; break; + case 600: baudcode = B600; break; + case 1200: baudcode = B1200; break; + case 1800: baudcode = B1800; break; + case 2400: baudcode = B2400; break; + case 4800: baudcode = B4800; break; + case 9600: baudcode = B9600; break; + case 19200: baudcode = B19200; break; + case 38400: baudcode = B38400; break; + case 57600: baudcode = B57600; break; + case 115200: baudcode = B115200; break; + case 230400: baudcode = B230400; break; + case 460800: baudcode = B460800; break; + default : err_occurred = TRUE; errno = EINVAL; break; + } + /* + * open device for serdbg operation + */ + if (!err_occurred && + (dev_name != NULL) && + (dev_name[0] != '\0')) { + termios_printk_fd = open(dev_name,O_RDWR); + if (termios_printk_fd < 0) { + err_occurred = TRUE; + } + } + /* + * capture tty structure + */ + if (!err_occurred) { + iop = &rtems_libio_iops[termios_printk_fd]; + termios_printk_tty = iop->data1; + } + /* + * set device baudrate + * (and transp mode, this is not really needed) + * ... + */ + /* + * ... get fd settings + */ + if (!err_occurred && + (0 != tcgetattr(termios_printk_fd,&act_termios))) { + err_occurred = TRUE; + } + if (!err_occurred) { + + cfsetospeed(&act_termios,baudcode); + cfsetispeed(&act_termios,baudcode); + + if (0 != tcsetattr(termios_printk_fd,TCSANOW,&act_termios)) { + err_occurred = TRUE; + } + } + if (!err_occurred) { + BSP_output_char = termios_printk_outputchar; + BSP_poll_char = termios_printk_inputchar; + } + return (err_occurred + ? -1 + : 0); +} diff --git a/cpukit/libmisc/serdbg/termios_printk.h b/cpukit/libmisc/serdbg/termios_printk.h new file mode 100644 index 0000000000..d3a3703b11 --- /dev/null +++ b/cpukit/libmisc/serdbg/termios_printk.h @@ -0,0 +1,95 @@ +/*===============================================================*\ +| Project: RTEMS remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: termios_printk.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file declares intialization functions to add | +| printk polled output via termios polled drivers | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 13.04.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ +#ifndef _TERMIOS_PRINTK_H +#define _TERMIOS_PRINTK_H + +#include <rtems.h> +#include <termios.h> + +typedef struct { + uint32_t baudrate; /* debug baud rate, e.g. 57600 */ + void (*callout)(void); /* callout pointer during polling */ + const char *devname; /* debug device, e.g. "/dev/tty01" */ +} termios_printk_conf_t; + +/* + * must be defined in init module... + */ +extern termios_printk_conf_t termios_printk_conf; + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +void termios_printk_outputchar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| send one character to serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + char c /* character to print */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| <none> | +\*=========================================================================*/ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +char termios_printk_inputchar +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait for one character from serial port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + void /* none */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| received character | +\*=========================================================================*/ + + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +int termios_printk_open + +/*-------------------------------------------------------------------------*\ +| Purpose: | +| try to open given serial debug port | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ +( + const char *dev_name, /* name of device to open */ + uint32_t baudrate /* baud rate to use */ + ); +/*-------------------------------------------------------------------------*\ +| Return Value: | +| 0 on success, -1 and errno otherwise | +\*=========================================================================*/ +#endif /* _TERMIOS_PRINTK_H */ diff --git a/cpukit/libmisc/serdbg/termios_printk_cnf.h b/cpukit/libmisc/serdbg/termios_printk_cnf.h new file mode 100644 index 0000000000..55aea94d03 --- /dev/null +++ b/cpukit/libmisc/serdbg/termios_printk_cnf.h @@ -0,0 +1,70 @@ +/*===============================================================*\ +| Project: RTEMS configure remote gdb over serial line | ++-----------------------------------------------------------------+ +| File: termios_printk_cnf.h | ++-----------------------------------------------------------------+ +| Copyright (c) 2002 IMD | +| Ingenieurbuero fuer Microcomputertechnik Th. Doerfler | +| <Thomas.Doerfler@imd-systems.de> | +| all rights reserved | ++-----------------------------------------------------------------+ +| this file declares intialization functions to add | +| printk support via polled termios | +| | ++-----------------------------------------------------------------+ +| date history ID | +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | +| 13.05.02 creation doe | +\*===============================================================*/ +/* + * $Id$ + */ +#ifndef _TERMIOS_PRINTK_CNF_H +#define _TERMIOS_PRINTK_CNF_H + +#include <rtems/termios_printk.h> + +#ifdef CONFIGURE_INIT + +/* + * fallback for baud rate to use + */ +#ifndef CONFIGURE_TERMIOS_PRINTK_BAUDRATE +#define CONFIGURE_TERMIOS_PRINTK_BAUDRATE 9600 +#endif + +/* + * fallback for device name to use + */ +#ifndef CONFIGURE_TERMIOS_PRINTK_DEVNAME +#define CONFIGURE_TERMIOS_PRINTK_DEVNAME "/dev/console" +#endif + +#ifdef CONFIGURE_USE_TERMIOS_PRINTK +/* + * fill in termios_printk_conf structure + */ +termios_printk_conf_t termios_printk_conf = { + CONFIGURE_TERMIOS_PRINTK_BAUDRATE, + +#ifdef CONFIGURE_TERMIOS_PRINTK_CALLOUT + CONFIGURE_TERMIOS_PRINTK_CALLOUT, +#else + NULL, +#endif + CONFIGURE_TERMIOS_PRINTK_DEVNAME, +}; +#endif + +int termios_printk_init(void) { +#ifdef CONFIGURE_USE_TERMIOS_PRINTK + return termios_printk_open(termios_printk_conf.devname, + termios_printk_conf.baudrate); +#else + return 0; +#endif +} + +#endif /* CONFIGURE_INIT */ + +#endif /* _TERMIOS_PRINTK_CNF_H */ diff --git a/cpukit/libmisc/shell/README b/cpukit/libmisc/shell/README new file mode 100644 index 0000000000..9798becc37 --- /dev/null +++ b/cpukit/libmisc/shell/README @@ -0,0 +1,27 @@ +# +# $Id$ +# + +This directory contains a shell user extension +primary features: + + + create a user shell terminal task. + +This code has not been extensively tested. It is provided as a tool +for RTEMS users to open more shell terminal. +Suggestions and comments are appreciated. + +NOTES: + +1. printf() & getchar() works but you can't use + 0,1,2 like fildes. You need to use fileno(stdin),fileno(stdout),... + +2. You only need a termios dev to start a new session, add your new commands + and enjoy it. + +3. Telnetd daemon uses this (browse libnetworking/rtems_telnetd) + Enjoy it. + +FUTURE: + +1. Adding new commands in cmds.c to give file manegement to shell. diff --git a/cpukit/libmisc/shell/cmds.c b/cpukit/libmisc/shell/cmds.c new file mode 100644 index 0000000000..ba3a4b030e --- /dev/null +++ b/cpukit/libmisc/shell/cmds.c @@ -0,0 +1,526 @@ +/* + * Author: Fernando RUIZ CASAS + * + * Work: fernando.ruiz@ctv.es + * Home: correo@fernando-ruiz.com + * + * This file is inspired in rtems_monitor & Chris John MyRightBoot + * + * But I want to make it more user friendly + * A 'monitor' command is added to adapt the call rtems monitor commands + * at my call procedure + * + * TODO: A lot of improvements of course. + * cp, mv, ... + * hexdump, + * + * More? Say me it, please... + * + * The BSP Specific are not welcome here. + * + * C&S welcome... + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <termios.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <dirent.h> +#include <time.h> +#include <fcntl.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include <sys/types.h> +#include <stddef.h> + +#include <rtems.h> +#include <rtems/monitor.h> +#include <rtems/score/tod.h> + +#include <rtems/imfs.h> +#include <rtems/shell.h> + +/* ----------------------------------------------- * + - str to int "0xaffe" "0b010010" "0123" "192939" + * ----------------------------------------------- */ +int str2int(char * s) { + int sign=1; + int base=10; + int value=0; + int digit; + if (!s) return 0; + if (*s) { + if (*s=='-') { + sign=-1; + s++; + if (!*s) return 0; + }; + if (*s=='0') { + s++; + switch(*s) { + case 'x': + case 'X':s++; + base=16; + break; + case 'b': + case 'B':s++; + base=2; + break; + default :base=8; + break; + } + }; + while (*s) { + switch(*s) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9':digit=*s-'0'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F':digit=*s-'A'+10; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f':digit=*s-'a'+10; + break; + default:return value*sign; + }; + if (digit>base) return value*sign; + value=value*base+digit; + s++; + }; + }; + return value*sign; +} +/*----------------------------------------------------------------------------* + * RAM MEMORY COMMANDS + *----------------------------------------------------------------------------*/ + +#define mdump_adr (current_shell_env->mdump_adr) /* static value */ + +int main_mdump(int argc,char * argv[]) { + unsigned char n,m,max=0; + int adr=mdump_adr; + unsigned char * pb; + if (argc>1) adr=str2int(argv[1]); + if (argc>2) max=str2int(argv[2]); + max/=16; + if (!max) max=20; + for (m=0;m<max;m++) { + fprintf(stdout,"0x%08X ",adr); + pb=(unsigned char*) adr; + for (n=0;n<16;n++) + fprintf(stdout,"%02X%c",pb[n],n==7?'-':' '); + for (n=0;n<16;n++) { + fprintf(stdout,"%c",isprint(pb[n])?pb[n]:'.'); + }; + fprintf(stdout,"\n"); + adr+=16; + }; + mdump_adr=adr; + return 0; +} +/*----------------------------------------------------------------------------*/ +int main_mwdump(int argc,char * argv[]) { + unsigned char n,m,max=0; + int adr=mdump_adr; + unsigned short * pw; + if (argc>1) adr=str2int(argv[1]); + if (argc>2) max=str2int(argv[2]); + max/=16; + if (!max) max=20; + for (m=0;m<max;m++) { + fprintf(stdout,"0x%08X ",adr); + pw=(unsigned short*) adr; + for (n=0;n<8;n++) + fprintf(stdout,"%02X %02X%c",pw[n]/0x100,pw[n]%0x100,n==3?'-':' '); + for (n=0;n<8;n++) { + fprintf(stdout,"%c",isprint(pw[n]/0x100)?pw[n]/0x100:'.'); + fprintf(stdout,"%c",isprint(pw[n]%0x100)?pw[n]%0x100:'.'); + }; + fprintf(stdout,"\n"); + adr+=16; + }; + mdump_adr=adr; + return 0; +} +/*----------------------------------------------------------------------------*/ +int main_medit(int argc,char * argv[]) { + unsigned char * pb; + int n,i; + if (argc<3) { + fprintf(stdout,"too few arguments\n"); + return 0; + }; + pb=(unsigned char*)str2int(argv[1]); + i=2; + n=0; + while (i<=argc) { + pb[n++]=str2int(argv[i++])%0x100; + } + mdump_adr=(int)pb; + return main_mdump(0,NULL); +} +/*----------------------------------------------------------------------------*/ +int main_mfill(int argc,char * argv[]) { + int adr; + int size; + unsigned char value; + if (argc<4) { + fprintf(stdout,"too few arguments\n"); + return 0; + }; + adr =str2int(argv[1]); + size =str2int(argv[2]); + value=str2int(argv[3])%0x100; + memset((unsigned char*)adr,size,value); + mdump_adr=adr; + return main_mdump(0,NULL); +} +/*----------------------------------------------------------------------------*/ +int main_mmove(int argc,char * argv[]) { + int src; + int dst; + int size; + if (argc<4) { + fprintf(stdout,"too few arguments\n"); + return 0; + }; + dst =str2int(argv[1]); + src =str2int(argv[2]); + size =str2int(argv[3]); + memcpy((unsigned char*)dst,(unsigned char*)src,size); + mdump_adr=dst; + return main_mdump(0,NULL); +} +/*----------------------------------------------------------------------------*/ +#ifdef MALLOC_STATS /* /rtems/s/src/lib/libc/malloc.c */ +int main_malloc_dump(int argc,char * argv[]) { + void malloc_dump(void); + malloc_dump(); + return 0; +} +#endif +/*---------------------------------------------------------------------------- + * Reset. Assumes that the watchdog is present. + *----------------------------------------------------------------------------*/ +int main_reset (int argc, char **argv) +{ + rtems_interrupt_level level; + fprintf(stdout,"Waiting for watchdog ... "); + tcdrain(fileno(stdout)); + + rtems_interrupt_disable (level); + for (;;) + ; + return 0; +} +/*---------------------------------------------------------------------------- + * Alias. make an alias + *----------------------------------------------------------------------------*/ +int main_alias (int argc, char **argv) +{ + if (argc<3) { + fprintf(stdout,"too few arguments\n"); + return 1; + }; + if (!shell_alias_cmd(argv[1],argv[2])) { + fprintf(stdout,"unable to make an alias(%s,%s)\n",argv[1],argv[2]); + }; + return 0; +} +/*-----------------------------------------------------------* + * Directory commands + *-----------------------------------------------------------*/ +int main_ls(int argc, char *argv[]) +{ + char * fname; + DIR *dirp; + struct dirent *dp; + struct stat stat_buf; + struct passwd * pwd; + struct group * grp; + char * user; + char * group; + char sbuf[256]; + char nbuf[1024]; + int n,size; + + fname="."; + if (argc>1) fname=argv[1]; + + if ((dirp = opendir(fname)) == NULL) + { + fprintf(stdout,"%s: No such file or directory.\n", fname); + return errno; + } + n=0; + size=0; + while ((dp = readdir(dirp)) != NULL) + { + strcpy(nbuf,fname); + if (nbuf[strlen(nbuf)-1]!='/') strcat(nbuf,"/"); + strcat(nbuf,dp->d_name); /* always the fullpathname. Avoid ftpd problem.*/ + if (stat(nbuf, &stat_buf) == 0) + { /* AWFUL buts works...*/ + strftime(sbuf,sizeof(sbuf)-1,"%b %d %H:%M",gmtime(&stat_buf.st_mtime)); + pwd=getpwuid(stat_buf.st_uid); + user=pwd?pwd->pw_name:"nouser"; + grp=getgrgid(stat_buf.st_gid); + group=grp?grp->gr_name:"nogrp"; + fprintf(stdout,"%c%c%c%c%c%c%c%c%c%c %3d %6.6s %6.6s %11d %s %s%c\n", + (S_ISLNK(stat_buf.st_mode)?('l'): + (S_ISDIR(stat_buf.st_mode)?('d'):('-'))), + (stat_buf.st_mode & S_IRUSR)?('r'):('-'), + (stat_buf.st_mode & S_IWUSR)?('w'):('-'), + (stat_buf.st_mode & S_IXUSR)?('x'):('-'), + (stat_buf.st_mode & S_IRGRP)?('r'):('-'), + (stat_buf.st_mode & S_IWGRP)?('w'):('-'), + (stat_buf.st_mode & S_IXGRP)?('x'):('-'), + (stat_buf.st_mode & S_IROTH)?('r'):('-'), + (stat_buf.st_mode & S_IWOTH)?('w'):('-'), + (stat_buf.st_mode & S_IXOTH)?('x'):('-'), + (int)stat_buf.st_nlink, + user,group, + (int)stat_buf.st_size, + sbuf, + dp->d_name, + S_ISDIR(stat_buf.st_mode)?'/':' '); + n++; + size+=stat_buf.st_size; + } + } + fprintf(stdout,"%d files %d bytes occupied\n",n,size); + closedir(dirp); + return 0; +} +/*-----------------------------------------------------------*/ +int main_pwd (int argc, char *argv[]) { + char dir[1024]; + getcwd(dir,1024); + fprintf(stdout,"%s\n",dir); + return 0; +} +/*-----------------------------------------------------------*/ +int main_chdir (int argc, char *argv[]) { + char *dir; + dir="/"; + if (argc>1) dir=argv[1]; + if (chdir(dir)) { + fprintf(stdout,"chdir to '%s' failed:%s\n",dir,strerror(errno)); + return errno; + }; + return 0; +} +/*-----------------------------------------------------------*/ +int main_mkdir (int argc, char *argv[]) { + char *dir; + int n; + n=1; + while (n<argc) { + dir=argv[n++]; + if (mkdir(dir,S_IRWXU|S_IRWXG|S_IRWXO)) { + fprintf(stdout,"mkdir '%s' failed:%s\n",dir,strerror(errno)); + }; + }; + return errno; +} +/*-----------------------------------------------------------*/ +int main_rmdir (int argc, char *argv[]) +{ + char *dir; + int n; + n=1; + while (n<argc) { + dir=argv[n++]; + if (rmdir(dir)) fprintf(stdout,"rmdir '%s' failed:%s\n",dir,strerror(errno)); + }; + return errno; +} +/*-----------------------------------------------------------*/ +int main_chroot(int argc,char * argv[]) { + char * new_root="/"; + if (argc==2) new_root=argv[1]; + if (chroot(new_root)<0) { + fprintf(stdout,"error %s:chroot(%s);\n",strerror(errno),new_root); + return -1; + }; + return 0; +} +/*-----------------------------------------------------------*/ +int main_cat (int argc, char *argv[]) +{ + int n; + n=1; + while (n<argc) cat_file(stdout,argv[n++]); + return 0; +} +/*-----------------------------------------------------------*/ +int main_rm (int argc, char *argv[]) +{ + int n; + n=1; + while (n<argc) { + if (unlink(argv[n])) { + fprintf(stdout,"error %s:rm %s\n",strerror(errno),argv[n]); + return -1; + }; + n++; + }; + return 0; +} +/*-----------------------------------------------------------*/ +/* date - print time and date */ + +int main_date(int argc,char *argv[]) +{ + time_t t; + time(&t); + fprintf(stdout,"%s", ctime(&t)); + return 0; +} +/*-----------------------------------------------------------*/ +int main_logoff(int argc,char *argv[]) +{ + fprintf(stdout,"logoff from the system..."); + current_shell_env->exit_shell=TRUE; + return 0; +} +/*-----------------------------------------------------------*/ +int main_tty (int argc,char *argv[]) +{ + fprintf(stdout,"%s\n",ttyname(fileno(stdin))); + return 0; +} +/*-----------------------------------------------------------*/ +int main_whoami(int argc,char *argv[]) +{ + struct passwd * pwd; + pwd=getpwuid(getuid()); + fprintf(stdout,"%s\n",pwd?pwd->pw_name:"nobody"); + return 0; +} +/*-----------------------------------------------------------*/ +int main_id (int argc,char *argv[]) +{ + struct passwd * pwd; + struct group * grp; + pwd=getpwuid(getuid()); + grp=getgrgid(getgid()); + fprintf(stdout,"uid=%d(%s),gid=%d(%s),", + getuid(),pwd?pwd->pw_name:"", + getgid(),grp?grp->gr_name:""); + pwd=getpwuid(geteuid()); + grp=getgrgid(getegid()); + fprintf(stdout,"euid=%d(%s),egid=%d(%s)\n", + geteuid(),pwd?pwd->pw_name:"", + getegid(),grp?grp->gr_name:""); + return 0; +} +/*-----------------------------------------------------------*/ +int main_umask(int argc,char *argv[]) +{ + mode_t msk=umask(0); + if (argc == 2) msk=str2int(argv[1]); + umask(msk); + msk=umask(0); + fprintf(stdout,"0%o\n", (unsigned int) msk); + umask(msk); + return 0; +} +/*-----------------------------------------------------------*/ +int main_chmod(int argc,char *argv[]) +{ + int n; + mode_t mode; + if (argc > 2){ + mode=str2int(argv[1])&0777; + n=2; + while (n<argc) chmod(argv[n++],mode); + }; + return 0; +} +/*-----------------------------------------------------------* + * with this you can call at all the rtems monitor commands. + * Not all work fine but you can show the rtems status and more. + *-----------------------------------------------------------*/ +int main_monitor(int argc,char * argv[]) { + rtems_monitor_command_entry_t *command; + rtems_task_ident(RTEMS_SELF,0,&rtems_monitor_task_id); + rtems_monitor_node = rtems_get_node(rtems_monitor_task_id); + rtems_monitor_default_node = rtems_monitor_node; + if ((command=rtems_monitor_command_lookup(rtems_monitor_commands,argc,argv))) + command->command_function(argc, argv, &command->command_arg, 0); + return 0; +} +/*-----------------------------------------------------------*/ +void register_cmds(void) { + rtems_monitor_command_entry_t *command; + /* monitor topic */ + command=rtems_monitor_commands; + while (command) { + if (strcmp("exit",command->command)) /*Exclude EXIT (alias quit)*/ + shell_add_cmd(command->command,"monitor", + command->usage ,main_monitor); + command=command->next; + }; + /* dir[ectories] topic */ + shell_add_cmd ("ls" ,"dir","ls [dir] # list files in the directory" ,main_ls ); + shell_add_cmd ("chdir" ,"dir","chdir [dir] # change the current directory",main_chdir); + shell_add_cmd ("rmdir" ,"dir","rmdir dir # remove directory" ,main_rmdir); + shell_add_cmd ("mkdir" ,"dir","mkdir dir # make a directory" ,main_mkdir); + shell_add_cmd ("pwd" ,"dir","pwd # print work directory" ,main_pwd ); + shell_add_cmd ("chroot","dir","chroot [dir] # change the root directory" ,main_chroot); + shell_add_cmd ("cat" ,"dir","cat n1 [n2 [n3...]]# show the ascii contents",main_cat ); + shell_add_cmd ("rm" ,"dir","rm n1 [n2 [n3...]]# remove files" ,main_rm ); + shell_add_cmd ("chmod" ,"dir","chmod 0777 n1 n2... #change filemode" ,main_chmod); + + shell_alias_cmd("ls" ,"dir"); + shell_alias_cmd("chdir" ,"cd"); + + /* misc. topic */ + shell_add_cmd ("logoff","misc","logoff from the system" ,main_logoff); + shell_alias_cmd("logoff","exit"); + shell_add_cmd ("date" ,"misc","date" ,main_date); + shell_add_cmd ("reset","misc","reset the BSP" ,main_reset); + shell_add_cmd ("alias","misc","alias old new" ,main_alias); + shell_add_cmd ("tty" ,"misc","show ttyname" ,main_tty ); + shell_add_cmd ("whoami","misc","show current user" ,main_whoami); + shell_add_cmd ("id" ,"misc","show uid,gid,euid,egid" ,main_id ); + shell_add_cmd ("umask" ,"misc","umask [new_umask]" ,main_umask ); + + + /* memory topic */ + shell_add_cmd ("mdump","mem" ,"mdump [adr [size]]" ,main_mdump); + shell_add_cmd ("wdump","mem" ,"wdump [adr [size]]" ,main_mwdump); + shell_add_cmd ("medit","mem" ,"medit adr value [value ...]" ,main_medit); + shell_add_cmd ("mfill","mem" ,"mfill adr size value" ,main_mfill); + shell_add_cmd ("mmove","mem" ,"mmove dst src size" ,main_mmove); +#ifdef MALLOC_STATS /* /rtems/s/src/lib/libc/malloc.c */ + shell_add_cmd ("malloc","mem","mem show memory malloc'ed" ,main_mem); +#endif +} +/*-----------------------------------------------------------*/ diff --git a/cpukit/libmisc/shell/shell.c b/cpukit/libmisc/shell/shell.c new file mode 100644 index 0000000000..01a9c36ec8 --- /dev/null +++ b/cpukit/libmisc/shell/shell.c @@ -0,0 +1,682 @@ +/* + * + * Instantatiate a new terminal shell. + * + * Author: + * + * WORK: fernando.ruiz@ctv.es + * HOME: correo@fernando-ruiz.com + * + * Thanks at: + * Chris John + * + * $Id$ + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <time.h> + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/libio.h> +#include <rtems/libio_.h> +#include <rtems/system.h> +#include <rtems/shell.h> + +#include <termios.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <pwd.h> + +/* ----------------------------------------------- * + * This is a stupidity but is cute. + * ----------------------------------------------- */ +uint32_t new_rtems_name(char * rtems_name) { + static char b[5]; + sprintf(b,"%-4.4s",rtems_name); + return rtems_build_name(b[0],b[1],b[2],b[3]); +} +/* ************************************************************** + * common linked list of shell commands. + * Because the help report is very long + * I have a topic for each command. + * Help list the topics + * help [topic] list the commands for the topic + * help [command] help for the command + * Can you see help rtems monitor report? + * ************************************************************** */ + +struct shell_topic_tt; +typedef struct shell_topic_tt shell_topic_t; + +struct shell_topic_tt { + char * topic; + shell_topic_t * next; +}; + + +static shell_cmd_t * shell_first_cmd; +static shell_topic_t * shell_first_topic; +/* ----------------------------------------------- * + * Using Chain I can reuse the rtems code. + * I am more comfortable with this, sorry. + * ----------------------------------------------- */ +shell_topic_t * shell_lookup_topic(char * topic) { + shell_topic_t * shell_topic; + shell_topic=shell_first_topic; + while (shell_topic) { + if (!strcmp(shell_topic->topic,topic)) return shell_topic; + shell_topic=shell_topic->next; + }; + return (shell_topic_t *) NULL; +} +/* ----------------------------------------------- */ +shell_topic_t * shell_add_topic(char * topic) { + shell_topic_t * current,*aux; + if (!shell_first_topic) { + aux=malloc(sizeof(shell_topic_t)); + aux->topic=topic; + aux->next=(shell_topic_t*)NULL; + return shell_first_topic=aux; + } else { + current=shell_first_topic; + if (!strcmp(topic,current->topic)) return current; + while (current->next) { + if (!strcmp(topic,current->next->topic)) return current->next; + current=current->next; + }; + aux=malloc(sizeof(shell_topic_t)); + aux->topic=topic; + aux->next=(shell_topic_t*)NULL; + current->next=aux; + return aux; + }; +} +/* ----------------------------------------------- */ +shell_cmd_t * shell_lookup_cmd(char * cmd) { + shell_cmd_t * shell_cmd; + shell_cmd=shell_first_cmd; + while (shell_cmd) { + if (!strcmp(shell_cmd->name,cmd)) return shell_cmd; + shell_cmd=shell_cmd->next; + }; + return (shell_cmd_t *) NULL; +} +/* ----------------------------------------------- */ +shell_cmd_t * shell_add_cmd(char * cmd, + char * topic, + char * usage, + shell_command_t command) { + int shell_help(int argc,char * argv[]); + shell_cmd_t * shell_cmd,*shell_pvt; + if (!shell_first_cmd) { + shell_first_cmd=(shell_cmd_t *) malloc(sizeof(shell_cmd_t)); + shell_first_cmd->name ="help"; + shell_first_cmd->topic ="help"; + shell_first_cmd->usage ="help [topic] # list of usage of commands"; + shell_first_cmd->command=shell_help; + shell_first_cmd->alias =(shell_cmd_t *) NULL; + shell_first_cmd->next =(shell_cmd_t *) NULL; + shell_add_topic(shell_first_cmd->topic); + register_cmds(); + }; + if (!cmd) return (shell_cmd_t *) NULL; + if (!command) return (shell_cmd_t *) NULL; + shell_cmd=(shell_cmd_t *) malloc(sizeof(shell_cmd_t)); + shell_cmd->name =cmd; + shell_cmd->topic =topic; + shell_cmd->usage =usage; + shell_cmd->command=command; + shell_cmd->alias =(shell_cmd_t *) NULL; + shell_cmd->next =(shell_cmd_t *) NULL; + shell_add_topic(shell_cmd->topic); + shell_pvt=shell_first_cmd; + while (shell_pvt->next) shell_pvt=shell_pvt->next; + return shell_pvt->next=shell_cmd; +} +/* ----------------------------------------------- * + * you can make an alias for every command. + * ----------------------------------------------- */ +shell_cmd_t * shell_alias_cmd(char * cmd, char * alias) { + shell_cmd_t * shell_cmd,* shell_aux; + shell_aux=(shell_cmd_t *) NULL; + if (alias) { + if ((shell_aux=shell_lookup_cmd(alias))!=NULL) { + return NULL; + }; + if ((shell_cmd=shell_lookup_cmd(cmd))!=NULL) { + shell_aux=shell_add_cmd(alias,shell_cmd->topic, + shell_cmd->usage,shell_cmd->command); + if (shell_aux) shell_aux->alias=shell_cmd; + }; + }; + return shell_aux; +} +/* ----------------------------------------------- * + * Poor but enough.. + * TODO: Redirection capture. "" evaluate, ... C&S welcome. + * ----------------------------------------------- */ +int shell_make_args(char * cmd, + int * pargc, + char * argv[]) { + int argc=0; + while ((cmd=strtok(cmd," \t\r\n"))!=NULL) { + argv[argc++]=cmd; + cmd=(char*)NULL; + }; + argv[argc]=(char*)NULL; + return *pargc=argc; +} +/* ----------------------------------------------- * + * show the help for one command. + * ----------------------------------------------- */ +int shell_help_cmd(shell_cmd_t * shell_cmd) { + char * pc; + int col,line; + fprintf(stdout,"%-10.10s -",shell_cmd->name); + col=12; + line=1; + if (shell_cmd->alias) { + fprintf(stdout,"is an <alias> for command '%s'",shell_cmd->alias->name); + } else + if (shell_cmd->usage) { + pc=shell_cmd->usage; + while (*pc) { + switch(*pc) { + case '\r':break; + case '\n':putchar('\n'); + col=0; + break; + default :putchar(*pc); + col++; + break; + }; + pc++; + if(col>78) { /* What daring... 78?*/ + if (*pc) { + putchar('\n'); + col=0; + }; + }; + if (!col && *pc) { + fprintf(stdout," "); + col=12;line++; + }; + }; + }; + puts(""); + return line; +} +/* ----------------------------------------------- * + * show the help. The first command implemented. + * Can you see the header of routine? Known? + * The same with all the commands.... + * ----------------------------------------------- */ +int shell_help(int argc,char * argv[]) { + int col,line,arg; + shell_topic_t *topic; + shell_cmd_t * shell_cmd=shell_first_cmd; + if (argc<2) { + fprintf(stdout,"help: ('r' repeat last cmd - 'e' edit last cmd)\n" + " TOPIC? The topics are\n"); + topic=shell_first_topic; + col=0; + while (topic) { + if (!col){ + col=fprintf(stdout," %s",topic->topic); + } else { + if ((col+strlen(topic->topic)+2)>78){ + fprintf(stdout,"\n"); + col=fprintf(stdout," %s",topic->topic); + } else { + col+=fprintf(stdout,", %s",topic->topic); + }; + }; + topic=topic->next; + }; + fprintf(stdout,"\n"); + return 1; + }; + line=0; + for (arg=1;arg<argc;arg++) { + if (line>16) { + fprintf(stdout,"Press any key to continue...");getchar(); + fprintf(stdout,"\n"); + line=0; + }; + topic=shell_lookup_topic(argv[arg]); + if (!topic){ + if ((shell_cmd=shell_lookup_cmd(argv[arg]))==NULL) { + fprintf(stdout,"help: topic or cmd '%s' not found. Try <help> alone for a list\n",argv[arg]); + line++; + } else { + line+=shell_help_cmd(shell_cmd); + } + continue; + }; + fprintf(stdout,"help: list for the topic '%s'\n",argv[arg]); + line++; + while (shell_cmd) { + if (!strcmp(topic->topic,shell_cmd->topic)) + line+=shell_help_cmd(shell_cmd); + if (line>16) { + fprintf(stdout,"Press any key to continue...");getchar(); + fprintf(stdout,"\n"); + line=0; + }; + shell_cmd=shell_cmd->next; + }; + }; + puts(""); + return 0; +} +/* ----------------------------------------------- * + * TODO: Add improvements. History, edit vi or emacs, ... + * ----------------------------------------------- */ +int shell_scanline(char * line,int size,FILE * in,FILE * out) { + int c,col; + col=0; + if (*line) { + col=strlen(line); + if (out) fprintf(out,"%s",line); + }; + tcdrain(fileno(in )); + if (out) tcdrain(fileno(out)); + for (;;) { + line[col]=0; + c=fgetc(in); + switch (c) { + case 0x04:/*Control-d*/ + if (col) break; + case EOF :return 0; + case '\n':break; + case '\f':if (out) fputc('\f',out); + case 0x03:/*Control-C*/ + line[0]=0; + case '\r':if (out) fputc('\n',out); + return 1; + case 127: + case '\b':if (col) { + if (out) { + fputc('\b',out); + fputc(' ',out); + fputc('\b',out); + }; + col--; + } else { + if (out) fputc('\a',out); + }; + break; + default :if (!iscntrl(c)) { + if (col<size-1) { + line[col++]=c; + if (out) fputc(c,out); + } else { + if (out) fputc('\a',out); + }; + } else { + if (out) + if (c=='\a') fputc('\a',out); + }; + break; + }; + }; +} +/* ----------------------------------------------- * + * - The shell TASK + * Poor but enough.. + * TODO: Redirection. Tty Signals. ENVVARs. Shell language. + * ----------------------------------------------- */ +shell_env_t global_shell_env , + * current_shell_env=&global_shell_env; + +extern char **environ; + +void cat_file(FILE * out,char * name) { + FILE * fd; + int c; + if (out) { + fd=fopen(name,"r"); + if (fd) { + while ((c=fgetc(fd))!=EOF) fputc(c,out); + fclose(fd); + }; + }; +} + +void write_file(char * name,char * content) { + FILE * fd; + fd=fopen(name,"w"); + if (fd) { + fwrite(content,1,strlen(content),fd); + fclose(fd); + }; +} + +void init_issue(void) { + static char issue_inited=FALSE; + struct stat buf; + if (issue_inited) return; + issue_inited=TRUE; + getpwnam("root"); /* dummy call to init /etc dir */ + if (stat("/etc/issue",&buf)) + write_file("/etc/issue", + "Welcome to @V\\n" + "Login into @S(@L)\\n"); + if (stat("/etc/issue.net",&buf)) + write_file("/etc/issue.net", + "Welcome to %v\n" + "running on %m\n"); +} + +int shell_login(FILE * in,FILE * out) { + FILE * fd; + int c; + time_t t; + int times; + char name[128]; + char pass[128]; + struct passwd * passwd; + init_issue(); + setuid(0); + setgid(0); + rtems_current_user_env->euid= + rtems_current_user_env->egid=0; + if (out) { + if((current_shell_env->devname[5]!='p')|| + (current_shell_env->devname[6]!='t')|| + (current_shell_env->devname[7]!='y')) { + fd=fopen("/etc/issue","r"); + if (fd) { + while ((c=fgetc(fd))!=EOF) { + if (c=='@') { + switch(c=fgetc(fd)) { + case 'L':fprintf(out,"%s",current_shell_env->devname); + break; + case 'B':fprintf(out,"0"); + break; + case 'T': + case 'D':time(&t); + fprintf(out,"%s",ctime(&t)); + break; + case 'S':fprintf(out,"RTEMS"); + break; + case 'V':fprintf(out,"%s\n%s",_RTEMS_version,_Copyright_Notice); + break; + case '@':fprintf(out,"@"); + break; + default :fprintf(out,"@%c",c); + break; + }; + } else + if (c=='\\') { + switch(c=fgetc(fd)) { + case '\\':fprintf(out,"\\"); + break; + case 'b':fprintf(out,"\b"); break; + case 'f':fprintf(out,"\f"); break; + case 'n':fprintf(out,"\n"); break; + case 'r':fprintf(out,"\r"); break; + case 's':fprintf(out," "); break; + case 't':fprintf(out,"\t"); break; + case '@':fprintf(out,"@"); break; + }; + } else { + fputc(c,out); + }; + }; + fclose(fd); + } + } else { + fd=fopen("/etc/issue.net","r"); + if (fd) { + while ((c=fgetc(fd))!=EOF) { + if (c=='%') { + switch(c=fgetc(fd)) { + case 't':fprintf(out,"%s",current_shell_env->devname); + break; + case 'h':fprintf(out,"0"); + break; + case 'D':fprintf(out," "); + break; + case 'd':time(&t); + fprintf(out,"%s",ctime(&t)); + break; + case 's':fprintf(out,"RTEMS"); + break; + case 'm':fprintf(out,"(" CPU_NAME "/" CPU_MODEL_NAME ")"); + break; + case 'r':fprintf(out,_RTEMS_version); + break; + case 'v':fprintf(out,"%s\n%s",_RTEMS_version,_Copyright_Notice); + break; + case '%':fprintf(out,"%%"); + break; + default :fprintf(out,"%%%c",c); + break; + }; + } else { + fputc(c,out); + }; + }; + fclose(fd); + } + }; + }; + times=0; + strcpy(name,""); + strcpy(pass,""); + for (;;) { + times++; + if (times>3) break; + if (out) fprintf(out,"\nlogin: "); + if (!shell_scanline(name,sizeof(name),in,out )) break; + if (out) fprintf(out,"Password: "); + if (!shell_scanline(pass,sizeof(pass),in,NULL)) break; + if (out) fprintf(out,"\n"); + if ((passwd=getpwnam(name))) { + if (strcmp(passwd->pw_passwd,"!")) { /* valid user */ + setuid(passwd->pw_uid); + setgid(passwd->pw_gid); + rtems_current_user_env->euid= + rtems_current_user_env->egid=0; + chown(current_shell_env->devname,passwd->pw_uid,0); + rtems_current_user_env->euid=passwd->pw_uid; + rtems_current_user_env->egid=passwd->pw_gid; + if (!strcmp(passwd->pw_passwd,"*")) { + /* /etc/shadow */ + return 0; + } else { + /* crypt() */ + return 0; + }; + }; + }; + if (out) fprintf(out,"Login incorrect\n"); + strcpy(name,""); + strcpy(pass,""); + }; + return -1; +} + +rtems_task shell_shell(rtems_task_argument task_argument) { + + shell_env_t * shell_env =(shell_env_t*) task_argument; + shell_cmd_t * shell_cmd; + + rtems_status_code sc; + + struct termios term; + char * devname; + + char curdir[256]; + char cmd[256]; + char last_cmd[256]; /* to repeat 'r' */ + int argc; + char * argv[128]; + + sc=rtems_task_variable_add(RTEMS_SELF,(void*)¤t_shell_env,free); + if (sc!=RTEMS_SUCCESSFUL) { + rtems_error(sc,"rtems_task_variable_add(current_shell_env):"); + rtems_task_delete(RTEMS_SELF); + }; + + current_shell_env=shell_env; + + sc=rtems_libio_set_private_env(); + if (sc!=RTEMS_SUCCESSFUL) { + rtems_error(sc,"rtems_libio_set_private_env():"); + rtems_task_delete(RTEMS_SELF); + }; + + + devname=shell_env->devname; + setuid(0); + setgid(0); + rtems_current_user_env->euid= + rtems_current_user_env->egid=0; + + stdin =fopen(devname,"r+"); + + if (!stdin) { + fprintf(stderr,"shell:unable to open stdin.%s:%s\n",devname,strerror(errno)); + rtems_task_delete(RTEMS_SELF); + }; + setvbuf(stdin,NULL,_IONBF,0); /* Not buffered*/ + /* make a raw terminal,Linux MANuals */ + if (tcgetattr (fileno(stdin), &term)>=0) { + term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + term.c_oflag &= ~OPOST; + term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */ + term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + term.c_cflag = CLOCAL | CREAD |(shell_env->tcflag); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; + if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) { + fprintf(stderr,"shell:cannot set terminal attributes(%s)\n",devname); + }; + stdout=fopen(devname,"r+"); + if (!stdout) { + fprintf(stderr,"shell:unable to open stdout.%s:%s\n",devname,strerror(errno)); + }; + setvbuf(stdout,NULL,_IONBF,0); /* Not buffered*/ + stderr=fopen(devname,"r+"); + if (!stderr) { + fprintf(stdout,"shell:unable to open stderr.%s:%s\n",devname,strerror(errno)); + }; + /* when the future user environment runs ok + * a freopen() reopens the terminals. Now this don't work + * (sorry but you can't use because FILENO_STDIN!=0. Better fileno(stdin)) + */ + }; + shell_add_cmd(NULL,NULL,NULL,NULL); /* init the chain list*/ + do { + /* Set again root user and root filesystem, side effect of set_priv..*/ + sc=rtems_libio_set_private_env(); + if (sc!=RTEMS_SUCCESSFUL) { + rtems_error(sc,"rtems_libio_set_private_env():"); + rtems_task_delete(RTEMS_SELF); + }; + if (!shell_login(stdin,stdout)) { + cat_file(stdout,"/etc/motd"); + strcpy(last_cmd,""); + strcpy(cmd,""); + fprintf(stdout,"\n" + "RTEMS SHELL (Ver.1.0-FRC):%s. "__DATE__". 'help' to list commands.\n",devname); + chdir("/"); /* XXX: chdir to getpwent homedir */ + shell_env->exit_shell=FALSE; + for (;;) { + /* Prompt section */ + /* XXX: show_prompt user adjustable */ + getcwd(curdir,sizeof(curdir)); + fprintf(stdout,"%s [%s] %c ",shell_env->taskname,curdir,geteuid()?'$':'#'); + /* getcmd section */ + if (!shell_scanline(cmd,sizeof(cmd),stdin,stdout)) break; /*EOF*/ + /* evaluate cmd section */ + if (!strcmp(cmd,"e")) { /* edit last command */ + strcpy(cmd,last_cmd); + continue; + } else + if (!strcmp(cmd,"r")) { /* repeat last command */ + strcpy(cmd,last_cmd); + } else + if (strcmp(cmd,"")) { /* only for get a new prompt */ + strcpy(last_cmd,cmd); + }; + /* exec cmd section */ + /* TODO: + * To avoid user crash catch the signals. + * Open a new stdio files with posibility of redirection * + * Run in a new shell task background. (unix &) + * Resuming. A little bash. + */ + if (shell_make_args(cmd,&argc,argv)) { + if ((shell_cmd=shell_lookup_cmd(argv[0]))!=NULL) { + shell_env->errorlevel=shell_cmd->command(argc,argv); + } else { + fprintf(stdout,"shell:%s command not found\n",argv[0]); + shell_env->errorlevel=-1; + }; + }; + /* end exec cmd section */ + if (shell_env->exit_shell) break; + cmd[0]=0; + }; + fprintf(stdout,"\nGoodbye from RTEMS SHELL :-(\n"); + }; + } while (shell_env->forever); + fclose(stdin ); + fclose(stdout); + fclose(stderr); + rtems_task_delete(RTEMS_SELF); +} +/* ----------------------------------------------- */ +rtems_status_code shell_init (char * task_name, + uint32_t task_stacksize, + rtems_task_priority task_priority, + char * devname, + tcflag_t tcflag, + int forever) { + rtems_id task_id; + rtems_status_code sc; + shell_env_t * shell_env; + sc=rtems_task_create(new_rtems_name(task_name), + task_priority, + task_stacksize?task_stacksize:RTEMS_MINIMUM_STACK_SIZE, + RTEMS_DEFAULT_MODES, + RTEMS_LOCAL | RTEMS_FLOATING_POINT, + &task_id); + if (sc!=RTEMS_SUCCESSFUL) { + rtems_error(sc,"creating task %s in shell_init()",task_name); + return sc; + }; + shell_env=malloc(sizeof(shell_env_t)); + if (!shell_env) { + rtems_task_delete(task_id); + sc=RTEMS_NO_MEMORY; + rtems_error(sc,"allocating shell_env %s in shell_init()",task_name); + return sc; + }; + if (global_shell_env.magic!=new_rtems_name("SENV")) { + global_shell_env.magic =new_rtems_name("SENV"); + global_shell_env.devname ="/dev/console"; + global_shell_env.taskname ="GLOBAL"; + global_shell_env.tcflag =0; + global_shell_env.exit_shell=0; + global_shell_env.forever =TRUE; + }; + shell_env->magic =global_shell_env.magic; + shell_env->devname =devname; + shell_env->taskname =task_name; + shell_env->tcflag =tcflag; + shell_env->exit_shell=FALSE; + shell_env->forever =forever; + return rtems_task_start(task_id,shell_shell,(rtems_task_argument) shell_env); +} diff --git a/cpukit/libmisc/shell/shell.h b/cpukit/libmisc/shell/shell.h new file mode 100644 index 0000000000..c95bef5436 --- /dev/null +++ b/cpukit/libmisc/shell/shell.h @@ -0,0 +1,88 @@ +/* + * + * Instantatiate a new terminal shell. + * + * Author: + * + * WORK: fernando.ruiz@ctv.es + * HOME: correo@fernando-ruiz.com + * + * Thanks at: + * Chris John + * + * $Id$ + */ + +#ifndef __SHELL_H__ +#define __SHELL_H__ + +#include <rtems.h> +#include <stdio.h> +#include <termios.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*shell_command_t)(int argc,char * argv[]); + +struct shell_cmd_tt ; +typedef struct shell_cmd_tt shell_cmd_t; + +struct shell_cmd_tt { + char * name; + char * usage; + char * topic; + shell_command_t command; + shell_cmd_t * alias; + shell_cmd_t * next; +}; + +uint32_t new_rtems_name(char * rtems_name); +shell_cmd_t * shell_lookup_cmd(char * cmd); +shell_cmd_t * shell_add_cmd(char * cmd, + char * topic, + char * usage, + shell_command_t command); +shell_cmd_t * shell_alias_cmd(char * cmd, char * alias); + +int shell_make_args(char * cmd, + int * pargc, + char * argv[]); + +typedef struct { + rtems_name magic; /* 'S','E','N','V': Shell Environment */ + char * devname; + char * taskname; + tcflag_t tcflag; + /* user extensions */ + int exit_shell; /* logout */ + int forever ; /* repeat login */ + int errorlevel; + int mdump_adr; +} shell_env_t; + +int shell_scanline(char * line,int size,FILE * in,FILE * out) ; +void cat_file(FILE * out,char *name); +void write_file(char *name,char * content); + +rtems_status_code shell_init(char * task_name , + uint32_t task_stacksize,/*0 default*/ + rtems_task_priority task_priority , + char * devname , + tcflag_t tcflag , + int forever ); + +extern shell_env_t global_shell_env, + * current_shell_env; +/*--------*/ +/* cmds.c */ +/*--------*/ +int str2int(char * s); +void register_cmds(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cpukit/libmisc/stackchk/README b/cpukit/libmisc/stackchk/README new file mode 100644 index 0000000000..0f6e2f2b10 --- /dev/null +++ b/cpukit/libmisc/stackchk/README @@ -0,0 +1,56 @@ +# +# $Id$ +# + +Introduction +============ + +This directory contains a stack bounds checker. It provides two +primary features: + + + check for stack overflow at each context switch + + provides an educated guess at each task's stack usage + +Enabling +======== + +Add the stack checker extension to the initial user extension set. +If using confdefs.h to build your configuration table, this is +as simple as adding -DSTACK_CHECK_ON to the gcc command line which +compiles the file defining the configuration table. In the RTEMS +test suites and samples, this is always init.c + +Background +========== + +The stack overflow check at context switch works by looking for +a 16 byte pattern at the logical end of the stack to be corrupted. +The "guesser" assumes that the entire stack was prefilled with a known +pattern and assumes that the pattern is still in place if the memory +has not been used as a stack. + +Both of these can be fooled by pushing large holes onto the stack +and not writing to them... or (much more unlikely) writing the +magic patterns into memory. + +This code has not been extensively tested. It is provided as a tool +for RTEMS users to catch the most common mistake in multitasking +systems ... too little stack space. Suggestions and comments are appreciated. + +NOTES: + +1. Stack usage information is questionable on CPUs which push + large holes on stack. + +2. The stack checker has a tendency to generate a fault when + trying to print the helpful diagnostic message. If it comes + out, congratulations. If not, then the variable Stack_check_Blown_task + contains a pointer to the TCB of the offending task. This + is usually enough to go on. + +FUTURE: + +1. Determine how/if gcc will generate stack probe calls and support that. + +2. Get accurate stack usage numbers on i960.. it pushes very large + holes on the stack. diff --git a/cpukit/libmisc/stackchk/check.c b/cpukit/libmisc/stackchk/check.c new file mode 100644 index 0000000000..e5ae3c7970 --- /dev/null +++ b/cpukit/libmisc/stackchk/check.c @@ -0,0 +1,553 @@ +/* + * Stack Overflow Check User Extension Set + * + * NOTE: This extension set automatically determines at + * initialization time whether the stack for this + * CPU grows up or down and installs the correct + * extension routines for that direction. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> + +/* + * HACK + * the stack dump information should be printed by a "fatal" extension. + * Fatal extensions only get called via rtems_fatal_error_occurred() + * and not when rtems_shutdown_executive() is called. + * I hope/think this is changing so that fatal extensions are renamed + * to "shutdown" extensions. + * When that happens, this #define should be deleted and all the code + * it marks. + */ +#define DONT_USE_FATAL_EXTENSION + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <rtems/stackchk.h> +#include "internal.h" + +/* + * This variable contains the name of the task which "blew" the stack. + * It is NULL if the system is all right. + */ + +Thread_Control *Stack_check_Blown_task; + +/* + * The extension table for the stack checker. + */ + +rtems_extensions_table Stack_check_Extension_table = { + Stack_check_Create_extension, /* rtems_task_create */ + 0, /* rtems_task_start */ + 0, /* rtems_task_restart */ + 0, /* rtems_task_delete */ + Stack_check_Switch_extension, /* task_switch */ + Stack_check_Begin_extension, /* task_begin */ + 0, /* task_exitted */ +#ifdef DONT_USE_FATAL_EXTENSION + 0, /* fatal */ +#else + Stack_check_Fatal_extension, /* fatal */ +#endif +}; + +/* + * The "magic pattern" used to mark the end of the stack. + */ + +Stack_check_Control Stack_check_Pattern; + +/* + * Where the pattern goes in the stack area is dependent upon + * whether the stack grow to the high or low area of the memory. + * + */ + +#if ( CPU_STACK_GROWS_UP == TRUE ) + +#define Stack_check_Get_pattern_area( _the_stack ) \ + ((Stack_check_Control *) ((char *)(_the_stack)->area + \ + (_the_stack)->size - sizeof( Stack_check_Control ) )) + +#define Stack_check_Calculate_used( _low, _size, _high_water ) \ + ((char *)(_high_water) - (char *)(_low)) + +#define Stack_check_usable_stack_start(_the_stack) \ + ((_the_stack)->area) + +#else + +#define Stack_check_Get_pattern_area( _the_stack ) \ + ((Stack_check_Control *) ((char *)(_the_stack)->area + HEAP_OVERHEAD)) + +#define Stack_check_Calculate_used( _low, _size, _high_water) \ + ( ((char *)(_low) + (_size)) - (char *)(_high_water) ) + +#define Stack_check_usable_stack_start(_the_stack) \ + ((char *)(_the_stack)->area + sizeof(Stack_check_Control)) + +#endif + +#define Stack_check_usable_stack_size(_the_stack) \ + ((_the_stack)->size - sizeof(Stack_check_Control)) + + +/* + * Do we have an interrupt stack? + * XXX it would sure be nice if the interrupt stack were also + * stored in a "stack" structure! + */ + + +Stack_Control stack_check_interrupt_stack; + +/* + * Prototypes necessary for forward references + */ + +void Stack_check_Dump_usage( void ); + +/* + * Fill an entire stack area with BYTE_PATTERN. + * This will be used by a Fatal extension to check for + * amount of actual stack used + */ + +void +stack_check_dope_stack(Stack_Control *stack) +{ + memset(stack->area, BYTE_PATTERN, stack->size); +} + + +/*PAGE + * + * Stack_check_Initialize + */ + +uint32_t stack_check_initialized = 0; + +void Stack_check_Initialize( void ) +{ +#if 0 + rtems_status_code status; + Objects_Id id_ignored; +#endif + uint32_t *p; +#if 0 + uint32_t i; + uint32_t api_index; + Thread_Control *the_thread; + Objects_Information *information; +#endif + + if (stack_check_initialized) + return; + + /* + * Dope the pattern and fill areas + */ + + for ( p = Stack_check_Pattern.pattern; + p < &Stack_check_Pattern.pattern[PATTERN_SIZE_WORDS]; + p += 4 + ) + { + p[0] = 0xFEEDF00D; /* FEED FOOD to BAD DOG */ + p[1] = 0x0BAD0D06; + p[2] = 0xDEADF00D; /* DEAD FOOD GOOD DOG */ + p[3] = 0x600D0D06; + }; + +#if 0 + status = rtems_extension_create( + rtems_build_name( 'S', 'T', 'C', 'K' ), + &Stack_check_Extension_table, + &id_ignored + ); + assert ( status == RTEMS_SUCCESSFUL ); +#endif + + Stack_check_Blown_task = 0; + + /* + * If installed by a task, that task will not get setup properly + * since it missed out on the create hook. This will cause a + * failure on first switch out of that task. + * So pretend here that we actually ran create and begin extensions. + */ + + /* XXX + * + * Technically this has not been done for any task created before this + * happened. So just run through them and fix the situation. + */ +#if 0 + if (_Thread_Executing) + { + Stack_check_Create_extension(_Thread_Executing, _Thread_Executing); + } +#endif + +#if 0 + for ( api_index = 1; + api_index <= OBJECTS_APIS_LAST ; + api_index++ ) { + if ( !_Objects_Information_table[ api_index ] ) + continue; + information = _Objects_Information_table[ api_index ][ 1 ]; + if ( information ) { + for ( i=1 ; i <= information->maximum ; i++ ) { + the_thread = (Thread_Control *)information->local_table[ i ]; + Stack_check_Create_extension( the_thread, the_thread ); + } + } + } +#endif + + /* + * If appropriate, setup the interrupt stack for high water testing + * also. + */ +#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) + if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high) + { + stack_check_interrupt_stack.area = _CPU_Interrupt_stack_low; + stack_check_interrupt_stack.size = (char *) _CPU_Interrupt_stack_high - + (char *) _CPU_Interrupt_stack_low; + + stack_check_dope_stack(&stack_check_interrupt_stack); + } +#endif + +#ifdef DONT_USE_FATAL_EXTENSION +#ifdef RTEMS_DEBUG + /* + * this would normally be called by a fatal extension + * handler, but we don't run fatal extensions unless + * we fatal error. + */ + atexit(Stack_check_Dump_usage); +#endif +#endif + + stack_check_initialized = 1; +} + +/*PAGE + * + * Stack_check_Create_extension + */ + +boolean Stack_check_Create_extension( + Thread_Control *running, + Thread_Control *the_thread +) +{ + if (!stack_check_initialized) + Stack_check_Initialize(); + + if (the_thread /* XXX && (the_thread != _Thread_Executing) */ ) + stack_check_dope_stack(&the_thread->Start.Initial_stack); + + return TRUE; +} + +/*PAGE + * + * Stack_check_Begin_extension + */ + +void Stack_check_Begin_extension( + Thread_Control *the_thread +) +{ + Stack_check_Control *the_pattern; + + if (!stack_check_initialized) + Stack_check_Initialize(); + + if ( the_thread->Object.id == 0 ) /* skip system tasks */ + return; + + the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack); + + *the_pattern = Stack_check_Pattern; +} + +/*PAGE + * + * Stack_check_report_blown_task + * Report a blown stack. Needs to be a separate routine + * so that interrupt handlers can use this too. + * + * Caller must have set the Stack_check_Blown_task. + * + * NOTE: The system is in a questionable state... we may not get + * the following message out. + */ + +void Stack_check_report_blown_task(void) +{ + Stack_Control *stack; + Thread_Control *running; + + running = Stack_check_Blown_task; + stack = &running->Start.Initial_stack; + + fprintf( + stderr, + "BLOWN STACK!!! Offending task(%p): id=0x%08x; name=0x%08x", + running, + running->Object.id, + (uint32_t )running->Object.name + ); + fflush(stderr); + + if (rtems_configuration_get_user_multiprocessing_table()) + fprintf( + stderr, + "; node=%d\n", + rtems_configuration_get_user_multiprocessing_table()->node + ); + else + fprintf(stderr, "\n"); + fflush(stderr); + + fprintf( + stderr, + " stack covers range 0x%08x - 0x%08x (%d bytes)\n", + (uint32_t ) stack->area, + (uint32_t ) stack->area + stack->size - 1, + (uint32_t ) stack->size); + fflush(stderr); + + fprintf( + stderr, + " Damaged pattern begins at 0x%08lx and is %ld bytes long\n", + (unsigned long) Stack_check_Get_pattern_area(stack), + (long) PATTERN_SIZE_BYTES); + fflush(stderr); + + rtems_fatal_error_occurred( (uint32_t ) "STACK BLOWN" ); +} + +/*PAGE + * + * Stack_check_Switch_extension + */ + +void Stack_check_Switch_extension( + Thread_Control *running, + Thread_Control *heir +) +{ + if ( running->Object.id == 0 ) /* skip system tasks */ + return; + + if (0 != memcmp( (void *) Stack_check_Get_pattern_area( &running->Start.Initial_stack)->pattern, + (void *) Stack_check_Pattern.pattern, + PATTERN_SIZE_BYTES)) + { + Stack_check_Blown_task = running; + Stack_check_report_blown_task(); + } +} + +void *Stack_check_find_high_water_mark( + const void *s, + size_t n +) +{ + const uint32_t *base, *ebase; + uint32_t length; + + base = s; + length = n/4; + +#if ( CPU_STACK_GROWS_UP == TRUE ) + /* + * start at higher memory and find first word that does not + * match pattern + */ + + base += length - 1; + for (ebase = s; base > ebase; base--) + if (*base != U32_PATTERN) + return (void *) base; +#else + /* + * start at lower memory and find first word that does not + * match pattern + */ + + base += PATTERN_SIZE_WORDS; + for (ebase = base + length; base < ebase; base++) + if (*base != U32_PATTERN) + return (void *) base; +#endif + + return (void *)0; +} + +/*PAGE + * + * Stack_check_Dump_threads_usage + * Try to print out how much stack was actually used by the task. + * + */ + +void Stack_check_Dump_threads_usage( + Thread_Control *the_thread +) +{ + uint32_t size, used; + void *low; + void *high_water_mark; + Stack_Control *stack; + uint32_t u32_name; + char name_str[5]; + char *name; + Objects_Information *info; + + if ( !the_thread ) + return; + + /* + * XXX HACK to get to interrupt stack + */ + + if (the_thread == (Thread_Control *) -1) + { + if (stack_check_interrupt_stack.area) + { + stack = &stack_check_interrupt_stack; + the_thread = 0; + } + else + return; + } + else + stack = &the_thread->Start.Initial_stack; + + low = Stack_check_usable_stack_start(stack); + size = Stack_check_usable_stack_size(stack); + + high_water_mark = Stack_check_find_high_water_mark(low, size); + + if ( high_water_mark ) + used = Stack_check_Calculate_used( low, size, high_water_mark ); + else + used = 0; + + name = name_str; + if ( the_thread ) { + info = _Objects_Get_information(the_thread->Object.id); + if ( info->is_string ) { + name = (char *) the_thread->Object.name; + } else { + u32_name = (uint32_t )the_thread->Object.name; + name[ 0 ] = (u32_name >> 24) & 0xff; + name[ 1 ] = (u32_name >> 16) & 0xff; + name[ 2 ] = (u32_name >> 8) & 0xff; + name[ 3 ] = (u32_name >> 0) & 0xff; + name[ 4 ] = '\0'; + } + } else { + u32_name = rtems_build_name('I', 'N', 'T', 'R'); + name[ 0 ] = (u32_name >> 24) & 0xff; + name[ 1 ] = (u32_name >> 16) & 0xff; + name[ 2 ] = (u32_name >> 8) & 0xff; + name[ 3 ] = (u32_name >> 0) & 0xff; + name[ 4 ] = '\0'; + } + + fprintf(stdout, "0x%08x %4s 0x%08x 0x%08x %8d %8d\n", + the_thread ? the_thread->Object.id : ~0, + name, + (uint32_t ) stack->area, + (uint32_t ) stack->area + (uint32_t ) stack->size - 1, + size, + used + ); +} + +/*PAGE + * + * Stack_check_Fatal_extension + */ + +void Stack_check_Fatal_extension( + Internal_errors_Source source, + boolean is_internal, + uint32_t status +) +{ +#ifndef DONT_USE_FATAL_EXTENSION + if (status == 0) + Stack_check_Dump_usage(); +#endif +} + + +/*PAGE + * + * Stack_check_Dump_usage + */ + +void Stack_check_Dump_usage( void ) +{ + uint32_t i; + uint32_t api_index; + Thread_Control *the_thread; + uint32_t hit_running = 0; + Objects_Information *information; + + if (stack_check_initialized == 0) + return; + + fprintf(stdout,"Stack usage by thread\n"); + fprintf(stdout, + " ID NAME LOW HIGH AVAILABLE USED\n" + ); + + for ( api_index = 1 ; + api_index <= OBJECTS_APIS_LAST ; + api_index++ ) { + if ( !_Objects_Information_table[ api_index ] ) + continue; + information = _Objects_Information_table[ api_index ][ 1 ]; + if ( information ) { + for ( i=1 ; i <= information->maximum ; i++ ) { + the_thread = (Thread_Control *)information->local_table[ i ]; + Stack_check_Dump_threads_usage( the_thread ); + if ( the_thread == _Thread_Executing ) + hit_running = 1; + } + } + } + + if ( !hit_running ) + Stack_check_Dump_threads_usage( _Thread_Executing ); + + /* dump interrupt stack info if any */ + Stack_check_Dump_threads_usage((Thread_Control *) -1); +} diff --git a/cpukit/libmisc/stackchk/internal.h b/cpukit/libmisc/stackchk/internal.h new file mode 100644 index 0000000000..c437eb94c1 --- /dev/null +++ b/cpukit/libmisc/stackchk/internal.h @@ -0,0 +1,95 @@ +/* internal.h + * + * This include file contains internal information + * for the RTEMS stack checker. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __INTERNAL_STACK_CHECK_h +#define __INTERNAL_STACK_CHECK_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This structure is used to fill in and compare the "end of stack" + * marker pattern. + * pattern area must be a multiple of 4 words. + */ + +#ifdef CPU_STACK_CHECK_SIZE +#define PATTERN_SIZE_WORDS (((CPU_STACK_CHECK_SIZE / 4) + 3) & ~0x3) +#else +#define PATTERN_SIZE_WORDS (4) +#endif + +#define PATTERN_SIZE_BYTES (PATTERN_SIZE_WORDS * sizeof(uint32_t )) + +typedef struct { + uint32_t pattern[ PATTERN_SIZE_WORDS ]; +} Stack_check_Control; + +/* + * The pattern used to fill the entire stack. + */ + +#define BYTE_PATTERN 0xA5 +#define U32_PATTERN 0xA5A5A5A5 + +/* + * Stack_check_Create_extension + */ + +boolean Stack_check_Create_extension( + Thread_Control *running, + Thread_Control *the_thread +); + +/* + * Stack_check_Begin_extension + */ + +void Stack_check_Begin_extension( + Thread_Control *the_thread +); + +/* + * Stack_check_Switch_extension + */ + +void Stack_check_Switch_extension( + Thread_Control *running, + Thread_Control *heir +); + +/* + * Stack_check_Fatal_extension + */ + +void Stack_check_Fatal_extension( + Internal_errors_Source source, + boolean is_internal, + uint32_t status +); + +/* + * Stack_check_Dump_usage + */ + +void Stack_check_Dump_usage( void ); + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/stackchk/stackchk.h b/cpukit/libmisc/stackchk/stackchk.h new file mode 100644 index 0000000000..efe4ce9803 --- /dev/null +++ b/cpukit/libmisc/stackchk/stackchk.h @@ -0,0 +1,82 @@ +/* stackchk.h + * + * This include file contains information necessary to utilize + * and install the stack checker mechanism. + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __STACK_CHECK_h +#define __STACK_CHECK_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Stack_check_Initialize + */ + +void Stack_check_Initialize( void ); + +/* + * Stack_check_Dump_usage + */ + +void Stack_check_Dump_usage( void ); + +/* + * Stack_check_Create_extension + */ + +boolean Stack_check_Create_extension( + Thread_Control *running, + Thread_Control *the_thread +); + +/* + * Stack_check_Begin_extension + */ + +void Stack_check_Begin_extension( + Thread_Control *the_thread +); + +/* + * Stack_check_Switch_extension + */ + +void Stack_check_Switch_extension( + Thread_Control *running, + Thread_Control *heir +); + +/* + * Extension set definition + */ + +#define STACK_CHECKER_EXTENSION \ +{ \ + Stack_check_Create_extension, /* rtems_task_create */ \ + 0, /* rtems_task_start */ \ + 0, /* rtems_task_restart */ \ + 0, /* rtems_task_delete */ \ + Stack_check_Switch_extension, /* task_switch */ \ + Stack_check_Begin_extension, /* task_begin */ \ + 0, /* task_exitted */ \ + 0 /* Stack_check_Fatal_extension */, /* fatal */ \ +} + +#ifdef __cplusplus +} +#endif + +#endif +/* end of include file */ diff --git a/cpukit/libmisc/untar/README b/cpukit/libmisc/untar/README new file mode 100644 index 0000000000..24c9e7fa62 --- /dev/null +++ b/cpukit/libmisc/untar/README @@ -0,0 +1,26 @@ +# +# +# untar information +# +# Author: Jake Janovetz 7.6.1999 +# +# $Id$ +# + +untar.c contains two procedures for extracting files from a UNIX +tar file: + + int Untar_FromMemory(unsigned char *tar_buf, unsigned long size); + int Untar_FromFile(char *tar_name); + +Untar_FromMemory(...) takes its input from a chunk of allocated memory. +This is particularly useful when the tar is stored in Flash memory or +comes from the FTP daemon by way of a hook. + +Untar_FromFile(...) is identical except the source is from an existing +file. The fully qualified filename is passed through char *tar_name. + + + +BUGS: Please email janovetz@uiuc.edu +----- diff --git a/cpukit/libmisc/untar/untar.c b/cpukit/libmisc/untar/untar.c new file mode 100644 index 0000000000..ad3b789f08 --- /dev/null +++ b/cpukit/libmisc/untar/untar.c @@ -0,0 +1,385 @@ +/* FIXME: + * 1. Symbolic links are not created. + * 2. Untar_FromMemory has printfs. + * 3. Untar_FromMemory uses FILE *fp. + * 4. How to determine end of archive? + * + * Written by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "untar.h" + + +/************************************************************************** + * TAR file format: + * + * Offset Length Contents + * 0 100 bytes File name ('\0' terminated, 99 maxmum length) + * 100 8 bytes File mode (in octal ascii) + * 108 8 bytes User ID (in octal ascii) + * 116 8 bytes Group ID (in octal ascii) + * 124 12 bytes File size (s) (in octal ascii) + * 136 12 bytes Modify time (in octal ascii) + * 148 8 bytes Header checksum (in octal ascii) + * 156 1 bytes Link flag + * 157 100 bytes Linkname ('\0' terminated, 99 maxmum length) + * 257 8 bytes Magic ("ustar \0") + * 265 32 bytes User name ('\0' terminated, 31 maxmum length) + * 297 32 bytes Group name ('\0' terminated, 31 maxmum length) + * 329 8 bytes Major device ID (in octal ascii) + * 337 8 bytes Minor device ID (in octal ascii) + * 345 167 bytes Padding + * 512 (s+p)bytes File contents (s+p) := (((s) + 511) & ~511), + * round up to 512 bytes + * + * Checksum: + * int i, sum; + * char* header = tar_header_pointer; + * sum = 0; + * for(i = 0; i < 512; i++) + * sum += 0xFF & header[i]; + *************************************************************************/ + +#define LF_OLDNORMAL '\0' /* Normal disk file, Unix compatible */ +#define LF_NORMAL '0' /* Normal disk file */ +#define LF_LINK '1' /* Link to previously dumped file */ +#define LF_SYMLINK '2' /* Symbolic link */ +#define LF_CHR '3' /* Character special file */ +#define LF_BLK '4' /* Block special file */ +#define LF_DIR '5' /* Directory */ +#define LF_FIFO '6' /* FIFO special file */ +#define LF_CONFIG '7' /* Contiguous file */ + +#define MAX_NAME_FIELD_SIZE 99 + +#define MIN(a,b) ((a)>(b)?(b):(a)) + + +/************************************************************************** + * This converts octal ASCII number representations into an + * unsigned long. Only support 32-bit numbers for now. + *************************************************************************/ +static unsigned long +octal2ulong(char *octascii, int len) +{ + int i; + unsigned long num; + unsigned long mult; + + num = 0; + mult = 1; + for (i=len-1; i>=0; i--) + { + if ((octascii[i] < '0') || (octascii[i] > '9')) + { + continue; + } + num += mult*((unsigned long)(octascii[i] - '0')); + mult *= 8; + } + return(num); +} + + +/************************************************************************** + * Function: Untar_FromMemory * + ************************************************************************** + * Description: * + * * + * This is a simple subroutine used to rip links, directories, and * + * files out of a block of memory. * + * * + * * + * Inputs: * + * * + * unsigned char *tar_buf - Pointer to TAR buffer. * + * unsigned long size - Length of TAR buffer. * + * * + * * + * Output: * + * * + * int - UNTAR_SUCCESSFUL (0) on successful completion. * + * UNTAR_INVALID_CHECKSUM for an invalid header checksum. * + * UNTAR_INVALID_HEADER for an invalid header. * + * * + ************************************************************************** + * Change History: * + * 12/30/1998 - Creation (JWJ) * + *************************************************************************/ +int +Untar_FromMemory(unsigned char *tar_buf, unsigned long size) +{ + FILE *fp; + char *bufr; + size_t n; + char fname[100]; + char linkname[100]; + int sum; + int hdr_chksum; + int retval; + unsigned long ptr; + unsigned long i; + unsigned long nblocks; + unsigned long file_size; + unsigned char linkflag; + + + ptr = 0; + while (1) + { + if (ptr + 512 > size) + { + retval = UNTAR_SUCCESSFUL; + break; + } + + /* Read the header */ + bufr = &tar_buf[ptr]; + ptr += 512; + if (strncmp(&bufr[257], "ustar ", 7)) + { + retval = UNTAR_SUCCESSFUL; + break; + } + + strncpy(fname, bufr, MAX_NAME_FIELD_SIZE); + fname[MAX_NAME_FIELD_SIZE] = '\0'; + + linkflag = bufr[156]; + file_size = octal2ulong(&bufr[124], 12); + + /****************************************************************** + * Compute the TAR checksum and check with the value in + * the archive. The checksum is computed over the entire + * header, but the checksum field is substituted with blanks. + ******************************************************************/ + hdr_chksum = (int)octal2ulong(&bufr[148], 8); + sum = 0; + for (i=0; i<512; i++) + { + if ((i >= 148) && (i < 156)) + { + sum += 0xff & ' '; + } + else + { + sum += 0xff & bufr[i]; + } + } + if (sum != hdr_chksum) + { + retval = UNTAR_INVALID_CHECKSUM; + break; + } + + + /****************************************************************** + * We've decoded the header, now figure out what it contains and + * do something with it. + *****************************************************************/ + if (linkflag == LF_SYMLINK) + { + strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE); + linkname[MAX_NAME_FIELD_SIZE] = '\0'; + /* symlink(fname, linkname); */ + } + else if (linkflag == LF_NORMAL) + { + nblocks = (((file_size) + 511) & ~511) / 512; + if ((fp = fopen(fname, "w")) == NULL) + { + fprintf(stdout,"Untar failed to create file %s\n", fname); + ptr += 512 * nblocks; + } + else + { + unsigned long sizeToGo = file_size; + unsigned long len; + + /*************************************************************** + * Read out the data. There are nblocks of data where nblocks + * is the file_size rounded to the nearest 512-byte boundary. + **************************************************************/ + for (i=0; i<nblocks; i++) + { + len = ((sizeToGo < 512L)?(sizeToGo):(512L)); + n = fwrite(&tar_buf[ptr], 1, len, fp); + if (n != len) + { + fprintf(stdout,"Error during write\n"); + break; + } + ptr += 512; + sizeToGo -= n; + } + fclose(fp); + } + } + else if (linkflag == LF_DIR) + { + mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO); + } + } + + return(retval); +} + + +/************************************************************************** + * Function: Untar_FromFile * + ************************************************************************** + * Description: * + * * + * This is a simple subroutine used to rip links, directories, and * + * files out of a TAR file. * + * * + * * + * Inputs: * + * * + * char *tar_name - TAR filename. * + * * + * * + * Output: * + * * + * int - UNTAR_SUCCESSFUL (0) on successful completion. * + * UNTAR_INVALID_CHECKSUM for an invalid header checksum. * + * UNTAR_INVALID_HEADER for an invalid header. * + * * + ************************************************************************** + * Change History: * + * 12/30/1998 - Creation (JWJ) * + *************************************************************************/ +int +Untar_FromFile(char *tar_name) +{ + int fd; + char *bufr; + size_t n; + char fname[100]; + char linkname[100]; + int sum; + int hdr_chksum; + int retval; + unsigned long i; + unsigned long nblocks; + unsigned long size; + unsigned char linkflag; + + + retval = UNTAR_SUCCESSFUL; + bufr = (char *)malloc(512); + if (bufr == NULL) + { + return(UNTAR_FAIL); + } + + fd = open(tar_name, O_RDONLY); + while (1) + { + /* Read the header */ + /* If the header read fails, we just consider it the end + of the tarfile. */ + if ((n = read(fd, bufr, 512)) != 512) + { + break; + } + + if (strncmp(&bufr[257], "ustar ", 7)) + { + break; + } + + strncpy(fname, bufr, MAX_NAME_FIELD_SIZE); + fname[MAX_NAME_FIELD_SIZE] = '\0'; + + linkflag = bufr[156]; + size = octal2ulong(&bufr[124], 12); + + /****************************************************************** + * Compute the TAR checksum and check with the value in + * the archive. The checksum is computed over the entire + * header, but the checksum field is substituted with blanks. + ******************************************************************/ + hdr_chksum = (int)octal2ulong(&bufr[148], 8); + sum = 0; + for (i=0; i<512; i++) + { + if ((i >= 148) && (i < 156)) + { + sum += 0xff & ' '; + } + else + { + sum += 0xff & bufr[i]; + } + } + if (sum != hdr_chksum) + { + retval = UNTAR_INVALID_CHECKSUM; + break; + } + + + /****************************************************************** + * We've decoded the header, now figure out what it contains and + * do something with it. + *****************************************************************/ + if (linkflag == LF_SYMLINK) + { + strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE); + linkname[MAX_NAME_FIELD_SIZE] = '\0'; + } + else if (linkflag == LF_NORMAL) + { + int out_fd; + + /****************************************************************** + * Read out the data. There are nblocks of data where nblocks + * is the size rounded to the nearest 512-byte boundary. + *****************************************************************/ + nblocks = (((size) + 511) & ~511) / 512; + + if ((out_fd = creat(fname, 0644)) == -1) + { + for (i=0; i<nblocks; i++) + { + n = read(fd, bufr, 512); + } + } + else + { + for (i=0; i<nblocks; i++) + { + n = read(fd, bufr, 512); + n = MIN(n, size - i*512); + write(out_fd, bufr, n); + } + close(out_fd); + } + } + else if (linkflag == LF_DIR) + { + mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO); + } + } + free(bufr); + close(fd); + + return(retval); +} diff --git a/cpukit/libmisc/untar/untar.h b/cpukit/libmisc/untar/untar.h new file mode 100644 index 0000000000..69c4d41ddf --- /dev/null +++ b/cpukit/libmisc/untar/untar.h @@ -0,0 +1,25 @@ +/* + * Written by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef __UNTAR_H__ +#define __UNTAR_H__ + + +#define UNTAR_SUCCESSFUL 0 +#define UNTAR_FAIL 1 +#define UNTAR_INVALID_CHECKSUM 2 +#define UNTAR_INVALID_HEADER 3 + + +int Untar_FromMemory(unsigned char *tar_buf, unsigned long size); +int Untar_FromFile(char *tar_name); + + +#endif /* __UNTAR_H__ */ |