You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
497 lines
12 KiB
497 lines
12 KiB
/* |
|
* Copyright (c) 2018 Nordic Semiconductor ASA |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/shell/shell.h> |
|
#include <zephyr/logging/log_ctrl.h> |
|
#include <zephyr/logging/log.h> |
|
#include <zephyr/logging/log_internal.h> |
|
#include <zephyr/sys/iterable_sections.h> |
|
#include <string.h> |
|
|
|
#define FRONTEND_NAME frontend |
|
#define FRONTEND_STR STRINGIFY(frontend) |
|
|
|
typedef int (*log_backend_cmd_t)(const struct shell *sh, |
|
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) |
|
{ |
|
size_t slen = strlen(name); |
|
|
|
STRUCT_SECTION_FOREACH(log_backend, backend) { |
|
if (strncmp(name, backend->name, slen) == 0) { |
|
return backend; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
static bool shell_state_precheck(const struct shell *sh) |
|
{ |
|
if (sh->log_backend->control_block->state |
|
== SHELL_LOG_BACKEND_UNINIT) { |
|
shell_error(sh, "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 *sh, |
|
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]; |
|
size_t slen = sizeof(FRONTEND_STR); |
|
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && |
|
strncmp(name, FRONTEND_STR, slen) == 0) { |
|
func(sh, NULL, argc, argv); |
|
return 0; |
|
} |
|
|
|
const struct log_backend *backend = backend_find(name); |
|
|
|
if (backend != NULL) { |
|
func(sh, backend, argc, argv); |
|
} else { |
|
shell_error(sh, "Invalid backend: %s", name); |
|
return -ENOEXEC; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
static int log_status(const struct shell *sh, |
|
const struct log_backend *backend, |
|
size_t argc, char **argv) |
|
{ |
|
uint32_t modules_cnt = log_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID); |
|
uint32_t dynamic_lvl; |
|
uint32_t compiled_lvl; |
|
|
|
if (backend && !log_backend_is_active(backend)) { |
|
shell_warn(sh, "Logs are halted!"); |
|
} |
|
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-40s | current | built-in \r\n", |
|
"module_name"); |
|
shell_fprintf(sh, SHELL_NORMAL, |
|
"----------------------------------------------------------\r\n"); |
|
|
|
for (int16_t i = 0U; i < modules_cnt; i++) { |
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && !backend) { |
|
dynamic_lvl = log_frontend_filter_get(i, true); |
|
compiled_lvl = log_frontend_filter_get(i, false); |
|
} else { |
|
dynamic_lvl = log_filter_get(backend, Z_LOG_LOCAL_DOMAIN_ID, i, true); |
|
compiled_lvl = log_filter_get(backend, Z_LOG_LOCAL_DOMAIN_ID, i, false); |
|
} |
|
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-40s | %-7s | %s\r\n", |
|
log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i), |
|
severity_lvls[dynamic_lvl], |
|
severity_lvls[compiled_lvl]); |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
static int cmd_log_self_status(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
if (!shell_state_precheck(sh)) { |
|
return 0; |
|
} |
|
|
|
log_status(sh, sh->log_backend->backend, argc, argv); |
|
return 0; |
|
} |
|
|
|
static int cmd_log_backend_status(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
shell_backend_cmd_execute(sh, argc, argv, log_status); |
|
return 0; |
|
} |
|
|
|
static int module_id_get(const char *name) |
|
{ |
|
uint32_t modules_cnt = log_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID); |
|
const char *tmp_name; |
|
uint32_t i; |
|
|
|
for (i = 0U; i < modules_cnt; i++) { |
|
tmp_name = log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i); |
|
|
|
if (strncmp(tmp_name, name, 64) == 0) { |
|
return i; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
static void filters_set(const struct shell *sh, |
|
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_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID) : argc; |
|
|
|
if (backend && !backend->cb->active) { |
|
shell_warn(sh, "Backend not active."); |
|
} |
|
|
|
for (i = 0; i < cnt; i++) { |
|
id = all ? i : module_id_get(argv[i]); |
|
if (id >= 0) { |
|
uint32_t set_lvl; |
|
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND) && !backend) { |
|
set_lvl = log_frontend_filter_set(id, level); |
|
} else { |
|
set_lvl = log_filter_set(backend, Z_LOG_LOCAL_DOMAIN_ID, id, level); |
|
} |
|
|
|
if (set_lvl != level) { |
|
const char *name; |
|
|
|
name = all ? |
|
log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i) : |
|
argv[i]; |
|
shell_warn(sh, "%s: level set to %s.", |
|
name, severity_lvls[set_lvl]); |
|
} |
|
} else { |
|
shell_error(sh, "%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 *sh, |
|
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(sh, "Invalid severity: %s", argv[1]); |
|
return -ENOEXEC; |
|
} |
|
|
|
/* Arguments following severity level are interpreted as module names.*/ |
|
filters_set(sh, backend, argc - 2, &argv[2], severity_level); |
|
return 0; |
|
} |
|
|
|
static int cmd_log_self_enable(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
if (!shell_state_precheck(sh)) { |
|
return 0; |
|
} |
|
|
|
return log_enable(sh, sh->log_backend->backend, argc, argv); |
|
} |
|
|
|
static int cmd_log_backend_enable(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
return shell_backend_cmd_execute(sh, argc, argv, log_enable); |
|
} |
|
|
|
static int log_disable(const struct shell *sh, |
|
const struct log_backend *backend, |
|
size_t argc, |
|
char **argv) |
|
{ |
|
filters_set(sh, backend, argc - 1, &argv[1], LOG_LEVEL_NONE); |
|
return 0; |
|
} |
|
|
|
static int cmd_log_self_disable(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
if (!shell_state_precheck(sh)) { |
|
return 0; |
|
} |
|
|
|
return log_disable(sh, sh->log_backend->backend, argc, argv); |
|
} |
|
|
|
static int cmd_log_backend_disable(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
return shell_backend_cmd_execute(sh, 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(Z_LOG_LOCAL_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 *sh, |
|
const struct log_backend *backend, |
|
size_t argc, |
|
char **argv) |
|
{ |
|
if (backend || !IS_ENABLED(CONFIG_LOG_FRONTEND)) { |
|
log_backend_deactivate(backend); |
|
return 0; |
|
} |
|
|
|
shell_warn(sh, "Not supported for frontend"); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
static int cmd_log_self_halt(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
if (!shell_state_precheck(sh)) { |
|
return 0; |
|
} |
|
|
|
return log_halt(sh, sh->log_backend->backend, argc, argv); |
|
} |
|
|
|
static int cmd_log_backend_halt(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
return shell_backend_cmd_execute(sh, argc, argv, log_halt); |
|
} |
|
|
|
static int log_go(const struct shell *sh, |
|
const struct log_backend *backend, |
|
size_t argc, |
|
char **argv) |
|
{ |
|
if (backend || !IS_ENABLED(CONFIG_LOG_FRONTEND)) { |
|
if (!backend->cb->initialized) { |
|
log_backend_init(backend); |
|
while (log_backend_is_ready(backend) != 0) { |
|
if (IS_ENABLED(CONFIG_MULTITHREADING)) { |
|
k_msleep(10); |
|
} |
|
} |
|
if (log_backend_is_ready(backend) == 0) { |
|
log_backend_enable(backend, backend->cb->ctx, CONFIG_LOG_MAX_LEVEL); |
|
} |
|
} else { |
|
log_backend_activate(backend, backend->cb->ctx); |
|
} |
|
return 0; |
|
} |
|
|
|
shell_warn(sh, "Not supported for frontend"); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
static int cmd_log_self_go(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
if (!shell_state_precheck(sh)) { |
|
return 0; |
|
} |
|
|
|
return log_go(sh, sh->log_backend->backend, argc, argv); |
|
} |
|
|
|
static int cmd_log_backend_go(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
return shell_backend_cmd_execute(sh, argc, argv, log_go); |
|
} |
|
|
|
|
|
static int cmd_log_backends_list(const struct shell *sh, |
|
size_t argc, char **argv) |
|
{ |
|
STRUCT_SECTION_FOREACH(log_backend, backend) { |
|
shell_fprintf(sh, 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); |
|
|
|
} |
|
|
|
if (IS_ENABLED(CONFIG_LOG_FRONTEND)) { |
|
shell_print(sh, "%s", FRONTEND_STR); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int cmd_log_mem(const struct shell *sh, size_t argc, char **argv) |
|
{ |
|
uint32_t size; |
|
uint32_t used; |
|
uint32_t max; |
|
int err; |
|
|
|
err = log_mem_get_usage(&size, &used); |
|
if (err < 0) { |
|
shell_error(sh, "Failed to get usage (mode does not support it?)"); |
|
return -ENOEXEC; |
|
} |
|
|
|
shell_print(sh, "Log message buffer utilization report:"); |
|
shell_print(sh, "\tCapacity: %u bytes", size); |
|
shell_print(sh, "\tCurrently in use: %u bytes", used); |
|
|
|
err = log_mem_get_max_usage(&max); |
|
if (err < 0) { |
|
shell_print(sh, "Enable CONFIG_LOG_MEM_UTILIZATION to get maximum usage"); |
|
return 0; |
|
} |
|
|
|
shell_print(sh, "\tMaximum usage: %u bytes", max); |
|
|
|
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, 1, 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) |
|
{ |
|
uint32_t section_count = 0; |
|
|
|
entry->handler = NULL; |
|
entry->help = NULL; |
|
entry->subcmd = &sub_log_backend; |
|
entry->syntax = NULL; |
|
|
|
STRUCT_SECTION_COUNT(log_backend, §ion_count); |
|
|
|
|
|
if (idx < section_count) { |
|
struct log_backend *backend = NULL; |
|
|
|
STRUCT_SECTION_GET(log_backend, idx, &backend); |
|
__ASSERT_NO_MSG(backend != NULL); |
|
entry->syntax = backend->name; |
|
} else if (IS_ENABLED(CONFIG_LOG_FRONTEND) && (idx == section_count)) { |
|
entry->syntax = FRONTEND_STR; |
|
} |
|
} |
|
|
|
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(CONFIG_LOG_MODE_DEFERRED, mem, NULL, "Logger memory usage", |
|
cmd_log_mem), |
|
SHELL_COND_CMD(CONFIG_LOG_FRONTEND, FRONTEND_NAME, &sub_log_backend, |
|
"Frontend control", NULL), |
|
SHELL_SUBCMD_SET_END); |
|
|
|
SHELL_CMD_REGISTER(log, &sub_log_stat, "Commands for controlling logger", |
|
NULL);
|
|
|