diff --git a/ChangeLog b/ChangeLog index 3f3c141f51c..423290c841e 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11565,4 +11565,6 @@ * sched/sched_note.c and include/nuttx/sched_note.c: Add a configuration option to buffer RTOS instrumentation data in an in-memory buffer (2016-03-17). + * drivers/syslog/note_driver.c: Add a character driver that will allow + an appliation to read buffered scheduler instrumentation data (2016-03-17). diff --git a/arch b/arch index 7ba04f55c09..feb41dfa8e1 160000 --- a/arch +++ b/arch @@ -1 +1 @@ -Subproject commit 7ba04f55c09c7db11b4744c01db8c68a1fffdba4 +Subproject commit feb41dfa8e178a1194fd17afae154bc55912993d diff --git a/configs b/configs index e147b03efbd..f7c42ff4a00 160000 --- a/configs +++ b/configs @@ -1 +1 @@ -Subproject commit e147b03efbd424a972e1dd61707c6182b90174f7 +Subproject commit f7c42ff4a006f5d37fa6a818aafaf20886309fd1 diff --git a/drivers/syslog/Kconfig b/drivers/syslog/Kconfig index bdbc4c34f85..d273e3d1a3a 100644 --- a/drivers/syslog/Kconfig +++ b/drivers/syslog/Kconfig @@ -82,3 +82,11 @@ config SYSLOG_CONSOLE output (syslog_putc). This is useful, for example, if the only console is a Telnet console. Then in that case, console output from non-Telnet threads will go to the syslog output. + +config DRIVER_NOTE + bool "Scheduler instrumentation driver" + default n + depends on SCHED_INSTRUMENTATION_BUFFER + ---help--- + Enable building a serial driver that can be used by an application to read data + from the in-memory, scheduler instrumentatin "note" buffer. diff --git a/drivers/syslog/Make.defs b/drivers/syslog/Make.defs index c9aa7a02404..f573217d6de 100644 --- a/drivers/syslog/Make.defs +++ b/drivers/syslog/Make.defs @@ -34,6 +34,7 @@ # ############################################################################ +############################################################################ # Include SYSLOG drivers (only one should be enabled) ifeq ($(CONFIG_SYSLOG),y) @@ -47,6 +48,10 @@ ifeq ($(CONFIG_RAMLOG),y) CSRCS += ramlog.c endif +ifeq ($(CONFIG_DRIVER_NOTE),y) + CSRCS += note_driver.c +endif + # (Add other SYSLOG_CONSOLE drivers here) ifeq ($(CONFIG_SYSLOG_CONSOLE),y) @@ -58,17 +63,32 @@ endif DEPPATH += --dep-path syslog VPATH += :syslog -else - +############################################################################ # The RAMLOG can be used even if system logging is not enabled. -ifeq ($(CONFIG_RAMLOG),y) +else ifeq ($(CONFIG_RAMLOG),y) + +CSRCS += ramlog.c + +ifeq ($(CONFIG_DRIVER_NOTE),y) + CSRCS += note_driver.c +endif # Include RAMLOG build support -CSRCS += ramlog.c +DEPPATH += --dep-path syslog +VPATH += :syslog + +############################################################################ +# The scheduler note driver can be used in any event. + +else ifeq ($(CONFIG_DRIVER_NOTE),y) + +CSRCS += note_driver.c + +# Include note driver build support + DEPPATH += --dep-path syslog VPATH += :syslog endif -endif diff --git a/drivers/syslog/note_driver.c b/drivers/syslog/note_driver.c new file mode 100644 index 00000000000..5b923079171 --- /dev/null +++ b/drivers/syslog/note_driver.c @@ -0,0 +1,169 @@ +/**************************************************************************** + * drivers/syslog/note_driver.c + * + * Copyright (C) 2016 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 + +#if defined(CONFIG_SCHED_INSTRUMENTATION_BUFFER) && \ + defined(CONFIG_DRIVER_NOTE) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static ssize_t note_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations note_fops = +{ + 0, /* open */ + 0, /* close */ + note_read, /* read */ + 0, /* write */ + 0, /* seek */ + 0 /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , 0 /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: note_read + ****************************************************************************/ + +static ssize_t note_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + ssize_t notelen; + ssize_t retlen ; + + DEBUGASSERT(filep != 0 && buffer != NULL && buflen > 0); + + /* Then loop, adding as many notes as possible to the user buffer. */ + + retlen = 0; + sched_lock(); + do + { + /* Get the next note (removing it from the buffer) */ + + notelen = sched_note_get((FAR uint8_t *)buffer, buflen); + if (notelen < 0) + { + /* We were unable to read the next note, probably because it will + * not fit into the user buffer. + */ + + if (retlen == 0) + { + /* If nothing was read then report the error. Otherwise, + * just silently drop the note. + */ + + retlen = notelen; + } + + break; + } + + /* Update pointers from the note that was transferred */ + + retlen += notelen; + buffer += notelen; + buflen -= notelen; + + /* Will the next note fit? There is a race here and even if the next + * note will fit, it may fail still when sched_note_get() is called. + * + * It won't fit (or an error occurred). Return what we have without + * trying to get the next note (which would cause it to be deleted). + */ + + notelen = sched_note_size(); + } + while (notelen > 0 && notelen <= buflen); + + sched_unlock(); + return retlen; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: note_register + * + * Description: + * Register a serial driver at /dev/note that can be used by an + * application to read data from the circular not buffer. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero is returned if the circular buffer is empty. Otherwise, a negated + * errno value is returned. + * + ****************************************************************************/ + +int note_register(void) +{ + return register_driver("/dev/note", ¬e_fops, 0666, NULL); +} + +#endif /* CONFIG_SCHED_INSTRUMENTATION_BUFFER && CONFIG_DRIVER_NOTE */ \ No newline at end of file diff --git a/include/nuttx/sched_note.h b/include/nuttx/sched_note.h index dd7ca875a05..39096228b58 100644 --- a/include/nuttx/sched_note.h +++ b/include/nuttx/sched_note.h @@ -195,11 +195,9 @@ void sched_note_csection(FAR struct tcb_s *tcb, bool enter); * buflen - The length of the user provided buffer. * * Returned Value: - * None - * - * Assumptions: - * On success, the length of the return note is provided. A negated - * errno value is returned on failure. + * On success, the positive, non-zero length of the return note is + * provided. Zero is returned only if ther circular buffer is empty. A + * negated errno value is returned in the event of any failure. * ****************************************************************************/ @@ -207,6 +205,46 @@ void sched_note_csection(FAR struct tcb_s *tcb, bool enter); ssize_t sched_note_get(FAR uint8_t *buffer, size_t buflen); #endif +/**************************************************************************** + * Name: sched_note_size + * + * Description: + * Return the size of the next note at the tail of the circular buffer. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero is returned if the circular buffer is empty. Otherwise, the size + * of the next note is returned. + * + ****************************************************************************/ + +#ifdef CONFIG_SCHED_INSTRUMENTATION_BUFFER +ssize_t sched_note_size(void); +#endif + +/**************************************************************************** + * Name: note_register + * + * Description: + * Register a serial driver at /dev/note that can be used by an + * application to read data from the circular not buffer. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero is returned if the circular buffer is empty. Otherwise, a negated + * errno value is returned. + * + ****************************************************************************/ + +#if defined(CONFIG_SCHED_INSTRUMENTATION_BUFFER) && \ + defined(CONFIG_DRIVER_NOTE) +int note_register(void); +#endif + #else /* CONFIG_SCHED_INSTRUMENTATION */ # define sched_note_start(t) diff --git a/sched/sched/sched_note.c b/sched/sched/sched_note.c index 3838bef8cda..83f14a63766 100644 --- a/sched/sched/sched_note.c +++ b/sched/sched/sched_note.c @@ -158,7 +158,6 @@ static void note_systime(FAR struct note_common_s *note) * ****************************************************************************/ -#ifdef CONFIG_DEBUG static unsigned int note_length(void) { unsigned int head = g_note_info.ni_head; @@ -171,7 +170,6 @@ static unsigned int note_length(void) return head - tail; } -#endif /**************************************************************************** * Name: note_remove @@ -422,11 +420,9 @@ void sched_note_csection(FAR struct tcb_s *tcb, bool enter) * buflen - The length of the user provided buffer. * * Returned Value: - * None - * - * Assumptions: - * On success, the length of the return note is provided. A negated - * errno value is returned on failure. + * On success, the positive, non-zero length of the return note is + * provided. Zero is returned only if ther circular buffer is empty. A + * negated errno value is returned in the event of any failure. * ****************************************************************************/ @@ -437,10 +433,20 @@ ssize_t sched_note_get(FAR uint8_t *buffer, size_t buflen) unsigned int remaining; unsigned int tail; ssize_t notelen; + size_t circlen; DEBUGASSERT(buffer != NULL); flags = enter_critical_section(); + /* Verify that the circular buffer is not empty */ + + circlen = note_length(); + if (circlen <= 0) + { + notelen = 0; + goto errout_with_csection; + } + /* Get the index to the tail of the circular buffer */ tail = g_note_info.ni_tail; @@ -448,9 +454,9 @@ ssize_t sched_note_get(FAR uint8_t *buffer, size_t buflen) /* Get the length of the note at the tail index */ - note = (FAR struct note_common_s *)&g_note_info.ni_buffer[tail]; + note = (FAR struct note_common_s *)&g_note_info.ni_buffer[tail]; notelen = note->nc_length; - DEBUGASSERT(notelen <= note_length()); + DEBUGASSERT(notelen <= circlen); /* Is the user buffer large enough to hold the note? */ @@ -488,4 +494,55 @@ errout_with_csection: return notelen; } +/**************************************************************************** + * Name: sched_note_size + * + * Description: + * Return the size of the next note at the tail of the circular buffer. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero is returned if the circular buffer is empty. Otherwise, the size + * of the next note is returned. + * + ****************************************************************************/ + +ssize_t sched_note_size(void) +{ + FAR struct note_common_s *note; + irqstate_t flags; + unsigned int tail; + ssize_t notelen; + size_t circlen; + + DEBUGASSERT(buffer != NULL); + flags = enter_critical_section(); + + /* Verify that the circular buffer is not empty */ + + circlen = note_length(); + if (circlen <= 0) + { + notelen = 0; + goto errout_with_csection; + } + + /* Get the index to the tail of the circular buffer */ + + tail = g_note_info.ni_tail; + DEBUGASSERT(tail < CONFIG_SCHED_NOTE_BUFSIZE); + + /* Get the length of the note at the tail index */ + + note = (FAR struct note_common_s *)&g_note_info.ni_buffer[tail]; + notelen = note->nc_length; + DEBUGASSERT(notelen <= circlen); + +errout_with_csection: + leave_critical_section(flags); + return notelen; +} + #endif /* CONFIG_SCHED_INSTRUMENTATION_BUFFER */