blob: fd7997a84bd42d99d7867fd097db6c8b80d94e62 [file] [log] [blame]
/*
* 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 &&
(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;
}
return log_status(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv);
}
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 ? sh->log_backend->backend : NULL, 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 ? sh->log_backend->backend : NULL, 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 ? sh->log_backend->backend : NULL, 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)) {
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 ? sh->log_backend->backend : NULL, 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, &section_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);