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