| /* |
| * Copyright (c) 2018 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <logging/log_msg.h> |
| #include "log_list.h" |
| #include <logging/log.h> |
| #include <logging/log_backend.h> |
| #include <logging/log_ctrl.h> |
| #include <logging/log_output.h> |
| #include <logging/log_backend_uart.h> |
| #include <misc/printk.h> |
| #include <assert.h> |
| #include <atomic.h> |
| |
| #ifndef CONFIG_LOG_PRINTK_MAX_STRING_LENGTH |
| #define CONFIG_LOG_PRINTK_MAX_STRING_LENGTH 1 |
| #endif |
| |
| #ifdef CONFIG_LOG_BACKEND_UART |
| LOG_BACKEND_UART_DEFINE(log_backend_uart); |
| const struct log_backend *uart_backend = &log_backend_uart; |
| #else |
| const struct log_backend *uart_backend; |
| #endif |
| |
| static struct log_list_t list; |
| static atomic_t initialized; |
| static bool panic_mode; |
| static atomic_t buffered_cnt; |
| static k_tid_t proc_tid; |
| |
| static u32_t dummy_timestamp(void); |
| static timestamp_get_t timestamp_func = dummy_timestamp; |
| |
| static u32_t dummy_timestamp(void) |
| { |
| return 0; |
| } |
| |
| static inline void msg_finalize(struct log_msg *msg, |
| struct log_msg_ids src_level) |
| { |
| unsigned int key; |
| |
| msg->hdr.ids = src_level; |
| msg->hdr.timestamp = timestamp_func(); |
| |
| atomic_inc(&buffered_cnt); |
| |
| key = irq_lock(); |
| |
| log_list_add_tail(&list, msg); |
| |
| irq_unlock(key); |
| |
| if (IS_ENABLED(CONFIG_LOG_INPLACE_PROCESS) || panic_mode) { |
| (void)log_process(false); |
| } else if (!IS_ENABLED(CONFIG_LOG_INPLACE_PROCESS) && |
| CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD) { |
| if (buffered_cnt == CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD && |
| proc_tid) { |
| k_wakeup(proc_tid); |
| } |
| } |
| } |
| |
| void log_0(const char *str, struct log_msg_ids src_level) |
| { |
| struct log_msg *msg = log_msg_create_0(str); |
| |
| if (msg == NULL) { |
| return; |
| } |
| msg_finalize(msg, src_level); |
| } |
| |
| void log_1(const char *str, |
| u32_t arg0, |
| struct log_msg_ids src_level) |
| { |
| struct log_msg *msg = log_msg_create_1(str, arg0); |
| |
| if (msg == NULL) { |
| return; |
| } |
| msg_finalize(msg, src_level); |
| } |
| |
| void log_2(const char *str, |
| u32_t arg0, |
| u32_t arg1, |
| struct log_msg_ids src_level) |
| { |
| struct log_msg *msg = log_msg_create_2(str, arg0, arg1); |
| |
| if (msg == NULL) { |
| return; |
| } |
| |
| msg_finalize(msg, src_level); |
| } |
| |
| void log_3(const char *str, |
| u32_t arg0, |
| u32_t arg1, |
| u32_t arg2, |
| struct log_msg_ids src_level) |
| { |
| struct log_msg *msg = log_msg_create_3(str, arg0, arg1, arg2); |
| |
| if (msg == NULL) { |
| return; |
| } |
| |
| msg_finalize(msg, src_level); |
| } |
| |
| void log_n(const char *str, |
| u32_t *args, |
| u32_t narg, |
| struct log_msg_ids src_level) |
| { |
| struct log_msg *msg = log_msg_create_n(str, args, narg); |
| |
| if (msg == NULL) { |
| return; |
| } |
| |
| msg_finalize(msg, src_level); |
| } |
| |
| void log_hexdump(const char *str, |
| const u8_t *data, |
| u32_t length, |
| struct log_msg_ids src_level) |
| { |
| struct log_msg *msg = log_msg_hexdump_create(str, data, length); |
| |
| if (msg == NULL) { |
| return; |
| } |
| |
| msg_finalize(msg, src_level); |
| } |
| |
| int log_printk(const char *fmt, va_list ap) |
| { |
| if (IS_ENABLED(CONFIG_LOG_PRINTK)) { |
| u8_t formatted_str[CONFIG_LOG_PRINTK_MAX_STRING_LENGTH]; |
| struct log_msg_ids empty_id = { 0 }; |
| struct log_msg *msg; |
| int length; |
| |
| length = vsnprintk(formatted_str, |
| sizeof(formatted_str), fmt, ap); |
| |
| length = (length > sizeof(formatted_str)) ? |
| sizeof(formatted_str) : length; |
| |
| msg = log_msg_hexdump_create(NULL, formatted_str, length); |
| if (!msg) { |
| return 0; |
| } |
| |
| msg->hdr.params.hexdump.raw_string = 1; |
| msg_finalize(msg, empty_id); |
| |
| return length; |
| } else { |
| return 0; |
| } |
| } |
| |
| void log_generic(struct log_msg_ids src_level, const char *fmt, va_list ap) |
| { |
| u32_t args[LOG_MAX_NARGS]; |
| |
| for (int i = 0; i < LOG_MAX_NARGS; i++) { |
| args[i] = va_arg(ap, u32_t); |
| } |
| |
| /* Assume maximum amount of parameters. Determining exact number would |
| * require string analysis. |
| */ |
| log_n(fmt, args, LOG_MAX_NARGS, src_level); |
| } |
| |
| static u32_t timestamp_get(void) |
| { |
| return k_cycle_get_32(); |
| } |
| |
| void log_core_init(void) |
| { |
| log_msg_pool_init(); |
| log_list_init(&list); |
| |
| /* |
| * Initialize aggregated runtime filter levels (no backends are |
| * attached yet, so leave backend slots in each dynamic filter set |
| * alone for now). |
| * |
| * Each log source's aggregated runtime level is set to match its |
| * compile-time level. When backends are attached later on in |
| * log_init(), they'll be initialized to the same value. |
| */ |
| if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) { |
| for (int i = 0; i < log_sources_count(); i++) { |
| u32_t *filters = log_dynamic_filters_get(i); |
| u8_t level = log_compiled_level_get(i); |
| |
| LOG_FILTER_SLOT_SET(filters, |
| LOG_FILTER_AGGR_SLOT_IDX, |
| level); |
| } |
| } |
| } |
| |
| /* |
| * Initialize a backend's runtime filters to match the compile-time |
| * settings. |
| * |
| * (Aggregated filters were already set up in log_core_init(). |
| */ |
| static void backend_filter_init(struct log_backend const *const backend) |
| { |
| u8_t level; |
| int i; |
| |
| if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) { |
| for (i = 0; i < log_sources_count(); i++) { |
| level = log_compiled_level_get(i); |
| |
| log_filter_set(backend, |
| CONFIG_LOG_DOMAIN_ID, |
| i, |
| level); |
| } |
| } |
| } |
| |
| void log_init(void) |
| { |
| assert(log_backend_count_get() < LOG_FILTERS_NUM_OF_SLOTS); |
| int i; |
| |
| if (atomic_inc(&initialized)) { |
| return; |
| } |
| |
| /* Set default timestamp. */ |
| timestamp_func = timestamp_get; |
| log_output_timestamp_freq_set(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC); |
| |
| /* Assign ids to backends. */ |
| for (i = 0; i < log_backend_count_get(); i++) { |
| log_backend_id_set(log_backend_get(i), |
| i + LOG_FILTER_FIRST_BACKEND_SLOT_IDX); |
| } |
| |
| if (IS_ENABLED(CONFIG_LOG_BACKEND_UART)) { |
| backend_filter_init(uart_backend); |
| log_backend_uart_init(); |
| log_backend_activate(uart_backend, NULL); |
| } |
| } |
| |
| static void thread_set(k_tid_t process_tid) |
| { |
| proc_tid = process_tid; |
| |
| if (!IS_ENABLED(CONFIG_LOG_INPLACE_PROCESS) && |
| CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD && |
| process_tid && |
| buffered_cnt >= CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD) { |
| k_wakeup(proc_tid); |
| } |
| } |
| |
| void log_thread_set(k_tid_t process_tid) |
| { |
| if (IS_ENABLED(CONFIG_LOG_PROCESS_THREAD)) { |
| assert(0); |
| } else { |
| thread_set(process_tid); |
| } |
| } |
| |
| int log_set_timestamp_func(timestamp_get_t timestamp_getter, u32_t freq) |
| { |
| if (!timestamp_getter) { |
| return -EINVAL; |
| } |
| |
| timestamp_func = timestamp_getter; |
| log_output_timestamp_freq_set(freq); |
| |
| return 0; |
| } |
| |
| void log_panic(void) |
| { |
| struct log_backend const *backend; |
| |
| for (int i = 0; i < log_backend_count_get(); i++) { |
| backend = log_backend_get(i); |
| |
| if (log_backend_is_active(backend)) { |
| log_backend_panic(backend); |
| } |
| } |
| |
| panic_mode = true; |
| |
| /* Flush */ |
| while (log_process(false) == true) { |
| } |
| } |
| |
| static bool msg_filter_check(struct log_backend const *backend, |
| struct log_msg *msg) |
| { |
| u32_t backend_level; |
| u32_t msg_level; |
| |
| backend_level = log_filter_get(backend, |
| log_msg_domain_id_get(msg), |
| log_msg_source_id_get(msg), |
| true /*enum RUNTIME, COMPILETIME*/); |
| msg_level = log_msg_level_get(msg); |
| |
| return (msg_level <= backend_level); |
| } |
| |
| static void msg_process(struct log_msg *msg, bool bypass) |
| { |
| struct log_backend const *backend; |
| |
| if (!bypass) { |
| for (int i = 0; i < log_backend_count_get(); i++) { |
| backend = log_backend_get(i); |
| |
| if (log_backend_is_active(backend) && |
| msg_filter_check(backend, msg)) { |
| log_backend_put(backend, msg); |
| } |
| } |
| } |
| |
| log_msg_put(msg); |
| } |
| |
| bool log_process(bool bypass) |
| { |
| struct log_msg *msg; |
| |
| unsigned int key = irq_lock(); |
| |
| msg = log_list_head_get(&list); |
| irq_unlock(key); |
| |
| if (msg != NULL) { |
| atomic_dec(&buffered_cnt); |
| msg_process(msg, bypass); |
| } |
| |
| return (log_list_head_peek(&list) != NULL); |
| } |
| |
| u32_t log_buffered_cnt(void) |
| { |
| return buffered_cnt; |
| } |
| |
| u32_t log_src_cnt_get(u32_t domain_id) |
| { |
| return log_sources_count(); |
| } |
| |
| const char *log_source_name_get(u32_t domain_id, u32_t src_id) |
| { |
| assert(src_id < log_sources_count()); |
| |
| return log_name_get(src_id); |
| } |
| |
| static u32_t max_filter_get(u32_t filters) |
| { |
| u32_t max_filter = LOG_LEVEL_NONE; |
| int first_slot = LOG_FILTER_FIRST_BACKEND_SLOT_IDX; |
| int i; |
| |
| for (i = first_slot; i < LOG_FILTERS_NUM_OF_SLOTS; i++) { |
| u32_t tmp_filter = LOG_FILTER_SLOT_GET(&filters, i); |
| |
| if (tmp_filter > max_filter) { |
| max_filter = tmp_filter; |
| } |
| } |
| |
| return max_filter; |
| } |
| |
| void log_filter_set(struct log_backend const *const backend, |
| u32_t domain_id, |
| u32_t src_id, |
| u32_t level) |
| { |
| assert(src_id < log_sources_count()); |
| |
| if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) { |
| u32_t new_aggr_filter; |
| |
| u32_t *filters = log_dynamic_filters_get(src_id); |
| |
| if (backend == NULL) { |
| struct log_backend const *backend; |
| |
| for (int i = 0; i < log_backend_count_get(); i++) { |
| backend = log_backend_get(i); |
| log_filter_set(backend, domain_id, |
| src_id, level); |
| } |
| } else { |
| LOG_FILTER_SLOT_SET(filters, |
| log_backend_id_get(backend), |
| level); |
| |
| /* Once current backend filter is updated recalculate |
| * aggregated maximal level |
| */ |
| new_aggr_filter = max_filter_get(*filters); |
| |
| LOG_FILTER_SLOT_SET(filters, |
| LOG_FILTER_AGGR_SLOT_IDX, |
| new_aggr_filter); |
| } |
| } |
| } |
| |
| static void backend_filter_set(struct log_backend const *const backend, |
| u32_t level) |
| { |
| if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) { |
| for (int i = 0; i < log_sources_count(); i++) { |
| log_filter_set(backend, |
| CONFIG_LOG_DOMAIN_ID, |
| i, |
| level); |
| |
| } |
| } |
| } |
| |
| void log_backend_enable(struct log_backend const *const backend, |
| void *ctx, |
| u32_t level) |
| { |
| backend_filter_set(backend, level); |
| log_backend_activate(backend, ctx); |
| } |
| |
| void log_backend_disable(struct log_backend const *const backend) |
| { |
| log_backend_deactivate(backend); |
| backend_filter_set(backend, LOG_LEVEL_NONE); |
| } |
| |
| u32_t log_filter_get(struct log_backend const *const backend, |
| u32_t domain_id, |
| u32_t src_id, |
| bool runtime) |
| { |
| assert(src_id < log_sources_count()); |
| |
| if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) && runtime) { |
| u32_t *filters = log_dynamic_filters_get(src_id); |
| |
| return LOG_FILTER_SLOT_GET(filters, |
| log_backend_id_get(backend)); |
| } else { |
| return log_compiled_level_get(src_id); |
| } |
| } |
| |
| #ifdef CONFIG_LOG_PROCESS_THREAD |
| static void log_process_thread_func(void *dummy1, void *dummy2, void *dummy3) |
| { |
| log_init(); |
| thread_set(k_current_get()); |
| |
| while (1) { |
| if (log_process(false) == false) { |
| k_sleep(CONFIG_LOG_PROCESS_THREAD_SLEEP_MS); |
| } |
| } |
| } |
| |
| K_THREAD_DEFINE(log_process_thread, CONFIG_LOG_PROCESS_THREAD_STACK_SIZE, |
| log_process_thread_func, NULL, NULL, NULL, |
| CONFIG_LOG_PROCESS_THREAD_PRIO, 0, K_NO_WAIT); |
| #endif /* CONFIG_LOG_PROCESS_THREAD */ |