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
process_log_msg(const struct shell * sh,const struct log_output * log_output,union log_msg_generic * msg,bool locked,bool colors)158 static void process_log_msg(const struct shell *sh,
159 const struct log_output *log_output,
160 union log_msg_generic *msg,
161 bool locked, bool colors)
162 {
163 unsigned int key = 0;
164 uint32_t flags = LOG_OUTPUT_FLAG_LEVEL |
165 LOG_OUTPUT_FLAG_TIMESTAMP |
166 (IS_ENABLED(CONFIG_SHELL_LOG_FORMAT_TIMESTAMP) ?
167 LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP : 0);
168
169 if (colors) {
170 flags |= LOG_OUTPUT_FLAG_COLORS;
171 }
172
173 if (locked) {
174 /*
175 * If running in the thread context, lock the shell mutex to synchronize with
176 * messages printed on the shell thread. In the ISR context, using a mutex is
177 * forbidden so use the IRQ lock to at least synchronize log messages printed
178 * in different contexts.
179 */
180 if (k_is_in_isr()) {
181 key = irq_lock();
182 } else {
183 k_mutex_lock(&sh->ctx->wr_mtx, K_FOREVER);
184 }
185 if (!z_flag_cmd_ctx_get(sh)) {
186 z_shell_cmd_line_erase(sh);
187 }
188 }
189
190 log_output_msg_process(log_output, &msg->log, flags);
191
192 if (locked) {
193 if (!z_flag_cmd_ctx_get(sh)) {
194 z_shell_print_prompt_and_cmd(sh);
195 }
196 if (k_is_in_isr()) {
197 irq_unlock(key);
198 } else {
199 k_mutex_unlock(&sh->ctx->wr_mtx);
200 }
201 }
202 }
203
process_msg_from_buffer(const struct shell * sh)204 static bool process_msg_from_buffer(const struct shell *sh)
205 {
206 const struct shell_log_backend *log_backend = sh->log_backend;
207 struct mpsc_pbuf_buffer *mpsc_buffer = log_backend->mpsc_buffer;
208 const struct log_output *log_output = log_backend->log_output;
209 union log_msg_generic *msg;
210 bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
211 z_flag_use_colors_get(sh);
212
213 msg = (union log_msg_generic *)mpsc_pbuf_claim(mpsc_buffer);
214 if (!msg) {
215 return false;
216 }
217
218 process_log_msg(sh, log_output, msg, false, colors);
219
220 mpsc_pbuf_free(mpsc_buffer, &msg->buf);
221
222 return true;
223 }
224
process(const struct log_backend * const backend,union log_msg_generic * msg)225 static void process(const struct log_backend *const backend,
226 union log_msg_generic *msg)
227 {
228 const struct shell *sh = (const struct shell *)backend->cb->ctx;
229 const struct shell_log_backend *log_backend = sh->log_backend;
230 struct mpsc_pbuf_buffer *mpsc_buffer = log_backend->mpsc_buffer;
231 const struct log_output *log_output = log_backend->log_output;
232 bool colors = IS_ENABLED(CONFIG_SHELL_VT100_COLORS) &&
233 z_flag_use_colors_get(sh);
234 struct k_poll_signal *signal;
235
236 switch (sh->log_backend->control_block->state) {
237 case SHELL_LOG_BACKEND_ENABLED:
238 if (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) {
239 process_log_msg(sh, log_output, msg, true, colors);
240 } else {
241 if (copy_to_pbuffer(mpsc_buffer, msg, log_backend->timeout)) {
242 if (IS_ENABLED(CONFIG_MULTITHREADING)) {
243 signal = &sh->ctx->signals[SHELL_SIGNAL_LOG_MSG];
244 k_poll_signal_raise(signal, 0);
245 }
246 } else {
247 dropped(backend, 1);
248 }
249 }
250
251 break;
252 case SHELL_LOG_BACKEND_PANIC:
253 z_shell_cmd_line_erase(sh);
254 process_log_msg(sh, log_output, msg, true, colors);
255
256 break;
257
258 case SHELL_LOG_BACKEND_DISABLED:
259 __fallthrough;
260 default:
261 break;
262 }
263 }
264
265 const struct log_backend_api log_backend_shell_api = {
266 .process = process,
267 .dropped = dropped,
268 .panic = panic,
269 };
270