kernel: extend CPU runtime stats

Extends the CPU usage runtime stats to track current, total, peak
and average usage (as bounded by the scheduling of the idle thread).
This permits a developer to obtain more system information if desired
to tune the system.

Signed-off-by: Peter Mitsis <peter.mitsis@intel.com>
This commit is contained in:
Peter Mitsis 2021-12-14 22:26:22 -05:00 committed by Anas Nashif
commit 4eb1dd02cc
5 changed files with 106 additions and 16 deletions

View file

@ -174,6 +174,7 @@ struct _thread_userspace_local_data {
typedef struct k_thread_runtime_stats { typedef struct k_thread_runtime_stats {
#ifdef CONFIG_SCHED_THREAD_USAGE #ifdef CONFIG_SCHED_THREAD_USAGE
uint64_t execution_cycles; uint64_t execution_cycles;
uint64_t total_cycles; /* total # of non-idle cycles */
/* /*
* In the context of thread statistics, [execution_cycles] is the same * In the context of thread statistics, [execution_cycles] is the same
* as the total # of non-idle cycles. In the context of CPU statistics, * as the total # of non-idle cycles. In the context of CPU statistics,
@ -182,11 +183,19 @@ typedef struct k_thread_runtime_stats {
#endif #endif
#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
/*
* For threads, the following fields refer to the time spent executing
* as bounded by when the thread was scheduled in and scheduled out.
* For CPUs, the same fields refer to the time spent executing
* non-idle threads as bounded by the idle thread(s).
*/
uint64_t current_cycles; /* current # of non-idle cycles */ uint64_t current_cycles; /* current # of non-idle cycles */
uint64_t peak_cycles; /* peak # of non-idle cycles */ uint64_t peak_cycles; /* peak # of non-idle cycles */
uint64_t total_cycles; /* total # of non-idle cycles */
uint64_t average_cycles; /* average # of non-idle cycles */ uint64_t average_cycles; /* average # of non-idle cycles */
#endif
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
/* /*
* This field is always zero for individual threads. It only comes * This field is always zero for individual threads. It only comes
* into play when gathering statistics for the CPU. In that case it * into play when gathering statistics for the CPU. In that case it

View file

@ -28,6 +28,7 @@
#include <sys/util.h> #include <sys/util.h>
#include <sys/sys_heap.h> #include <sys/sys_heap.h>
#include <arch/structs.h> #include <arch/structs.h>
#include <kernel/stats.h>
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
@ -131,7 +132,17 @@ struct _cpu {
#endif #endif
#ifdef CONFIG_SCHED_THREAD_USAGE #ifdef CONFIG_SCHED_THREAD_USAGE
/*
* [usage0] is used as a timestamp to mark the beginning of an
* execution window. [0] is a special value indicating that it
* has been stopped (but not disabled).
*/
uint32_t usage0; uint32_t usage0;
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
struct k_cycle_stats usage;
#endif
#endif #endif
/* Per CPU architecture specifics */ /* Per CPU architecture specifics */
@ -172,11 +183,6 @@ struct z_kernel {
#if defined(CONFIG_THREAD_MONITOR) #if defined(CONFIG_THREAD_MONITOR)
struct k_thread *threads; /* singly linked list of ALL threads */ struct k_thread *threads; /* singly linked list of ALL threads */
#endif #endif
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
uint64_t all_thread_usage;
uint64_t idle_thread_usage;
#endif
}; };
typedef struct z_kernel _kernel_t; typedef struct z_kernel _kernel_t;

View file

@ -385,6 +385,11 @@ void z_sched_usage_stop(void);
void z_sched_usage_start(struct k_thread *thread); void z_sched_usage_start(struct k_thread *thread);
/**
* @brief Retrieves CPU cycle usage data for specified core
*/
void z_sched_cpu_usage(uint8_t core_id, struct k_thread_runtime_stats *stats);
/** /**
* @brief Retrieves thread cycle usage data for specified thread * @brief Retrieves thread cycle usage data for specified thread
*/ */

View file

@ -1037,6 +1037,10 @@ int k_thread_runtime_stats_get(k_tid_t thread,
int k_thread_runtime_stats_all_get(k_thread_runtime_stats_t *stats) int k_thread_runtime_stats_all_get(k_thread_runtime_stats_t *stats)
{ {
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
k_thread_runtime_stats_t tmp_stats;
#endif
if (stats == NULL) { if (stats == NULL) {
return -EINVAL; return -EINVAL;
} }
@ -1044,8 +1048,19 @@ int k_thread_runtime_stats_all_get(k_thread_runtime_stats_t *stats)
*stats = (k_thread_runtime_stats_t) {}; *stats = (k_thread_runtime_stats_t) {};
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL #ifdef CONFIG_SCHED_THREAD_USAGE_ALL
stats->execution_cycles = (_kernel.all_thread_usage /* Retrieve the usage stats for each core and amalgamate them. */
+ _kernel.idle_thread_usage);
for (uint8_t i = 0; i < CONFIG_MP_NUM_CPUS; i++) {
z_sched_cpu_usage(i, &tmp_stats);
stats->execution_cycles += tmp_stats.execution_cycles;
stats->total_cycles += tmp_stats.total_cycles;
#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
stats->peak_cycles += tmp_stats.peak_cycles;
stats->average_cycles += tmp_stats.average_cycles;
#endif
stats->idle_cycles += tmp_stats.idle_cycles;
}
#endif #endif
return 0; return 0;

View file

@ -34,13 +34,20 @@ static uint32_t usage_now(void)
/** /**
* Update the usage statistics for the specified CPU and thread * Update the usage statistics for the specified CPU and thread
*/ */
static void sched_update_usage(struct k_thread *thread, uint32_t cycles) static void sched_update_usage(struct _cpu *cpu, struct k_thread *thread,
uint32_t cycles)
{ {
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL #ifdef CONFIG_SCHED_THREAD_USAGE_ALL
if (z_is_idle_thread_object(thread)) { if (!z_is_idle_thread_object(thread)) {
_kernel.idle_thread_usage += cycles; #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
} else { cpu->usage.current += cycles;
_kernel.all_thread_usage += cycles;
if (cpu->usage.longest < cpu->usage.current) {
cpu->usage.longest = cpu->usage.current;
}
#endif
cpu->usage.total += cycles;
} }
#endif #endif
@ -87,13 +94,59 @@ void z_sched_usage_stop(void)
if (u0 != 0) { if (u0 != 0) {
uint32_t dt = usage_now() - u0; uint32_t dt = usage_now() - u0;
sched_update_usage(cpu->current, dt); sched_update_usage(cpu, cpu->current, dt);
} }
cpu->usage0 = 0; cpu->usage0 = 0;
k_spin_unlock(&usage_lock, k); k_spin_unlock(&usage_lock, k);
} }
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
void z_sched_cpu_usage(uint8_t core_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 ((u0 != 0) && (&_kernel.cpus[core_id] == cpu)) {
uint32_t dt = now - u0;
/* It is safe to update the CPU's usage stats */
sched_update_usage(cpu, cpu->current, dt);
cpu->usage0 = now;
}
stats->total_cycles = cpu->usage.total;
#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
stats->current_cycles = cpu->usage.current;
stats->peak_cycles = cpu->usage.longest;
if (cpu->usage.num_windows == 0) {
stats->average_cycles = 0;
} else {
stats->average_cycles = stats->total_cycles /
cpu->usage.num_windows;
}
#endif
stats->idle_cycles =
_kernel.cpus[core_id].idle_thread->base.usage.total;
stats->execution_cycles = stats->total_cycles + stats->idle_cycles;
k_spin_unlock(&usage_lock, key);
}
#endif
void z_sched_thread_usage(struct k_thread *thread, void z_sched_thread_usage(struct k_thread *thread,
struct k_thread_runtime_stats *stats) struct k_thread_runtime_stats *stats)
{ {
@ -116,19 +169,19 @@ void z_sched_thread_usage(struct k_thread *thread,
* running on the current core. * running on the current core.
*/ */
sched_update_usage(thread, dt); sched_update_usage(cpu, thread, dt);
cpu->usage0 = now; cpu->usage0 = now;
} }
stats->execution_cycles = thread->base.usage.total; stats->execution_cycles = thread->base.usage.total;
stats->total_cycles = thread->base.usage.total;
/* Copy-out the thread's usage stats */ /* Copy-out the thread's usage stats */
#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
stats->current_cycles = thread->base.usage.current; stats->current_cycles = thread->base.usage.current;
stats->peak_cycles = thread->base.usage.longest; stats->peak_cycles = thread->base.usage.longest;
stats->total_cycles = thread->base.usage.total;
if (thread->base.usage.num_windows == 0) { if (thread->base.usage.num_windows == 0) {
stats->average_cycles = 0; stats->average_cycles = 0;
@ -136,7 +189,9 @@ void z_sched_thread_usage(struct k_thread *thread,
stats->average_cycles = stats->total_cycles / stats->average_cycles = stats->total_cycles /
thread->base.usage.num_windows; thread->base.usage.num_windows;
} }
#endif
#ifdef CONFIG_SCHED_THREAD_USAGE_ALL
stats->idle_cycles = 0; stats->idle_cycles = 0;
#endif #endif
stats->execution_cycles = thread->base.usage.total; stats->execution_cycles = thread->base.usage.total;