| /* |
| * Copyright (c) 2015 Intel Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * @file |
| * @brief Kernel event logger support. |
| */ |
| |
| |
| #include <misc/kernel_event_logger.h> |
| #include <misc/util.h> |
| #include <init.h> |
| #include <nano_private.h> |
| #include <kernel_event_logger_arch.h> |
| |
| uint32_t _sys_k_event_logger_buffer[CONFIG_KERNEL_EVENT_LOGGER_BUFFER_SIZE]; |
| |
| #ifdef CONFIG_KERNEL_EVENT_LOGGER_CONTEXT_SWITCH |
| void *_collector_fiber; |
| #endif |
| |
| #ifdef CONFIG_KERNEL_EVENT_LOGGER_SLEEP |
| uint32_t _sys_k_event_logger_sleep_start_time; |
| #endif |
| |
| |
| /** |
| * @brief Initialize the kernel event logger system. |
| * |
| * @details Initialize the ring buffer and the sync semaphore. |
| * |
| * @return No return value. |
| */ |
| static int _sys_k_event_logger_init(struct device *arg) |
| { |
| ARG_UNUSED(arg); |
| |
| sys_event_logger_init(&sys_k_event_logger, _sys_k_event_logger_buffer, |
| CONFIG_KERNEL_EVENT_LOGGER_BUFFER_SIZE); |
| |
| return 0; |
| } |
| DECLARE_DEVICE_INIT_CONFIG(kernel_event_logger_0, "", |
| _sys_k_event_logger_init, NULL); |
| nano_early_init(kernel_event_logger_0, NULL); |
| |
| |
| void sys_k_event_logger_put_timed(uint16_t event_id) |
| { |
| uint32_t data[1]; |
| |
| data[0] = nano_tick_get_32(); |
| |
| sys_event_logger_put(&sys_k_event_logger, event_id, data, |
| ARRAY_SIZE(data)); |
| } |
| |
| #ifdef CONFIG_KERNEL_EVENT_LOGGER_CONTEXT_SWITCH |
| void _sys_k_event_logger_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 kernel event logger has not been initialized, we do nothing */ |
| if (sys_k_event_logger.ring_buf.buf == NULL) { |
| return; |
| } |
| |
| if (_collector_fiber != _nanokernel.current) { |
| data[0] = nano_tick_get_32(); |
| data[1] = (uint32_t)_nanokernel.current; |
| |
| /* |
| * The mechanism we use to log the kernel 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. When we |
| * signal a semaphore from a task 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 thread. 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_k_event_logger, |
| KERNEL_EVENT_LOGGER_CONTEXT_SWITCH_EVENT_ID, data, |
| ARRAY_SIZE(data)); |
| } |
| } |
| |
| void sys_k_event_logger_register_as_collector(void) |
| { |
| _collector_fiber = _nanokernel.current; |
| } |
| #endif /* CONFIG_KERNEL_EVENT_LOGGER_CONTEXT_SWITCH */ |
| |
| |
| #ifdef CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT |
| void _sys_k_event_logger_interrupt(void) |
| { |
| uint32_t data[2]; |
| |
| data[0] = nano_tick_get_32(); |
| data[1] = _sys_current_irq_key_get(); |
| |
| sys_k_event_logger_put(KERNEL_EVENT_LOGGER_INTERRUPT_EVENT_ID, data, |
| ARRAY_SIZE(data)); |
| } |
| #endif /* CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT */ |
| |
| |
| #ifdef CONFIG_KERNEL_EVENT_LOGGER_SLEEP |
| void _sys_k_event_logger_enter_sleep(void) |
| { |
| _sys_k_event_logger_sleep_start_time = nano_cycle_get_32(); |
| } |
| |
| void _sys_k_event_logger_exit_sleep(void) |
| { |
| uint32_t data[3]; |
| |
| data[0] = nano_tick_get_32(); |
| data[1] = (nano_cycle_get_32() - _sys_k_event_logger_sleep_start_time) |
| / sys_clock_hw_cycles_per_tick; |
| /* register the cause of exiting sleep mode */ |
| data[2] = _sys_current_irq_key_get(); |
| |
| sys_k_event_logger_put(KERNEL_EVENT_LOGGER_SLEEP_EVENT_ID, data, |
| ARRAY_SIZE(data)); |
| } |
| #endif /* CONFIG_KERNEL_EVENT_LOGGER_SLEEP */ |