From f980561ee073a7f24f65ed572852fa96edfa1708 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 10 Mar 2014 10:04:09 +0100 Subject: score: Add per-CPU profiling Add per-CPU profiling stats API. Implement the thread dispatch disable level profiling. The interrupt profiling must be implemented in CPU port specific parts (mostly assembler code). Add a support function _Profiling_Outer_most_interrupt_entry_and_exit() for this purpose. --- cpukit/sapi/src/profilingiterate.c | 61 ++++++++++++ cpukit/score/Makefile.am | 2 + cpukit/score/include/rtems/score/percpu.h | 74 ++++++++++++++ cpukit/score/include/rtems/score/profiling.h | 113 ++++++++++++++++++++++ cpukit/score/include/rtems/score/threaddispatch.h | 37 +++++-- cpukit/score/preinstall.am | 4 + cpukit/score/src/profilingisrentryexit.c | 45 +++++++++ cpukit/score/src/threaddispatch.c | 2 + cpukit/score/src/threaddispatchdisablelevel.c | 3 + cpukit/score/src/threadhandler.c | 1 + cpukit/score/src/threadstartmultitasking.c | 1 + 11 files changed, 335 insertions(+), 8 deletions(-) create mode 100644 cpukit/score/include/rtems/score/profiling.h create mode 100644 cpukit/score/src/profilingisrentryexit.c (limited to 'cpukit') diff --git a/cpukit/sapi/src/profilingiterate.c b/cpukit/sapi/src/profilingiterate.c index e528932db9..28c06a4689 100644 --- a/cpukit/sapi/src/profilingiterate.c +++ b/cpukit/sapi/src/profilingiterate.c @@ -17,10 +17,71 @@ #endif #include +#include +#include + +#include + +static void per_cpu_stats_iterate( + rtems_profiling_visitor visitor, + void *visitor_arg, + rtems_profiling_data *data +) +{ +#ifdef RTEMS_PROFILING + uint32_t n = rtems_smp_get_processor_count(); + uint32_t i; + + memset(data, 0, sizeof(*data)); + data->header.type = RTEMS_PROFILING_PER_CPU; + for (i = 0; i < n; ++i) { + const Per_CPU_Control *per_cpu = _Per_CPU_Get_by_index(i); + const Per_CPU_Stats *stats = &per_cpu->Stats; + rtems_profiling_per_cpu *per_cpu_data = &data->per_cpu; + + per_cpu_data->processor_index = i; + + per_cpu_data->max_thread_dispatch_disabled_time = + rtems_counter_ticks_to_nanoseconds( + stats->max_thread_dispatch_disabled_time + ); + + per_cpu_data->max_interrupt_time = + rtems_counter_ticks_to_nanoseconds(stats->max_interrupt_time); + + per_cpu_data->max_interrupt_delay = + rtems_counter_ticks_to_nanoseconds(stats->max_interrupt_delay); + + per_cpu_data->thread_dispatch_disabled_count = + stats->thread_dispatch_disabled_count; + + per_cpu_data->total_thread_dispatch_disabled_time = + rtems_counter_ticks_to_nanoseconds( + stats->total_thread_dispatch_disabled_time + ); + + per_cpu_data->interrupt_count = stats->interrupt_count; + + per_cpu_data->total_interrupt_time = + rtems_counter_ticks_to_nanoseconds( + stats->total_interrupt_time + ); + + (*visitor)(visitor_arg, data); + } +#else + (void) visitor; + (void) visitor_arg; + (void) data; +#endif +} void rtems_profiling_iterate( rtems_profiling_visitor visitor, void *visitor_arg ) { + rtems_profiling_data data; + + per_cpu_stats_iterate(visitor, visitor_arg, &data); } diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am index 0dc21b202b..f3f53a980c 100644 --- a/cpukit/score/Makefile.am +++ b/cpukit/score/Makefile.am @@ -45,6 +45,7 @@ include_rtems_score_HEADERS += include/rtems/score/percpu.h include_rtems_score_HEADERS += include/rtems/score/priority.h include_rtems_score_HEADERS += include/rtems/score/prioritybitmap.h include_rtems_score_HEADERS += include/rtems/score/prioritybitmapimpl.h +include_rtems_score_HEADERS += include/rtems/score/profiling.h include_rtems_score_HEADERS += include/rtems/score/rbtree.h include_rtems_score_HEADERS += include/rtems/score/rbtreeimpl.h include_rtems_score_HEADERS += include/rtems/score/scheduler.h @@ -331,6 +332,7 @@ libscore_a_SOURCES += src/apiext.c src/chain.c src/chainappend.c \ src/chainnodecount.c \ src/assertthreaddispatchingrepressed.c \ src/interr.c src/isr.c src/wkspace.c src/wkstringduplicate.c +libscore_a_SOURCES += src/profilingisrentryexit.c EXTRA_DIST = src/Unlimited.txt diff --git a/cpukit/score/include/rtems/score/percpu.h b/cpukit/score/include/rtems/score/percpu.h index 067cb84a29..5ba463e812 100644 --- a/cpukit/score/include/rtems/score/percpu.h +++ b/cpukit/score/include/rtems/score/percpu.h @@ -154,6 +154,78 @@ typedef enum { #endif /* defined( RTEMS_SMP ) */ +/** + * @brief Per-CPU statistics. + */ +typedef struct { +#if defined( RTEMS_PROFILING ) + /** + * @brief The thread dispatch disabled begin instant in CPU counter ticks. + * + * This value is used to measure the time of disabled thread dispatching. + */ + CPU_Counter_ticks thread_dispatch_disabled_instant; + + /** + * @brief The maximum time of disabled thread dispatching in CPU counter + * ticks. + */ + CPU_Counter_ticks max_thread_dispatch_disabled_time; + + /** + * @brief The maximum time spent to process a single sequence of nested + * interrupts in CPU counter ticks. + * + * This is the time interval between the change of the interrupt nest level + * from zero to one and the change back from one to zero. + */ + CPU_Counter_ticks max_interrupt_time; + + /** + * @brief The maximum interrupt delay in CPU counter ticks if supported by + * the hardware. + */ + CPU_Counter_ticks max_interrupt_delay; + + /** + * @brief Count of times when the thread dispatch disable level changes from + * zero to one in thread context. + * + * This value may overflow. + */ + uint64_t thread_dispatch_disabled_count; + + /** + * @brief Total time of disabled thread dispatching in CPU counter ticks. + * + * The average time of disabled thread dispatching is the total time of + * disabled thread dispatching divided by the thread dispatch disabled + * count. + * + * This value may overflow. + */ + uint64_t total_thread_dispatch_disabled_time; + + /** + * @brief Count of times when the interrupt nest level changes from zero to + * one. + * + * This value may overflow. + */ + uint64_t interrupt_count; + + /** + * @brief Total time of interrupt processing in CPU counter ticks. + * + * The average time of interrupt processing is the total time of interrupt + * processing divided by the interrupt count. + * + * This value may overflow. + */ + uint64_t total_interrupt_time; +#endif /* defined( RTEMS_PROFILING ) */ +} Per_CPU_Stats; + /** * @brief Per CPU Core Structure * @@ -236,6 +308,8 @@ typedef struct { */ Per_CPU_State state; #endif + + Per_CPU_Stats Stats; } Per_CPU_Control; #if defined( RTEMS_SMP ) diff --git a/cpukit/score/include/rtems/score/profiling.h b/cpukit/score/include/rtems/score/profiling.h new file mode 100644 index 0000000000..468c124450 --- /dev/null +++ b/cpukit/score/include/rtems/score/profiling.h @@ -0,0 +1,113 @@ +/** + * @file + * + * @ingroup ScoreProfiling + * + * @brief Profiling Support API + */ + +/* + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * 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. + */ + +#ifndef _RTEMS_SCORE_PROFILING +#define _RTEMS_SCORE_PROFILING + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @defgroup ScoreProfiling Profiling Support + * + * @brief Profiling support. + * + * @{ + */ + +static inline void _Profiling_Thread_dispatch_disable( + Per_CPU_Control *per_cpu, + uint32_t previous_thread_dispatch_disable_level +) +{ +#if defined( RTEMS_PROFILING ) + if ( previous_thread_dispatch_disable_level == 0 ) { + Per_CPU_Stats *stats = &per_cpu->Stats; + + stats->thread_dispatch_disabled_instant = _CPU_Counter_read(); + ++stats->thread_dispatch_disabled_count; + } +#else + (void) per_cpu; + (void) previous_thread_dispatch_disable_level; +#endif +} + +static inline void _Profiling_Thread_dispatch_enable( + Per_CPU_Control *per_cpu, + uint32_t new_thread_dispatch_disable_level +) +{ +#if defined( RTEMS_PROFILING ) + if ( new_thread_dispatch_disable_level == 0 ) { + Per_CPU_Stats *stats = &per_cpu->Stats; + CPU_Counter_ticks now = _CPU_Counter_read(); + CPU_Counter_ticks delta = _CPU_Counter_difference( + now, + stats->thread_dispatch_disabled_instant + ); + + stats->total_thread_dispatch_disabled_time += delta; + + if ( stats->max_thread_dispatch_disabled_time < delta ) { + stats->max_thread_dispatch_disabled_time = delta; + } + } +#else + (void) per_cpu; + (void) new_thread_dispatch_disable_level; +#endif +} + +static inline void _Profiling_Update_max_interrupt_delay( + Per_CPU_Control *per_cpu, + CPU_Counter_ticks interrupt_delay +) +{ +#if defined( RTEMS_PROFILING ) + Per_CPU_Stats *stats = &per_cpu->Stats; + + if ( stats->max_interrupt_delay < interrupt_delay ) { + stats->max_interrupt_delay = interrupt_delay; + } +#else + (void) per_cpu; + (void) interrupt_delay; +#endif +} + +void _Profiling_Outer_most_interrupt_entry_and_exit( + Per_CPU_Control *per_cpu, + CPU_Counter_ticks interrupt_entry_instant, + CPU_Counter_ticks interrupt_exit_instant +); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _RTEMS_SCORE_PROFILING */ diff --git a/cpukit/score/include/rtems/score/threaddispatch.h b/cpukit/score/include/rtems/score/threaddispatch.h index 5b25212de9..7e616d69f7 100644 --- a/cpukit/score/include/rtems/score/threaddispatch.h +++ b/cpukit/score/include/rtems/score/threaddispatch.h @@ -16,6 +16,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -140,12 +141,22 @@ RTEMS_INLINE_ROUTINE void _Thread_Dispatch_initialization( void ) */ RTEMS_INLINE_ROUTINE uint32_t _Thread_Dispatch_increment_disable_level(void) { - uint32_t level = _Thread_Dispatch_disable_level; + uint32_t disable_level = _Thread_Dispatch_disable_level; +#if defined( RTEMS_PROFILING ) + ISR_Level level; - ++level; - _Thread_Dispatch_disable_level = level; + _ISR_Disable( level ); + _Profiling_Thread_dispatch_disable( _Per_CPU_Get(), disable_level ); +#endif + + ++disable_level; + _Thread_Dispatch_disable_level = disable_level; + +#if defined( RTEMS_PROFILING ) + _ISR_Enable( level ); +#endif - return level; + return disable_level; } /** @@ -155,12 +166,22 @@ RTEMS_INLINE_ROUTINE void _Thread_Dispatch_initialization( void ) */ RTEMS_INLINE_ROUTINE uint32_t _Thread_Dispatch_decrement_disable_level(void) { - uint32_t level = _Thread_Dispatch_disable_level; + uint32_t disable_level = _Thread_Dispatch_disable_level; +#if defined( RTEMS_PROFILING ) + ISR_Level level; - --level; - _Thread_Dispatch_disable_level = level; + _ISR_Disable( level ); +#endif + + --disable_level; + _Thread_Dispatch_disable_level = disable_level; + +#if defined( RTEMS_PROFILING ) + _Profiling_Thread_dispatch_enable( _Per_CPU_Get(), disable_level ); + _ISR_Enable( level ); +#endif - return level; + return disable_level; } #endif /* RTEMS_SMP */ diff --git a/cpukit/score/preinstall.am b/cpukit/score/preinstall.am index 051582ccc2..bb6508c7d3 100644 --- a/cpukit/score/preinstall.am +++ b/cpukit/score/preinstall.am @@ -163,6 +163,10 @@ $(PROJECT_INCLUDE)/rtems/score/prioritybitmapimpl.h: include/rtems/score/priorit $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/prioritybitmapimpl.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/prioritybitmapimpl.h +$(PROJECT_INCLUDE)/rtems/score/profiling.h: include/rtems/score/profiling.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/profiling.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/profiling.h + $(PROJECT_INCLUDE)/rtems/score/rbtree.h: include/rtems/score/rbtree.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/rbtree.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/rbtree.h diff --git a/cpukit/score/src/profilingisrentryexit.c b/cpukit/score/src/profilingisrentryexit.c new file mode 100644 index 0000000000..3367e9800e --- /dev/null +++ b/cpukit/score/src/profilingisrentryexit.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * 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. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +void _Profiling_Outer_most_interrupt_entry_and_exit( + Per_CPU_Control *per_cpu, + CPU_Counter_ticks interrupt_entry_instant, + CPU_Counter_ticks interrupt_exit_instant +) +{ +#if defined( RTEMS_PROFILING ) + Per_CPU_Stats *stats = &per_cpu->Stats; + CPU_Counter_ticks delta = _CPU_Counter_difference( + interrupt_exit_instant, + interrupt_entry_instant + ); + + ++stats->interrupt_count; + stats->total_interrupt_time += delta; + + if ( stats->max_interrupt_time < delta ) { + stats->max_interrupt_time = delta; + } +#else + (void) per_cpu; + (void) interrupt_entry_instant; + (void) interrupt_exit_instant; +#endif +} diff --git a/cpukit/score/src/threaddispatch.c b/cpukit/score/src/threaddispatch.c index ecf6810716..da357c4a44 100644 --- a/cpukit/score/src/threaddispatch.c +++ b/cpukit/score/src/threaddispatch.c @@ -40,6 +40,7 @@ void _Thread_Dispatch( void ) per_cpu = _Per_CPU_Get(); _Assert( per_cpu->thread_dispatch_disable_level == 0 ); + _Profiling_Thread_dispatch_disable( per_cpu, 0 ); per_cpu->thread_dispatch_disable_level = 1; #if defined( RTEMS_SMP ) @@ -170,6 +171,7 @@ void _Thread_Dispatch( void ) post_switch: _Assert( per_cpu->thread_dispatch_disable_level == 1 ); per_cpu->thread_dispatch_disable_level = 0; + _Profiling_Thread_dispatch_enable( per_cpu, 0 ); _Per_CPU_Release_and_ISR_enable( per_cpu, level ); diff --git a/cpukit/score/src/threaddispatchdisablelevel.c b/cpukit/score/src/threaddispatchdisablelevel.c index dc03a702a4..ab004c1662 100644 --- a/cpukit/score/src/threaddispatchdisablelevel.c +++ b/cpukit/score/src/threaddispatchdisablelevel.c @@ -17,6 +17,7 @@ #include #include +#include #include #define NO_OWNER_CPU 0xffffffffU @@ -89,6 +90,7 @@ uint32_t _Thread_Dispatch_increment_disable_level( void ) _Giant_Do_acquire( self_cpu ); disable_level = self_cpu->thread_dispatch_disable_level; + _Profiling_Thread_dispatch_disable( self_cpu, disable_level ); ++disable_level; self_cpu->thread_dispatch_disable_level = disable_level; @@ -113,6 +115,7 @@ uint32_t _Thread_Dispatch_decrement_disable_level( void ) _Giant_Do_release( self_cpu ); _Assert( disable_level != 0 || _Giant.owner_cpu == NO_OWNER_CPU ); + _Profiling_Thread_dispatch_enable( self_cpu, disable_level ); _ISR_Enable_without_giant( isr_level ); return disable_level; diff --git a/cpukit/score/src/threadhandler.c b/cpukit/score/src/threadhandler.c index 161bb43a6b..a85cebe905 100644 --- a/cpukit/score/src/threadhandler.c +++ b/cpukit/score/src/threadhandler.c @@ -153,6 +153,7 @@ void _Thread_Handler( void ) _Assert( _ISR_Get_level() != 0 ); per_cpu->thread_dispatch_disable_level = 0; + _Profiling_Thread_dispatch_enable( per_cpu, 0 ); _Per_CPU_Release( per_cpu ); diff --git a/cpukit/score/src/threadstartmultitasking.c b/cpukit/score/src/threadstartmultitasking.c index 2c40170f36..5eee14c410 100644 --- a/cpukit/score/src/threadstartmultitasking.c +++ b/cpukit/score/src/threadstartmultitasking.c @@ -34,6 +34,7 @@ void _Thread_Start_multitasking( void ) * _Per_CPU_Release(). */ _Per_CPU_Acquire( self_cpu ); + _Profiling_Thread_dispatch_disable( self_cpu, 0 ); self_cpu->thread_dispatch_disable_level = 1; #endif -- cgit v1.2.3