sched_note: Add spinlock instrumentation; In SMP configurations, select to log only notes from certain CPUs

This commit is contained in:
Gregory Nutt 2016-11-28 10:33:46 -06:00
parent d65be718c2
commit 00215fbc98
6 changed files with 267 additions and 3 deletions

View File

@ -50,6 +50,22 @@
#ifdef CONFIG_SCHED_INSTRUMENTATION #ifdef CONFIG_SCHED_INSTRUMENTATION
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Provide defaults for some configuration settings (could be undefined with
* old configuration files)
*/
#ifdef CONFIG_SCHED_INSTRUMENTATION_CPUSET
# define CONFIG_SCHED_INSTRUMENTATION_CPUSET 0xffff
#endif
#ifdef CONFIG_SCHED_NOTE_BUFSIZE
# define CONFIG_SCHED_NOTE_BUFSIZE 2048
#endif
/**************************************************************************** /****************************************************************************
* Public Types * Public Types
****************************************************************************/ ****************************************************************************/
@ -81,6 +97,13 @@ enum note_type_e
NOTE_CSECTION_ENTER, NOTE_CSECTION_ENTER,
NOTE_CSECTION_LEAVE NOTE_CSECTION_LEAVE
#endif #endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
,
NOTE_SPINLOCK_LOCK,
NOTE_SPINLOCK_LOCKED,
NOTE_SPINLOCK_UNLOCK,
NOTE_SPINLOCK_ABORT
#endif
}; };
/* This structure provides the common header of each note */ /* This structure provides the common header of each note */
@ -182,6 +205,17 @@ struct note_csection_s
#endif #endif
}; };
#endif /* CONFIG_SCHED_INSTRUMENTATION_CSECTION */ #endif /* CONFIG_SCHED_INSTRUMENTATION_CSECTION */
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* This is the specific form of the NOTE_SPINLOCK_LOCK/LOCKED/UNLOCK/ABORT note */
struct note_spinlock_s
{
struct note_common_s nsp_cmn; /* Common note parameters */
FAR void *nsp_spinlock; /* Address of spinlock */
uint8_t nsp_value; /* Value of spinlock */
};
#endif /* CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS */
#endif /* CONFIG_SCHED_INSTRUMENTATION_BUFFER */ #endif /* CONFIG_SCHED_INSTRUMENTATION_BUFFER */
/**************************************************************************** /****************************************************************************
@ -237,6 +271,18 @@ void sched_note_csection(FAR struct tcb_s *tcb, bool enter);
# define sched_note_csection(t,e) # define sched_note_csection(t,e)
#endif #endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
void sched_note_spinlock(FAR struct tcb_s *tcb, FAR volatile void *spinlock);
void sched_note_spinlocked(FAR struct tcb_s *tcb, FAR volatile void *spinlock);
void sched_note_spinunlock(FAR struct tcb_s *tcb, FAR volatile void *spinlock);
void sched_note_spinabort(FAR struct tcb_s *tcb, FAR volatile void *spinlock);
#else
# define sched_note_spinlock(t,s)
# define sched_note_spinlocked(t,s)
# define sched_note_spinunlock(t,s)
# define sched_note_spinabort(t,s)
#endif
/**************************************************************************** /****************************************************************************
* Name: sched_note_get * Name: sched_note_get
* *
@ -311,6 +357,10 @@ int note_register(void);
# define sched_note_cpu_resumed(t) # define sched_note_cpu_resumed(t)
# define sched_note_premption(t,l) # define sched_note_premption(t,l)
# define sched_note_csection(t,e) # define sched_note_csection(t,e)
# define sched_note_spinlock(t,s)
# define sched_note_spinlocked(t,s)
# define sched_note_spinunlock(t,s)
# define sched_note_spinabort(t,s)
#endif /* CONFIG_SCHED_INSTRUMENTATION */ #endif /* CONFIG_SCHED_INSTRUMENTATION */
#endif /* __INCLUDE_NUTTX_SCHED_NOTE_H */ #endif /* __INCLUDE_NUTTX_SCHED_NOTE_H */

