1 /*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * Bernard the first version
9 * 2013-06-26 Grissiom refactor
10 */
11
12 #include <rtthread.h>
13 #include <rthw.h>
14 #include <stdio.h>
15 #include "log_trace.h"
16
17 #ifdef RT_USING_FINSH
18 #include <finsh.h>
19 #else
20 #define FINSH_FUNCTION_EXPORT(...)
21 #define FINSH_FUNCTION_EXPORT_ALIAS(...)
22 #endif
23
24 /* log pseudo device */
25 static struct rt_device _log_device;
26
27 static rt_device_t _traceout_device = RT_NULL;
28
29 /* define a default lg session. The name is empty. */
30 static struct log_trace_session _def_session = {{"\0"}, LOG_TRACE_LEVEL_DEFAULT};
31 static const struct log_trace_session *_the_sessions[LOG_TRACE_MAX_SESSION] = {&_def_session};
32 /* there is a default session at least */
33 static rt_uint16_t _the_sess_nr = 1;
34
_idname_len(log_trace_idnum_t id)35 rt_inline int _idname_len(log_trace_idnum_t id)
36 {
37 /* little endian */
38 if ((id & 0x000000FF) == 0)
39 return 0;
40 if ((id & 0x0000FF00) == 0)
41 return 1;
42 if ((id & 0x00FF0000) == 0)
43 return 2;
44 if ((id & 0xFF000000) == 0)
45 return 3;
46 #ifndef LOG_TRACE_USE_LONGNAME
47 return 4;
48 #else
49 {
50 rt_uint32_t id2 = id >> 32;
51 if ((id2 & 0x000000FF) == 0)
52 return 4;
53 if ((id2 & 0x0000FF00) == 0)
54 return 5;
55 if ((id2 & 0x00FF0000) == 0)
56 return 6;
57 if ((id2 & 0xFF000000) == 0)
58 return 7;
59 return 8;
60 }
61 #endif
62 }
63
64 /* lookup the session according to name.
65 *
66 * @param len is the length of the name
67 * @return the pointer to the named session. RT_NULL when there is no such a
68 * session.
69 */
_lg_lookup_session(log_trace_idnum_t num)70 static struct log_trace_session* _lg_lookup_session(log_trace_idnum_t num)
71 {
72 static const struct log_trace_session *_cache = &_def_session;
73 rt_uint16_t first, last;
74
75 if (_cache->id.num == num)
76 return (struct log_trace_session *)_cache;
77
78 first = 0;
79 last = _the_sess_nr;
80 do {
81 unsigned int i = (first + last)/2;
82
83 RT_ASSERT(_the_sessions[i]);
84
85 if (_the_sessions[i]->id.num == num)
86 {
87 /* there is no need to protect the _cache because write a pointer
88 * is atomic. So we cannot get a invalid pointer. The worst thing
89 * could happen is there is an interrupt in the read/modify/write
90 * process and we wrote the old one to _cache. But it doesn't harm
91 * a lot because it will be flushed in the next time. */
92 _cache = _the_sessions[i];
93 return (struct log_trace_session *)_the_sessions[i];
94 }
95 else if (_the_sessions[i]->id.num > num)
96 {
97 last = i;
98 }
99 else // _the_sessions[i]->id.num < num
100 {
101 first = i;
102 }
103 } while (first != last-1);
104
105 return RT_NULL;
106 }
107
log_trace_register_session(const struct log_trace_session * session)108 rt_err_t log_trace_register_session(const struct log_trace_session *session)
109 {
110 unsigned int lvl, i;
111
112 if (_the_sess_nr == LOG_TRACE_MAX_SESSION)
113 return -RT_EFULL;
114
115 if (session == RT_NULL)
116 return RT_EOK;
117
118 lvl = rt_hw_interrupt_disable();
119 /* inserting the sessions in ascending order.
120 *
121 * this might take relatively long time. But since the register should only
122 * happen when initialize the whole system, this should not be a matter. */
123 for (i = 0; i < _the_sess_nr; i++)
124 {
125 if (_the_sessions[i]->id.num > session->id.num)
126 {
127 rt_memmove(_the_sessions+i+1, _the_sessions+i,
128 (_the_sess_nr-i)*sizeof(&_the_sessions[0]));
129 _the_sessions[i] = session;
130 break;
131 }
132 else if (_the_sessions[i]->id.num == session->id.num)
133 {
134 rt_kprintf("registering session 0x%p twice\n", session);
135 rt_hw_interrupt_enable(lvl);
136 return -RT_ERROR;
137 }
138 }
139 if (i == _the_sess_nr)
140 _the_sessions[i] = session;
141 _the_sess_nr++;
142 rt_hw_interrupt_enable(lvl);
143
144 return RT_EOK;
145 }
146
log_trace_session_find(const char * name)147 struct log_trace_session* log_trace_session_find(const char *name)
148 {
149 union log_trace_id *idp;
150
151 RT_ASSERT(name);
152 idp = (union log_trace_id*)name;
153 return _lg_lookup_session(idp->num);
154 }
155
log_trace_set_level(rt_uint8_t level)156 void log_trace_set_level(rt_uint8_t level)
157 {
158 _def_session.lvl = level;
159 }
160 FINSH_FUNCTION_EXPORT_ALIAS(log_trace_set_level, log_level, set the filter level of log trace);
161
log_trace_session_set_level(struct log_trace_session * sess,rt_uint8_t level)162 void log_trace_session_set_level(struct log_trace_session *sess, rt_uint8_t level)
163 {
164 RT_ASSERT(sess);
165 sess->lvl = level;
166 }
167
168 /* parse the level info in fmt
169 *
170 * @param flen the length of the format.
171 * @param lvlp the pointer to level. It will store the level in the memory the
172 * lvlp points to. The default value is LOG_TRACE_LEVEL_DEFAULT.
173 * @return the number of char it scaned.
174 */
_lg_parse_lvl(const char * fmt,rt_size_t flen,int * lvlp)175 static rt_size_t _lg_parse_lvl(const char *fmt, rt_size_t flen, int *lvlp)
176 {
177 RT_ASSERT(fmt);
178 RT_ASSERT(lvlp);
179
180 /* setup default value */
181 *lvlp = LOG_TRACE_LEVEL_DEFAULT;
182
183 if (flen < 3)
184 {
185 return 0;
186 }
187
188 if (fmt[0] == '<' && fmt[2] == '>')
189 {
190 *lvlp = fmt[1] - '0';
191 return 3;
192 }
193 return 0;
194 }
195
196 /* parse the header in fmt
197 *
198 * @param flen the length of the format.
199 * @param sessp the pointer of pointer to the session. It will store the
200 * session pointer in the memory the sessp points to. When failed to
201 * find the session, it will be setted to the default session.
202 * @return the number of char it scaned, i.e., the length of header.
203 */
_lg_parse_session(const char * fmt,rt_size_t flen,struct log_trace_session ** sessp)204 static rt_size_t _lg_parse_session(
205 const char *fmt, rt_size_t flen, struct log_trace_session **sessp)
206 {
207 unsigned int i;
208 struct log_trace_session *tmpsess;
209 union log_trace_id id;
210
211 RT_ASSERT(fmt);
212 RT_ASSERT(sessp);
213
214 /* setup default value */
215 *sessp = &_def_session;
216
217 /* no name space left */
218 if (flen < sizeof(id) + 2)
219 return 0;
220
221 if (fmt[0] != '[')
222 return 0;
223
224 id.num = 0;
225 /* skip '[' and convert the string to id number. */
226 for (i = 1; fmt[i] != ']'; i++)
227 {
228 if (i - 1 == sizeof(id))
229 return 0;
230 id.name[i-1] = fmt[i];
231 }
232 tmpsess = _lg_lookup_session(id.num);
233 if (tmpsess != RT_NULL)
234 {
235 *sessp = tmpsess;
236 /* only count the header length when we found the session. So
237 * the wrong [name] will be printed out. */
238 return i + 1;
239 }
240
241 return 0;
242 }
243
__logtrace_vfmtout(const struct log_trace_session * session,const char * fmt,va_list argptr)244 void __logtrace_vfmtout(const struct log_trace_session *session,
245 const char *fmt,
246 va_list argptr)
247 {
248 /* 1 for ']' */
249 static char _trace_buf[1+LOG_TRACE_BUFSZ];
250 char *ptr;
251 rt_size_t length;
252
253 RT_ASSERT(session);
254 RT_ASSERT(fmt);
255
256 /* it's default session */
257 if (session->id.name[0] == '\0')
258 {
259 rt_snprintf(_trace_buf, sizeof(_trace_buf), "[%08x]", rt_tick_get());
260 if (_traceout_device != RT_NULL)
261 {
262 rt_device_write(_traceout_device, -1, _trace_buf, 10);
263 }
264
265 ptr = &_trace_buf[0];
266 }
267 else
268 {
269 rt_snprintf(_trace_buf, sizeof(_trace_buf), "[%08x][", rt_tick_get());
270 if (_traceout_device != RT_NULL)
271 {
272 rt_device_write(_traceout_device, -1, _trace_buf, 11);
273 rt_device_write(_traceout_device, -1,
274 session->id.name, _idname_len(session->id.num));
275 }
276
277 _trace_buf[0] = ']';
278 ptr = &_trace_buf[1];
279 }
280
281 length = rt_vsnprintf(ptr, LOG_TRACE_BUFSZ, fmt, argptr);
282
283 if (length >= LOG_TRACE_BUFSZ)
284 length = LOG_TRACE_BUFSZ - 1;
285
286 if (_traceout_device != RT_NULL)
287 {
288 rt_device_write(_traceout_device, -1, _trace_buf, length + 1);
289 }
290 }
291
log_trace(const char * fmt,...)292 void log_trace(const char *fmt, ...)
293 {
294 va_list args;
295 int level;
296 struct log_trace_session *session;
297
298 RT_ASSERT(fmt);
299
300 fmt += _lg_parse_lvl(fmt, strlen(fmt), &level);
301 fmt += _lg_parse_session(fmt, strlen(fmt), &session);
302
303 /* filter by level */
304 if (level > session->lvl)
305 return;
306
307 va_start(args, fmt);
308 __logtrace_vfmtout(session, fmt, args);
309 va_end(args);
310 }
311 FINSH_FUNCTION_EXPORT(log_trace, log trace);
312
log_session(const struct log_trace_session * session,const char * fmt,...)313 void log_session(const struct log_trace_session *session, const char *fmt, ...)
314 {
315 va_list args;
316 int level;
317
318 RT_ASSERT(session);
319 RT_ASSERT(fmt);
320
321 fmt += _lg_parse_lvl(fmt, strlen(fmt), &level);
322 if (level > session->lvl)
323 return;
324
325 va_start(args, fmt);
326 __logtrace_vfmtout(session, fmt, args);
327 va_end(args);
328 }
329
log_trace_flush(void)330 void log_trace_flush(void)
331 {
332 rt_device_control(_traceout_device, LOG_TRACE_CTRL_FLUSH, RT_NULL);
333 }
334 FINSH_FUNCTION_EXPORT_ALIAS(log_trace_flush, log_flush, flush log on the buffer);
335
336 /* RT-Thread common device interface */
_log_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)337 static rt_size_t _log_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
338 {
339 char c;
340 int level;
341 rt_size_t head_len;
342 const char *ptr = buffer;
343 struct log_trace_session *session;
344
345 head_len = _lg_parse_lvl(ptr, size, &level);
346 head_len += _lg_parse_session(ptr+head_len, size-head_len, &session);
347
348 /* filter by level */
349 if (level > session->lvl)
350 return size;
351
352 if (_traceout_device != RT_NULL)
353 {
354 c = '[';
355 rt_device_write(_traceout_device, -1, &c, 1);
356 rt_device_write(_traceout_device, -1, session->id.name, _idname_len(session->id.num));
357 c = ']';
358 rt_device_write(_traceout_device, -1, &c, 1);
359 rt_device_write(_traceout_device, -1, ((char*)buffer)+head_len, size - head_len);
360 }
361
362 return size;
363 }
364
_log_control(rt_device_t dev,int cmd,void * arg)365 static rt_err_t _log_control(rt_device_t dev, int cmd, void *arg)
366 {
367 if (_traceout_device == RT_NULL) return -RT_ERROR;
368
369 return rt_device_control(_traceout_device, cmd, arg);
370 }
371
372 #ifdef RT_USING_DEVICE_OPS
373 const static struct rt_device_ops log_device_ops =
374 {
375 RT_NULL,
376 RT_NULL,
377 RT_NULL,
378 RT_NULL,
379 _log_write,
380 _log_control
381 };
382 #endif
383
log_trace_init(void)384 int log_trace_init(void)
385 {
386 rt_memset(&_log_device, 0x00, sizeof(_log_device));
387
388 _log_device.type = RT_Device_Class_Char;
389 #ifdef RT_USING_DEVICE_OPS
390 _log_device.ops = &log_device_ops;
391 #else
392 _log_device.init = RT_NULL;
393 _log_device.open = RT_NULL;
394 _log_device.close = RT_NULL;
395 _log_device.read = RT_NULL;
396 _log_device.write = _log_write;
397 _log_device.control = _log_control;
398 #endif
399
400 /* no indication and complete callback */
401 _log_device.rx_indicate = RT_NULL;
402 _log_device.tx_complete = RT_NULL;
403
404 rt_device_register(&_log_device, "log", RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_RDWR);
405
406 /* set console as default device */
407 _traceout_device = rt_console_get_device();
408
409 return 0;
410 }
411 INIT_DEVICE_EXPORT(log_trace_init);
412
log_trace_get_device(void)413 rt_device_t log_trace_get_device(void)
414 {
415 return _traceout_device;
416 }
417
log_trace_set_device(const char * device_name)418 rt_err_t log_trace_set_device(const char *device_name)
419 {
420 struct rt_device *output_device;
421
422 /* find out output device */
423 output_device = rt_device_find(device_name);
424 if (output_device != RT_NULL)
425 {
426 rt_err_t result;
427
428 /* open device */
429 result = rt_device_open(output_device, RT_DEVICE_FLAG_STREAM | RT_DEVICE_OFLAG_RDWR);
430 if (result != RT_EOK)
431 {
432 rt_kprintf("Open trace device failed.\n");
433 return -RT_ERROR;
434 }
435 }
436
437 /* set trace out device */
438 if (_traceout_device != RT_NULL)
439 rt_device_close(_traceout_device);
440 _traceout_device = output_device;
441
442 return RT_EOK;
443 }
444 FINSH_FUNCTION_EXPORT_ALIAS(log_trace_set_device, log_device, set device of log trace);
445
446