1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/shell/shell.h>
8 #include <zephyr/logging/log_ctrl.h>
9 #include <zephyr/logging/log.h>
10 #include <zephyr/logging/log_internal.h>
11 #include <zephyr/sys/iterable_sections.h>
12 #include <string.h>
13
14 #define FRONTEND_NAME frontend
15 #define FRONTEND_STR STRINGIFY(frontend)
16
17 typedef int (*log_backend_cmd_t)(const struct shell *sh,
18 const struct log_backend *backend,
19 size_t argc,
20 char **argv);
21
22 static const char * const severity_lvls[] = {
23 "none",
24 "err",
25 "wrn",
26 "inf",
27 "dbg",
28 };
29
30 static const char * const severity_lvls_sorted[] = {
31 "dbg",
32 "err",
33 "inf",
34 "none",
35 "wrn",
36 };
37
38 /**
39 * @brief Function for finding backend instance with given name.
40 *
41 * @param p_name Name of the backend instance.
42 *
43 * @return Pointer to the instance or NULL.
44 *
45 */
backend_find(char const * name)46 static const struct log_backend *backend_find(char const *name)
47 {
48 size_t slen = strlen(name);
49
50 STRUCT_SECTION_FOREACH(log_backend, backend) {
51 if (strncmp(name, backend->name, slen) == 0) {
52 return backend;
53 }
54 }
55
56 return NULL;
57 }
58
shell_state_precheck(const struct shell * sh)59 static bool shell_state_precheck(const struct shell *sh)
60 {
61 if (sh->log_backend->control_block->state
62 == SHELL_LOG_BACKEND_UNINIT) {
63 shell_error(sh, "Shell log backend not initialized.");
64 return false;
65 }
66
67 return true;
68 }
69
70 /**
71 * @brief Function for executing command on given backend.
72 */
shell_backend_cmd_execute(const struct shell * sh,size_t argc,char ** argv,log_backend_cmd_t func)73 static int shell_backend_cmd_execute(const struct shell *sh,
74 size_t argc,
75 char **argv,
76 log_backend_cmd_t func)
77 {
78 /* Based on the structure of backend commands, name of the backend can
79 * be found at -1 (log backend <name> command).
80 */
81 char const *name = argv[-1];
82 size_t slen = sizeof(FRONTEND_STR);
83
84 if (IS_ENABLED(CONFIG_LOG_FRONTEND) &&
85 strncmp(name, FRONTEND_STR, slen) == 0) {
86 func(sh, NULL, argc, argv);
87 return 0;
88 }
89
90 const struct log_backend *backend = backend_find(name);
91
92 if (backend != NULL) {
93 func(sh, backend, argc, argv);
94 } else {
95 shell_error(sh, "Invalid backend: %s", name);
96 return -ENOEXEC;
97 }
98
99 return 0;
100 }
101
102
log_status(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)103 static int log_status(const struct shell *sh,
104 const struct log_backend *backend,
105 size_t argc, char **argv)
106 {
107 uint32_t modules_cnt = log_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID);
108 uint32_t dynamic_lvl;
109 uint32_t compiled_lvl;
110
111 if (backend && !log_backend_is_active(backend)) {
112 shell_warn(sh, "Logs are halted!");
113 }
114
115 shell_fprintf(sh, SHELL_NORMAL, "%-40s | current | built-in \r\n",
116 "module_name");
117 shell_fprintf(sh, SHELL_NORMAL,
118 "----------------------------------------------------------\r\n");
119
120 for (int16_t i = 0U; i < modules_cnt; i++) {
121 if (IS_ENABLED(CONFIG_LOG_FRONTEND) && !backend) {
122 dynamic_lvl = log_frontend_filter_get(i, true);
123 compiled_lvl = log_frontend_filter_get(i, false);
124 } else {
125 dynamic_lvl = log_filter_get(backend, Z_LOG_LOCAL_DOMAIN_ID, i, true);
126 compiled_lvl = log_filter_get(backend, Z_LOG_LOCAL_DOMAIN_ID, i, false);
127 }
128
129 shell_fprintf(sh, SHELL_NORMAL, "%-40s | %-7s | %s\r\n",
130 log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i),
131 severity_lvls[dynamic_lvl],
132 severity_lvls[compiled_lvl]);
133 }
134 return 0;
135 }
136
137
cmd_log_self_status(const struct shell * sh,size_t argc,char ** argv)138 static int cmd_log_self_status(const struct shell *sh,
139 size_t argc, char **argv)
140 {
141 if (!shell_state_precheck(sh)) {
142 return 0;
143 }
144
145 log_status(sh, sh->log_backend->backend, argc, argv);
146 return 0;
147 }
148
cmd_log_backend_status(const struct shell * sh,size_t argc,char ** argv)149 static int cmd_log_backend_status(const struct shell *sh,
150 size_t argc, char **argv)
151 {
152 shell_backend_cmd_execute(sh, argc, argv, log_status);
153 return 0;
154 }
155
module_id_get(const char * name)156 static int module_id_get(const char *name)
157 {
158 uint32_t modules_cnt = log_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID);
159 const char *tmp_name;
160 uint32_t i;
161
162 for (i = 0U; i < modules_cnt; i++) {
163 tmp_name = log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i);
164
165 if (strncmp(tmp_name, name, 64) == 0) {
166 return i;
167 }
168 }
169 return -1;
170 }
171
filters_set(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv,uint32_t level)172 static void filters_set(const struct shell *sh,
173 const struct log_backend *backend,
174 size_t argc, char **argv, uint32_t level)
175 {
176 int i;
177 int id;
178 bool all = argc ? false : true;
179 int cnt = all ? log_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID) : argc;
180
181 if (backend && !backend->cb->active) {
182 shell_warn(sh, "Backend not active.");
183 }
184
185 for (i = 0; i < cnt; i++) {
186 id = all ? i : module_id_get(argv[i]);
187 if (id >= 0) {
188 uint32_t set_lvl;
189
190 if (IS_ENABLED(CONFIG_LOG_FRONTEND) && !backend) {
191 set_lvl = log_frontend_filter_set(id, level);
192 } else {
193 set_lvl = log_filter_set(backend, Z_LOG_LOCAL_DOMAIN_ID, id, level);
194 }
195
196 if (set_lvl != level) {
197 const char *name;
198
199 name = all ?
200 log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i) :
201 argv[i];
202 shell_warn(sh, "%s: level set to %s.",
203 name, severity_lvls[set_lvl]);
204 }
205 } else {
206 shell_error(sh, "%s: unknown source name.", argv[i]);
207 }
208 }
209 }
210
severity_level_get(const char * str)211 static int severity_level_get(const char *str)
212 {
213 int i;
214
215 for (i = 0; i < ARRAY_SIZE(severity_lvls); i++) {
216 if (strncmp(str, severity_lvls[i], 4) == 0) {
217 return i;
218 }
219 }
220
221 return -1;
222 }
log_enable(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)223 static int log_enable(const struct shell *sh,
224 const struct log_backend *backend,
225 size_t argc,
226 char **argv)
227 {
228 int severity_level;
229
230 severity_level = severity_level_get(argv[1]);
231
232 if (severity_level < 0) {
233 shell_error(sh, "Invalid severity: %s", argv[1]);
234 return -ENOEXEC;
235 }
236
237 /* Arguments following severity level are interpreted as module names.*/
238 filters_set(sh, backend, argc - 2, &argv[2], severity_level);
239 return 0;
240 }
241
cmd_log_self_enable(const struct shell * sh,size_t argc,char ** argv)242 static int cmd_log_self_enable(const struct shell *sh,
243 size_t argc, char **argv)
244 {
245 if (!shell_state_precheck(sh)) {
246 return 0;
247 }
248
249 return log_enable(sh, sh->log_backend->backend, argc, argv);
250 }
251
cmd_log_backend_enable(const struct shell * sh,size_t argc,char ** argv)252 static int cmd_log_backend_enable(const struct shell *sh,
253 size_t argc, char **argv)
254 {
255 return shell_backend_cmd_execute(sh, argc, argv, log_enable);
256 }
257
log_disable(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)258 static int log_disable(const struct shell *sh,
259 const struct log_backend *backend,
260 size_t argc,
261 char **argv)
262 {
263 filters_set(sh, backend, argc - 1, &argv[1], LOG_LEVEL_NONE);
264 return 0;
265 }
266
cmd_log_self_disable(const struct shell * sh,size_t argc,char ** argv)267 static int cmd_log_self_disable(const struct shell *sh,
268 size_t argc, char **argv)
269 {
270 if (!shell_state_precheck(sh)) {
271 return 0;
272 }
273
274 return log_disable(sh, sh->log_backend->backend, argc, argv);
275 }
276
cmd_log_backend_disable(const struct shell * sh,size_t argc,char ** argv)277 static int cmd_log_backend_disable(const struct shell *sh,
278 size_t argc, char **argv)
279 {
280 return shell_backend_cmd_execute(sh, argc, argv, log_disable);
281 }
282
283 static void module_name_get(size_t idx, struct shell_static_entry *entry);
284
285 SHELL_DYNAMIC_CMD_CREATE(dsub_module_name, module_name_get);
286
module_name_get(size_t idx,struct shell_static_entry * entry)287 static void module_name_get(size_t idx, struct shell_static_entry *entry)
288 {
289 entry->handler = NULL;
290 entry->help = NULL;
291 entry->subcmd = &dsub_module_name;
292 entry->syntax = log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, idx);
293 }
294
295
severity_lvl_get(size_t idx,struct shell_static_entry * entry)296 static void severity_lvl_get(size_t idx, struct shell_static_entry *entry)
297 {
298 entry->handler = NULL;
299 entry->help = NULL;
300 entry->subcmd = &dsub_module_name;
301 entry->syntax = (idx < ARRAY_SIZE(severity_lvls_sorted)) ?
302 severity_lvls_sorted[idx] : NULL;
303 }
304
305 SHELL_DYNAMIC_CMD_CREATE(dsub_severity_lvl, severity_lvl_get);
306
log_halt(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)307 static int log_halt(const struct shell *sh,
308 const struct log_backend *backend,
309 size_t argc,
310 char **argv)
311 {
312 if (backend || !IS_ENABLED(CONFIG_LOG_FRONTEND)) {
313 log_backend_deactivate(backend);
314 return 0;
315 }
316
317 shell_warn(sh, "Not supported for frontend");
318
319 return 0;
320 }
321
322
cmd_log_self_halt(const struct shell * sh,size_t argc,char ** argv)323 static int cmd_log_self_halt(const struct shell *sh,
324 size_t argc, char **argv)
325 {
326 if (!shell_state_precheck(sh)) {
327 return 0;
328 }
329
330 return log_halt(sh, sh->log_backend->backend, argc, argv);
331 }
332
cmd_log_backend_halt(const struct shell * sh,size_t argc,char ** argv)333 static int cmd_log_backend_halt(const struct shell *sh,
334 size_t argc, char **argv)
335 {
336 return shell_backend_cmd_execute(sh, argc, argv, log_halt);
337 }
338
log_go(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)339 static int log_go(const struct shell *sh,
340 const struct log_backend *backend,
341 size_t argc,
342 char **argv)
343 {
344 if (backend || !IS_ENABLED(CONFIG_LOG_FRONTEND)) {
345 log_backend_activate(backend, backend->cb->ctx);
346 return 0;
347 }
348
349 shell_warn(sh, "Not supported for frontend");
350
351 return 0;
352 }
353
354
cmd_log_self_go(const struct shell * sh,size_t argc,char ** argv)355 static int cmd_log_self_go(const struct shell *sh,
356 size_t argc, char **argv)
357 {
358 if (!shell_state_precheck(sh)) {
359 return 0;
360 }
361
362 return log_go(sh, sh->log_backend->backend, argc, argv);
363 }
364
cmd_log_backend_go(const struct shell * sh,size_t argc,char ** argv)365 static int cmd_log_backend_go(const struct shell *sh,
366 size_t argc, char **argv)
367 {
368 return shell_backend_cmd_execute(sh, argc, argv, log_go);
369 }
370
371
cmd_log_backends_list(const struct shell * sh,size_t argc,char ** argv)372 static int cmd_log_backends_list(const struct shell *sh,
373 size_t argc, char **argv)
374 {
375 STRUCT_SECTION_FOREACH(log_backend, backend) {
376 shell_fprintf(sh, SHELL_NORMAL,
377 "%s\r\n"
378 "\t- Status: %s\r\n"
379 "\t- ID: %d\r\n\r\n",
380 backend->name,
381 backend->cb->active ? "enabled" : "disabled",
382 backend->cb->id);
383
384 }
385
386 if (IS_ENABLED(CONFIG_LOG_FRONTEND)) {
387 shell_print(sh, "%s", FRONTEND_STR);
388 }
389
390 return 0;
391 }
392
cmd_log_mem(const struct shell * sh,size_t argc,char ** argv)393 static int cmd_log_mem(const struct shell *sh, size_t argc, char **argv)
394 {
395 uint32_t size;
396 uint32_t used;
397 uint32_t max;
398 int err;
399
400 err = log_mem_get_usage(&size, &used);
401 if (err < 0) {
402 shell_error(sh, "Failed to get usage (mode does not support it?)");
403 return -ENOEXEC;
404 }
405
406 shell_print(sh, "Log message buffer utilization report:");
407 shell_print(sh, "\tCapacity: %u bytes", size);
408 shell_print(sh, "\tCurrently in use: %u bytes", used);
409
410 err = log_mem_get_max_usage(&max);
411 if (err < 0) {
412 shell_print(sh, "Enable CONFIG_LOG_MEM_UTILIZATION to get maximum usage");
413 return 0;
414 }
415
416 shell_print(sh, "\tMaximum usage: %u bytes", max);
417
418 return 0;
419 }
420
421 SHELL_STATIC_SUBCMD_SET_CREATE(sub_log_backend,
422 SHELL_CMD_ARG(disable, &dsub_module_name,
423 "'log disable <module_0> .. <module_n>' disables logs in "
424 "specified modules (all if no modules specified).",
425 cmd_log_backend_disable, 1, 255),
426 SHELL_CMD_ARG(enable, &dsub_severity_lvl,
427 "'log enable <level> <module_0> ... <module_n>' enables logs"
428 " up to given level in specified modules (all if no modules "
429 "specified).",
430 cmd_log_backend_enable, 2, 255),
431 SHELL_CMD(go, NULL, "Resume logging", cmd_log_backend_go),
432 SHELL_CMD(halt, NULL, "Halt logging", cmd_log_backend_halt),
433 SHELL_CMD(status, NULL, "Logger status", cmd_log_backend_status),
434 SHELL_SUBCMD_SET_END
435 );
436
backend_name_get(size_t idx,struct shell_static_entry * entry)437 static void backend_name_get(size_t idx, struct shell_static_entry *entry)
438 {
439 uint32_t section_count = 0;
440
441 entry->handler = NULL;
442 entry->help = NULL;
443 entry->subcmd = &sub_log_backend;
444 entry->syntax = NULL;
445
446 STRUCT_SECTION_COUNT(log_backend, §ion_count);
447
448
449 if (idx < section_count) {
450 struct log_backend *backend = NULL;
451
452 STRUCT_SECTION_GET(log_backend, idx, &backend);
453 __ASSERT_NO_MSG(backend != NULL);
454 entry->syntax = backend->name;
455 } else if (IS_ENABLED(CONFIG_LOG_FRONTEND) && (idx == section_count)) {
456 entry->syntax = FRONTEND_STR;
457 }
458 }
459
460 SHELL_DYNAMIC_CMD_CREATE(dsub_backend_name_dynamic, backend_name_get);
461
462 SHELL_STATIC_SUBCMD_SET_CREATE(
463 sub_log_stat,
464 SHELL_CMD(backend, &dsub_backend_name_dynamic, "Logger backends commands.", NULL),
465 SHELL_COND_CMD_ARG(CONFIG_SHELL_LOG_BACKEND, disable, &dsub_module_name,
466 "'log disable <module_0> .. <module_n>' disables logs in specified "
467 "modules (all if no modules specified).",
468 cmd_log_self_disable, 1, 255),
469 SHELL_COND_CMD_ARG(CONFIG_SHELL_LOG_BACKEND, enable, &dsub_severity_lvl,
470 "'log enable <level> <module_0> ... <module_n>' enables logs up to"
471 " given level in specified modules (all if no modules specified).",
472 cmd_log_self_enable, 2, 255),
473 SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, go, NULL, "Resume logging", cmd_log_self_go),
474 SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, halt, NULL, "Halt logging", cmd_log_self_halt),
475 SHELL_CMD_ARG(list_backends, NULL, "Lists logger backends.", cmd_log_backends_list, 1, 0),
476 SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, status, NULL, "Logger status",
477 cmd_log_self_status),
478 SHELL_COND_CMD(CONFIG_LOG_MODE_DEFERRED, mem, NULL, "Logger memory usage",
479 cmd_log_mem),
480 SHELL_COND_CMD(CONFIG_LOG_FRONTEND, FRONTEND_NAME, &sub_log_backend,
481 "Frontend control", NULL),
482 SHELL_SUBCMD_SET_END);
483
484 SHELL_CMD_REGISTER(log, &sub_log_stat, "Commands for controlling logger",
485 NULL);
486