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 &&
62 	    (sh->log_backend->control_block->state == 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 	return log_status(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv);
146 }
147 
cmd_log_backend_status(const struct shell * sh,size_t argc,char ** argv)148 static int cmd_log_backend_status(const struct shell *sh,
149 				  size_t argc, char **argv)
150 {
151 	shell_backend_cmd_execute(sh, argc, argv, log_status);
152 	return 0;
153 }
154 
module_id_get(const char * name)155 static int module_id_get(const char *name)
156 {
157 	uint32_t modules_cnt = log_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID);
158 	const char *tmp_name;
159 	uint32_t i;
160 
161 	for (i = 0U; i < modules_cnt; i++) {
162 		tmp_name = log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i);
163 
164 		if (strncmp(tmp_name, name, 64) == 0) {
165 			return i;
166 		}
167 	}
168 	return -1;
169 }
170 
filters_set(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv,uint32_t level)171 static void filters_set(const struct shell *sh,
172 			const struct log_backend *backend,
173 			size_t argc, char **argv, uint32_t level)
174 {
175 	int i;
176 	int id;
177 	bool all = argc ? false : true;
178 	int cnt = all ? log_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID) : argc;
179 
180 	if (backend && !backend->cb->active) {
181 		shell_warn(sh, "Backend not active.");
182 	}
183 
184 	for (i = 0; i < cnt; i++) {
185 		id = all ? i : module_id_get(argv[i]);
186 		if (id >= 0) {
187 			uint32_t set_lvl;
188 
189 			if (IS_ENABLED(CONFIG_LOG_FRONTEND) && !backend) {
190 				set_lvl = log_frontend_filter_set(id, level);
191 			} else {
192 				set_lvl = log_filter_set(backend, Z_LOG_LOCAL_DOMAIN_ID, id, level);
193 			}
194 
195 			if (set_lvl != level) {
196 				const char *name;
197 
198 				name = all ?
199 					log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i) :
200 					argv[i];
201 				shell_warn(sh, "%s: level set to %s.",
202 					   name, severity_lvls[set_lvl]);
203 			}
204 		} else {
205 			shell_error(sh, "%s: unknown source name.", argv[i]);
206 		}
207 	}
208 }
209 
severity_level_get(const char * str)210 static int severity_level_get(const char *str)
211 {
212 	int i;
213 
214 	for (i = 0; i < ARRAY_SIZE(severity_lvls); i++) {
215 		if (strncmp(str, severity_lvls[i], 4) == 0) {
216 			return i;
217 		}
218 	}
219 
220 	return -1;
221 }
log_enable(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)222 static int log_enable(const struct shell *sh,
223 		      const struct log_backend *backend,
224 		      size_t argc,
225 		      char **argv)
226 {
227 	int severity_level;
228 
229 	severity_level = severity_level_get(argv[1]);
230 
231 	if (severity_level < 0) {
232 		shell_error(sh, "Invalid severity: %s", argv[1]);
233 		return -ENOEXEC;
234 	}
235 
236 	/* Arguments following severity level are interpreted as module names.*/
237 	filters_set(sh, backend, argc - 2, &argv[2], severity_level);
238 	return 0;
239 }
240 
cmd_log_self_enable(const struct shell * sh,size_t argc,char ** argv)241 static int cmd_log_self_enable(const struct shell *sh,
242 			       size_t argc, char **argv)
243 {
244 	if (!shell_state_precheck(sh)) {
245 		return 0;
246 	}
247 
248 	return log_enable(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv);
249 }
250 
cmd_log_backend_enable(const struct shell * sh,size_t argc,char ** argv)251 static int cmd_log_backend_enable(const struct shell *sh,
252 				  size_t argc, char **argv)
253 {
254 	return shell_backend_cmd_execute(sh, argc, argv, log_enable);
255 }
256 
log_disable(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)257 static int log_disable(const struct shell *sh,
258 		       const struct log_backend *backend,
259 		       size_t argc,
260 		       char **argv)
261 {
262 	filters_set(sh, backend, argc - 1, &argv[1], LOG_LEVEL_NONE);
263 	return 0;
264 }
265 
cmd_log_self_disable(const struct shell * sh,size_t argc,char ** argv)266 static int cmd_log_self_disable(const struct shell *sh,
267 				 size_t argc, char **argv)
268 {
269 	if (!shell_state_precheck(sh)) {
270 		return 0;
271 	}
272 
273 	return log_disable(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv);
274 }
275 
cmd_log_backend_disable(const struct shell * sh,size_t argc,char ** argv)276 static int cmd_log_backend_disable(const struct shell *sh,
277 				   size_t argc, char **argv)
278 {
279 	return shell_backend_cmd_execute(sh, argc, argv, log_disable);
280 }
281 
282 static void module_name_get(size_t idx, struct shell_static_entry *entry);
283 
284 SHELL_DYNAMIC_CMD_CREATE(dsub_module_name, module_name_get);
285 
module_name_get(size_t idx,struct shell_static_entry * entry)286 static void module_name_get(size_t idx, struct shell_static_entry *entry)
287 {
288 	entry->handler = NULL;
289 	entry->help  = NULL;
290 	entry->subcmd = &dsub_module_name;
291 	entry->syntax = log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, idx);
292 }
293 
294 
severity_lvl_get(size_t idx,struct shell_static_entry * entry)295 static void severity_lvl_get(size_t idx, struct shell_static_entry *entry)
296 {
297 	entry->handler = NULL;
298 	entry->help  = NULL;
299 	entry->subcmd = &dsub_module_name;
300 	entry->syntax = (idx < ARRAY_SIZE(severity_lvls_sorted)) ?
301 					severity_lvls_sorted[idx] : NULL;
302 }
303 
304 SHELL_DYNAMIC_CMD_CREATE(dsub_severity_lvl, severity_lvl_get);
305 
log_halt(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)306 static int log_halt(const struct shell *sh,
307 		    const struct log_backend *backend,
308 		    size_t argc,
309 		    char **argv)
310 {
311 	if (backend || !IS_ENABLED(CONFIG_LOG_FRONTEND)) {
312 		log_backend_deactivate(backend);
313 		return 0;
314 	}
315 
316 	shell_warn(sh, "Not supported for frontend");
317 
318 	return 0;
319 }
320 
321 
cmd_log_self_halt(const struct shell * sh,size_t argc,char ** argv)322 static int cmd_log_self_halt(const struct shell *sh,
323 			      size_t argc, char **argv)
324 {
325 	if (!shell_state_precheck(sh)) {
326 		return 0;
327 	}
328 
329 	return log_halt(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv);
330 }
331 
cmd_log_backend_halt(const struct shell * sh,size_t argc,char ** argv)332 static int cmd_log_backend_halt(const struct shell *sh,
333 				size_t argc, char **argv)
334 {
335 	return shell_backend_cmd_execute(sh, argc, argv, log_halt);
336 }
337 
log_go(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)338 static int log_go(const struct shell *sh,
339 		  const struct log_backend *backend,
340 		  size_t argc,
341 		  char **argv)
342 {
343 	if (backend || !IS_ENABLED(CONFIG_LOG_FRONTEND)) {
344 		log_backend_activate(backend, backend->cb->ctx);
345 		return 0;
346 	}
347 
348 	shell_warn(sh, "Not supported for frontend");
349 
350 	return 0;
351 }
352 
353 
cmd_log_self_go(const struct shell * sh,size_t argc,char ** argv)354 static int cmd_log_self_go(const struct shell *sh,
355 			   size_t argc, char **argv)
356 {
357 	if (!shell_state_precheck(sh)) {
358 		return 0;
359 	}
360 
361 	return log_go(sh, sh->log_backend ? sh->log_backend->backend : NULL, argc, argv);
362 }
363 
cmd_log_backend_go(const struct shell * sh,size_t argc,char ** argv)364 static int cmd_log_backend_go(const struct shell *sh,
365 			      size_t argc, char **argv)
366 {
367 	return shell_backend_cmd_execute(sh, argc, argv, log_go);
368 }
369 
370 
cmd_log_backends_list(const struct shell * sh,size_t argc,char ** argv)371 static int cmd_log_backends_list(const struct shell *sh,
372 				 size_t argc, char **argv)
373 {
374 	STRUCT_SECTION_FOREACH(log_backend, backend) {
375 		shell_fprintf(sh, SHELL_NORMAL,
376 			      "%s\r\n"
377 			      "\t- Status: %s\r\n"
378 			      "\t- ID: %d\r\n\r\n",
379 			      backend->name,
380 			      backend->cb->active ? "enabled" : "disabled",
381 			      backend->cb->id);
382 
383 	}
384 
385 	if (IS_ENABLED(CONFIG_LOG_FRONTEND)) {
386 		shell_print(sh, "%s", FRONTEND_STR);
387 	}
388 
389 	return 0;
390 }
391 
cmd_log_mem(const struct shell * sh,size_t argc,char ** argv)392 static int cmd_log_mem(const struct shell *sh, size_t argc, char **argv)
393 {
394 	uint32_t size;
395 	uint32_t used;
396 	uint32_t max;
397 	int err;
398 
399 	err = log_mem_get_usage(&size, &used);
400 	if (err < 0) {
401 		shell_error(sh, "Failed to get usage (mode does not support it?)");
402 		return -ENOEXEC;
403 	}
404 
405 	shell_print(sh, "Log message buffer utilization report:");
406 	shell_print(sh, "\tCapacity: %u bytes", size);
407 	shell_print(sh, "\tCurrently in use: %u bytes", used);
408 
409 	err = log_mem_get_max_usage(&max);
410 	if (err < 0) {
411 		shell_print(sh, "Enable CONFIG_LOG_MEM_UTILIZATION to get maximum usage");
412 		return 0;
413 	}
414 
415 	shell_print(sh, "\tMaximum usage: %u bytes", max);
416 
417 	return 0;
418 }
419 
420 SHELL_STATIC_SUBCMD_SET_CREATE(sub_log_backend,
421 	SHELL_CMD_ARG(disable, &dsub_module_name,
422 		  "'log disable <module_0> .. <module_n>' disables logs in "
423 		  "specified modules (all if no modules specified).",
424 		  cmd_log_backend_disable, 1, 255),
425 	SHELL_CMD_ARG(enable, &dsub_severity_lvl,
426 		  "'log enable <level> <module_0> ...  <module_n>' enables logs"
427 		  " up to given level in specified modules (all if no modules "
428 		  "specified).",
429 		  cmd_log_backend_enable, 2, 255),
430 	SHELL_CMD(go, NULL, "Resume logging", cmd_log_backend_go),
431 	SHELL_CMD(halt, NULL, "Halt logging", cmd_log_backend_halt),
432 	SHELL_CMD(status, NULL, "Logger status", cmd_log_backend_status),
433 	SHELL_SUBCMD_SET_END
434 );
435 
backend_name_get(size_t idx,struct shell_static_entry * entry)436 static void backend_name_get(size_t idx, struct shell_static_entry *entry)
437 {
438 	uint32_t section_count = 0;
439 
440 	entry->handler = NULL;
441 	entry->help  = NULL;
442 	entry->subcmd = &sub_log_backend;
443 	entry->syntax  = NULL;
444 
445 	STRUCT_SECTION_COUNT(log_backend, &section_count);
446 
447 
448 	if (idx < section_count) {
449 		struct log_backend *backend = NULL;
450 
451 		STRUCT_SECTION_GET(log_backend, idx, &backend);
452 		__ASSERT_NO_MSG(backend != NULL);
453 		entry->syntax = backend->name;
454 	} else if (IS_ENABLED(CONFIG_LOG_FRONTEND) && (idx == section_count)) {
455 		entry->syntax = FRONTEND_STR;
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(CONFIG_LOG_MODE_DEFERRED, mem, NULL, "Logger memory usage",
478 		       cmd_log_mem),
479 	SHELL_COND_CMD(CONFIG_LOG_FRONTEND, FRONTEND_NAME, &sub_log_backend,
480 		"Frontend control", NULL),
481 	SHELL_SUBCMD_SET_END);
482 
483 SHELL_CMD_REGISTER(log, &sub_log_stat, "Commands for controlling logger",
484 		   NULL);
485