From 943abb19d2e3ed12727e5e56ff87f41da3f88678 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 24 Nov 2018 13:35:53 -0600 Subject: [PATCH] apps/system/critmon: Add a daemon on monitor critical sections. --- system/critmon/.gitignore | 11 + system/critmon/Kconfig | 42 +++ system/critmon/Make.defs | 39 +++ system/critmon/Makefile | 52 ++++ system/critmon/critmon.c | 534 +++++++++++++++++++++++++++++++++++ system/stackmonitor/Makefile | 2 +- 6 files changed, 679 insertions(+), 1 deletion(-) create mode 100644 system/critmon/.gitignore create mode 100644 system/critmon/Kconfig create mode 100644 system/critmon/Make.defs create mode 100644 system/critmon/Makefile create mode 100644 system/critmon/critmon.c diff --git a/system/critmon/.gitignore b/system/critmon/.gitignore new file mode 100644 index 000000000..83bd7b811 --- /dev/null +++ b/system/critmon/.gitignore @@ -0,0 +1,11 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src +/*.obj diff --git a/system/critmon/Kconfig b/system/critmon/Kconfig new file mode 100644 index 000000000..3961537d2 --- /dev/null +++ b/system/critmon/Kconfig @@ -0,0 +1,42 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig SYSTEM_CRITMONITOR + tristate "Critcal Section Monitor" + default n + depends on FS_PROCFS && !_FS_PROCFS_EXCLUDE_PROCESS && SCHED_CRITMONITOR + ---help--- + If the critical section monitor is enabled (CONFIGSCHED_CRITMONITOR) + this option will enable a critical section monitor daemon. This daemon + that will periodically assess usage of critical sections by all tasks + and threads in the system. + +if SYSTEM_CRITMONITOR + +config SYSTEM_CRITMONITOR_STACKSIZE + int "Stack monitor daemon stack size" + default 2048 + ---help--- + The stack size to use the stack monitor daemon. Default: 2048 + +config SYSTEM_CRITMONITOR_PRIORITY + int "Stack monitor daemon priority" + default 50 + ---help--- + The priority to use the stack monitor daemon. Default: 50 + +config SYSTEM_CRITMONITOR_INTERVAL + int "Stack monitor dump frequency" + default 2 + ---help--- + The rate in seconds that the stack monitor will wait before dumping + the next set stack usage information. Default: 2 seconds. + +config SYSTEM_CRITMONITOR_MOUNTPOINT + string "procfs mountpoint" + default "/proc" + +endif + diff --git a/system/critmon/Make.defs b/system/critmon/Make.defs new file mode 100644 index 000000000..83f6c98fa --- /dev/null +++ b/system/critmon/Make.defs @@ -0,0 +1,39 @@ +############################################################################ +# apps/system/critmon/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2018 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_CRITMONITOR),) +CONFIGURED_APPS += system/critmon +endif diff --git a/system/critmon/Makefile b/system/critmon/Makefile new file mode 100644 index 000000000..4eba214e6 --- /dev/null +++ b/system/critmon/Makefile @@ -0,0 +1,52 @@ +############################################################################ +# apps/system/critmon/Makefile +# +# Copyright (C) 2018 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/Make.defs + +# Stack Monitor Application + +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 2048 + +MAINSRC = critmon.c + +CONFIG_XYZ_PROGNAME ?= critmon$(EXEEXT) +PROGNAME = $(CONFIG_XYZ_PROGNAME) + +APPNAME = critmon_start critmon_stop + +MODULE = CONFIG_SYSTEM_CRITMONITOR + +include $(APPDIR)/Application.mk diff --git a/system/critmon/critmon.c b/system/critmon/critmon.c new file mode 100644 index 000000000..1da8b6b46 --- /dev/null +++ b/system/critmon/critmon.c @@ -0,0 +1,534 @@ +/**************************************************************************** + * apps/system/critmon/critmon.c + * + * Copyright (C) 2013, 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SYSTEM_CRITMONITOR + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_SYSTEM_CRITMONITOR_STACKSIZE +# define CONFIG_SYSTEM_CRITMONITOR_STACKSIZE 2048 +#endif + +#ifndef CONFIG_SYSTEM_CRITMONITOR_PRIORITY +# define CONFIG_SYSTEM_CRITMONITOR_PRIORITY 50 +#endif + +#ifndef CONFIG_SYSTEM_CRITMONITOR_INTERVAL +# define CONFIG_SYSTEM_CRITMONITOR_INTERVAL 2 +#endif + +#ifndef CONFIG_SYSTEM_CRITMONITOR_MOUNTPOINT +# define CONFIG_SYSTEM_CRITMONITOR_MOUNTPOINT "/proc" +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct critmon_state_s +{ + volatile bool started; + volatile bool stop; + pid_t pid; + char line[80]; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct critmon_state_s g_critmon; + +#if CONFIG_TASK_NAME_SIZE > 0 +static const char g_name[] = "Name:"; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: critmon_isolate_value + ****************************************************************************/ + +static FAR char *critmon_isolate_value(FAR char *line) +{ + FAR char *ptr; + + while (isblank(*line) && *line != '\0') + { + line++; + } + + ptr = line; + while (*ptr != '\n' && *ptr != '\r' && *ptr != '\0') + { + ptr++; + } + + *ptr = '\0'; + return line; +} + +/**************************************************************************** + * Name: critmon_process_directory + ****************************************************************************/ + +static int critmon_process_directory(FAR struct dirent *entryp) + +{ + FAR const char *tmpstr; + FAR char *filepath; + FAR char *maxpreemp; + FAR char *maxcrit; + FILE *stream; + int errcode; + int len; + int ret; + +#if CONFIG_TASK_NAME_SIZE > 0 + FAR char *name = NULL; + + /* Read the task status to get the task name */ + + filepath = NULL; + ret = asprintf(&filepath, CONFIG_SYSTEM_CRITMONITOR_MOUNTPOINT "/%s/status", + entryp->d_name); + if (ret < 0 || filepath == NULL) + { + errcode = errno; + fprintf(stderr, "Csection Monitor: Failed to create path to status file: %d\n", + errcode); + return -errcode; + } + + /* Open the status file */ + + stream = fopen(filepath, "r"); + if (stream == NULL) + { + ret = -errno; + fprintf(stderr, "Csection Monitor: Failed to open %s: %d\n", + filepath, ret); + goto errout_with_filepath; + } + + while (fgets(g_critmon.line, 80, stream) != NULL) + { + g_critmon.line[79] = '\n'; + len = strlen(g_name); + if (strncmp(g_critmon.line, g_name, len) == 0) + { + tmpstr = critmon_isolate_value(&g_critmon.line[len]); + if (*tmpstr == '\0') + { + ret = -EINVAL; + goto errout_with_stream; + } + + name = strdup(tmpstr); + if (name == NULL) + { + ret = -EINVAL; + goto errout_with_stream; + } + } + } + + free(filepath); + fclose(stream); +#endif + + /* Read critical section information */ + + filepath = NULL; + + ret = asprintf(&filepath, CONFIG_SYSTEM_CRITMONITOR_MOUNTPOINT "/%s/critmon", + entryp->d_name); + if (ret < 0 || filepath == NULL) + { + errcode = errno; + fprintf(stderr, "Csection Monitor: Failed to create path to Csection file: %d\n", + errcode); + ret = -EINVAL; + goto errout_with_name; + } + + /* Open the Csection file */ + + stream = fopen(filepath, "r"); + if (stream == NULL) + { + ret = -errno; + fprintf(stderr, "Csection Monitor: Failed to open %s: %d\n", + filepath, ret); + goto errout_with_filepath; + } + + /* Read the line containing the Csection max durations */ + + if (fgets(g_critmon.line, 80, stream) == NULL) + { + ret = -errno; + fprintf(stderr, "Csection Monitor: Failed to read from %s: %d\n", + filepath, ret); + goto errout_with_filepath; + } + + /* Format: X.XXXXXXXXXXXX,X.XXXXXXXXX */ + + maxpreemp = g_critmon.line; + maxcrit = strchr(g_critmon.line, ','); + + if (maxcrit != NULL) + { + *maxcrit++ = '\0'; + } + else + { + maxcrit = "None"; + } + + /* Finally, output the stack info that we gleaned from the procfs */ + +#if CONFIG_TASK_NAME_SIZE > 0 + printf("Thread %s (ID %s):\n", name, entryp->d_name); +#else + printf("Thread ID %s:\n", entryp->d_name); +#endif + printf(" Max Pre-emption Disable: %s%12s\n", maxpreemp); + printf(" Max Critical Section: %s%12s\n", maxcrit); + + ret = OK; + +errout_with_stream: + fclose(stream); + +errout_with_filepath: + free(filepath); + +errout_with_name: +#if CONFIG_TASK_NAME_SIZE > 0 + if (name != NULL) + { + free(name); + } +#endif + + return ret; +} + +/**************************************************************************** + * Name: critmon_check_name + ****************************************************************************/ + +static bool critmon_check_name(FAR char *name) +{ + int i; + + /* Check each character in the name */ + + for (i = 0; i < NAME_MAX && name[i] != '\0'; i++) + { + if (!isdigit(name[i])) + { + /* Name contains something other than a decimal numeric character */ + + return false; + } + } + + return true; +} + +/**************************************************************************** + * Name: critmon_global_crit + ****************************************************************************/ + +static void critmon_global_crit(void) +{ + FAR char *filepath; + FAR char *cpu; + FAR char *maxpreemp; + FAR char *maxcrit; + FILE *stream; + int errcode; + int ret; + + /* Read critical section information */ + + filepath = NULL; + + ret = asprintf(&filepath, CONFIG_SYSTEM_CRITMONITOR_MOUNTPOINT "critmon"); + if (ret < 0 || filepath == NULL) + { + errcode = errno; + fprintf(stderr, "Csection Monitor: Failed to create path to Csection file: %d\n", + errcode); + return; + } + + /* Open the Csection file */ + + stream = fopen(filepath, "r"); + if (stream == NULL) + { + errcode = errno; + fprintf(stderr, "Csection Monitor: Failed to open %s: %d\n", + filepath, errcode); + goto errout_with_filepath; + } + + /* Read the line containing the Csection max durations for each CPU */ + + while (fgets(g_critmon.line, 80, stream) != NULL) + { + /* Format: X,X.XXXXXXXXXXXX,X.XXXXXXXXX */ + + cpu = g_critmon.line; + maxpreemp = strchr(g_critmon.line, ','); + + if (maxpreemp != NULL) + { + *maxpreemp++ = '\0'; + maxcrit = strchr(g_critmon.line, ','); + if (maxcrit != NULL) + { + *maxcrit++ = '\0'; + } + else + { + maxcrit = "None"; + } + } + else + { + maxpreemp = "None"; + maxcrit = "None"; + } + + /* Finally, output the stack info that we gleaned from the procfs */ + + printf("CPU %s:\n", cpu); + printf(" Max Pre-emption Disable: %s%12s\n", maxpreemp); + printf(" Max Critical Section: %s%12s\n", maxcrit); + } + + fclose(stream); + +errout_with_filepath: + free(filepath); +} + +/**************************************************************************** + * Name: critmon_daemon + ****************************************************************************/ + +static int critmon_daemon(int argc, char **argv) +{ + DIR *dirp; + int exitcode = EXIT_SUCCESS; + int errcount = 0; + int ret; + + printf("Csection Monitor: Running: %d\n", g_critmon.pid); + + /* Loop until we detect that there is a request to stop. */ + + while (!g_critmon.stop) + { + /* Wait for the next sample interval */ + + sleep(CONFIG_SYSTEM_CRITMONITOR_INTERVAL); + + /* Should global usage first */ + + critmon_global_crit(); + + /* Open the top-level procfs directory */ + + dirp = opendir(CONFIG_SYSTEM_CRITMONITOR_MOUNTPOINT); + if (dirp == NULL) + { + /* Failed to open the directory */ + + fprintf(stderr, "Csection Monitor: Failed to open directory: %s\n", + CONFIG_SYSTEM_CRITMONITOR_MOUNTPOINT); + + if (++errcount > 100) + { + fprintf(stderr, "Csection Monitor: Too many errors ... exiting\n"); + exitcode = EXIT_FAILURE; + break; + } + } + + /* Read each directory entry */ + + for (; ; ) + { + FAR struct dirent *entryp = readdir(dirp); + if (entryp == NULL) + { + /* Finished with this directory */ + + break; + } + + /* Task/thread entries in the /proc directory will all be (1) + * directories with (2) all numeric names. + */ + + if (DIRENT_ISDIRECTORY(entryp->d_type) && + critmon_check_name(entryp->d_name)) + { + /* Looks good -- process the directory */ + + ret = critmon_process_directory(entryp); + if (ret < 0) + { + /* Failed to process the thread directory */ + + fprintf(stderr, + "Csection Monitor: Failed to process sub-directory: %s\n", + entryp->d_name); + + if (++errcount > 100) + { + fprintf(stderr, "Csection Monitor: Too many errors ... exiting\n"); + exitcode = EXIT_FAILURE; + break; + } + } + } + } + + closedir(dirp); + } + + /* Stopped */ + + g_critmon.stop = false; + g_critmon.started = false; + printf("Csection Monitor: Stopped: %d\n", g_critmon.pid); + + return exitcode; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int critmon_start_main(int argc, char **argv) +{ + /* Has the monitor already started? */ + + sched_lock(); + if (!g_critmon.started) + { + int ret; + + /* No.. start it now */ + + /* Then start the stack monitoring daemon */ + + g_critmon.started = true; + g_critmon.stop = false; + + ret = task_create("Csection Monitor", CONFIG_SYSTEM_CRITMONITOR_PRIORITY, + CONFIG_SYSTEM_CRITMONITOR_STACKSIZE, + (main_t)critmon_daemon, (FAR char * const *)NULL); + if (ret < 0) + { + int errcode = errno; + printf("Csection Monitor ERROR: Failed to start the stack monitor: %d\n", + errcode); + } + else + { + g_critmon.pid = ret; + printf("Csection Monitor: Started: %d\n", g_critmon.pid); + } + + sched_unlock(); + return 0; + } + + sched_unlock(); + printf("Csection Monitor: %s: %d\n", + g_critmon.stop ? "Stopping" : "Running", g_critmon.pid); + return 0; +} + +int critmon_stop_main(int argc, char **argv) +{ + /* Has the monitor already started? */ + + if (g_critmon.started) + { + /* Stop the stack monitor. The next time the monitor wakes up, + * it will see the stop indication and will exist. + */ + + printf("Csection Monitor: Stopping: %d\n", g_critmon.pid); + g_critmon.stop = true; + } + + printf("Csection Monitor: Stopped: %d\n", g_critmon.pid); + return 0; +} + +#endif /* CONFIG_SYSTEM_CRITMONITOR */ diff --git a/system/stackmonitor/Makefile b/system/stackmonitor/Makefile index 215b5368f..24a49963e 100644 --- a/system/stackmonitor/Makefile +++ b/system/stackmonitor/Makefile @@ -42,7 +42,7 @@ STACKSIZE = 2048 MAINSRC = stackmonitor.c -CONFIG_XYZ_PROGNAME ?= ramtest$(EXEEXT) +CONFIG_XYZ_PROGNAME ?= stackmon$(EXEEXT) PROGNAME = $(CONFIG_XYZ_PROGNAME) APPNAME = stackmonitor_start stackmonitor_stop