1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <shell/shell_log_backend.h>
8 #include <shell/shell.h>
9 #include "shell_ops.h"
10 #include <logging/log_ctrl.h>
11
12 static bool process_msg2_from_buffer(const struct shell *shell);
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
msg_from_fifo(const struct shell_log_backend * backend)20 static struct log_msg *msg_from_fifo(const struct shell_log_backend *backend)
21 {
22 struct shell_log_backend_msg msg;
23 int err;
24
25 err = k_msgq_get(backend->msgq, &msg, K_NO_WAIT);
26
27 return (err == 0) ? msg.msg : NULL;
28 }
29
30 /* Set fifo clean state (in case of deferred mode). */
fifo_reset(const struct shell_log_backend * backend)31 static void fifo_reset(const struct shell_log_backend *backend)
32 {
33 if (IS_ENABLED(CONFIG_LOG2_MODE_DEFERRED)) {
34 mpsc_pbuf_init(backend->mpsc_buffer,
35 backend->mpsc_buffer_config);
36 return;
37 }
38
39 /* Flush pending log messages without processing. */
40 if (IS_ENABLED(CONFIG_LOG_MODE_DEFERRED)) {
41 struct log_msg *msg;
42
43 while ((msg = msg_from_fifo(backend)) != NULL) {
44 log_msg_put(msg);
45 }
46 }
47 }
48
z_shell_log_backend_enable(const struct shell_log_backend * backend,void * ctx,uint32_t init_log_level)49 void z_shell_log_backend_enable(const struct shell_log_backend *backend,
50 void *ctx, uint32_t init_log_level)
51 {
52 int err = 0;
53
54 if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) {
55 const struct shell *shell;
56
57 shell = (const struct shell *)ctx;
58
59 /* Reenable transport in blocking mode */
60 err = shell->iface->api->enable(shell->iface, true);
61 }
62
63 if (err == 0) {
64 fifo_reset(backend);
65 log_backend_enable(backend->backend, ctx, init_log_level);
66 log_output_ctx_set(backend->log_output, ctx);
67 backend->control_block->dropped_cnt = 0;
68 backend->control_block->state = SHELL_LOG_BACKEND_ENABLED;
69 }
70 }
71
flush_expired_messages(const struct shell * shell)72 static void flush_expired_messages(const struct shell *shell)
73 {
74 int err;
75 struct shell_log_backend_msg msg;
76 struct k_msgq *msgq = shell->log_backend->msgq;
77 uint32_t timeout = shell->log_backend->timeout;
78 uint32_t now = k_uptime_get_32();
79
80 while (1) {
81 err = k_msgq_peek(msgq, &msg);
82
83 if (err == 0 && ((now - msg.timestamp) > timeout)) {
84 (void)k_msgq_get(msgq, &msg, K_NO_WAIT);
85 log_msg_put(msg.msg);
86
87 if (IS_ENABLED(CONFIG_SHELL_STATS)) {
88 atomic_inc(&shell->stats->log_lost_cnt);
89 }
90 } else {
91 break;
92 }
93 }
94 }
95
msg_to_fifo(const struct shell * shell,struct log_msg * msg)96 static void msg_to_fifo(const struct shell *shell,
97 struct log_msg *msg)
98 {
99 int err;
100 bool cont;
101 struct shell_log_backend_msg t_msg = {
102 .msg = msg,
103 .timestamp = k_uptime_get_32()
104 };
105
106 do {
107 cont = false;
108 err = k_msgq_put(shell->log_backend->msgq, &t_msg,
109 K_MSEC(shell->log_backend->timeout));
110
111 switch (err) {
112 case 0:
113 break;
114 case -EAGAIN:
115 case -ENOMSG:
116 {
117 /* Attempt to drop old message. */
118 flush_expired_messages(shell);
119
120 /* Retry putting message. */
121 cont = true;
122
123 break;
124 }
125 default:
126 /* Other errors are not expected. */
127 __ASSERT_NO_MSG(0);
128 break;
129 }
130 } while (cont);
131 }
132
z_shell_log_backend_disable(const struct shell_log_backend * backend)133 void z_shell_log_backend_disable(const struct shell_log_backend *backend)
134 {
135 log_backend_disable(backend->backend);
136 backend->control_block->state = SHELL_LOG_BACKEND_DISABLED;
137 }
138
msg_process(const struct log_output * log_output,struct log_msg * msg,bool colors)139 static void msg_process(const struct log_output *log_output,
140 struct log_msg *msg, bool colors)
141 {
142 uint32_t flags = LOG_OUTPUT_FLAG_LEVEL |
143 LOG_OUTPUT_FLAG_TIMESTAMP |
144 LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
145
146 if (colors) {
147 flags |= LOG_OUTPUT_FLAG_COLORS;
148 }
149
150 log_output_msg_process(log_output, msg, flags);
151 log_msg_put(msg);
152 }
153
z_shell_log_backend_process(const struct shell_log_backend * backend)154 bool z_shell_log_backend_process(const struct shell_log_backend *backend)
155 {
156 const struct shell *shell =
157 (const struct shell *)backend->backend->cb->ctx;
158 uint32_t dropped;
159 bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
160 shell->ctx->internal.flags.use_colors;
161
162 dropped = atomic_set(&backend->control_block->dropped_cnt, 0);
163 if (dropped) {
164 struct shell_vt100_colors col;
165
166 if (colors) {
167 z_shell_vt100_colors_store(shell, &col);
168 z_shell_vt100_color_set(shell, SHELL_VT100_COLOR_RED);
169 }
170
171 log_output_dropped_process(backend->log_output, dropped);
172
173 if (colors) {
174 z_shell_vt100_colors_restore(shell, &col);
175 }
176 }
177
178 if (IS_ENABLED(CONFIG_LOG2_MODE_DEFERRED)) {
179 return process_msg2_from_buffer(shell);
180 }
181
182 struct log_msg *msg = msg_from_fifo(backend);
183
184 if (!msg) {
185 return false;
186 }
187 msg_process(shell->log_backend->log_output, msg, colors);
188
189 return true;
190 }
191
put(const struct log_backend * const backend,struct log_msg * msg)192 static void put(const struct log_backend *const backend, struct log_msg *msg)
193 {
194 const struct shell *shell = (const struct shell *)backend->cb->ctx;
195 bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
196 shell->ctx->internal.flags.use_colors;
197 struct k_poll_signal *signal;
198
199 log_msg_get(msg);
200
201 switch (shell->log_backend->control_block->state) {
202 case SHELL_LOG_BACKEND_ENABLED:
203 msg_to_fifo(shell, msg);
204
205 if (IS_ENABLED(CONFIG_MULTITHREADING)) {
206 signal = &shell->ctx->signals[SHELL_SIGNAL_LOG_MSG];
207 k_poll_signal_raise(signal, 0);
208 }
209
210 break;
211 case SHELL_LOG_BACKEND_PANIC:
212 z_shell_cmd_line_erase(shell);
213 msg_process(shell->log_backend->log_output, msg, colors);
214
215 break;
216
217 case SHELL_LOG_BACKEND_DISABLED:
218 __fallthrough;
219 default:
220 /* Discard message. */
221 log_msg_put(msg);
222 }
223 }
224
put_sync_string(const struct log_backend * const backend,struct log_msg_ids src_level,uint32_t timestamp,const char * fmt,va_list ap)225 static void put_sync_string(const struct log_backend *const backend,
226 struct log_msg_ids src_level, uint32_t timestamp,
227 const char *fmt, va_list ap)
228 {
229 const struct shell *shell = (const struct shell *)backend->cb->ctx;
230 uint32_t key;
231 uint32_t flags = LOG_OUTPUT_FLAG_LEVEL |
232 LOG_OUTPUT_FLAG_TIMESTAMP |
233 LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
234
235 if (IS_ENABLED(CONFIG_SHELL_VT100_COLORS)) {
236 flags |= LOG_OUTPUT_FLAG_COLORS;
237 }
238
239 key = irq_lock();
240 if (!z_flag_cmd_ctx_get(shell)) {
241 z_shell_cmd_line_erase(shell);
242 }
243 log_output_string(shell->log_backend->log_output, src_level, timestamp,
244 fmt, ap, flags);
245 if (!z_flag_cmd_ctx_get(shell)) {
246 z_shell_print_prompt_and_cmd(shell);
247 }
248 irq_unlock(key);
249 }
250
put_sync_hexdump(const struct log_backend * const backend,struct log_msg_ids src_level,uint32_t timestamp,const char * metadata,const uint8_t * data,uint32_t length)251 static void put_sync_hexdump(const struct log_backend *const backend,
252 struct log_msg_ids src_level, uint32_t timestamp,
253 const char *metadata, const uint8_t *data,
254 uint32_t length)
255 {
256 const struct shell *shell = (const struct shell *)backend->cb->ctx;
257 uint32_t key;
258 uint32_t flags = LOG_OUTPUT_FLAG_LEVEL |
259 LOG_OUTPUT_FLAG_TIMESTAMP |
260 LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
261
262 if (IS_ENABLED(CONFIG_SHELL_VT100_COLORS)) {
263 flags |= LOG_OUTPUT_FLAG_COLORS;
264 }
265
266 key = irq_lock();
267 if (!z_flag_cmd_ctx_get(shell)) {
268 z_shell_cmd_line_erase(shell);
269 }
270 log_output_hexdump(shell->log_backend->log_output, src_level,
271 timestamp, metadata, data, length, flags);
272 if (!z_flag_cmd_ctx_get(shell)) {
273 z_shell_print_prompt_and_cmd(shell);
274 }
275 irq_unlock(key);
276 }
277
panic(const struct log_backend * const backend)278 static void panic(const struct log_backend *const backend)
279 {
280 const struct shell *shell = (const struct shell *)backend->cb->ctx;
281 int err;
282
283 if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) {
284 return;
285 }
286
287 err = shell->iface->api->enable(shell->iface, true);
288
289 if (err == 0) {
290 shell->log_backend->control_block->state =
291 SHELL_LOG_BACKEND_PANIC;
292 z_flag_panic_mode_set(shell, true);
293
294 /* Move to the start of next line. */
295 z_shell_multiline_data_calc(&shell->ctx->vt100_ctx.cons,
296 shell->ctx->cmd_buff_pos,
297 shell->ctx->cmd_buff_len);
298 z_shell_op_cursor_vert_move(shell, -1);
299 z_shell_op_cursor_horiz_move(shell,
300 -shell->ctx->vt100_ctx.cons.cur_x);
301
302 if (IS_ENABLED(CONFIG_LOG2_MODE_DEFERRED)) {
303 while (process_msg2_from_buffer(shell)) {
304 /* empty */
305 }
306 } else if (IS_ENABLED(CONFIG_LOG_MODE_DEFERRED)) {
307 while (z_shell_log_backend_process(
308 shell->log_backend)) {
309 /* empty */
310 }
311 }
312 } else {
313 z_shell_log_backend_disable(shell->log_backend);
314 }
315 }
316
dropped(const struct log_backend * const backend,uint32_t cnt)317 static void dropped(const struct log_backend *const backend, uint32_t cnt)
318 {
319 const struct shell *shell = (const struct shell *)backend->cb->ctx;
320 const struct shell_log_backend *log_backend = shell->log_backend;
321
322 if (IS_ENABLED(CONFIG_SHELL_STATS)) {
323 atomic_add(&shell->stats->log_lost_cnt, cnt);
324 }
325 atomic_add(&log_backend->control_block->dropped_cnt, cnt);
326 }
327
copy_to_pbuffer(struct mpsc_pbuf_buffer * mpsc_buffer,union log_msg2_generic * msg,uint32_t timeout)328 static void copy_to_pbuffer(struct mpsc_pbuf_buffer *mpsc_buffer,
329 union log_msg2_generic *msg, uint32_t timeout)
330 {
331 size_t wlen;
332 union mpsc_pbuf_generic *dst;
333
334 wlen = log_msg2_generic_get_wlen((union mpsc_pbuf_generic *)msg);
335 dst = mpsc_pbuf_alloc(mpsc_buffer, wlen, K_MSEC(timeout));
336 if (!dst) {
337 /* No space to store the log */
338 return;
339 }
340
341 /* First word contains intenal mpsc packet flags and when copying
342 * those flags must be omitted.
343 */
344 uint8_t *dst_data = (uint8_t *)dst + sizeof(struct mpsc_pbuf_hdr);
345 uint8_t *src_data = (uint8_t *)msg + sizeof(struct mpsc_pbuf_hdr);
346 size_t hdr_wlen = ceiling_fraction(sizeof(struct mpsc_pbuf_hdr),
347 sizeof(uint32_t));
348
349 dst->hdr.data = msg->buf.hdr.data;
350 memcpy(dst_data, src_data, (wlen - hdr_wlen) * sizeof(uint32_t));
351
352 mpsc_pbuf_commit(mpsc_buffer, dst);
353 }
354
process_log_msg2(const struct shell * shell,const struct log_output * log_output,union log_msg2_generic * msg,bool locked,bool colors)355 static void process_log_msg2(const struct shell *shell,
356 const struct log_output *log_output,
357 union log_msg2_generic *msg,
358 bool locked, bool colors)
359 {
360 unsigned int key;
361 uint32_t flags = LOG_OUTPUT_FLAG_LEVEL |
362 LOG_OUTPUT_FLAG_TIMESTAMP |
363 LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
364
365 if (colors) {
366 flags |= LOG_OUTPUT_FLAG_COLORS;
367 }
368
369 if (locked) {
370 key = irq_lock();
371 if (!z_flag_cmd_ctx_get(shell)) {
372 z_shell_cmd_line_erase(shell);
373 }
374 }
375
376 log_output_msg2_process(log_output, &msg->log, flags);
377
378 if (locked) {
379 if (!z_flag_cmd_ctx_get(shell)) {
380 z_shell_print_prompt_and_cmd(shell);
381 }
382 irq_unlock(key);
383 }
384 }
385
process_msg2_from_buffer(const struct shell * shell)386 static bool process_msg2_from_buffer(const struct shell *shell)
387 {
388 const struct shell_log_backend *log_backend = shell->log_backend;
389 struct mpsc_pbuf_buffer *mpsc_buffer = log_backend->mpsc_buffer;
390 const struct log_output *log_output = log_backend->log_output;
391 union log_msg2_generic *msg;
392 bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
393 shell->ctx->internal.flags.use_colors;
394
395 msg = (union log_msg2_generic *)mpsc_pbuf_claim(mpsc_buffer);
396 if (!msg) {
397 return false;
398 }
399
400 process_log_msg2(shell, log_output, msg, false, colors);
401
402 mpsc_pbuf_free(mpsc_buffer, &msg->buf);
403
404 return true;
405 }
406
log2_process(const struct log_backend * const backend,union log_msg2_generic * msg)407 static void log2_process(const struct log_backend *const backend,
408 union log_msg2_generic *msg)
409 {
410 const struct shell *shell = (const struct shell *)backend->cb->ctx;
411 const struct shell_log_backend *log_backend = shell->log_backend;
412 struct mpsc_pbuf_buffer *mpsc_buffer = log_backend->mpsc_buffer;
413 const struct log_output *log_output = log_backend->log_output;
414 bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
415 shell->ctx->internal.flags.use_colors;
416 struct k_poll_signal *signal;
417
418 switch (shell->log_backend->control_block->state) {
419 case SHELL_LOG_BACKEND_ENABLED:
420 if (IS_ENABLED(CONFIG_LOG_IMMEDIATE)) {
421 process_log_msg2(shell, log_output, msg, true, colors);
422 } else {
423 copy_to_pbuffer(mpsc_buffer, msg,
424 log_backend->timeout);
425
426 if (IS_ENABLED(CONFIG_MULTITHREADING)) {
427 signal =
428 &shell->ctx->signals[SHELL_SIGNAL_LOG_MSG];
429 k_poll_signal_raise(signal, 0);
430 }
431 }
432
433 break;
434 case SHELL_LOG_BACKEND_PANIC:
435 z_shell_cmd_line_erase(shell);
436 process_log_msg2(shell, log_output, msg, true, colors);
437
438 break;
439
440 case SHELL_LOG_BACKEND_DISABLED:
441 __fallthrough;
442 default:
443 break;
444 }
445 }
446
447 const struct log_backend_api log_backend_shell_api = {
448 .process = IS_ENABLED(CONFIG_LOG2) ? log2_process : NULL,
449 .put = IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? put : NULL,
450 .put_sync_string = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
451 put_sync_string : NULL,
452 .put_sync_hexdump = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
453 put_sync_hexdump : NULL,
454 .dropped = dropped,
455 .panic = panic,
456 };
457