[rtems commit] score: Add per-CPU profiling

Sebastian Huber sebh at rtems.org
Fri Mar 14 07:42:46 UTC 2014


Module:    rtems
Branch:    master
Commit:    f980561ee073a7f24f65ed572852fa96edfa1708
Changeset: http://git.rtems.org/rtems/commit/?id=f980561ee073a7f24f65ed572852fa96edfa1708

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Mon Mar 10 10:04:09 2014 +0100

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(-)

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 067cb84..5ba463e 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
+  (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 5b25212..7e616d6 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_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 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
 




More information about the vc mailing list