kernel/sched: enable/disable runtime stats
Adds support to enable/disable both thread and cpu runtime
stats.
Signed-off-by: Peter Mitsis <peter.mitsis@intel.com>
diff --git a/include/kernel.h b/include/kernel.h
index 5941954..63938ef 100644
--- a/include/kernel.h
+++ b/include/kernel.h
@@ -5903,6 +5903,50 @@
*/
int k_thread_runtime_stats_all_get(k_thread_runtime_stats_t *stats);
+/**
+ * @brief Enable gathering of runtime statistics for specified thread
+ *
+ * This routine enables the gathering of runtime statistics for the specified
+ * thread.
+ *
+ * @param thread ID of thread
+ * @return -EINVAL if invalid thread ID, otherwise 0
+ */
+extern int k_thread_runtime_stats_enable(k_tid_t thread);
+
+/**
+ * @brief Disable gathering of runtime statistics for specified thread
+ *
+ * This routine disables the gathering of runtime statistics for the specified
+ * thread.
+ *
+ * @param thread ID of thread
+ * @return -EINVAL if invalid thread ID, otherwise 0
+ */
+extern int k_thread_runtime_stats_disable(k_tid_t thread);
+
+/**
+ * @brief Enable gathering of system runtime statistics
+ *
+ * This routine enables the gathering of system runtime statistics. Note that
+ * it does not affect the gathering of similar statistics for individual
+ * threads.
+ *
+ * @return N/A
+ */
+extern void k_sys_runtime_stats_enable(void);
+
+/**
+ * @brief Disable gathering of system runtime statistics
+ *
+ * This routine disables the gathering of system runtime statistics. Note that
+ * it does not affect the gathering of similar statistics for individual
+ * threads.
+ *
+ * @return N/A
+ */
+extern void k_sys_runtime_stats_disable(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/kernel/stats.h b/include/kernel/stats.h
index 4e11457..e5d4083 100644
--- a/include/kernel/stats.h
+++ b/include/kernel/stats.h
@@ -21,6 +21,7 @@
uint64_t longest; /* # of cycles in longest usage window */
uint32_t num_windows; /* # of usage windows */
#endif
+ bool track_usage; /* true if gathering usage stats */
};
#endif
diff --git a/kernel/Kconfig b/kernel/Kconfig
index 7ce118f..9af3df1 100644
--- a/kernel/Kconfig
+++ b/kernel/Kconfig
@@ -406,6 +406,14 @@
help
Maintain a sum of all non-idle thread cycle usage.
+config SCHED_THREAD_USAGE_AUTO_ENABLE
+ bool "Automatically enable runtime usage statistics"
+ default y
+ depends on SCHED_THREAD_USAGE
+ help
+ When set, this option automatically enables the gathering of both
+ the thread and CPU usage statistics.
+
endif # THREAD_RUNTIME_STATS
endmenu
diff --git a/kernel/init.c b/kernel/init.c
index 0a4e4ba..8c8031d 100644
--- a/kernel/init.c
+++ b/kernel/init.c
@@ -303,6 +303,10 @@
_kernel.cpus[i].irq_stack =
(Z_KERNEL_STACK_BUFFER(z_interrupt_stacks[i]) +
K_KERNEL_STACK_SIZEOF(z_interrupt_stacks[i]));
+#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
+ _kernel.cpus[i].usage.track_usage =
+ CONFIG_SCHED_THREAD_USAGE_AUTO_ENABLE;
+#endif
}
return stack_ptr;
diff --git a/kernel/thread.c b/kernel/thread.c
index 280c532..e6ef522 100644
--- a/kernel/thread.c
+++ b/kernel/thread.c
@@ -601,6 +601,12 @@
#endif
new_thread->resource_pool = _current->resource_pool;
+#ifdef CONFIG_SCHED_THREAD_USAGE
+ new_thread->base.usage = (struct k_cycle_stats) {};
+ new_thread->base.usage.track_usage =
+ CONFIG_SCHED_THREAD_USAGE_AUTO_ENABLE;
+#endif
+
SYS_PORT_TRACING_OBJ_FUNC(k_thread, create, new_thread);
return stack_ptr;
diff --git a/kernel/usage.c b/kernel/usage.c
index ee0ad86..c5da1de 100644
--- a/kernel/usage.c
+++ b/kernel/usage.c
@@ -9,6 +9,7 @@
#include <timing/timing.h>
#include <ksched.h>
#include <spinlock.h>
+#include <sys/check.h>
/* Need one of these for this to work */
#if !defined(CONFIG_USE_SWITCH) && !defined(CONFIG_INSTRUMENT_THREAD_SWITCHING)
@@ -31,26 +32,29 @@
return (now == 0) ? 1 : now;
}
-/**
- * Update the usage statistics for the specified CPU and thread
- */
-static void sched_update_usage(struct _cpu *cpu, struct k_thread *thread,
- uint32_t cycles)
-{
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
- if (!z_is_idle_thread_object(thread)) {
+static void sched_cpu_update_usage(struct _cpu *cpu, uint32_t cycles)
+{
+ if (!cpu->usage.track_usage) {
+ return;
+ }
+
#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
- cpu->usage.current += cycles;
+ cpu->usage.current += cycles;
- if (cpu->usage.longest < cpu->usage.current) {
- cpu->usage.longest = cpu->usage.current;
- }
-#endif
-
- cpu->usage.total += cycles;
+ if (cpu->usage.longest < cpu->usage.current) {
+ cpu->usage.longest = cpu->usage.current;
}
#endif
+ cpu->usage.total += cycles;
+}
+#else
+#define sched_cpu_update_usage(cpu, cycles) do { } while (0)
+#endif
+
+static void sched_thread_update_usage(struct k_thread *thread, uint32_t cycles)
+{
thread->base.usage.total += cycles;
#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
@@ -69,10 +73,12 @@
key = k_spin_lock(&usage_lock);
- _current_cpu->usage0 = usage_now();
+ _current_cpu->usage0 = usage_now(); /* Always update */
- thread->base.usage.num_windows++;
- thread->base.usage.current = 0;
+ if (thread->base.usage.track_usage) {
+ thread->base.usage.num_windows++;
+ thread->base.usage.current = 0;
+ }
k_spin_unlock(&usage_lock, key);
#else
@@ -92,9 +98,13 @@
uint32_t u0 = cpu->usage0;
if (u0 != 0) {
- uint32_t dt = usage_now() - u0;
+ uint32_t cycles = usage_now() - u0;
- sched_update_usage(cpu, cpu->current, dt);
+ if (cpu->current->base.usage.track_usage) {
+ sched_thread_update_usage(cpu->current, cycles);
+ }
+
+ sched_cpu_update_usage(cpu, cycles);
}
cpu->usage0 = 0;
@@ -102,25 +112,30 @@
}
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
-void z_sched_cpu_usage(uint8_t core_id, struct k_thread_runtime_stats *stats)
+void z_sched_cpu_usage(uint8_t cpu_id, struct k_thread_runtime_stats *stats)
{
k_spinlock_key_t key;
struct _cpu *cpu;
- uint32_t now;
- uint32_t u0;
cpu = _current_cpu;
key = k_spin_lock(&usage_lock);
- u0 = cpu->usage0;
- now = usage_now();
+ if (&_kernel.cpus[cpu_id] == cpu) {
+ uint32_t now = usage_now();
+ uint32_t cycles = now - cpu->usage0;
- if ((u0 != 0) && (&_kernel.cpus[core_id] == cpu)) {
- uint32_t dt = now - u0;
+ /*
+ * Getting stats for the current CPU. Update both its
+ * current thread stats and the CPU stats as the CPU's
+ * [usage0] field will also get updated. This keeps all
+ * that information up-to-date.
+ */
- /* It is safe to update the CPU's usage stats */
+ if (cpu->current->base.usage.track_usage) {
+ sched_thread_update_usage(cpu->current, cycles);
+ }
- sched_update_usage(cpu, cpu->current, dt);
+ sched_cpu_update_usage(cpu, cycles);
cpu->usage0 = now;
}
@@ -139,7 +154,7 @@
#endif
stats->idle_cycles =
- _kernel.cpus[core_id].idle_thread->base.usage.total;
+ _kernel.cpus[cpu_id].idle_thread->base.usage.total;
stats->execution_cycles = stats->total_cycles + stats->idle_cycles;
@@ -150,26 +165,28 @@
void z_sched_thread_usage(struct k_thread *thread,
struct k_thread_runtime_stats *stats)
{
- uint32_t u0;
- uint32_t now;
struct _cpu *cpu;
k_spinlock_key_t key;
cpu = _current_cpu;
key = k_spin_lock(&usage_lock);
- u0 = cpu->usage0;
- now = usage_now();
-
- if ((u0 != 0) && (thread == cpu->current)) {
- uint32_t dt = now - u0;
+ if (thread == cpu->current) {
+ uint32_t now = usage_now();
+ uint32_t cycles = now - cpu->usage0;
/*
- * Update the thread's usage stats if it is the current thread
- * running on the current core.
+ * Getting stats for the current thread. Update both the
+ * current thread stats and its CPU stats as the CPU's
+ * [usage0] field will also get updated. This keeps all
+ * that information up-to-date.
*/
- sched_update_usage(cpu, thread, dt);
+ if (thread->base.usage.track_usage) {
+ sched_thread_update_usage(thread, cycles);
+ }
+
+ sched_cpu_update_usage(cpu, cycles);
cpu->usage0 = now;
}
@@ -198,3 +215,117 @@
k_spin_unlock(&usage_lock, key);
}
+
+#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
+int k_thread_runtime_stats_enable(k_tid_t thread)
+{
+ k_spinlock_key_t key;
+
+ CHECKIF(thread == NULL) {
+ return -EINVAL;
+ }
+
+ key = k_spin_lock(&usage_lock);
+
+ if (!thread->base.usage.track_usage) {
+ thread->base.usage.track_usage = true;
+ thread->base.usage.num_windows++;
+ thread->base.usage.current = 0;
+ }
+
+ k_spin_unlock(&usage_lock, key);
+
+ return 0;
+}
+
+int k_thread_runtime_stats_disable(k_tid_t thread)
+{
+ struct _cpu *cpu = _current_cpu;
+ k_spinlock_key_t key;
+
+ CHECKIF(thread == NULL) {
+ return -EINVAL;
+ }
+
+ key = k_spin_lock(&usage_lock);
+ if (thread->base.usage.track_usage) {
+ thread->base.usage.track_usage = false;
+
+ if (thread == cpu->current) {
+ uint32_t cycles = usage_now() - cpu->usage0;
+
+ sched_thread_update_usage(thread, cycles);
+ sched_cpu_update_usage(cpu, cycles);
+ }
+ }
+
+ k_spin_unlock(&usage_lock, key);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
+void k_sys_runtime_stats_enable(void)
+{
+ k_spinlock_key_t key;
+
+ key = k_spin_lock(&usage_lock);
+
+ if (_current_cpu->usage.track_usage) {
+
+ /*
+ * Usage tracking is already enabled on the current CPU
+ * and thus on all other CPUs (if applicable). There is
+ * nothing left to do.
+ */
+
+ k_spin_unlock(&usage_lock, key);
+ return;
+ }
+
+ /* Enable gathering of runtime stats on each CPU */
+
+ for (uint8_t i = 0; i < CONFIG_MP_NUM_CPUS; i++) {
+ _kernel.cpus[i].usage.track_usage = true;
+#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
+ _kernel.cpus[i].usage.num_windows++;
+ _kernel.cpus[i].usage.current = 0;
+#endif
+ }
+
+ k_spin_unlock(&usage_lock, key);
+}
+
+void k_sys_runtime_stats_disable(void)
+{
+ struct _cpu *cpu;
+ k_spinlock_key_t key;
+
+ key = k_spin_lock(&usage_lock);
+
+ if (!_current_cpu->usage.track_usage) {
+
+ /*
+ * Usage tracking is already disabled on the current CPU
+ * and thus on all other CPUs (if applicable). There is
+ * nothing left to do.
+ */
+
+ k_spin_unlock(&usage_lock, key);
+ return;
+ }
+
+ uint32_t now = usage_now();
+
+ for (uint8_t i = 0; i < CONFIG_MP_NUM_CPUS; i++) {
+ cpu = &_kernel.cpus[i];
+ if (cpu->usage0 != 0) {
+ sched_cpu_update_usage(cpu, now - cpu->usage0);
+ }
+ cpu->usage.track_usage = false;
+ }
+
+ k_spin_unlock(&usage_lock, key);
+}
+#endif