kernel: gather basic thread runtime statistics

This adds the bits to gather the first thread runtime statictic:
thread execution time. It provides a rough idea of how much time
a thread is spent in active execution. Currently it is not being
used, pending following commits where it combines with the trace
points on context switch as they instrument the same locations.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
Daniel Leung 2020-08-27 13:54:14 -07:00 committed by Anas Nashif
commit fc577c4bd1
5 changed files with 137 additions and 1 deletions

View file

@ -280,6 +280,22 @@ struct z_poller {
uint8_t mode;
};
#ifdef CONFIG_THREAD_RUNTIME_STATS
struct k_thread_runtime_stats {
/* Thread execution cycles */
uint64_t execution_cycles;
};
typedef struct k_thread_runtime_stats k_thread_runtime_stats_t;
struct _thread_runtime_stats {
/* Timestamp when last switched in */
uint32_t last_switched_in;
k_thread_runtime_stats_t stats;
};
#endif
/**
* @ingroup thread_apis
* Thread Structure
@ -384,6 +400,11 @@ struct k_thread {
uintptr_t tls;
#endif /* CONFIG_THREAD_LOCAL_STORAGE */
#ifdef CONFIG_THREAD_RUNTIME_STATS
/** Runtime statistics */
struct _thread_runtime_stats rt_stats;
#endif
/** arch-specifics: must always be at the end */
struct _thread_arch arch;
};
@ -5062,6 +5083,28 @@ __syscall void k_str_out(char *c, size_t n);
*/
__syscall int k_float_disable(struct k_thread *thread);
#ifdef CONFIG_THREAD_RUNTIME_STATS
/**
* @brief Get the runtime statistics of a thread
*
* @param thread ID of thread.
* @param stats Pointer to struct to copy statistics into.
* @return -EINVAL if null pointers, otherwise 0
*/
int k_thread_runtime_stats_get(k_tid_t thread,
k_thread_runtime_stats_t *stats);
/**
* @brief Get the runtime statistics of all threads
*
* @param stats Pointer to struct to copy statistics into.
* @return -EINVAL if null pointers, otherwise 0
*/
int k_thread_runtime_stats_all_get(k_thread_runtime_stats_t *stats);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -351,6 +351,19 @@ config THREAD_MAX_NAME_LEN
Thread names get stored in the k_thread struct. Indicate the max
name length, including the terminating NULL byte. Reduce this value
to conserve memory.
config INSTRUMENT_THREAD_SWITCHING
bool
config THREAD_RUNTIME_STATS
bool "Thread runtime statistics"
select INSTRUMENT_THREAD_SWITCHING
help
Gather thread runtime statistics.
For example:
- Thread total execution cycles
endmenu
menu "Work Queue Options"

View file

@ -155,6 +155,18 @@ struct gdb_ctx;
extern int z_gdb_main_loop(struct gdb_ctx *ctx, bool start);
#endif
#ifdef CONFIG_THREAD_RUNTIME_STATS
void z_thread_mark_switched_in(void);
void z_thread_mark_switched_out(void);
#else
static inline void z_thread_mark_switched_in(void)
{
}
static inline void z_thread_mark_switched_out(void)
{
}
#endif
#ifdef __cplusplus
}

View file

@ -113,7 +113,6 @@ static ALWAYS_INLINE unsigned int do_swap(unsigned int key,
arch_switch(new_thread->switch_handle,
&old_thread->switch_handle);
}
if (is_spinlock) {

View file

@ -33,6 +33,10 @@
#include <logging/log.h>
LOG_MODULE_DECLARE(os);
#ifdef CONFIG_THREAD_RUNTIME_STATS
k_thread_runtime_stats_t threads_runtime_stats;
#endif
#ifdef CONFIG_THREAD_MONITOR
/* This lock protects the linked list of active threads; i.e. the
* initial _kernel.threads pointer and the linked list made up of
@ -640,6 +644,10 @@ char *z_setup_new_thread(struct k_thread *new_thread,
new_thread->resource_pool = _current->resource_pool;
sys_trace_thread_create(new_thread);
#ifdef CONFIG_THREAD_RUNTIME_STATS
memset(&new_thread->rt_stats, 0, sizeof(new_thread->rt_stats));
#endif
return stack_ptr;
}
@ -1016,3 +1024,64 @@ static inline k_ticks_t z_vrfy_k_thread_timeout_expires_ticks(
}
#include <syscalls/k_thread_timeout_expires_ticks_mrsh.c>
#endif
#ifdef CONFIG_THREAD_RUNTIME_STATS
void z_thread_mark_switched_in(void)
{
struct k_thread *thread;
thread = k_current_get();
thread->rt_stats.last_switched_in = k_cycle_get_32();
}
void z_thread_mark_switched_out(void)
{
uint32_t now;
uint64_t diff;
struct k_thread *thread;
thread = k_current_get();
if (unlikely(thread->rt_stats.last_switched_in == 0)) {
/* Has not run before */
return;
}
if (unlikely(thread->base.thread_state == _THREAD_DUMMY)) {
/* dummy thread has no stat struct */
return;
}
now = k_cycle_get_32();
diff = (uint64_t)now - thread->rt_stats.last_switched_in;
thread->rt_stats.stats.execution_cycles += diff;
thread->rt_stats.last_switched_in = 0;
threads_runtime_stats.execution_cycles += diff;
}
int k_thread_runtime_stats_get(k_tid_t thread,
k_thread_runtime_stats_t *stats)
{
if ((thread == NULL) || (stats == NULL)) {
return -EINVAL;
}
(void)memcpy(stats, &thread->rt_stats.stats,
sizeof(thread->rt_stats.stats));
return 0;
}
int k_thread_runtime_stats_all_get(k_thread_runtime_stats_t *stats)
{
if (stats == NULL) {
return -EINVAL;
}
(void)memcpy(stats, &threads_runtime_stats,
sizeof(threads_runtime_stats));
return 0;
}
#endif /* CONFIG_THREAD_RUNTIME_STATS */