1 /*
2  * Copyright (c) 2018 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/shell/shell_log_backend.h>
8 #include <zephyr/shell/shell.h>
9 #include "shell_ops.h"
10 #include <zephyr/logging/log_ctrl.h>
11 
12 static bool process_msg_from_buffer(const struct shell *sh);
13 
z_shell_log_backend_output_func(uint8_t * data,size_t length,void * ctx)14 int z_shell_log_backend_output_func(uint8_t *data, size_t length, void *ctx)
15 {
16 	z_shell_print_stream(ctx, data, length);
17 	return length;
18 }
19 
20 /* Set fifo clean state (in case of deferred mode). */
fifo_reset(const struct shell_log_backend * backend)21 static void fifo_reset(const struct shell_log_backend *backend)
22 {
23 	mpsc_pbuf_init(backend->mpsc_buffer, backend->mpsc_buffer_config);
24 }
25 
z_shell_log_backend_enable(const struct shell_log_backend * backend,void * ctx,uint32_t init_log_level)26 void z_shell_log_backend_enable(const struct shell_log_backend *backend,
27 				void *ctx, uint32_t init_log_level)
28 {
29 	int err = 0;
30 
31 	if (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) {
32 		const struct shell *sh;
33 
34 		sh = (const struct shell *)ctx;
35 
36 		z_flag_sync_mode_set(sh, true);
37 		/* Reenable transport in blocking mode */
38 		err = sh->iface->api->enable(sh->iface, true);
39 	}
40 
41 	if (err == 0) {
42 		fifo_reset(backend);
43 		log_backend_enable(backend->backend, ctx, init_log_level);
44 		log_output_ctx_set(backend->log_output, ctx);
45 		backend->control_block->dropped_cnt = 0;
46 		backend->control_block->state = SHELL_LOG_BACKEND_ENABLED;
47 	}
48 }
49 
z_shell_log_backend_disable(const struct shell_log_backend * backend)50 void z_shell_log_backend_disable(const struct shell_log_backend *backend)
51 {
52 	log_backend_disable(backend->backend);
53 	backend->control_block->state = SHELL_LOG_BACKEND_DISABLED;
54 }
55 
z_shell_log_backend_process(const struct shell_log_backend * backend)56 bool z_shell_log_backend_process(const struct shell_log_backend *backend)
57 {
58 	const struct shell *sh =
59 			(const struct shell *)backend->backend->cb->ctx;
60 	uint32_t dropped;
61 	bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
62 			z_flag_use_colors_get(sh);
63 
64 	dropped = atomic_set(&backend->control_block->dropped_cnt, 0);
65 	if (dropped) {
66 		struct shell_vt100_colors col;
67 
68 		if (colors) {
69 			z_shell_vt100_colors_store(sh, &col);
70 			z_shell_vt100_color_set(sh, SHELL_VT100_COLOR_RED);
71 		}
72 
73 		log_output_dropped_process(backend->log_output, dropped);
74 
75 		if (colors) {
76 			z_shell_vt100_colors_restore(sh, &col);
77 		}
78 	}
79 
80 	return process_msg_from_buffer(sh);
81 }
82 
panic(const struct log_backend * const backend)83 static void panic(const struct log_backend *const backend)
84 {
85 	const struct shell *sh = (const struct shell *)backend->cb->ctx;
86 	int err;
87 
88 	if (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) {
89 		return;
90 	}
91 
92 	err = sh->iface->api->enable(sh->iface, true);
93 
94 	if (err == 0) {
95 		sh->log_backend->control_block->state =
96 						SHELL_LOG_BACKEND_PANIC;
97 		z_flag_sync_mode_set(sh, true);
98 
99 		/* Move to the start of next line. */
100 		z_shell_multiline_data_calc(&sh->ctx->vt100_ctx.cons,
101 					    sh->ctx->cmd_buff_pos,
102 					    sh->ctx->cmd_buff_len);
103 		z_shell_op_cursor_vert_move(sh, -1);
104 		z_shell_op_cursor_horiz_move(sh,
105 					   -sh->ctx->vt100_ctx.cons.cur_x);
106 
107 		while (process_msg_from_buffer(sh)) {
108 			/* empty */
109 		}
110 	} else {
111 		z_shell_log_backend_disable(sh->log_backend);
112 	}
113 }
114 
dropped(const struct log_backend * const backend,uint32_t cnt)115 static void dropped(const struct log_backend *const backend, uint32_t cnt)
116 {
117 	const struct shell *sh = (const struct shell *)backend->cb->ctx;
118 	const struct shell_log_backend *log_backend = sh->log_backend;
119 
120 	if (IS_ENABLED(CONFIG_SHELL_STATS)) {
121 		atomic_add(&sh->stats->log_lost_cnt, cnt);
122 	}
123 	atomic_add(&log_backend->control_block->dropped_cnt, cnt);
124 }
125 
copy_to_pbuffer(struct mpsc_pbuf_buffer * mpsc_buffer,union log_msg_generic * msg,uint32_t timeout)126 static bool copy_to_pbuffer(struct mpsc_pbuf_buffer *mpsc_buffer,
127 			    union log_msg_generic *msg, uint32_t timeout)
128 {
129 	size_t wlen;
130 	union mpsc_pbuf_generic *dst;
131 
132 	wlen = log_msg_generic_get_wlen((union mpsc_pbuf_generic *)msg);
133 	dst = mpsc_pbuf_alloc(mpsc_buffer, wlen, K_MSEC(timeout));
134 	if (!dst) {
135 		/* No space to store the log */
136 		return false;
137 	}
138 
139 	/* First word contains internal mpsc packet flags and when copying
140 	 * those flags must be omitted.
141 	 */
142 	uint8_t *dst_data = (uint8_t *)dst + sizeof(struct mpsc_pbuf_hdr);
143 	uint8_t *src_data = (uint8_t *)msg + sizeof(struct mpsc_pbuf_hdr);
144 	size_t hdr_wlen = DIV_ROUND_UP(sizeof(struct mpsc_pbuf_hdr),
145 					   sizeof(uint32_t));
146 	if (wlen <= hdr_wlen) {
147 		return false;
148 	}
149 
150 	dst->hdr.data = msg->buf.hdr.data;
151 	memcpy(dst_data, src_data, (wlen - hdr_wlen) * sizeof(uint32_t));
152 
153 	mpsc_pbuf_commit(mpsc_buffer, dst);
154 
155 	return true;
156 }
157 
158 /* Compile-time computed shell log output flags */
159 #define SHELL_LOG_BASE_FLAGS                                                                       \
160 	((IS_ENABLED(CONFIG_SHELL_LOG_OUTPUT_TIMESTAMP) ? LOG_OUTPUT_FLAG_TIMESTAMP : 0) |         \
161 	 (IS_ENABLED(CONFIG_SHELL_LOG_FORMAT_TIMESTAMP) ? LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP : 0) |  \
162 	 (IS_ENABLED(CONFIG_SHELL_LOG_OUTPUT_LEVEL) ? LOG_OUTPUT_FLAG_LEVEL : 0) |                 \
163 	 (IS_ENABLED(CONFIG_SHELL_LOG_OUTPUT_CRLF_NONE) ? LOG_OUTPUT_FLAG_CRLF_NONE : 0) |         \
164 	 (IS_ENABLED(CONFIG_SHELL_LOG_OUTPUT_CRLF_LFONLY) ? LOG_OUTPUT_FLAG_CRLF_LFONLY : 0) |     \
165 	 (IS_ENABLED(CONFIG_SHELL_LOG_OUTPUT_THREAD) ? LOG_OUTPUT_FLAG_THREAD : 0) |               \
166 	 (IS_ENABLED(CONFIG_SHELL_LOG_OUTPUT_SKIP_SOURCE) ? LOG_OUTPUT_FLAG_SKIP_SOURCE : 0))
167 
process_log_msg(const struct shell * sh,const struct log_output * log_output,union log_msg_generic * msg,bool locked,bool colors)168 static void process_log_msg(const struct shell *sh,
169 			     const struct log_output *log_output,
170 			     union log_msg_generic *msg,
171 			     bool locked, bool colors)
172 {
173 	unsigned int key = 0;
174 	uint32_t flags = SHELL_LOG_BASE_FLAGS;
175 
176 	if (colors) {
177 		flags |= LOG_OUTPUT_FLAG_COLORS;
178 	}
179 
180 	if (locked) {
181 		/*
182 		 * If running in the thread context, lock the shell mutex to synchronize with
183 		 * messages printed on the shell thread. In the ISR context, using a mutex is
184 		 * forbidden so use the IRQ lock to at least synchronize log messages printed
185 		 * in different contexts.
186 		 */
187 		if (k_is_in_isr()) {
188 			key = irq_lock();
189 		} else {
190 			z_shell_lock(sh);
191 		}
192 		if (!z_flag_cmd_ctx_get(sh)) {
193 			z_shell_cmd_line_erase(sh);
194 		}
195 	}
196 
197 	log_output_msg_process(log_output, &msg->log, flags);
198 
199 	if (locked) {
200 		if (!z_flag_cmd_ctx_get(sh)) {
201 			z_shell_print_prompt_and_cmd(sh);
202 		}
203 		if (k_is_in_isr()) {
204 			irq_unlock(key);
205 		} else {
206 			z_shell_unlock(sh);
207 		}
208 	}
209 }
210 
process_msg_from_buffer(const struct shell * sh)211 static bool process_msg_from_buffer(const struct shell *sh)
212 {
213 	const struct shell_log_backend *log_backend = sh->log_backend;
214 	struct mpsc_pbuf_buffer *mpsc_buffer = log_backend->mpsc_buffer;
215 	const struct log_output *log_output = log_backend->log_output;
216 	union log_msg_generic *msg;
217 	bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
218 			z_flag_use_colors_get(sh);
219 
220 	msg = (union log_msg_generic *)mpsc_pbuf_claim(mpsc_buffer);
221 	if (!msg) {
222 		return false;
223 	}
224 
225 	process_log_msg(sh, log_output, msg, false, colors);
226 
227 	mpsc_pbuf_free(mpsc_buffer, &msg->buf);
228 
229 	return true;
230 }
231 
process(const struct log_backend * const backend,union log_msg_generic * msg)232 static void process(const struct log_backend *const backend,
233 		    union log_msg_generic *msg)
234 {
235 	const struct shell *sh = (const struct shell *)backend->cb->ctx;
236 	const struct shell_log_backend *log_backend = sh->log_backend;
237 	struct mpsc_pbuf_buffer *mpsc_buffer = log_backend->mpsc_buffer;
238 	const struct log_output *log_output = log_backend->log_output;
239 	bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
240 			z_flag_use_colors_get(sh);
241 
242 	switch (sh->log_backend->control_block->state) {
243 	case SHELL_LOG_BACKEND_ENABLED:
244 		if (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) {
245 			process_log_msg(sh, log_output, msg, true, colors);
246 		} else {
247 			if (copy_to_pbuffer(mpsc_buffer, msg, log_backend->timeout)) {
248 				if (IS_ENABLED(CONFIG_MULTITHREADING)) {
249 					k_event_post(&sh->ctx->signal_event,
250 						     SHELL_SIGNAL_LOG_MSG);
251 				}
252 			} else {
253 				dropped(backend, 1);
254 			}
255 		}
256 
257 		break;
258 	case SHELL_LOG_BACKEND_PANIC:
259 		z_shell_cmd_line_erase(sh);
260 		process_log_msg(sh, log_output, msg, true, colors);
261 
262 		break;
263 
264 	case SHELL_LOG_BACKEND_DISABLED:
265 		__fallthrough;
266 	default:
267 		break;
268 	}
269 }
270 
271 const struct log_backend_api log_backend_shell_api = {
272 	.process = process,
273 	.dropped = dropped,
274 	.panic = panic,
275 };
276