View File

@ -80,6 +80,10 @@
# define SP_DSB() # define SP_DSB()
#endif #endif
#if defined(CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS) && !defined(__SP_UNLOCK_FUNCTION)
# define __SP_UNLOCK_FUNCTION 1
#endif
/* If the target CPU supports a data cache then it may be necessary to /* If the target CPU supports a data cache then it may be necessary to
* manage spinlocks in a special way, perhaps linking them all into a * manage spinlocks in a special way, perhaps linking them all into a
* special non-cacheable memory region. * special non-cacheable memory region.

View File

@ -678,6 +678,13 @@ config SCHED_INSTRUMENTATION
if SCHED_INSTRUMENTATION if SCHED_INSTRUMENTATION
config SCHED_INSTRUMENTATION_CPUSET
hex "CPU bit set"
default 0xffff
depends on SMP
---help---
Monitor only CPUs in the bitset. Bit 0=CPU0, Bit1=CPU1, etc.
config SCHED_INSTRUMENTATION_PREEMPTION config SCHED_INSTRUMENTATION_PREEMPTION
bool "Preemption monitor hooks" bool "Preemption monitor hooks"
default n default n
@ -687,10 +694,15 @@ config SCHED_INSTRUMENTATION_PREEMPTION
void sched_note_premption(FAR struct tcb_s *tcb, bool state); void sched_note_premption(FAR struct tcb_s *tcb, bool state);
config SCHED_INSTRUMENTATION_FAUXPAS
bool
default y if !EXPERIMENTAL && SCHED_INSTRUMENTATION_BUFFER
default n if EXPERIMENTAL || !SCHED_INSTRUMENTATION_BUFFER
config SCHED_INSTRUMENTATION_CSECTION config SCHED_INSTRUMENTATION_CSECTION
bool "Critical section monitor hooks" bool "Critical section monitor hooks"
default n default n
depends on EXPERIMENTAL || !SCHED_INSTRUMENTATION_BUFFER depends on !SCHED_INSTRUMENTATION_FAUXPAS
---help--- ---help---
Enables additional hooks for entry and exit from critical sections. Enables additional hooks for entry and exit from critical sections.
Interrupts are disabled while within a critical section. Board- Interrupts are disabled while within a critical section. Board-
@ -706,6 +718,27 @@ config SCHED_INSTRUMENTATION_CSECTION
added from the note buffer in order to remove one entry. Not added from the note buffer in order to remove one entry. Not
very useful in its current state! very useful in its current state!
config SCHED_INSTRUMENTATION_SPINLOCK
bool "Spinlock monitor hooks"
default n
depends on SPINLOCK && (!SMP || !SCHED_INSTRUMENTATION_FAUXPAS)
---help---
Enables additional hooks for spinlock state. Board-specific logic
must provide this additional logic.
void sched_note_spinlock(FAR struct tcb_s *tcb, bool state);
void sched_note_spinlocked(FAR struct tcb_s *tcb, bool state);
void sched_note_spinunlock(FAR struct tcb_s *tcb, bool state);
void sched_note_spinabort(FAR struct tcb_s *tcb, bool state);
NOTE: This option is marked EXPERIMENTAL because there is a logical
error in the design when this feature is used with
CONFIG_SCHED_INSTRUMENTATION_BUFFER. That error is that
sched_note_get() calls enter_ and leave_critical_section which use
spinlocks in SMP mode. That means that each call to sched_note_get()
causes several additional entries to be added from the note buffer in
order to remove one entry. Not very useful in its current state!
config SCHED_INSTRUMENTATION_BUFFER config SCHED_INSTRUMENTATION_BUFFER
bool "Buffer instrumentation data in memory" bool "Buffer instrumentation data in memory"
default n default n

View File

@ -128,6 +128,14 @@ static uint8_t g_cpu_nestcount[CONFIG_SMP_NCPUS];
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static inline bool irq_waitlock(int cpu) static inline bool irq_waitlock(int cpu)
{ {
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
FAR struct tcb_s *tcb = this_task();
/* Notify that we are waiting for a spinlock */
sched_note_spinlock(tcb, &g_cpu_irqlock);
#endif
/* Duplicate the spin_lock() logic from spinlock.c, but adding the check /* Duplicate the spin_lock() logic from spinlock.c, but adding the check
* for the deadlock condition. * for the deadlock condition.
*/ */
@ -142,6 +150,12 @@ static inline bool irq_waitlock(int cpu)
* Abort the wait and return false. * Abort the wait and return false.
*/ */
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we are waiting for a spinlock */
sched_note_spinabort(tcb, &g_cpu_irqlock);
#endif
return false; return false;
} }
@ -150,6 +164,12 @@ static inline bool irq_waitlock(int cpu)
/* We have g_cpu_irqlock! */ /* We have g_cpu_irqlock! */
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we have the spinlock */
sched_note_spinlocked(tcb, &g_cpu_irqlock);
#endif
SP_DMB(); SP_DMB();
return true; return true;
} }

