| /* |
| * Copyright (c) 2018 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <shell/shell.h> |
| #include <logging/log_ctrl.h> |
| #include <logging/log.h> |
| #include <string.h> |
| |
| typedef int (*log_backend_cmd_t)(const struct shell *shell, |
| const struct log_backend *backend, |
| size_t argc, |
| char **argv); |
| |
| static const char * const severity_lvls[] = { |
| "none", |
| "err", |
| "wrn", |
| "inf", |
| "dbg", |
| }; |
| |
| static const char * const severity_lvls_sorted[] = { |
| "dbg", |
| "err", |
| "inf", |
| "none", |
| "wrn", |
| }; |
| |
| /** |
| * @brief Function for finding backend instance with given name. |
| * |
| * @param p_name Name of the backend instance. |
| * |
| * @return Pointer to the instance or NULL. |
| * |
| */ |
| static const struct log_backend *backend_find(char const *name) |
| { |
| const struct log_backend *backend; |
| size_t slen = strlen(name); |
| |
| for (int i = 0; i < log_backend_count_get(); i++) { |
| backend = log_backend_get(i); |
| if (strncmp(name, backend->name, slen) == 0) { |
| return backend; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static bool shell_state_precheck(const struct shell *shell) |
| { |
| if (shell->log_backend->control_block->state |
| == SHELL_LOG_BACKEND_UNINIT) { |
| shell_error(shell, "Shell log backend not initialized."); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * @brief Function for executing command on given backend. |
| */ |
| static int shell_backend_cmd_execute(const struct shell *shell, |
| size_t argc, |
| char **argv, |
| log_backend_cmd_t func) |
| { |
| /* Based on the structure of backend commands, name of the backend can |
| * be found at -1 (log backend <name> command). |
| */ |
| char const *name = argv[-1]; |
| const struct log_backend *backend = backend_find(name); |
| |
| if (backend != NULL) { |
| func(shell, backend, argc, argv); |
| } else { |
| shell_error(shell, "Invalid backend: %s", name); |
| return -ENOEXEC; |
| } |
| return 0; |
| } |
| |
| |
| static int log_status(const struct shell *shell, |
| const struct log_backend *backend, |
| size_t argc, char **argv) |
| { |
| uint32_t modules_cnt = log_sources_count(); |
| uint32_t dynamic_lvl; |
| uint32_t compiled_lvl; |
| |
| if (!log_backend_is_active(backend)) { |
| shell_warn(shell, "Logs are halted!"); |
| } |
| |
| shell_fprintf(shell, SHELL_NORMAL, "%-40s | current | built-in \r\n", |
| "module_name"); |
| shell_fprintf(shell, SHELL_NORMAL, |
| "----------------------------------------------------------\r\n"); |
| |
| for (int16_t i = 0U; i < modules_cnt; i++) { |
| dynamic_lvl = log_filter_get(backend, CONFIG_LOG_DOMAIN_ID, |
| i, true); |
| compiled_lvl = log_filter_get(backend, CONFIG_LOG_DOMAIN_ID, |
| i, false); |
| |
| shell_fprintf(shell, SHELL_NORMAL, "%-40s | %-7s | %s\r\n", |
| log_source_name_get(CONFIG_LOG_DOMAIN_ID, i), |
| severity_lvls[dynamic_lvl], |
| severity_lvls[compiled_lvl]); |
| } |
| return 0; |
| } |
| |
| |
| static int cmd_log_self_status(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| if (!shell_state_precheck(shell)) { |
| return 0; |
| } |
| |
| log_status(shell, shell->log_backend->backend, argc, argv); |
| return 0; |
| } |
| |
| static int cmd_log_backend_status(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| shell_backend_cmd_execute(shell, argc, argv, log_status); |
| return 0; |
| } |
| |
| static int module_id_get(const char *name) |
| { |
| uint32_t modules_cnt = log_sources_count(); |
| const char *tmp_name; |
| uint32_t i; |
| |
| for (i = 0U; i < modules_cnt; i++) { |
| tmp_name = log_source_name_get(CONFIG_LOG_DOMAIN_ID, i); |
| |
| if (strncmp(tmp_name, name, 64) == 0) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| static void filters_set(const struct shell *shell, |
| const struct log_backend *backend, |
| size_t argc, char **argv, uint32_t level) |
| { |
| int i; |
| int id; |
| bool all = argc ? false : true; |
| int cnt = all ? log_sources_count() : argc; |
| |
| if (!backend->cb->active) { |
| shell_warn(shell, "Backend not active."); |
| } |
| |
| for (i = 0; i < cnt; i++) { |
| id = all ? i : module_id_get(argv[i]); |
| if (id >= 0) { |
| uint32_t set_lvl = log_filter_set(backend, |
| CONFIG_LOG_DOMAIN_ID, |
| id, level); |
| |
| if (set_lvl != level) { |
| const char *name; |
| |
| name = all ? |
| log_source_name_get( |
| CONFIG_LOG_DOMAIN_ID, i) : |
| argv[i]; |
| shell_warn(shell, "%s: level set to %s.", |
| name, severity_lvls[set_lvl]); |
| } |
| } else { |
| shell_error(shell, "%s: unknown source name.", argv[i]); |
| } |
| } |
| } |
| |
| static int severity_level_get(const char *str) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(severity_lvls); i++) { |
| if (strncmp(str, severity_lvls[i], 4) == 0) { |
| return i; |
| } |
| } |
| |
| return -1; |
| } |
| static int log_enable(const struct shell *shell, |
| const struct log_backend *backend, |
| size_t argc, |
| char **argv) |
| { |
| int severity_level; |
| |
| severity_level = severity_level_get(argv[1]); |
| |
| if (severity_level < 0) { |
| shell_error(shell, "Invalid severity: %s", argv[1]); |
| return -ENOEXEC; |
| } |
| |
| /* Arguments following severity level are interpreted as module names.*/ |
| filters_set(shell, backend, argc - 2, &argv[2], severity_level); |
| return 0; |
| } |
| |
| static int cmd_log_self_enable(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| if (!shell_state_precheck(shell)) { |
| return 0; |
| } |
| |
| return log_enable(shell, shell->log_backend->backend, argc, argv); |
| } |
| |
| static int cmd_log_backend_enable(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| return shell_backend_cmd_execute(shell, argc, argv, log_enable); |
| } |
| |
| static int log_disable(const struct shell *shell, |
| const struct log_backend *backend, |
| size_t argc, |
| char **argv) |
| { |
| filters_set(shell, backend, argc - 1, &argv[1], LOG_LEVEL_NONE); |
| return 0; |
| } |
| |
| static int cmd_log_self_disable(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| if (!shell_state_precheck(shell)) { |
| return 0; |
| } |
| |
| return log_disable(shell, shell->log_backend->backend, argc, argv); |
| } |
| |
| static int cmd_log_backend_disable(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| return shell_backend_cmd_execute(shell, argc, argv, log_disable); |
| } |
| |
| static void module_name_get(size_t idx, struct shell_static_entry *entry); |
| |
| SHELL_DYNAMIC_CMD_CREATE(dsub_module_name, module_name_get); |
| |
| static void module_name_get(size_t idx, struct shell_static_entry *entry) |
| { |
| entry->handler = NULL; |
| entry->help = NULL; |
| entry->subcmd = &dsub_module_name; |
| entry->syntax = log_source_name_get(CONFIG_LOG_DOMAIN_ID, idx); |
| } |
| |
| |
| static void severity_lvl_get(size_t idx, struct shell_static_entry *entry) |
| { |
| entry->handler = NULL; |
| entry->help = NULL; |
| entry->subcmd = &dsub_module_name; |
| entry->syntax = (idx < ARRAY_SIZE(severity_lvls_sorted)) ? |
| severity_lvls_sorted[idx] : NULL; |
| } |
| |
| SHELL_DYNAMIC_CMD_CREATE(dsub_severity_lvl, severity_lvl_get); |
| |
| static int log_halt(const struct shell *shell, |
| const struct log_backend *backend, |
| size_t argc, |
| char **argv) |
| { |
| log_backend_deactivate(backend); |
| return 0; |
| } |
| |
| |
| static int cmd_log_self_halt(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| if (!shell_state_precheck(shell)) { |
| return 0; |
| } |
| |
| return log_halt(shell, shell->log_backend->backend, argc, argv); |
| } |
| |
| static int cmd_log_backend_halt(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| return shell_backend_cmd_execute(shell, argc, argv, log_halt); |
| } |
| |
| static int log_go(const struct shell *shell, |
| const struct log_backend *backend, |
| size_t argc, |
| char **argv) |
| { |
| log_backend_activate(backend, backend->cb->ctx); |
| return 0; |
| } |
| |
| |
| static int cmd_log_self_go(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| if (!shell_state_precheck(shell)) { |
| return 0; |
| } |
| |
| return log_go(shell, shell->log_backend->backend, argc, argv); |
| } |
| |
| static int cmd_log_backend_go(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| return shell_backend_cmd_execute(shell, argc, argv, log_go); |
| } |
| |
| |
| static int cmd_log_backends_list(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| int backend_count; |
| |
| backend_count = log_backend_count_get(); |
| |
| for (int i = 0; i < backend_count; i++) { |
| const struct log_backend *backend = log_backend_get(i); |
| |
| shell_fprintf(shell, SHELL_NORMAL, |
| "%s\r\n" |
| "\t- Status: %s\r\n" |
| "\t- ID: %d\r\n\r\n", |
| backend->name, |
| backend->cb->active ? "enabled" : "disabled", |
| backend->cb->id); |
| |
| } |
| return 0; |
| } |
| |
| static int cmd_log_strdup_utilization(const struct shell *shell, |
| size_t argc, char **argv) |
| { |
| |
| /* Defines needed when string duplication is disabled (LOG_IMMEDIATE is |
| * on). In that case, this function is not compiled in. |
| */ |
| #ifndef CONFIG_LOG_STRDUP_BUF_COUNT |
| #define CONFIG_LOG_STRDUP_BUF_COUNT 0 |
| #endif |
| |
| #ifndef CONFIG_LOG_STRDUP_MAX_STRING |
| #define CONFIG_LOG_STRDUP_MAX_STRING 0 |
| #endif |
| |
| uint32_t cur_cnt = log_get_strdup_pool_current_utilization(); |
| uint32_t buf_cnt = log_get_strdup_pool_utilization(); |
| uint32_t buf_size = log_get_strdup_longest_string(); |
| uint32_t percent = CONFIG_LOG_STRDUP_BUF_COUNT ? |
| buf_cnt * 100U / CONFIG_LOG_STRDUP_BUF_COUNT : 0U; |
| |
| shell_print(shell, "Current utilization of the buffer pool: %d.", |
| cur_cnt); |
| |
| shell_print(shell, |
| "Maximal utilization of the buffer pool: %d / %d (%d %%).", |
| buf_cnt, CONFIG_LOG_STRDUP_BUF_COUNT, percent); |
| if (buf_cnt == CONFIG_LOG_STRDUP_BUF_COUNT) { |
| shell_warn(shell, "Buffer count too small."); |
| } |
| |
| shell_print(shell, |
| "Longest duplicated string: %d, buffer capacity: %d.", |
| buf_size, CONFIG_LOG_STRDUP_MAX_STRING); |
| if (buf_size > CONFIG_LOG_STRDUP_MAX_STRING) { |
| shell_warn(shell, "Buffer size too small."); |
| |
| } |
| |
| return 0; |
| } |
| |
| static int cmd_log_memory_slabs(const struct shell *sh, size_t argc, char **argv) |
| { |
| uint32_t slabs_free; |
| uint32_t used; |
| uint32_t max; |
| |
| slabs_free = log_msg_mem_get_free(); |
| used = log_msg_mem_get_used(); |
| |
| shell_print(sh, "Blocks used:\t%d", used); |
| shell_print(sh, "Blocks free:\t%d", slabs_free); |
| if (IS_ENABLED(CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION)) { |
| max = log_msg_mem_get_max_used(); |
| shell_print(sh, "Blocks max:\t%d", max); |
| } else { |
| shell_print( |
| sh, |
| "Enable CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION to get max memory utilization"); |
| } |
| |
| return 0; |
| } |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE(sub_log_backend, |
| SHELL_CMD_ARG(disable, &dsub_module_name, |
| "'log disable <module_0> .. <module_n>' disables logs in " |
| "specified modules (all if no modules specified).", |
| cmd_log_backend_disable, 2, 255), |
| SHELL_CMD_ARG(enable, &dsub_severity_lvl, |
| "'log enable <level> <module_0> ... <module_n>' enables logs" |
| " up to given level in specified modules (all if no modules " |
| "specified).", |
| cmd_log_backend_enable, 2, 255), |
| SHELL_CMD(go, NULL, "Resume logging", cmd_log_backend_go), |
| SHELL_CMD(halt, NULL, "Halt logging", cmd_log_backend_halt), |
| SHELL_CMD(status, NULL, "Logger status", cmd_log_backend_status), |
| SHELL_SUBCMD_SET_END |
| ); |
| |
| static void backend_name_get(size_t idx, struct shell_static_entry *entry) |
| { |
| entry->handler = NULL; |
| entry->help = NULL; |
| entry->subcmd = &sub_log_backend; |
| entry->syntax = NULL; |
| |
| if (idx < log_backend_count_get()) { |
| const struct log_backend *backend = log_backend_get(idx); |
| |
| entry->syntax = backend->name; |
| } |
| } |
| |
| SHELL_DYNAMIC_CMD_CREATE(dsub_backend_name_dynamic, backend_name_get); |
| |
| SHELL_STATIC_SUBCMD_SET_CREATE( |
| sub_log_stat, |
| SHELL_CMD(backend, &dsub_backend_name_dynamic, "Logger backends commands.", NULL), |
| SHELL_COND_CMD_ARG(CONFIG_SHELL_LOG_BACKEND, disable, &dsub_module_name, |
| "'log disable <module_0> .. <module_n>' disables logs in specified " |
| "modules (all if no modules specified).", |
| cmd_log_self_disable, 1, 255), |
| SHELL_COND_CMD_ARG(CONFIG_SHELL_LOG_BACKEND, enable, &dsub_severity_lvl, |
| "'log enable <level> <module_0> ... <module_n>' enables logs up to" |
| " given level in specified modules (all if no modules specified).", |
| cmd_log_self_enable, 2, 255), |
| SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, go, NULL, "Resume logging", cmd_log_self_go), |
| SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, halt, NULL, "Halt logging", cmd_log_self_halt), |
| SHELL_CMD_ARG(list_backends, NULL, "Lists logger backends.", cmd_log_backends_list, 1, 0), |
| SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, status, NULL, "Logger status", |
| cmd_log_self_status), |
| SHELL_COND_CMD_ARG(CONFIG_LOG_STRDUP_POOL_PROFILING, strdup_utilization, NULL, |
| "Get utilization of string duplicates pool", cmd_log_strdup_utilization, |
| 1, 0), |
| SHELL_COND_CMD(CONFIG_LOG_MODE_DEFERRED, mem, NULL, "Logger memory usage", |
| cmd_log_memory_slabs), |
| SHELL_SUBCMD_SET_END); |
| |
| SHELL_CMD_REGISTER(log, &sub_log_stat, "Commands for controlling logger", |
| NULL); |