diff --git a/arch/arc/defconfig b/arch/arc/defconfig index fb33f26163d..cdbd2b4c3d7 100644 --- a/arch/arc/defconfig +++ b/arch/arc/defconfig @@ -16,6 +16,7 @@ CONFIG_XIP=y # CONFIG_EVENT_LOGGER is not set # CONFIG_KERNEL_PROFILER is not set # CONFIG_PROFILER_BUFFER_SIZE is not set +# CONFIG_PROFILER_CONTEXT_SWITCH is not set # # Nanokernel Options diff --git a/arch/arm/core/swap.S b/arch/arm/core/swap.S index 0e296c5396b..888a0bff2ff 100644 --- a/arch/arm/core/swap.S +++ b/arch/arm/core/swap.S @@ -73,6 +73,13 @@ SECTION_FUNC(TEXT, __pendsv) _GDB_STUB_EXC_ENTRY +#ifdef CONFIG_PROFILER_CONTEXT_SWITCH + /* Register the context switch */ + push {lr} + bl _sys_profiler_context_switch + pop {lr} +#endif + /* load _Nanokernel into r1 and current tCCS into r2 */ ldr r1, =_nanokernel ldr r2, [r1, #__tNANO_current_OFFSET] diff --git a/arch/arm/defconfig b/arch/arm/defconfig index 47d6bafd903..1ce3b61fdac 100644 --- a/arch/arm/defconfig +++ b/arch/arm/defconfig @@ -17,6 +17,7 @@ CONFIG_XIP=y # CONFIG_EVENT_LOGGER is not set # CONFIG_KERNEL_PROFILER is not set # CONFIG_PROFILER_BUFFER_SIZE is not set +# CONFIG_PROFILER_CONTEXT_SWITCH is not set # # Nanokernel Options diff --git a/arch/x86/core/swap.S b/arch/x86/core/swap.S index 3a05c4e4d6f..63d2d9d0cbe 100644 --- a/arch/x86/core/swap.S +++ b/arch/x86/core/swap.S @@ -136,6 +136,14 @@ SECTION_FUNC(TEXT, _Swap) movl __tNANO_current_OFFSET (%eax), %ecx movl %esp, __tCCS_coopReg_OFFSET + __tCoopReg_esp_OFFSET (%ecx) +#ifdef CONFIG_PROFILER_CONTEXT_SWITCH + /* save %eax since it used as the return value for _Swap */ + pushl %eax + /* Register the context switch */ + call _sys_profiler_context_switch + /* restore _Swap's %eax */ + popl %eax +#endif /* * Determine what FIBER or TASK context needs to be swapped in. diff --git a/arch/x86/defconfig b/arch/x86/defconfig index e36f338aa86..fb8f78a1d65 100644 --- a/arch/x86/defconfig +++ b/arch/x86/defconfig @@ -19,6 +19,7 @@ CONFIG_ENHANCED_SECURITY=y # CONFIG_EVENT_LOGGER is not set # CONFIG_KERNEL_PROFILER is not set # CONFIG_PROFILER_BUFFER_SIZE is not set +# CONFIG_PROFILER_CONTEXT_SWITCH is not set # # Security Options diff --git a/include/misc/profiler.h b/include/misc/profiler.h index bf70db1c9ea..5e9762a4e24 100644 --- a/include/misc/profiler.h +++ b/include/misc/profiler.h @@ -41,6 +41,10 @@ #ifdef CONFIG_KERNEL_PROFILER +#ifdef CONFIG_PROFILER_CONTEXT_SWITCH +#define PROFILER_CONTEXT_SWITCH_EVENT_ID 0x0001 +#endif /* CONFIG_PROFILER_CONTEXT_SWITCH */ + /** * Global variable of the ring buffer that allows user to implement * their own reading routine. @@ -142,6 +146,23 @@ void sys_profiler_put_timed(uint16_t event_id); buffer_size, timeout) #endif /* CONFIG_NANO_TIMEOUTS */ + +#ifdef CONFIG_PROFILER_CONTEXT_SWITCH + +/** + * @brief Register the fiber that calls the function as collector + * + * @details Initialize internal profiling data. This avoid registering the + * context switch of the collector fiber when CONFIG_PROFILE_CONTEXT_SWITCH + * is enable. + * + * @return No return value. + */ +void sys_profiler_register_as_collector(void); +#else /* !CONFIG_PROFILER_CONTEXT_SWITCH */ +static inline void sys_profiler_register_as_collector(void) {}; +#endif /* CONFIG_PROFILER_CONTEXT_SWITCH */ + #else /* !CONFIG_KERNEL_PROFILER */ static inline void sys_profiler_put(uint16_t event_id, uint32_t *event_data, diff --git a/kernel/Kconfig b/kernel/Kconfig index 4d1c93becca..6efda6d92c2 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -130,6 +130,18 @@ config PROFILER_BUFFER_SIZE help Buffer size in 32-bit words. +menu "Profiler points" +depends on KERNEL_PROFILER + +config PROFILER_CONTEXT_SWITCH + bool + prompt "Context switch profiler point" + default n + depends on KERNEL_PROFILER + help + Enable the context switch event messages. +endmenu + menu "Security Options" depends on ENHANCED_SECURITY diff --git a/kernel/nanokernel/profiler.c b/kernel/nanokernel/profiler.c index 40cba70d557..5ab08fbb565 100644 --- a/kernel/nanokernel/profiler.c +++ b/kernel/nanokernel/profiler.c @@ -37,9 +37,14 @@ #include #include #include +#include uint32_t _sys_profiler_buffer[CONFIG_PROFILER_BUFFER_SIZE]; +#ifdef CONFIG_PROFILER_CONTEXT_SWITCH +void *_collector_context=NULL; +#endif + /** * @brief Initialize the profiler system. * @@ -69,3 +74,49 @@ void sys_profiler_put_timed(uint16_t event_id) sys_event_logger_put(&sys_profiler_logger, event_id, data, ARRAY_SIZE(data)); } + +#ifdef CONFIG_PROFILER_CONTEXT_SWITCH +void _sys_profiler_context_switch(void) +{ + extern tNANO _nanokernel; + uint32_t data[2]; + extern void _sys_event_logger_put_non_preemptible( + struct event_logger *logger, uint16_t event_id, uint32_t *event_data, + uint8_t data_size); + + /* if the profiler has not been initialized, we do nothing */ + if (sys_profiler_logger.buffer == NULL) { + return; + } + + if (_collector_context != _nanokernel.current) { + data[0] = nano_tick_get_32(); + data[1] = (uint32_t)_nanokernel.current; + + /* + * The mechanism we use to log the profile events uses a sync semaphore + * to inform that there are available events to be collected. The + * context switch event can be triggered from a task context. When we + * signal a semaphore from a task context and a fiber is waiting for + * that semaphore, a context switch is generated immediately. Due to + * the fact that we register the context switch event while the context + * switch is being processed, a new context switch can be generated + * before the kernel finishes processing the current context switch. We + * need to prevent this because the kernel is not able to handle it. + * The _sem_give_non_preemptible function does not trigger a context + * switch when we signal the semaphore from any type of context. Using + * _sys_event_logger_put_non_preemptible function, that internally uses + * _sem_give_non_preemptible function for signaling the sync semaphore, + * allow us registering the context switch event without triggering any + * new context switch during the process. + */ + _sys_event_logger_put_non_preemptible(&sys_profiler_logger, + PROFILER_CONTEXT_SWITCH_EVENT_ID, data, ARRAY_SIZE(data)); + } +} + +void sys_profiler_register_as_collector(void) +{ + _collector_context = _nanokernel.current; +} +#endif /* CONFIG_PROFILER_CONTEXT_SWITCH */