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