[PATCH 05/11] score: Add per-CPU profiling

Gedare Bloom gedare at rtems.org
Mon Mar 10 16:13:44 UTC 2014


Please add a little more to the commit message, especially since this
also adds some of the ISR profiling support. Unless you did not mean
to add the isr stuff yet.. Some of it looks unused to me.

On Mon, Mar 10, 2014 at 9:28 AM, Sebastian Huber
<sebastian.huber at embedded-brains.de> wrote:
> ---
>  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
>
> diff --git a/cpukit/sapi/src/profilingiterate.c b/cpukit/sapi/src/profilingiterate.c
> index e528932..28c06a4 100644
> --- a/cpukit/sapi/src/profilingiterate.c
> +++ b/cpukit/sapi/src/profilingiterate.c
> @@ -17,10 +17,71 @@
>  #endif
>
>  #include <rtems/profiling.h>
> +#include <rtems/counter.h>
> +#include <rtems.h>
> +
> +#include <string.h>
> +
> +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 0dc21b2..f3f53a9 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 1abbee5..e8f3370 100644
> --- a/cpukit/score/include/rtems/score/percpu.h
> +++ b/cpukit/score/include/rtems/score/percpu.h
> @@ -155,6 +155,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
>   *
>   *  This structure is used to hold per core state information.
> @@ -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 0000000..468c124
> --- /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
> + *  <rtems at embedded-brains.de>
> + *
> + * 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 <rtems/score/percpu.h>
> +
> +#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
It would be cleaner to choose to make these CPP checks either in these
functions or around their call points, but not both.

You could also provide a section of empty CPP macros in case there is
no RTEMS_PROFILING.

> +  (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
> +);
I don't like this name, but I have no better one yet until I see where
it is used. Perhaps _Profiling_Nested_isr? I liked the above functions
that were named after the function they profile, although here it is
less clear since the entire function does not get profiled I would
guess.

> +
> +/** @} */
> +
> +#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 5b25212..90a524d 100644
> --- a/cpukit/score/include/rtems/score/threaddispatch.h
> +++ b/cpukit/score/include/rtems/score/threaddispatch.h
> @@ -16,6 +16,7 @@
>
>  #include <rtems/score/percpu.h>
>  #include <rtems/score/smplock.h>
> +#include <rtems/score/profiling.h>
>
>  #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_disable( _Per_CPU_Get(), disable_level );
Shouldn't this one be enable_dispatch?

> +    _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 051582c..bb6508c 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 0000000..3367e98
> --- /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
> + *  <rtems at embedded-brains.de>
> + *
> + * 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 <rtems/score/profiling.h>
> +
> +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 ecf6810..da357c4 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 dc03a70..ab004c1 100644
> --- a/cpukit/score/src/threaddispatchdisablelevel.c
> +++ b/cpukit/score/src/threaddispatchdisablelevel.c
> @@ -17,6 +17,7 @@
>
>  #include <rtems/score/threaddispatch.h>
>  #include <rtems/score/assert.h>
> +#include <rtems/score/profiling.h>
>  #include <rtems/score/sysstate.h>
>
>  #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 161bb43..a85cebe 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 2c40170..5eee14c 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
>
> --
> 1.7.7
>
> _______________________________________________
> rtems-devel mailing list
> rtems-devel at rtems.org
> http://www.rtems.org/mailman/listinfo/rtems-devel



More information about the devel mailing list