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