View File

@ -46,8 +46,11 @@
#include <nuttx/sched.h> #include <nuttx/sched.h>
#include <nuttx/clock.h> #include <nuttx/clock.h>
#include <nuttx/spinlock.h>
#include <nuttx/sched_note.h> #include <nuttx/sched_note.h>
#include "sched/sched.h"
#ifdef CONFIG_SCHED_INSTRUMENTATION_BUFFER #ifdef CONFIG_SCHED_INSTRUMENTATION_BUFFER
/**************************************************************************** /****************************************************************************
@ -122,8 +125,10 @@ static inline unsigned int note_next(unsigned int ndx, unsigned int offset)
* Fill in some of the common fields in the note structure. * Fill in some of the common fields in the note structure.
* *
* Input Parameters: * Input Parameters:
* tcb - The TCB containing the information * tcb - The TCB containing the information
* note - The common note structure to use * note - The common note structure to use
* length - The total lengthof the note structure
* type - The type of the note
* *
* Returned Value: * Returned Value:
* None * None
@ -154,6 +159,39 @@ static void note_common(FAR struct tcb_s *tcb, FAR struct note_common_s *note,
note->nc_systime[3] = (uint8_t)((systime >> 24) & 0xff); note->nc_systime[3] = (uint8_t)((systime >> 24) & 0xff);
} }
/****************************************************************************
* Name: note_spincommon
*
* Description:
* Common logic for NOTE_SPINLOCK, NOTE_SPINLOCKED, and NOTE_SPINUNLOCK
*
* Input Parameters:
* tcb - The TCB containing the information
* note - The common note structure to use
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
void note_spincommon(FAR struct tcb_s *tcb, FAR volatile spinlock_t *spinlock,
int type)
{
struct note_spinlock_s note;
/* Format the note */
note_common(tcb, &note.nsp_cmn, sizeof(struct note_spinlock_s), type);
note.nsp_spinlock = (FAR void *)spinlock;
note.nsp_value = (uint8_t)*spinlock;
/* Add the note to circular buffer */
note_add((FAR const uint8_t *)&note, sizeof(struct note_spinlock_s));
}
#endif
/**************************************************************************** /****************************************************************************
* Name: note_length * Name: note_length
* *
@ -244,6 +282,17 @@ static void note_add(FAR const uint8_t *note, uint8_t notelen)
unsigned int head; unsigned int head;
unsigned int next; unsigned int next;
#ifdef CONFIG_SMP
/* Ignore notes that are not in the set of monitored CPUs */
if ((CONFIG_SCHED_INSTRUMENTATION_CPUSET & (1 << this_cpu())) == 0)
{
/* Not in the set of monitored CPUs. Do not log the note. */
return;
}
#endif
/* Get the index to the head of the circular buffer */ /* Get the index to the head of the circular buffer */
DEBUGASSERT(note != NULL && notelen < CONFIG_SCHED_NOTE_BUFSIZE); DEBUGASSERT(note != NULL && notelen < CONFIG_SCHED_NOTE_BUFSIZE);
@ -463,6 +512,27 @@ void sched_note_csection(FAR struct tcb_s *tcb, bool enter)
} }
#endif #endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
void sched_note_spinlock(FAR struct tcb_s *tcb, FAR volatile void *spinlock)
{
note_spincommon(tcb, spinlock, NOTE_SPINLOCK_LOCK)
}
void sched_note_spinlocked(FAR struct tcb_s *tcb, FAR volatile void *spinlock);
{
note_spincommon(tcb, spinlock, NOTE_SPINLOCK_LOCKED)
}
void sched_note_spinunlock(FAR struct tcb_s *tcb, FAR volatile void *spinlock);
{
note_spincommon(tcb, spinlock, NOTE_SPINLOCK_UNLOCK)
}
void sched_note_spinabort(FAR struct tcb_s *tcb, FAR volatile void *spinlock);
{
note_spincommon(tcb, spinlock, NOTE_SPINLOCK_ABORT)
}
#endif
/**************************************************************************** /****************************************************************************
* Name: sched_note_get * Name: sched_note_get
* *

View File

@ -44,6 +44,7 @@
#include <assert.h> #include <assert.h>
#include <nuttx/spinlock.h> #include <nuttx/spinlock.h>
#include <nuttx/sched_note.h>
#include <arch/irq.h> #include <arch/irq.h>
#include "sched/sched.h" #include "sched/sched.h"
@ -119,11 +120,22 @@ void spin_initializer(FAR struct spinlock_s *lock)
void spin_lock(FAR volatile spinlock_t *lock) void spin_lock(FAR volatile spinlock_t *lock)
{ {
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we are waiting for a spinlock */
sched_note_spinlock(this_task(), lock);
#endif
while (up_testset(lock) == SP_LOCKED) while (up_testset(lock) == SP_LOCKED)
{ {
SP_DSB(); SP_DSB();
} }
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we have the spinlock */
sched_note_spinlocked(this_task(), lock);
#endif
SP_DMB(); SP_DMB();
} }
@ -147,6 +159,12 @@ void spin_lock(FAR volatile spinlock_t *lock)
#ifdef __SP_UNLOCK_FUNCTION #ifdef __SP_UNLOCK_FUNCTION
void spin_unlock(FAR volatile spinlock_t *lock) void spin_unlock(FAR volatile spinlock_t *lock)
{ {
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we are unlocking the spinlock */
sched_note_spinunlock(this_task(), lock);
#endif
*lock = SP_UNLOCKED; *lock = SP_UNLOCKED;
SP_DMB(); SP_DMB();
} }
@ -208,6 +226,13 @@ void spin_lockr(FAR struct spinlock_s *lock)
# warning Missing logic # warning Missing logic
#endif #endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we are waiting for a spinlock */
sched_note_spinlock(this_task(), &lock->sp_lock);
#endif
/* Take the lock. REVISIT: We should set an indication in the TCB /* Take the lock. REVISIT: We should set an indication in the TCB
* that the thread is spinning. This might be useful in determining * that the thread is spinning. This might be useful in determining
* some scheduling actions? * some scheduling actions?
@ -221,6 +246,12 @@ void spin_lockr(FAR struct spinlock_s *lock)
SP_DSB(); SP_DSB();
} }
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we have thespinlock */
sched_note_spinlocked(this_task(), &lock->sp_lock);
#endif
SP_DMB(); SP_DMB();
/* Take one count on the lock */ /* Take one count on the lock */
@ -233,6 +264,12 @@ void spin_lockr(FAR struct spinlock_s *lock)
#else /* CONFIG_SMP */ #else /* CONFIG_SMP */
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we are waiting for a spinlock */
sched_note_spinlock(this_task(), &lock->sp_lock);
#endif
/* Take the lock. REVISIT: We should set an indication in the TCB that /* Take the lock. REVISIT: We should set an indication in the TCB that
* the thread is spinning. This might be useful in determining some * the thread is spinning. This might be useful in determining some
* scheduling actions? * scheduling actions?
@ -244,6 +281,12 @@ void spin_lockr(FAR struct spinlock_s *lock)
SP_DSB() SP_DSB()
} }
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we have thespinlock */
sched_note_spinlocked(this_task(), &lock->sp_lock);
#endif
SP_DMB(); SP_DMB();
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
} }
@ -303,6 +346,11 @@ void spin_unlockr(FAR struct spinlock_s *lock)
if (lock->sp_count <= 1) if (lock->sp_count <= 1)
{ {
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we are unlocking the spinlock */
sched_note_spinunlock(this_task(), &lock->sp_lock);
#endif
/* The count must decremented to zero */ /* The count must decremented to zero */
lock->sp_count = 0; lock->sp_count = 0;
@ -318,6 +366,13 @@ void spin_unlockr(FAR struct spinlock_s *lock)
up_irq_restore(flags); up_irq_restore(flags);
#else /* CONFIG_SMP */ #else /* CONFIG_SMP */
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
/* Notify that we are unlocking the spinlock */
sched_note_spinunlock(this_task(), &lock->sp_lock);
#endif
/* Just mark the spinlock unlocked */ /* Just mark the spinlock unlocked */
DEBUGASSERT(lock != NULL && lock->sp_lock == SP_LOCKED); DEBUGASSERT(lock != NULL && lock->sp_lock == SP_LOCKED);
@ -347,15 +402,31 @@ void spin_setbit(FAR volatile cpu_set_t *set, unsigned int cpu,
FAR volatile spinlock_t *setlock, FAR volatile spinlock_t *setlock,
FAR volatile spinlock_t *orlock) FAR volatile spinlock_t *orlock)
{ {
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
cpu_set_t prev;
#endif
/* First, get the 'setlock' spinlock */ /* First, get the 'setlock' spinlock */
spin_lock(setlock); spin_lock(setlock);
/* Then set the bit and mark the 'orlock' as locked */ /* Then set the bit and mark the 'orlock' as locked */
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
prev = *set;
#endif
*set |= (1 << cpu); *set |= (1 << cpu);
*orlock = SP_LOCKED; *orlock = SP_LOCKED;
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
if (prev == 0)
{
/* Notify that we have locked the spinlock */
sched_note_spinlocked(this_task(), orlock);
}
#endif
/* Release the 'setlock' */ /* Release the 'setlock' */
spin_unlock(setlock); spin_unlock(setlock);
@ -382,6 +453,10 @@ void spin_clrbit(FAR volatile cpu_set_t *set, unsigned int cpu,
FAR volatile spinlock_t *setlock, FAR volatile spinlock_t *setlock,
FAR volatile spinlock_t *orlock) FAR volatile spinlock_t *orlock)
{ {
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
cpu_set_t prev;
#endif
/* First, get the 'setlock' spinlock */ /* First, get the 'setlock' spinlock */
spin_lock(setlock); spin_lock(setlock);
@ -390,9 +465,21 @@ void spin_clrbit(FAR volatile cpu_set_t *set, unsigned int cpu,
* upon the resulting state of the CPU set. * upon the resulting state of the CPU set.
*/ */
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
prev = *set;
#endif
*set &= ~(1 << cpu); *set &= ~(1 << cpu);
*orlock = (*set != 0) ? SP_LOCKED : SP_UNLOCKED; *orlock = (*set != 0) ? SP_LOCKED : SP_UNLOCKED;
#ifdef CONFIG_SCHED_INSTRUMENTATION_SPINLOCKS
if (prev != 0 && *set == 0)
{
/* Notify that we have unlocked the spinlock */
sched_note_spinunlock(this_task(), orlock);
}
#endif
/* Release the 'setlock' */ /* Release the 'setlock' */
spin_unlock(setlock); spin_unlock(setlock);