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 typedef int (*log_backend_cmd_t)(const struct shell *sh,
15 				 const struct log_backend *backend,
16 				 size_t argc,
17 				 char **argv);
18 
19 static const char * const severity_lvls[] = {
20 	"none",
21 	"err",
22 	"wrn",
23 	"inf",
24 	"dbg",
25 };
26 
27 static const char * const severity_lvls_sorted[] = {
28 	"dbg",
29 	"err",
30 	"inf",
31 	"none",
32 	"wrn",
33 };
34 
35 /**
36  * @brief Function for finding backend instance with given name.
37  *
38  * @param p_name Name of the backend instance.
39  *
40  * @return Pointer to the instance or NULL.
41  *
42  */
backend_find(char const * name)43 static const struct log_backend *backend_find(char const *name)
44 {
45 	size_t slen = strlen(name);
46 
47 	STRUCT_SECTION_FOREACH(log_backend, backend) {
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 * sh)56 static bool shell_state_precheck(const struct shell *sh)
57 {
58 	if (sh->log_backend->control_block->state
59 				== SHELL_LOG_BACKEND_UNINIT) {
60 		shell_error(sh, "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 * sh,size_t argc,char ** argv,log_backend_cmd_t func)70 static int shell_backend_cmd_execute(const struct shell *sh,
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(sh, backend, argc, argv);
83 	} else {
84 		shell_error(sh, "Invalid backend: %s", name);
85 		return -ENOEXEC;
86 	}
87 	return 0;
88 }
89 
90 
log_status(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)91 static int log_status(const struct shell *sh,
92 		      const struct log_backend *backend,
93 		      size_t argc, char **argv)
94 {
95 	uint32_t modules_cnt = log_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID);
96 	uint32_t dynamic_lvl;
97 	uint32_t compiled_lvl;
98 
99 	if (!log_backend_is_active(backend)) {
100 		shell_warn(sh, "Logs are halted!");
101 	}
102 
103 	shell_fprintf(sh, SHELL_NORMAL, "%-40s | current | built-in \r\n",
104 					   "module_name");
105 	shell_fprintf(sh, SHELL_NORMAL,
106 	      "----------------------------------------------------------\r\n");
107 
108 	for (int16_t i = 0U; i < modules_cnt; i++) {
109 		dynamic_lvl = log_filter_get(backend, Z_LOG_LOCAL_DOMAIN_ID,
110 					     i, true);
111 		compiled_lvl = log_filter_get(backend, Z_LOG_LOCAL_DOMAIN_ID,
112 					      i, false);
113 
114 		shell_fprintf(sh, SHELL_NORMAL, "%-40s | %-7s | %s\r\n",
115 			      log_source_name_get(Z_LOG_LOCAL_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 * sh,size_t argc,char ** argv)123 static int cmd_log_self_status(const struct shell *sh,
124 			       size_t argc, char **argv)
125 {
126 	if (!shell_state_precheck(sh)) {
127 		return 0;
128 	}
129 
130 	log_status(sh, sh->log_backend->backend, argc, argv);
131 	return 0;
132 }
133 
cmd_log_backend_status(const struct shell * sh,size_t argc,char ** argv)134 static int cmd_log_backend_status(const struct shell *sh,
135 				  size_t argc, char **argv)
136 {
137 	shell_backend_cmd_execute(sh, 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_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID);
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(Z_LOG_LOCAL_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 * sh,const struct log_backend * backend,size_t argc,char ** argv,uint32_t level)157 static void filters_set(const struct shell *sh,
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_src_cnt_get(Z_LOG_LOCAL_DOMAIN_ID) : argc;
165 
166 	if (!backend->cb->active) {
167 		shell_warn(sh, "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 						       Z_LOG_LOCAL_DOMAIN_ID,
175 						       id, level);
176 
177 			if (set_lvl != level) {
178 				const char *name;
179 
180 				name = all ?
181 					log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, i) :
182 					argv[i];
183 				shell_warn(sh, "%s: level set to %s.",
184 					   name, severity_lvls[set_lvl]);
185 			}
186 		} else {
187 			shell_error(sh, "%s: unknown source name.", argv[i]);
188 		}
189 	}
190 }
191 
severity_level_get(const char * str)192 static int severity_level_get(const char *str)
193 {
194 	int i;
195 
196 	for (i = 0; i < ARRAY_SIZE(severity_lvls); i++) {
197 		if (strncmp(str, severity_lvls[i], 4) == 0) {
198 			return i;
199 		}
200 	}
201 
202 	return -1;
203 }
log_enable(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)204 static int log_enable(const struct shell *sh,
205 		      const struct log_backend *backend,
206 		      size_t argc,
207 		      char **argv)
208 {
209 	int severity_level;
210 
211 	severity_level = severity_level_get(argv[1]);
212 
213 	if (severity_level < 0) {
214 		shell_error(sh, "Invalid severity: %s", argv[1]);
215 		return -ENOEXEC;
216 	}
217 
218 	/* Arguments following severity level are interpreted as module names.*/
219 	filters_set(sh, backend, argc - 2, &argv[2], severity_level);
220 	return 0;
221 }
222 
cmd_log_self_enable(const struct shell * sh,size_t argc,char ** argv)223 static int cmd_log_self_enable(const struct shell *sh,
224 			       size_t argc, char **argv)
225 {
226 	if (!shell_state_precheck(sh)) {
227 		return 0;
228 	}
229 
230 	return log_enable(sh, sh->log_backend->backend, argc, argv);
231 }
232 
cmd_log_backend_enable(const struct shell * sh,size_t argc,char ** argv)233 static int cmd_log_backend_enable(const struct shell *sh,
234 				  size_t argc, char **argv)
235 {
236 	return shell_backend_cmd_execute(sh, argc, argv, log_enable);
237 }
238 
log_disable(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)239 static int log_disable(const struct shell *sh,
240 		       const struct log_backend *backend,
241 		       size_t argc,
242 		       char **argv)
243 {
244 	filters_set(sh, backend, argc - 1, &argv[1], LOG_LEVEL_NONE);
245 	return 0;
246 }
247 
cmd_log_self_disable(const struct shell * sh,size_t argc,char ** argv)248 static int cmd_log_self_disable(const struct shell *sh,
249 				 size_t argc, char **argv)
250 {
251 	if (!shell_state_precheck(sh)) {
252 		return 0;
253 	}
254 
255 	return log_disable(sh, sh->log_backend->backend, argc, argv);
256 }
257 
cmd_log_backend_disable(const struct shell * sh,size_t argc,char ** argv)258 static int cmd_log_backend_disable(const struct shell *sh,
259 				   size_t argc, char **argv)
260 {
261 	return shell_backend_cmd_execute(sh, argc, argv, log_disable);
262 }
263 
264 static void module_name_get(size_t idx, struct shell_static_entry *entry);
265 
266 SHELL_DYNAMIC_CMD_CREATE(dsub_module_name, module_name_get);
267 
module_name_get(size_t idx,struct shell_static_entry * entry)268 static void module_name_get(size_t idx, struct shell_static_entry *entry)
269 {
270 	entry->handler = NULL;
271 	entry->help  = NULL;
272 	entry->subcmd = &dsub_module_name;
273 	entry->syntax = log_source_name_get(Z_LOG_LOCAL_DOMAIN_ID, idx);
274 }
275 
276 
severity_lvl_get(size_t idx,struct shell_static_entry * entry)277 static void severity_lvl_get(size_t idx, struct shell_static_entry *entry)
278 {
279 	entry->handler = NULL;
280 	entry->help  = NULL;
281 	entry->subcmd = &dsub_module_name;
282 	entry->syntax = (idx < ARRAY_SIZE(severity_lvls_sorted)) ?
283 					severity_lvls_sorted[idx] : NULL;
284 }
285 
286 SHELL_DYNAMIC_CMD_CREATE(dsub_severity_lvl, severity_lvl_get);
287 
log_halt(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)288 static int log_halt(const struct shell *sh,
289 		    const struct log_backend *backend,
290 		    size_t argc,
291 		    char **argv)
292 {
293 	log_backend_deactivate(backend);
294 	return 0;
295 }
296 
297 
cmd_log_self_halt(const struct shell * sh,size_t argc,char ** argv)298 static int cmd_log_self_halt(const struct shell *sh,
299 			      size_t argc, char **argv)
300 {
301 	if (!shell_state_precheck(sh)) {
302 		return 0;
303 	}
304 
305 	return log_halt(sh, sh->log_backend->backend, argc, argv);
306 }
307 
cmd_log_backend_halt(const struct shell * sh,size_t argc,char ** argv)308 static int cmd_log_backend_halt(const struct shell *sh,
309 				size_t argc, char **argv)
310 {
311 	return shell_backend_cmd_execute(sh, argc, argv, log_halt);
312 }
313 
log_go(const struct shell * sh,const struct log_backend * backend,size_t argc,char ** argv)314 static int log_go(const struct shell *sh,
315 		  const struct log_backend *backend,
316 		  size_t argc,
317 		  char **argv)
318 {
319 	log_backend_activate(backend, backend->cb->ctx);
320 	return 0;
321 }
322 
323 
cmd_log_self_go(const struct shell * sh,size_t argc,char ** argv)324 static int cmd_log_self_go(const struct shell *sh,
325 			   size_t argc, char **argv)
326 {
327 	if (!shell_state_precheck(sh)) {
328 		return 0;
329 	}
330 
331 	return log_go(sh, sh->log_backend->backend, argc, argv);
332 }
333 
cmd_log_backend_go(const struct shell * sh,size_t argc,char ** argv)334 static int cmd_log_backend_go(const struct shell *sh,
335 			      size_t argc, char **argv)
336 {
337 	return shell_backend_cmd_execute(sh, argc, argv, log_go);
338 }
339 
340 
cmd_log_backends_list(const struct shell * sh,size_t argc,char ** argv)341 static int cmd_log_backends_list(const struct shell *sh,
342 				 size_t argc, char **argv)
343 {
344 	STRUCT_SECTION_FOREACH(log_backend, backend) {
345 		shell_fprintf(sh, SHELL_NORMAL,
346 			      "%s\r\n"
347 			      "\t- Status: %s\r\n"
348 			      "\t- ID: %d\r\n\r\n",
349 			      backend->name,
350 			      backend->cb->active ? "enabled" : "disabled",
351 			      backend->cb->id);
352 
353 	}
354 	return 0;
355 }
356 
cmd_log_mem(const struct shell * sh,size_t argc,char ** argv)357 static int cmd_log_mem(const struct shell *sh, size_t argc, char **argv)
358 {
359 	uint32_t size;
360 	uint32_t used;
361 	uint32_t max;
362 	int err;
363 
364 	err = log_mem_get_usage(&size, &used);
365 	if (err < 0) {
366 		shell_error(sh, "Failed to get usage (mode does not support it?)");
367 		return -ENOEXEC;
368 	}
369 
370 	shell_print(sh, "Log message buffer utilization report:");
371 	shell_print(sh, "\tCapacity: %u bytes", size);
372 	shell_print(sh, "\tCurrently in use: %u bytes", used);
373 
374 	err = log_mem_get_max_usage(&max);
375 	if (err < 0) {
376 		shell_print(sh, "Enable CONFIG_LOG_MEM_UTILIZATION to get maximum usage");
377 		return 0;
378 	}
379 
380 	shell_print(sh, "\tMaximum usage: %u bytes", max);
381 
382 	return 0;
383 }
384 
385 SHELL_STATIC_SUBCMD_SET_CREATE(sub_log_backend,
386 	SHELL_CMD_ARG(disable, &dsub_module_name,
387 		  "'log disable <module_0> .. <module_n>' disables logs in "
388 		  "specified modules (all if no modules specified).",
389 		  cmd_log_backend_disable, 1, 255),
390 	SHELL_CMD_ARG(enable, &dsub_severity_lvl,
391 		  "'log enable <level> <module_0> ...  <module_n>' enables logs"
392 		  " up to given level in specified modules (all if no modules "
393 		  "specified).",
394 		  cmd_log_backend_enable, 2, 255),
395 	SHELL_CMD(go, NULL, "Resume logging", cmd_log_backend_go),
396 	SHELL_CMD(halt, NULL, "Halt logging", cmd_log_backend_halt),
397 	SHELL_CMD(status, NULL, "Logger status", cmd_log_backend_status),
398 	SHELL_SUBCMD_SET_END
399 );
400 
backend_name_get(size_t idx,struct shell_static_entry * entry)401 static void backend_name_get(size_t idx, struct shell_static_entry *entry)
402 {
403 	uint32_t section_count = 0;
404 
405 	entry->handler = NULL;
406 	entry->help  = NULL;
407 	entry->subcmd = &sub_log_backend;
408 	entry->syntax  = NULL;
409 
410 	STRUCT_SECTION_COUNT(log_backend, &section_count);
411 
412 	if (idx < section_count) {
413 		struct log_backend *backend = NULL;
414 
415 		STRUCT_SECTION_GET(log_backend, idx, &backend);
416 		__ASSERT_NO_MSG(backend != NULL);
417 		entry->syntax = backend->name;
418 	}
419 }
420 
421 SHELL_DYNAMIC_CMD_CREATE(dsub_backend_name_dynamic, backend_name_get);
422 
423 SHELL_STATIC_SUBCMD_SET_CREATE(
424 	sub_log_stat,
425 	SHELL_CMD(backend, &dsub_backend_name_dynamic, "Logger backends commands.", NULL),
426 	SHELL_COND_CMD_ARG(CONFIG_SHELL_LOG_BACKEND, disable, &dsub_module_name,
427 			   "'log disable <module_0> .. <module_n>' disables logs in specified "
428 			   "modules (all if no modules specified).",
429 			   cmd_log_self_disable, 1, 255),
430 	SHELL_COND_CMD_ARG(CONFIG_SHELL_LOG_BACKEND, enable, &dsub_severity_lvl,
431 			   "'log enable <level> <module_0> ...  <module_n>' enables logs up to"
432 			   " given level in specified modules (all if no modules specified).",
433 			   cmd_log_self_enable, 2, 255),
434 	SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, go, NULL, "Resume logging", cmd_log_self_go),
435 	SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, halt, NULL, "Halt logging", cmd_log_self_halt),
436 	SHELL_CMD_ARG(list_backends, NULL, "Lists logger backends.", cmd_log_backends_list, 1, 0),
437 	SHELL_COND_CMD(CONFIG_SHELL_LOG_BACKEND, status, NULL, "Logger status",
438 		       cmd_log_self_status),
439 	SHELL_COND_CMD(CONFIG_LOG_MODE_DEFERRED, mem, NULL, "Logger memory usage",
440 		       cmd_log_mem),
441 	SHELL_SUBCMD_SET_END);
442 
443 SHELL_CMD_REGISTER(log, &sub_log_stat, "Commands for controlling logger",
444 		   NULL);
445