summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2014-03-10 10:04:09 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-03-14 08:46:49 +0100
commitf980561ee073a7f24f65ed572852fa96edfa1708 (patch)
treea2e62dd469acbe201388c9b8a3e0ebd9d7277e9f
parentsapi: Add SMP lock profiling app. level data (diff)
downloadrtems-f980561ee073a7f24f65ed572852fa96edfa1708.tar.bz2
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.
-rw-r--r--cpukit/sapi/src/profilingiterate.c61
-rw-r--r--cpukit/score/Makefile.am2
-rw-r--r--cpukit/score/include/rtems/score/percpu.h74
-rw-r--r--cpukit/score/include/rtems/score/profiling.h113
-rw-r--r--cpukit/score/include/rtems/score/threaddispatch.h37
-rw-r--r--cpukit/score/preinstall.am4
-rw-r--r--cpukit/score/src/profilingisrentryexit.c45
-rw-r--r--cpukit/score/src/threaddispatch.c2
-rw-r--r--cpukit/score/src/threaddispatchdisablelevel.c3
-rw-r--r--cpukit/score/src/threadhandler.c1
-rw-r--r--cpukit/score/src/threadstartmultitasking.c1
11 files changed, 335 insertions, 8 deletions
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 <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 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
@@ -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 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
+ * <rtems@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 5b25212de9..7e616d69f7 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 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
+ * <rtems@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 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 <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 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