xref: /nrf52832-nimble/rt-thread/components/utilities/logtrace/log_trace.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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 
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  */
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 
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 
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 
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 
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  */
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  */
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 
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 
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 
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 
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 */
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 
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 
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 
413 rt_device_t log_trace_get_device(void)
414 {
415     return _traceout_device;
416 }
417 
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