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