sched/: Add option to buffer scheduler instrumentation data in memory.
This commit is contained in:
parent
1951b9df57
commit
7f19631338
@ -11562,4 +11562,7 @@
|
||||
* sched/tls and include/nuttx/tls.h: Basic definitions needed to support
|
||||
thread local storage (TLS). Not used anywhere yet (and may not be used
|
||||
in the near future either) (2016-03-10).
|
||||
* 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).
|
||||
|
||||
|
@ -843,48 +843,6 @@ void sched_suspend_scheduler(FAR struct tcb_s *tcb);
|
||||
# define sched_suspend_scheduler(tcb)
|
||||
#endif
|
||||
|
||||
/********************************************************************************
|
||||
* Name: sched_note_*
|
||||
*
|
||||
* Description:
|
||||
* If instrumentation of the scheduler is enabled, then some outboard logic
|
||||
* must provide the following interfaces. These interfaces are not availalble
|
||||
* to application code.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The TCB of the thread.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION
|
||||
|
||||
void sched_note_start(FAR struct tcb_s *tcb);
|
||||
void sched_note_stop(FAR struct tcb_s *tcb);
|
||||
void sched_note_switch(FAR struct tcb_s *fromtcb, FAR struct tcb_s *totcb);
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION
|
||||
void sched_note_premption(FAR struct tcb_s *tcb, bool locked);
|
||||
#else
|
||||
# define sched_note_premption(t,l)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION
|
||||
void sched_note_csection(FAR struct tcb_s *tcb, bool enter);
|
||||
#else
|
||||
# define sched_note_csection(t,e)
|
||||
#endif
|
||||
|
||||
#else
|
||||
# define sched_note_start(t)
|
||||
# define sched_note_stop(t)
|
||||
# define sched_note_switch(t1, t2)
|
||||
# define sched_note_premption(t,l)
|
||||
# define sched_note_csection(t,e)
|
||||
#endif /* CONFIG_SCHED_INSTRUMENTATION */
|
||||
|
||||
#undef EXTERN
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
219
include/nuttx/sched_note.h
Normal file
219
include/nuttx/sched_note.h
Normal file
@ -0,0 +1,219 @@
|
||||
/****************************************************************************
|
||||
* include/nuttx/sched_note.h
|
||||
*
|
||||
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __INCLUDE_NUTTX_SCHED_NOTE_H
|
||||
#define __INCLUDE_NUTTX_SCHED_NOTE_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_BUFFER
|
||||
|
||||
/* This type identifies a note structure */
|
||||
|
||||
enum note_type_e
|
||||
{
|
||||
NOTE_START = 0,
|
||||
NOTE_STOP,
|
||||
NOTE_SWITCH
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION
|
||||
,
|
||||
NOTE_PREEMPT_LOCK,
|
||||
NOTE_PREEMPT_UNLOCK
|
||||
#endif
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION
|
||||
,
|
||||
NOTE_CSECTION_ENTER,
|
||||
NOTE_CSECTION_LEAVE
|
||||
#endif
|
||||
};
|
||||
|
||||
/* This structure provides the common header of each note */
|
||||
|
||||
struct note_common_s
|
||||
{
|
||||
uint8_t nc_length; /* Length of the note */
|
||||
uint8_t nc_type; /* See enum note_type_e */
|
||||
uint8_t nc_systime[4]; /* Time when note buffered */
|
||||
};
|
||||
|
||||
/* This is the specific form of the NOTE_START note */
|
||||
|
||||
struct note_start_s
|
||||
{
|
||||
uint8_t nst_length; /* Length of the note */
|
||||
uint8_t nst_type; /* Must be NOTE_START */
|
||||
uint8_t nst_systime[4]; /* Time when note buffered */
|
||||
uint8_t nst_pid[2]; /* ID of the new thread/task */
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
char nst_name[1]; /* Start of the name of the thread/task */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* This is the specific form of the NOTE_STOP note */
|
||||
|
||||
struct note_stop_s
|
||||
{
|
||||
uint8_t nsp_length; /* Length of the note */
|
||||
uint8_t nsp_type; /* Must be NOTE_STOP */
|
||||
uint8_t nsp_systime[4]; /* Time when note buffered */
|
||||
uint8_t nsp_pid[2]; /* ID of the thread/task that stopped */
|
||||
};
|
||||
|
||||
/* This is the specific form of the NOTE_SWITCH note */
|
||||
|
||||
struct note_switch_s
|
||||
{
|
||||
uint8_t nsw_length; /* Length of the note */
|
||||
uint8_t nsw_type; /* Must be NOTE_SWITCH */
|
||||
uint8_t nsw_systime[4]; /* Time when note buffered */
|
||||
uint8_t nsw_pidout[2]; /* ID of the thread/task that was blocked */
|
||||
uint8_t nsw_pidin[2]; /* ID of the thread/task that was started */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION
|
||||
/* This is the specific form of the NOTE_PREEMPT_LOCK/UNLOCK note */
|
||||
|
||||
struct note_preempt_s
|
||||
{
|
||||
uint8_t npr_length; /* Length of the note */
|
||||
uint8_t npr_type; /* Must be NOTE_PREEMPT_LOCK or _UNLOCK */
|
||||
uint8_t npr_systime[4]; /* Time when note buffered */
|
||||
uint8_t npr_pid[2]; /* ID of the thread/task that change pre-emption */
|
||||
uint8_t npr_count[2]; /* Count of nested locks */
|
||||
};
|
||||
#endif /* CONFIG_SCHED_INSTRUMENTATION_PREEMPTION */
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION
|
||||
/* This is the specific form of the NOTE_CSECTION_ENTER/LEAVE note */
|
||||
|
||||
struct note_csection_s
|
||||
{
|
||||
uint8_t ncs_length; /* Length of the note */
|
||||
uint8_t ncs_type; /* Must be NOTE_CSECTION_ENTER or _LEAVE */
|
||||
uint8_t ncs_systime[4]; /* Time when note buffered */
|
||||
uint8_t ncs_pid[2]; /* ID of the thread/task that changed critical section */
|
||||
#ifdef CONFIG_SMP
|
||||
uint8_t ncs_count[2]; /* Count of nested csections */
|
||||
#endif
|
||||
};
|
||||
#endif /* CONFIG_SCHED_INSTRUMENTATION_CSECTION */
|
||||
#endif /* CONFIG_SCHED_INSTRUMENTATION_BUFFER */
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* Name: sched_note_*
|
||||
*
|
||||
* Description:
|
||||
* If instrumentation of the scheduler is enabled, then some outboard logic
|
||||
* must provide the following interfaces. These interfaces are not availalble
|
||||
* to application code.
|
||||
*
|
||||
* NOTE: if CONFIG_SCHED_INSTRUMENTATION_BUFFER, then these interfaces are
|
||||
* *not* available to the platform-specific logic. Rather, they provided by
|
||||
* the note buffering logic. See sched_note_get() below.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The TCB of the thread.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
void sched_note_start(FAR struct tcb_s *tcb);
|
||||
void sched_note_stop(FAR struct tcb_s *tcb);
|
||||
void sched_note_switch(FAR struct tcb_s *fromtcb, FAR struct tcb_s *totcb);
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION
|
||||
void sched_note_premption(FAR struct tcb_s *tcb, bool locked);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION
|
||||
void sched_note_csection(FAR struct tcb_s *tcb, bool enter);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sched_note_get
|
||||
*
|
||||
* Description:
|
||||
* Remove the next note from the tail of the circular buffer. The note
|
||||
* is also removed from the circular buffer to make room for futher notes.
|
||||
*
|
||||
* Input Parameters:
|
||||
* buffer - Location to return the next note
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_BUFFER
|
||||
ssize_t sched_note_get(FAR uint8_t *buffer, size_t buflen);
|
||||
#endif
|
||||
|
||||
#else /* CONFIG_SCHED_INSTRUMENTATION */
|
||||
|
||||
# define sched_note_start(t)
|
||||
# define sched_note_stop(t)
|
||||
# define sched_note_switch(t1, t2)
|
||||
# define sched_note_premption(t,l)
|
||||
# define sched_note_csection(t,e)
|
||||
|
||||
#endif /* CONFIG_SCHED_INSTRUMENTATION */
|
||||
#endif /* __INCLUDE_NUTTX_SCHED_NOTE_H */
|
@ -370,10 +370,10 @@ config SPORADIC_INSTRUMENTATION
|
||||
scheduler behavior. If enabled, then the board-specific logic must
|
||||
provide the following functions:
|
||||
|
||||
void arch_sporadic_start(FAR struct tcb_s *tcb);
|
||||
void arch_sporadic_lowpriority(FAR struct tcb_s *tcb);
|
||||
void arch_sporadic_suspend(FAR struct tcb_s *tcb);
|
||||
void arch_sporadic_resume(FAR struct tcb_s *tcb);
|
||||
void arch_sporadic_start(FAR struct tcb_s *tcb);
|
||||
void arch_sporadic_lowpriority(FAR struct tcb_s *tcb);
|
||||
void arch_sporadic_suspend(FAR struct tcb_s *tcb);
|
||||
void arch_sporadic_resume(FAR struct tcb_s *tcb);
|
||||
|
||||
endif # SCHED_SPORADIC
|
||||
|
||||
@ -605,7 +605,15 @@ config SCHED_INSTRUMENTATION
|
||||
void sched_note_stop(FAR struct tcb_s *tcb);
|
||||
void sched_note_switch(FAR struct tcb_s *pFromTcb, FAR struct tcb_s *pToTcb);
|
||||
|
||||
NOTE: These are internal OS interfaces and are called at at very
|
||||
critical locations in the OS. There is very little that can be
|
||||
done in these interfaces. For example, normal devices may not be
|
||||
used; syslog output cannot be performed.
|
||||
|
||||
An option is to use SCHED_INSTRUMENTATION_BUFFER below.
|
||||
|
||||
if SCHED_INSTRUMENTATION
|
||||
|
||||
config SCHED_INSTRUMENTATION_PREEMPTION
|
||||
bool "Preemption monitor hooks"
|
||||
default n
|
||||
@ -626,6 +634,38 @@ config SCHED_INSTRUMENTATION_CSECTION
|
||||
|
||||
void sched_note_csection(FAR struct tcb_s *tcb, bool state);
|
||||
|
||||
config SCHED_INSTRUMENTATION_BUFFER
|
||||
bool "Buffer instrumentation data in memory"
|
||||
default n
|
||||
---help---
|
||||
If this option is selected, then in-memory buffering logic is
|
||||
enabled to capature scheduler instrumentation data. This has
|
||||
the advantage that (1) the platform logic does not have to provide
|
||||
the sched_note_* interaces described for the previous settings.
|
||||
Instead, the buffering logic catches all of these. It encodes
|
||||
timestamps the scheduler note and adds the note to an in-memory,
|
||||
circular buffer. And (2) buffering the scheduler instrumentation
|
||||
data (versus performing some output operation) minimizes the impact
|
||||
of the instrumentation on the behavior of the system.
|
||||
|
||||
If the in-memory buffer becomes full, then older notes are
|
||||
overwritten by newer notes. The following interface is provided:
|
||||
|
||||
ssize_t sched_note_get(FAR uint8_t *buffer, size_t buflen);
|
||||
|
||||
Platform specific information must call this function and dispose
|
||||
of it quickly so that overwriting of the tail of the circular buffer
|
||||
does not occur. See include/nuttx/sched_note.h for additional
|
||||
information.
|
||||
|
||||
config SCHED_NOTE_BUFSIZE
|
||||
int "Instrumentation buffer size"
|
||||
default 2048
|
||||
depends on SCHED_INSTRUMENTATION_BUFFER
|
||||
---help---
|
||||
The size of the in-memory, circular instrumentation buffer (in
|
||||
bytes).
|
||||
|
||||
endif # SCHED_INSTRUMENTATION
|
||||
endmenu # Performance Monitoring
|
||||
|
||||
|
@ -96,10 +96,20 @@ static const char g_idlename[] = "CPUn Idle"
|
||||
|
||||
void os_idle_trampoline(void)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION
|
||||
FAR struct tcb_s *tcb = this_task();
|
||||
#endif
|
||||
|
||||
/* Perform architecture-specific initialization for this CPU */
|
||||
|
||||
up_cpu_initialize();
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION
|
||||
/* Announce that the IDLE task has started */
|
||||
|
||||
sched_note_start(tcb);
|
||||
#endif
|
||||
|
||||
/* Then transfer control to the IDLE task */
|
||||
|
||||
(void)os_idle_task(0, NULL);
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include <nuttx/mm/mm.h>
|
||||
#include <nuttx/mm/shm.h>
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
#include <nuttx/init.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
@ -708,8 +709,13 @@ void os_start(void)
|
||||
lib_initialize();
|
||||
|
||||
/* IDLE Group Initialization **********************************************/
|
||||
/* Announce that the CPU0 IDLE task has started */
|
||||
|
||||
sched_note_start(&g_idletcb[0].cmn);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Initialize the IDLE group for the IDLE task of each CPU */
|
||||
|
||||
for (cpu = 0; cpu < CONFIG_SMP_NCPUS; cpu++)
|
||||
#endif
|
||||
{
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <nuttx/spinlock.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
#include <arch/irq.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
|
@ -82,6 +82,10 @@ else
|
||||
CSRCS += sched_processtimer.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SCHED_INSTRUMENTATION_BUFFER),y)
|
||||
CSRCS += sched_note.c
|
||||
endif
|
||||
|
||||
# Include sched build support
|
||||
|
||||
DEPPATH += --dep-path sched
|
||||
|
@ -43,6 +43,8 @@
|
||||
#include <queue.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
#include "irq/irq.h"
|
||||
#include "sched/sched.h"
|
||||
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include <queue.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
|
||||
/****************************************************************************
|
||||
|
491
sched/sched/sched_note.c
Normal file
491
sched/sched/sched_note.c
Normal file
@ -0,0 +1,491 @@
|
||||
/****************************************************************************
|
||||
* sched/sched/sched_note.c
|
||||
*
|
||||
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* 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 <nuttx/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/clock.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_BUFFER
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct note_info_s
|
||||
{
|
||||
unsigned int ni_head;
|
||||
unsigned int ni_tail;
|
||||
uint8_t ni_buffer[CONFIG_SCHED_NOTE_BUFSIZE];
|
||||
};
|
||||
|
||||
struct note_startalloc_s
|
||||
{
|
||||
uint8_t nsa_length;
|
||||
uint8_t nsa_type;
|
||||
uint8_t nsa_systime[4];
|
||||
uint8_t nsa_pid[2];
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
char nsa_name[CONFIG_TASK_NAME_SIZE + 1];
|
||||
#endif
|
||||
};
|
||||
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
# define SIZEOF_NOTE_START(n) (sizeof(struct note_start_s) + (n) - 1)
|
||||
#else
|
||||
# define SIZEOF_NOTE_START(n) (sizeof(struct note_start_s))
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static struct note_info_s g_note_info;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: note_next
|
||||
*
|
||||
* Description:
|
||||
* Return the circular buffer index at offset from the specified index
|
||||
* value, handling wraparound
|
||||
*
|
||||
* Input Parameters:
|
||||
* ndx - Old circular buffer index
|
||||
*
|
||||
* Returned Value:
|
||||
* New circular buffer index
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline unsigned int note_next(unsigned int ndx, unsigned int offset)
|
||||
{
|
||||
ndx += offset;
|
||||
if (ndx >= CONFIG_SCHED_NOTE_BUFSIZE)
|
||||
{
|
||||
ndx -= CONFIG_SCHED_NOTE_BUFSIZE;
|
||||
}
|
||||
|
||||
return ndx;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: note_systime
|
||||
*
|
||||
* Description:
|
||||
* Save the current system time in the note structure as a 32-bit value.
|
||||
*
|
||||
* Input Parameters:
|
||||
* note - The note structure to use
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void note_systime(FAR struct note_common_s *note)
|
||||
{
|
||||
uint32_t systime = (uint32_t)clock_systimer();
|
||||
|
||||
/* Save the LS 32-bits of the system timer in little endian order */
|
||||
|
||||
note->nc_systime[0] = (uint8_t)( systime & 0xff);
|
||||
note->nc_systime[1] = (uint8_t)((systime >> 8) & 0xff);
|
||||
note->nc_systime[2] = (uint8_t)((systime >> 16) & 0xff);
|
||||
note->nc_systime[3] = (uint8_t)((systime >> 24) & 0xff);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: note_length
|
||||
*
|
||||
* Description:
|
||||
* Length of data currently in circular buffer.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* Length of data currently in circular buffer.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_DEBUG
|
||||
static unsigned int note_length(void)
|
||||
{
|
||||
unsigned int head = g_note_info.ni_head;
|
||||
unsigned int tail = g_note_info.ni_tail;
|
||||
|
||||
if (tail > head)
|
||||
{
|
||||
head += CONFIG_SCHED_NOTE_BUFSIZE;
|
||||
}
|
||||
|
||||
return head - tail;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: note_remove
|
||||
*
|
||||
* Description:
|
||||
* Remove the variable length note from the tail of the circular buffer
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* We are within a critical section.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void note_remove(void)
|
||||
{
|
||||
FAR struct note_common_s *note;
|
||||
unsigned int tail;
|
||||
unsigned int length;
|
||||
|
||||
/* Get the tail index 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];
|
||||
length = note->nc_length;
|
||||
DEBUGASSERT(length <= note_length());
|
||||
|
||||
/* Increment the tail index to remove the entire note from the circular
|
||||
* buffer.
|
||||
*/
|
||||
|
||||
g_note_info.ni_tail = note_next(tail, length);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: note_add
|
||||
*
|
||||
* Description:
|
||||
* Add the variable length note to the head of the circular buffer
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* We are within a critical section.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void note_add(FAR const uint8_t *note, uint8_t notelen)
|
||||
{
|
||||
unsigned int head;
|
||||
unsigned int next;
|
||||
|
||||
/* Get the index to the head of the circular buffer */
|
||||
|
||||
DEBUGASSERT(note != NULL && notelen < CONFIG_SCHED_NOTE_BUFSIZE);
|
||||
head = g_note_info.ni_head;
|
||||
|
||||
/* Loop until all bytes have been transferred to the circular buffer */
|
||||
|
||||
while (notelen > 0)
|
||||
{
|
||||
/* Get the next head index. Would it collide with the current tail
|
||||
* index?
|
||||
*/
|
||||
|
||||
next = note_next(head, 1);
|
||||
if (next == g_note_info.ni_tail)
|
||||
{
|
||||
/* Yes, then remove the note at the tail index */
|
||||
|
||||
note_remove();
|
||||
}
|
||||
|
||||
/* Save the next byte at the head index */
|
||||
|
||||
g_note_info.ni_buffer[head] = *note++;
|
||||
|
||||
head = next;
|
||||
notelen--;
|
||||
}
|
||||
|
||||
g_note_info.ni_head = head;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sched_note_*
|
||||
*
|
||||
* Description:
|
||||
* These are the hooks into the scheduling instrumentation logic. Each
|
||||
* simply formats the note associated with the schedule event and adds
|
||||
* that note to the circular buffer.
|
||||
*
|
||||
* Input Parameters:
|
||||
* tcb - The TCB of the thread.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* We are within a critical section.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void sched_note_start(FAR struct tcb_s *tcb)
|
||||
{
|
||||
struct note_startalloc_s note;
|
||||
unsigned int length;
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
int namelen;
|
||||
#endif
|
||||
|
||||
/* Copy the task name (if possible) and get the length of the note */
|
||||
|
||||
#if CONFIG_TASK_NAME_SIZE > 0
|
||||
namelen = strlen(tcb->name);
|
||||
|
||||
DEBUGASSERT(namelen <= CONFIG_TASK_NAME_SIZE);
|
||||
strncpy(note.nsa_name, tcb->name, CONFIG_TASK_NAME_SIZE + 1);
|
||||
|
||||
length = SIZEOF_NOTE_START(namelen + 1);
|
||||
#else
|
||||
length = SIZEOF_NOTE_START(0)
|
||||
#endif
|
||||
|
||||
/* Finish formatting the note */
|
||||
|
||||
note.nsa_length = length;
|
||||
note.nsa_type = NOTE_START;
|
||||
note.nsa_pid[0] = (uint8_t)(tcb->pid & 0xff);
|
||||
note.nsa_pid[1] = (uint8_t)((tcb->pid >> 8) & 0xff);
|
||||
|
||||
note_systime((FAR struct note_common_s *)¬e);
|
||||
|
||||
/* Add the note to circular buffer */
|
||||
|
||||
note_add((FAR const uint8_t *)¬e, length);
|
||||
}
|
||||
|
||||
void sched_note_stop(FAR struct tcb_s *tcb)
|
||||
{
|
||||
struct note_stop_s note;
|
||||
|
||||
/* Format the note */
|
||||
|
||||
note.nsp_length = sizeof(struct note_stop_s);
|
||||
note.nsp_type = NOTE_STOP;
|
||||
note.nsp_pid[0] = (uint8_t)(tcb->pid & 0xff);
|
||||
note.nsp_pid[1] = (uint8_t)((tcb->pid >> 8) & 0xff);
|
||||
|
||||
note_systime((FAR struct note_common_s *)¬e);
|
||||
|
||||
/* Add the note to circular buffer */
|
||||
|
||||
note_add((FAR const uint8_t *)¬e, sizeof(struct note_stop_s));
|
||||
}
|
||||
|
||||
void sched_note_switch(FAR struct tcb_s *fromtcb, FAR struct tcb_s *totcb)
|
||||
{
|
||||
struct note_switch_s note;
|
||||
|
||||
/* Format the note */
|
||||
|
||||
note.nsw_length = sizeof(struct note_switch_s);
|
||||
note.nsw_type = NOTE_SWITCH;
|
||||
note.nsw_pidout[0] = (uint8_t)(fromtcb->pid & 0xff);
|
||||
note.nsw_pidout[1] = (uint8_t)((fromtcb->pid >> 8) & 0xff);
|
||||
note.nsw_pidin[0] = (uint8_t)(totcb->pid & 0xff);
|
||||
note.nsw_pidin[1] = (uint8_t)((totcb->pid >> 8) & 0xff);
|
||||
|
||||
note_systime((FAR struct note_common_s *)¬e);
|
||||
|
||||
/* Add the note to circular buffer */
|
||||
|
||||
note_add((FAR const uint8_t *)¬e, sizeof(struct note_switch_s));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_PREEMPTION
|
||||
void sched_note_premption(FAR struct tcb_s *tcb, bool locked)
|
||||
{
|
||||
struct note_preempt_s note;
|
||||
|
||||
/* Format the note */
|
||||
|
||||
note.npr_length = sizeof(struct note_preempt_s);
|
||||
note.npr_type = locked ? NOTE_PREEMPT_LOCK : NOTE_PREEMPT_UNLOCK;
|
||||
note.npr_pid[0] = (uint8_t)(tcb->pid & 0xff);
|
||||
note.npr_pid[1] = (uint8_t)((tcb->pid >> 8) & 0xff);
|
||||
note.npr_count[0] = (uint8_t)(tcb->lockcount & 0xff);
|
||||
note.npr_count[1] = (uint8_t)((tcb->lockcount >> 8) & 0xff);
|
||||
|
||||
note_systime((FAR struct note_common_s *)¬e);
|
||||
|
||||
/* Add the note to circular buffer */
|
||||
|
||||
note_add((FAR const uint8_t *)¬e, sizeof(struct note_preempt_s));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCHED_INSTRUMENTATION_CSECTION
|
||||
void sched_note_csection(FAR struct tcb_s *tcb, bool enter)
|
||||
{
|
||||
struct note_preempt_s note;
|
||||
|
||||
/* Format the note */
|
||||
|
||||
note.ncs_length = sizeof(struct note_preempt_s);
|
||||
note.ncs_type = enter ? NOTE_CSECTION_ENTER : NOTE_CSECTION_LEAVE;
|
||||
note.ncs_pid[0] = (uint8_t)(tcb->pid & 0xff);
|
||||
note.ncs_pid[1] = (uint8_t)((tcb->pid >> 8) & 0xff);
|
||||
#ifdef CONFIG_SMP
|
||||
note.ncs_count[0] = (uint8_t)(tcb->irqcount & 0xff);
|
||||
note.ncs_count[1] = (uint8_t)((tcb->irqcount >> 8) & 0xff);
|
||||
#endif
|
||||
|
||||
note_systime((FAR struct note_common_s *)¬e);
|
||||
|
||||
/* Add the note to circular buffer */
|
||||
|
||||
note_add((FAR const uint8_t *)¬e, sizeof(struct note_preempt_s));
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sched_note_get
|
||||
*
|
||||
* Description:
|
||||
* Remove the next note from the tail of the circular buffer. The note
|
||||
* is also removed from the circular buffer to make room for futher notes.
|
||||
*
|
||||
* Input Parameters:
|
||||
* buffer - Location to return the next note
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t sched_note_get(FAR uint8_t *buffer, size_t buflen)
|
||||
{
|
||||
FAR struct note_common_s *note;
|
||||
irqstate_t flags;
|
||||
unsigned int remaining;
|
||||
unsigned int tail;
|
||||
ssize_t notelen;
|
||||
|
||||
DEBUGASSERT(buffer != NULL);
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* 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 <= note_length());
|
||||
|
||||
/* Is the user buffer large enough to hold the note? */
|
||||
|
||||
if (buflen < notelen)
|
||||
{
|
||||
/* Remove the large note so that we do not get constipated. */
|
||||
|
||||
note_remove();
|
||||
|
||||
/* and return and error */
|
||||
|
||||
notelen = -EFBIG;
|
||||
goto errout_with_csection;
|
||||
}
|
||||
|
||||
/* Loop until the note has been transferred to the user buffer */
|
||||
|
||||
remaining = (unsigned int)notelen;
|
||||
while (remaining > 0)
|
||||
{
|
||||
/* Copy the next byte at the tail index */
|
||||
|
||||
*buffer++ = g_note_info.ni_buffer[tail];
|
||||
|
||||
/* Adjust indices and counts */
|
||||
|
||||
tail = note_next(tail, 1);
|
||||
remaining--;
|
||||
}
|
||||
|
||||
g_note_info.ni_tail = tail;
|
||||
|
||||
errout_with_csection:
|
||||
leave_critical_section(flags);
|
||||
return notelen;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCHED_INSTRUMENTATION_BUFFER */
|
@ -43,6 +43,8 @@
|
||||
#include <queue.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
#include "irq/irq.h"
|
||||
#include "sched/sched.h"
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/clock.h>
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
|
@ -46,6 +46,7 @@
|
||||
|
||||
#include <nuttx/sched.h>
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/sched_note.h>
|
||||
|
||||
#include "sched/sched.h"
|
||||
#ifndef CONFIG_DISABLE_SIGNALS
|
||||
|
Loading…
x
Reference in New Issue
Block a user