1 /* EINA - EFL data type library
2 * Copyright (C) 2007-2009 Jorge Luis Zapata Muga, Cedric Bail, Andre Dieb
3 * Martins
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library;
17 * if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <fnmatch.h>
29 #include <assert.h>
30 #include <errno.h>
31
32 #ifdef _WIN32
33 # include <windows.h>
34 #endif
35
36 #ifdef HAVE_EXECINFO_H
37 # include <execinfo.h>
38 #endif
39
40 #include "eina_debug_private.h"
41
42 #ifdef HAVE_BACKTRACE
43 #define EINA_LOG_BACKTRACE
44 #endif
45
46 #include "eina_config.h"
47 #include "eina_private.h"
48 #include "eina_inlist.h"
49 #include "eina_lock.h"
50 #include "eina_thread.h"
51 #include "eina_convert.h"
52 #include "eina_strbuf.h"
53 #include "eina_module.h"
54
55 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
56 #include "eina_safety_checks.h"
57 #include "eina_log.h"
58
59 #include "eina_inline_private.h"
60
61 /* TODO
62 * + printing logs to stdout or stderr can be implemented
63 * using a queue, useful for multiple threads printing
64 * + add a wrapper for assert?
65 */
66
67 /*============================================================================*
68 * Local *
69 *============================================================================*/
70
71 /**
72 * @cond LOCAL
73 */
74
75 #define EINA_LOG_ENV_ABORT "EINA_LOG_ABORT"
76 #define EINA_LOG_ENV_ABORT_LEVEL "EINA_LOG_ABORT_LEVEL"
77 #define EINA_LOG_ENV_LEVEL "EINA_LOG_LEVEL"
78 #define EINA_LOG_ENV_LEVELS "EINA_LOG_LEVELS"
79 #define EINA_LOG_ENV_LEVELS_GLOB "EINA_LOG_LEVELS_GLOB"
80 #define EINA_LOG_ENV_COLOR_DISABLE "EINA_LOG_COLOR_DISABLE"
81 #define EINA_LOG_ENV_FILE_DISABLE "EINA_LOG_FILE_DISABLE"
82 #define EINA_LOG_ENV_FUNCTION_DISABLE "EINA_LOG_FUNCTION_DISABLE"
83 #define EINA_LOG_ENV_BACKTRACE "EINA_LOG_BACKTRACE"
84
85 #ifdef EINA_ENABLE_LOG
86
87 // Structure for storing domain level settings passed from the command line
88 // that will be matched with application-defined domains.
89 typedef struct _Eina_Log_Domain_Level_Pending Eina_Log_Domain_Level_Pending;
90 struct _Eina_Log_Domain_Level_Pending
91 {
92 EINA_INLIST;
93 unsigned int level;
94 size_t namelen;
95 char name[];
96 };
97
98 typedef struct _Eina_Log_Timing Eina_Log_Timing;
99 struct _Eina_Log_Timing
100 {
101 const char *phase;
102 Eina_Nano_Time start;
103 Eina_Log_State state;
104 };
105
106 EAPI const char *_eina_log_state_init = "init";
107 EAPI const char *_eina_log_state_shutdown = "shutdown";
108
109 /*
110 * List of levels for domains set by the user before the domains are registered,
111 * updates the domain levels on the first log and clears itself.
112 */
113 static Eina_Inlist *_pending_list = NULL;
114 static Eina_Inlist *_glob_list = NULL;
115
116 // Disable color flag (can be changed through the env var
117 // EINA_LOG_ENV_COLOR_DISABLE).
118 static Eina_Bool _disable_color = EINA_FALSE;
119 static Eina_Bool _disable_file = EINA_FALSE;
120 static Eina_Bool _disable_function = EINA_FALSE;
121 static Eina_Bool _abort_on_critical = EINA_FALSE;
122 static Eina_Bool _disable_timing = EINA_TRUE;
123 static int _abort_level_on_critical = EINA_LOG_LEVEL_CRITICAL;
124
125 #ifdef EINA_LOG_BACKTRACE
126 // CRI & ERR by default in release mode, nothing in dev mode
127 # ifndef EINA_LOG_BACKTRACE_ENABLE
128 static int _backtrace_level = -1;
129 # else
130 static int _backtrace_level = EINA_LOG_LEVEL_ERR;
131 # endif
132 #endif
133
134 static Eina_Bool _threads_enabled = EINA_FALSE;
135 static Eina_Bool _threads_inited = EINA_FALSE;
136
137 static Eina_Thread _main_thread;
138
139 # define SELF() eina_thread_self()
140 # define IS_MAIN(t) eina_thread_equal(t, _main_thread)
141 # define IS_OTHER(t) EINA_UNLIKELY(!IS_MAIN(t))
142 # define CHECK_MAIN(...) \
143 do { \
144 if (!IS_MAIN(eina_thread_self())) { \
145 fprintf(stderr, \
146 "ERR: not main thread! current=%lu, main=%lu\n", \
147 (unsigned long)eina_thread_self(), \
148 (unsigned long)_main_thread); \
149 return __VA_ARGS__; \
150 } \
151 } while (0)
152
153 static Eina_Spinlock _log_mutex;
154 # define LOG_LOCK() if(_threads_enabled) {eina_spinlock_take(&_log_mutex); }
155 # define LOG_UNLOCK() if(_threads_enabled) {eina_spinlock_release(&_log_mutex); }
156 # define INIT() eina_spinlock_new(&_log_mutex)
157 # define SHUTDOWN() eina_spinlock_free(&_log_mutex)
158
159 // List of domains registered
160 static Eina_Log_Domain *_log_domains = NULL;
161 static Eina_Log_Timing *_log_timing = NULL;
162 static unsigned int _log_domains_count = 0;
163 static size_t _log_domains_allocated = 0;
164
165 // Default function for printing on domains
166 static Eina_Log_Print_Cb _print_cb = eina_log_print_cb_stderr;
167 static void *_print_cb_data = NULL;
168
169 #ifdef DEBUG
170 static Eina_Log_Level _log_level = EINA_LOG_LEVEL_DBG;
171 #elif DEBUG_CRITICAL
172 static Eina_Log_Level _log_level = EINA_LOG_LEVEL_CRITICAL;
173 #else
174 static Eina_Log_Level _log_level = EINA_LOG_LEVEL_ERR;
175 #endif
176
177 /* NOTE: if you change this, also change:
178 * eina_log_print_level_name_get()
179 * eina_log_print_level_name_color_get()
180 */
181 static const char *_names[] = {
182 "CRI",
183 "ERR",
184 "WRN",
185 "INF",
186 "DBG",
187 };
188
189 #ifdef _WIN32
190
191 static Eina_Bool _eina_log_win32_is_console = EINA_FALSE;
192 /* TODO: query win32_def_attr on eina_log_init() */
193 static int win32_def_attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
194
195 /* NOTE: can't use eina_log from inside this function */
196 static int
eina_log_win32_color_convert(const char * color,const char ** endptr)197 eina_log_win32_color_convert(const char *color, const char **endptr)
198 {
199 const char *p;
200 int attr = 0;
201
202 if (endptr) *endptr = color;
203
204 if (color[0] != '\033') return 0;
205 if (color[1] != '[') return 0;
206
207 p = color + 2;
208 while (1)
209 {
210 char *end;
211 int code = strtol(p, &end, 10);
212
213 if (p == end)
214 {
215 //fputs("empty color string\n", stderr);
216 if (endptr) *endptr = end;
217 attr = 0; /* assume it was not color, must end with 'm' */
218 break;
219 }
220
221 if (code)
222 {
223 if (code == 0) attr = win32_def_attr;
224 else if (code == 1) attr |= FOREGROUND_INTENSITY;
225 else if (code == 4) attr |= COMMON_LVB_UNDERSCORE;
226 else if (code == 7) attr |= COMMON_LVB_REVERSE_VIDEO;
227 else if ((code >= 30) && (code <= 37))
228 {
229 /* clear foreground */
230 attr &= ~(FOREGROUND_RED |
231 FOREGROUND_GREEN |
232 FOREGROUND_BLUE);
233
234 if (code == 31)
235 attr |= FOREGROUND_RED;
236 else if (code == 32)
237 attr |= FOREGROUND_GREEN;
238 else if (code == 33)
239 attr |= FOREGROUND_RED | FOREGROUND_GREEN;
240 else if (code == 34)
241 attr |= FOREGROUND_BLUE;
242 else if (code == 35)
243 attr |= FOREGROUND_RED | FOREGROUND_BLUE;
244 else if (code == 36)
245 attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
246 else if (code == 37)
247 attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
248 }
249 else if ((code >= 40) && (code <= 47))
250 {
251 /* clear background */
252 attr &= ~(BACKGROUND_RED |
253 BACKGROUND_GREEN |
254 BACKGROUND_BLUE);
255
256 if (code == 41)
257 attr |= BACKGROUND_RED;
258 else if (code == 42)
259 attr |= BACKGROUND_GREEN;
260 else if (code == 43)
261 attr |= BACKGROUND_RED | BACKGROUND_GREEN;
262 else if (code == 44)
263 attr |= BACKGROUND_BLUE;
264 else if (code == 45)
265 attr |= BACKGROUND_RED | BACKGROUND_BLUE;
266 else if (code == 46)
267 attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
268 else if (code == 47)
269 attr |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
270 }
271 }
272
273 if (*end == 'm')
274 {
275 if (endptr) *endptr = end + 1;
276 break;
277 }
278 else if (*end == ';')
279 p = end + 1;
280 else
281 {
282 //fprintf(stderr, "unexpected char in color string: %s\n", end);
283 attr = 0; /* assume it was not color */
284 if (endptr) *endptr = end;
285 break;
286 }
287 }
288
289 return attr;
290 }
291
292 static int
eina_log_win32_color_get(const char * color)293 eina_log_win32_color_get(const char *color)
294 {
295 return eina_log_win32_color_convert(color, NULL);
296 }
297 #endif
298
299 static inline unsigned int
eina_log_pid_get(void)300 eina_log_pid_get(void)
301 {
302 return (unsigned int)getpid();
303 }
304
305 static inline void
eina_log_print_level_name_get(int level,const char ** p_name)306 eina_log_print_level_name_get(int level, const char **p_name)
307 {
308 static char buf[12];
309 /* NOTE: if you change this, also change
310 * eina_log_print_level_name_color_get()
311 * eina_log_level_name_get() (at eina_inline_log.x)
312 */
313 if (EINA_UNLIKELY(level < 0))
314 {
315 snprintf(buf, sizeof(buf), "%03d", level);
316 *p_name = buf;
317 }
318 else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS))
319 {
320 snprintf(buf, sizeof(buf), "%03d", level);
321 *p_name = buf;
322 }
323 else
324 *p_name = _names[level];
325 }
326
327 #ifdef _WIN32
328 static inline void
eina_log_print_level_name_color_get_win32_console(int level,const char ** p_name,int * p_color)329 eina_log_print_level_name_color_get_win32_console(int level,
330 const char **p_name,
331 int *p_color)
332 {
333 static char buf[12];
334 /* NOTE: if you change this, also change:
335 * eina_log_print_level_name_get()
336 */
337 if (EINA_UNLIKELY(level < 0))
338 {
339 snprintf(buf, sizeof(buf), "%03d", level);
340 *p_name = buf;
341 }
342 else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS))
343 {
344 snprintf(buf, sizeof(buf), "%03d", level);
345 *p_name = buf;
346 }
347 else
348 *p_name = _names[level];
349
350 *p_color = eina_log_win32_color_get(eina_log_level_color_get(level));
351 }
352 #endif
353 static inline void
eina_log_print_level_name_color_get_posix(int level,const char ** p_name,const char ** p_color)354 eina_log_print_level_name_color_get_posix(int level,
355 const char **p_name,
356 const char **p_color)
357 {
358 static char buf[12];
359 /* NOTE: if you change this, also change:
360 * eina_log_print_level_name_get()
361 */
362 if (EINA_UNLIKELY(level < 0))
363 {
364 snprintf(buf, sizeof(buf), "%03d", level);
365 *p_name = buf;
366 }
367 else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS))
368 {
369 snprintf(buf, sizeof(buf), "%03d", level);
370 *p_name = buf;
371 }
372 else
373 *p_name = _names[level];
374
375 *p_color = eina_log_level_color_get(level);
376 }
377
378
379 #define DECLARE_LEVEL_NAME(level) const char *name; \
380 eina_log_print_level_name_get(level, &name)
381 #ifdef _WIN32
382 # define DECLARE_LEVEL_NAME_COLOR_WIN32_CONSOLE(level) const char *name; int color; \
383 eina_log_print_level_name_color_get_win32_console(level, &name, &color)
384 #endif
385 #define DECLARE_LEVEL_NAME_COLOR_POSIX(level) const char *name, *color; \
386 eina_log_print_level_name_color_get_posix(level, &name, &color)
387
388 /** No threads, No color */
389 static void
eina_log_print_prefix_NOthreads_NOcolor_file_func(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line)390 eina_log_print_prefix_NOthreads_NOcolor_file_func(FILE *fp,
391 const Eina_Log_Domain *d,
392 Eina_Log_Level level,
393 const char *file,
394 const char *fnc,
395 int line)
396 {
397 DECLARE_LEVEL_NAME(level);
398 fprintf(fp, "%s<%u>:%s %s:%d %s() ", name, eina_log_pid_get(),
399 d->domain_str, file, line, fnc);
400 }
401
402 static void
eina_log_print_prefix_NOthreads_NOcolor_NOfile_func(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file EINA_UNUSED,const char * fnc,int line EINA_UNUSED)403 eina_log_print_prefix_NOthreads_NOcolor_NOfile_func(FILE *fp,
404 const Eina_Log_Domain *d,
405 Eina_Log_Level level,
406 const char *file EINA_UNUSED,
407 const char *fnc,
408 int line EINA_UNUSED)
409 {
410 DECLARE_LEVEL_NAME(level);
411 fprintf(fp, "%s<%u>:%s %s() ", name, eina_log_pid_get(), d->domain_str,
412 fnc);
413 }
414
415 static void
eina_log_print_prefix_NOthreads_NOcolor_file_NOfunc(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc EINA_UNUSED,int line)416 eina_log_print_prefix_NOthreads_NOcolor_file_NOfunc(FILE *fp,
417 const Eina_Log_Domain *d,
418 Eina_Log_Level level,
419 const char *file,
420 const char *fnc EINA_UNUSED,
421 int line)
422 {
423 DECLARE_LEVEL_NAME(level);
424 fprintf(fp, "%s<%u>:%s %s:%d ", name, eina_log_pid_get(), d->domain_str,
425 file, line);
426 }
427
428 /* No threads, color */
429 #ifdef _WIN32
430 static void
eina_log_print_prefix_NOthreads_color_file_func_win32_console(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line)431 eina_log_print_prefix_NOthreads_color_file_func_win32_console(FILE *fp,
432 const Eina_Log_Domain *d,
433 Eina_Log_Level level,
434 const char *file,
435 const char *fnc,
436 int line)
437 {
438 DECLARE_LEVEL_NAME_COLOR_WIN32_CONSOLE(level);
439 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
440 fprintf(fp, "%s", name);
441 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
442 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
443 fprintf(fp, ":");
444 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
445 eina_log_win32_color_get(d->domain_str));
446 fprintf(fp, "%s", d->name);
447 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
448 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
449 fprintf(fp, " %s:%d ", file, line);
450 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
451 FOREGROUND_INTENSITY | FOREGROUND_RED |
452 FOREGROUND_GREEN | FOREGROUND_BLUE);
453 fprintf(fp, "%s()", fnc);
454 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
455 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
456 fprintf(fp, " ");
457 }
458 #endif
459
460 static void
eina_log_print_prefix_NOthreads_color_file_func_posix(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line)461 eina_log_print_prefix_NOthreads_color_file_func_posix(FILE *fp,
462 const Eina_Log_Domain *d,
463 Eina_Log_Level level,
464 const char *file,
465 const char *fnc,
466 int line)
467 {
468 DECLARE_LEVEL_NAME_COLOR_POSIX(level);
469 fprintf(fp, "%s%s<%u>" EINA_COLOR_RESET ":%s %s:%d "
470 EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
471 color, name, eina_log_pid_get(), d->domain_str, file, line, fnc);
472 }
473
474 static void
eina_log_print_prefix_NOthreads_color_file_func(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line)475 eina_log_print_prefix_NOthreads_color_file_func(FILE *fp,
476 const Eina_Log_Domain *d,
477 Eina_Log_Level level,
478 const char *file,
479 const char *fnc,
480 int line)
481 {
482 #ifdef _WIN32
483 if (_eina_log_win32_is_console)
484 eina_log_print_prefix_NOthreads_color_file_func_win32_console(fp,
485 d,
486 level,
487 file,
488 fnc,
489 line);
490 else
491 #endif
492 eina_log_print_prefix_NOthreads_color_file_func_posix(fp,
493 d,
494 level,
495 file,
496 fnc,
497 line);
498 }
499
500 #ifdef _WIN32
501 static void
eina_log_print_prefix_NOthreads_color_NOfile_func_win32_console(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * fnc)502 eina_log_print_prefix_NOthreads_color_NOfile_func_win32_console(FILE *fp,
503 const Eina_Log_Domain *d,
504 Eina_Log_Level level,
505 const char *fnc)
506 {
507 DECLARE_LEVEL_NAME_COLOR_WIN32_CONSOLE(level);
508 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),color);
509 fprintf(fp, "%s", name);
510 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
511 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
512 fprintf(fp, ":");
513 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
514 eina_log_win32_color_get(d->domain_str));
515 fprintf(fp, "%s", d->name);
516 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
517 FOREGROUND_INTENSITY | FOREGROUND_RED |
518 FOREGROUND_GREEN | FOREGROUND_BLUE);
519 fprintf(fp, "%s()", fnc);
520 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
521 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
522 fprintf(fp, " ");
523 }
524 #endif
525
526 static void
eina_log_print_prefix_NOthreads_color_NOfile_func_posix(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * fnc)527 eina_log_print_prefix_NOthreads_color_NOfile_func_posix(FILE *fp,
528 const Eina_Log_Domain *d,
529 Eina_Log_Level level,
530 const char *fnc)
531 {
532 DECLARE_LEVEL_NAME_COLOR_POSIX(level);
533 fprintf(fp, "%s%s<%u>" EINA_COLOR_RESET ":%s "
534 EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
535 color, name, eina_log_pid_get(), d->domain_str, fnc);
536 }
537
538 static void
eina_log_print_prefix_NOthreads_color_NOfile_func(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file EINA_UNUSED,const char * fnc,int line EINA_UNUSED)539 eina_log_print_prefix_NOthreads_color_NOfile_func(FILE *fp,
540 const Eina_Log_Domain *d,
541 Eina_Log_Level level,
542 const char *file EINA_UNUSED,
543 const char *fnc,
544 int line EINA_UNUSED)
545 {
546 #ifdef _WIN32
547 if (_eina_log_win32_is_console)
548 eina_log_print_prefix_NOthreads_color_NOfile_func_win32_console(fp,
549 d,
550 level,
551 fnc);
552 else
553 #endif
554 eina_log_print_prefix_NOthreads_color_NOfile_func_posix(fp,
555 d,
556 level,
557 fnc);
558 }
559
560 #ifdef _WIN32
561 static void
eina_log_print_prefix_NOthreads_color_file_NOfunc_win32_console(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,int line)562 eina_log_print_prefix_NOthreads_color_file_NOfunc_win32_console(FILE *fp,
563 const Eina_Log_Domain *d,
564 Eina_Log_Level level,
565 const char *file,
566 int line)
567 {
568 DECLARE_LEVEL_NAME_COLOR_WIN32_CONSOLE(level);
569 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
570 color);
571 fprintf(fp, "%s", name);
572 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
573 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
574 fprintf(fp, ":");
575 SetConsoleTextAttribute(GetStdHandle(
576 STD_OUTPUT_HANDLE),
577 eina_log_win32_color_get(d->domain_str));
578 fprintf(fp, "%s", d->name);
579 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
580 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
581 fprintf(fp, " %s:%d ", file, line);
582 }
583 #endif
584
585 static void
eina_log_print_prefix_NOthreads_color_file_NOfunc_posix(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,int line)586 eina_log_print_prefix_NOthreads_color_file_NOfunc_posix(FILE *fp,
587 const Eina_Log_Domain *d,
588 Eina_Log_Level level,
589 const char *file,
590 int line)
591 {
592 DECLARE_LEVEL_NAME_COLOR_POSIX(level);
593 fprintf(fp, "%s%s<%u>" EINA_COLOR_RESET ":%s %s:%d ",
594 color, name, eina_log_pid_get(), d->domain_str, file, line);
595 }
596
597 static void
eina_log_print_prefix_NOthreads_color_file_NOfunc(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc EINA_UNUSED,int line)598 eina_log_print_prefix_NOthreads_color_file_NOfunc(FILE *fp,
599 const Eina_Log_Domain *d,
600 Eina_Log_Level level,
601 const char *file,
602 const char *fnc EINA_UNUSED,
603 int line)
604 {
605 #ifdef _WIN32
606 if (_eina_log_win32_is_console)
607 eina_log_print_prefix_NOthreads_color_file_NOfunc_win32_console(fp,
608 d,
609 level,
610 file,
611 line);
612 else
613 #endif
614 eina_log_print_prefix_NOthreads_color_file_NOfunc_posix(fp,
615 d,
616 level,
617 file,
618 line);
619 }
620
621 /** threads, No color */
622 static void
eina_log_print_prefix_threads_NOcolor_file_func(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line)623 eina_log_print_prefix_threads_NOcolor_file_func(FILE *fp,
624 const Eina_Log_Domain *d,
625 Eina_Log_Level level,
626 const char *file,
627 const char *fnc,
628 int line)
629 {
630 Eina_Thread cur;
631
632 DECLARE_LEVEL_NAME(level);
633 cur = SELF();
634 if (IS_OTHER(cur))
635 {
636 fprintf(fp, "%s<%u>:%s[T:%lu] %s:%d %s() ",
637 name, eina_log_pid_get(), d->domain_str,
638 (unsigned long)cur, file, line, fnc);
639 return;
640 }
641 fprintf(fp, "%s<%u>:%s %s:%d %s() ",
642 name, eina_log_pid_get(), d->domain_str, file, line, fnc);
643 }
644
645 static void
eina_log_print_prefix_threads_NOcolor_NOfile_func(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file EINA_UNUSED,const char * fnc,int line EINA_UNUSED)646 eina_log_print_prefix_threads_NOcolor_NOfile_func(FILE *fp,
647 const Eina_Log_Domain *d,
648 Eina_Log_Level level,
649 const char *file EINA_UNUSED,
650 const char *fnc,
651 int line EINA_UNUSED)
652 {
653 Eina_Thread cur;
654
655 DECLARE_LEVEL_NAME(level);
656 cur = SELF();
657 if (IS_OTHER(cur))
658 {
659 fprintf(fp, "%s<%u>:%s[T:%lu] %s() ",
660 name, eina_log_pid_get(), d->domain_str,
661 (unsigned long)cur, fnc);
662 return;
663 }
664 fprintf(fp, "%s<%u>:%s %s() ",
665 name, eina_log_pid_get(), d->domain_str, fnc);
666 }
667
668 static void
eina_log_print_prefix_threads_NOcolor_file_NOfunc(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc EINA_UNUSED,int line)669 eina_log_print_prefix_threads_NOcolor_file_NOfunc(FILE *fp,
670 const Eina_Log_Domain *d,
671 Eina_Log_Level level,
672 const char *file,
673 const char *fnc EINA_UNUSED,
674 int line)
675 {
676 Eina_Thread cur;
677
678 DECLARE_LEVEL_NAME(level);
679 cur = SELF();
680 if (IS_OTHER(cur))
681 {
682 fprintf(fp, "%s<%u>:%s[T:%lu] %s:%d ",
683 name, eina_log_pid_get(), d->domain_str, (unsigned long)cur,
684 file, line);
685 return;
686 }
687
688 fprintf(fp, "%s<%u>:%s %s:%d ",
689 name, eina_log_pid_get(), d->domain_str, file, line);
690 }
691
692 /* threads, color */
693 #ifdef _WIN32
694 static void
eina_log_print_prefix_threads_color_file_func_win32_console(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line,Eina_Thread cur)695 eina_log_print_prefix_threads_color_file_func_win32_console(FILE *fp,
696 const Eina_Log_Domain *d,
697 Eina_Log_Level level,
698 const char *file,
699 const char *fnc,
700 int line,
701 Eina_Thread cur)
702 {
703 DECLARE_LEVEL_NAME_COLOR_WIN32_CONSOLE(level);
704 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
705 color);
706 fprintf(fp, "%s", name);
707 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
708 FOREGROUND_RED | FOREGROUND_GREEN |
709 FOREGROUND_BLUE);
710 fprintf(fp, ":");
711 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
712 eina_log_win32_color_get(d->domain_str));
713 fprintf(fp, "%s[T:", d->name);
714 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
715 FOREGROUND_RED | FOREGROUND_GREEN |
716 FOREGROUND_BLUE);
717 fprintf(fp, "[T:");
718 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
719 FOREGROUND_GREEN | FOREGROUND_BLUE);
720 fprintf(fp, "%lu", (unsigned long)cur);
721 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
722 FOREGROUND_RED | FOREGROUND_GREEN |
723 FOREGROUND_BLUE);
724 fprintf(fp, "] %s:%d ", file, line);
725 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
726 FOREGROUND_INTENSITY | FOREGROUND_RED |
727 FOREGROUND_GREEN | FOREGROUND_BLUE);
728 fprintf(fp, "%s()", fnc);
729 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
730 FOREGROUND_RED | FOREGROUND_GREEN |
731 FOREGROUND_BLUE);
732 fprintf(fp, " ");
733 }
734 #endif
735
736 static void
eina_log_print_prefix_threads_color_file_func_posix(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line,Eina_Thread cur)737 eina_log_print_prefix_threads_color_file_func_posix(FILE *fp,
738 const Eina_Log_Domain *d,
739 Eina_Log_Level level,
740 const char *file,
741 const char *fnc,
742 int line,
743 Eina_Thread cur)
744 {
745 DECLARE_LEVEL_NAME_COLOR_POSIX(level);
746 fprintf(fp, "%s%s<%u>" EINA_COLOR_RESET ":%s[T:"
747 EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] %s:%d "
748 EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
749 color, name, eina_log_pid_get() ,d->domain_str,
750 (unsigned long)cur, file, line, fnc);
751 }
752
753 static void
eina_log_print_prefix_threads_color_file_func(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line)754 eina_log_print_prefix_threads_color_file_func(FILE *fp,
755 const Eina_Log_Domain *d,
756 Eina_Log_Level level,
757 const char *file,
758 const char *fnc,
759 int line)
760 {
761 Eina_Thread cur;
762
763 cur = SELF();
764 if (IS_OTHER(cur))
765 {
766 #ifdef _WIN32
767 if (_eina_log_win32_is_console)
768 eina_log_print_prefix_threads_color_file_func_win32_console(fp,
769 d,
770 level,
771 file,
772 fnc,
773 line,
774 cur);
775 else
776 #endif
777 eina_log_print_prefix_threads_color_file_func_posix(fp,
778 d,
779 level,
780 file,
781 fnc,
782 line,
783 cur);
784 return;
785 }
786
787 eina_log_print_prefix_NOthreads_color_file_func(fp,
788 d,
789 level,
790 file,
791 fnc,
792 line);
793 }
794
795 #ifdef _WIN32
796 static void
eina_log_print_prefix_threads_color_NOfile_func_win32_console(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * fnc,Eina_Thread cur)797 eina_log_print_prefix_threads_color_NOfile_func_win32_console(FILE *fp,
798 const Eina_Log_Domain *d,
799 Eina_Log_Level level,
800 const char *fnc,
801 Eina_Thread cur)
802 {
803 DECLARE_LEVEL_NAME_COLOR_WIN32_CONSOLE(level);
804 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
805 color);
806 fprintf(fp, "%s", name);
807 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
808 FOREGROUND_RED | FOREGROUND_GREEN |
809 FOREGROUND_BLUE);
810 fprintf(fp, ":");
811 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
812 eina_log_win32_color_get(d->domain_str));
813 fprintf(fp, "%s[T:", d->name);
814 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
815 FOREGROUND_RED | FOREGROUND_GREEN |
816 FOREGROUND_BLUE);
817 fprintf(fp, "[T:");
818 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
819 FOREGROUND_GREEN | FOREGROUND_BLUE);
820 fprintf(fp, "%lu", (unsigned long)cur);
821 SetConsoleTextAttribute(GetStdHandle(
822 STD_OUTPUT_HANDLE),
823 FOREGROUND_INTENSITY | FOREGROUND_RED |
824 FOREGROUND_GREEN | FOREGROUND_BLUE);
825 fprintf(fp, "%s()", fnc);
826 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
827 FOREGROUND_RED | FOREGROUND_GREEN |
828 FOREGROUND_BLUE);
829 fprintf(fp, " ");
830 }
831 #endif
832
833 static void
eina_log_print_prefix_threads_color_NOfile_func_posix(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * fnc,Eina_Thread cur)834 eina_log_print_prefix_threads_color_NOfile_func_posix(FILE *fp,
835 const Eina_Log_Domain *d,
836 Eina_Log_Level level,
837 const char *fnc,
838 Eina_Thread cur)
839 {
840 DECLARE_LEVEL_NAME_COLOR_POSIX(level);
841 fprintf(fp, "%s%s<%u>" EINA_COLOR_RESET ":%s[T:"
842 EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] "
843 EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
844 color, name, eina_log_pid_get(), d->domain_str,
845 (unsigned long)cur, fnc);
846 }
847
848 static void
eina_log_print_prefix_threads_color_NOfile_func(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file EINA_UNUSED,const char * fnc,int line EINA_UNUSED)849 eina_log_print_prefix_threads_color_NOfile_func(FILE *fp,
850 const Eina_Log_Domain *d,
851 Eina_Log_Level level,
852 const char *file EINA_UNUSED,
853 const char *fnc,
854 int line EINA_UNUSED)
855 {
856 Eina_Thread cur;
857
858 cur = SELF();
859 if (IS_OTHER(cur))
860 {
861 #ifdef _WIN32
862 if (_eina_log_win32_is_console)
863 eina_log_print_prefix_threads_color_NOfile_func_win32_console(fp,
864 d,
865 level,
866 fnc,
867 cur);
868 else
869 #endif
870 eina_log_print_prefix_threads_color_NOfile_func_posix(fp,
871 d,
872 level,
873 fnc,
874 cur);
875 return;
876 }
877
878 eina_log_print_prefix_NOthreads_color_NOfile_func(fp,
879 d,
880 level,
881 file,
882 fnc,
883 line);
884 }
885
886 #ifdef _WIN32
887 static void
eina_log_print_prefix_threads_color_file_NOfunc_win32_console(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,int line,Eina_Thread cur)888 eina_log_print_prefix_threads_color_file_NOfunc_win32_console(FILE *fp,
889 const Eina_Log_Domain *d,
890 Eina_Log_Level level,
891 const char *file,
892 int line,
893 Eina_Thread cur)
894 {
895 DECLARE_LEVEL_NAME_COLOR_WIN32_CONSOLE(level);
896 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
897 color);
898 fprintf(fp, "%s", name);
899 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
900 FOREGROUND_RED | FOREGROUND_GREEN |
901 FOREGROUND_BLUE);
902 fprintf(fp, ":");
903 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
904 eina_log_win32_color_get(d->domain_str));
905 fprintf(fp, "%s[T:", d->name);
906 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
907 FOREGROUND_RED | FOREGROUND_GREEN |
908 FOREGROUND_BLUE);
909 fprintf(fp, "[T:");
910 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
911 FOREGROUND_GREEN | FOREGROUND_BLUE);
912 fprintf(fp, "%lu", (unsigned long)cur);
913 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
914 FOREGROUND_RED | FOREGROUND_GREEN |
915 FOREGROUND_BLUE);
916 fprintf(fp, "] %s:%d ", file, line);
917 }
918 #endif
919
920 static void
eina_log_print_prefix_threads_color_file_NOfunc_posix(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,int line,Eina_Thread cur)921 eina_log_print_prefix_threads_color_file_NOfunc_posix(FILE *fp,
922 const Eina_Log_Domain *d,
923 Eina_Log_Level level,
924 const char *file,
925 int line,
926 Eina_Thread cur)
927 {
928 DECLARE_LEVEL_NAME_COLOR_POSIX(level);
929 fprintf(fp, "%s%s<%u>" EINA_COLOR_RESET ":%s[T:"
930 EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] %s:%d ",
931 color, name, eina_log_pid_get(), d->domain_str,
932 (unsigned long)cur, file, line);
933 }
934
935 static void
eina_log_print_prefix_threads_color_file_NOfunc(FILE * fp,const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc EINA_UNUSED,int line)936 eina_log_print_prefix_threads_color_file_NOfunc(FILE *fp,
937 const Eina_Log_Domain *d,
938 Eina_Log_Level level,
939 const char *file,
940 const char *fnc EINA_UNUSED,
941 int line)
942 {
943 Eina_Thread cur;
944
945 cur = SELF();
946 if (IS_OTHER(cur))
947 {
948 #ifdef _WIN32
949 if (_eina_log_win32_is_console)
950 eina_log_print_prefix_threads_color_file_NOfunc_win32_console(fp,
951 d,
952 level,
953 file,
954 line,
955 cur);
956 else
957 #endif
958 eina_log_print_prefix_threads_color_file_NOfunc_posix(fp,
959 d,
960 level,
961 file,
962 line,
963 cur);
964 return;
965 }
966
967 eina_log_print_prefix_NOthreads_color_file_NOfunc(fp,
968 d,
969 level,
970 file,
971 fnc,
972 line);
973 }
974
975 static void (*_eina_log_print_prefix)(FILE *fp, const Eina_Log_Domain *d,
976 Eina_Log_Level level, const char *file,
977 const char *fnc,
978 int line) =
979 eina_log_print_prefix_NOthreads_color_file_func;
980
981 static inline void
eina_log_print_prefix_update(void)982 eina_log_print_prefix_update(void)
983 {
984 if (_disable_file && _disable_function)
985 {
986 fprintf(stderr, "ERROR: cannot have " EINA_LOG_ENV_FILE_DISABLE " and "
987 EINA_LOG_ENV_FUNCTION_DISABLE " set at the same time, will "
988 "just disable function.\n");
989 _disable_file = 0;
990 }
991
992 #define S(NOthread, NOcolor, NOfile, NOfunc) \
993 _eina_log_print_prefix = \
994 eina_log_print_prefix_ ## NOthread ## threads_ ## NOcolor ## color_ ## \
995 NOfile \
996 ## file_ ## NOfunc ## func
997
998 if (_threads_enabled)
999 {
1000 if (_disable_color)
1001 {
1002 if (_disable_file)
1003 S(,NO,NO,);
1004 else if (_disable_function)
1005 S(,NO,,NO);
1006 else
1007 S(,NO,,);
1008 }
1009 else
1010 {
1011 if (_disable_file)
1012 S(,,NO,);
1013 else if (_disable_function)
1014 S(,,,NO);
1015 else
1016 S(,,,);
1017 }
1018
1019 return;
1020 }
1021
1022 if (_disable_color)
1023 {
1024 if (_disable_file)
1025 S(NO,NO,NO,);
1026 else if (_disable_function)
1027 S(NO,NO,,NO);
1028 else
1029 S(NO,NO,,);
1030 }
1031 else
1032 {
1033 if (_disable_file)
1034 S(NO,,NO,);
1035 else if (_disable_function)
1036 S(NO,,,NO);
1037 else
1038 S(NO,,,);
1039 }
1040
1041 #undef S
1042 }
1043
1044 /*
1045 * Creates a colored domain name string.
1046 */
1047 static const char *
eina_log_domain_str_get(const char * name,const char * color)1048 eina_log_domain_str_get(const char *name, const char *color)
1049 {
1050 const char *d;
1051
1052 if (color)
1053 {
1054 size_t name_len;
1055 size_t color_len;
1056
1057 name_len = strlen(name);
1058 color_len = strlen(color);
1059 d =
1060 malloc(sizeof(char) *
1061 (color_len + name_len + strlen(EINA_COLOR_RESET) + 1));
1062 if (!d)
1063 return NULL;
1064
1065 memcpy((char *)d, color, color_len);
1066 memcpy((char *)(d + color_len), name, name_len);
1067 memcpy((char *)(d + color_len + name_len), EINA_COLOR_RESET,
1068 strlen(EINA_COLOR_RESET));
1069 ((char *)d)[color_len + name_len + strlen(EINA_COLOR_RESET)] = '\0';
1070 }
1071 else
1072 d = strdup(name);
1073
1074 return d;
1075 }
1076
1077 /*
1078 * Setups a new logging domain to the name and color specified. Note that this
1079 * constructor acts upon an pre-allocated object.
1080 */
1081 static Eina_Log_Domain *
eina_log_domain_new(Eina_Log_Domain * d,Eina_Log_Timing * t,const char * name,const char * color)1082 eina_log_domain_new(Eina_Log_Domain *d, Eina_Log_Timing *t,
1083 const char *name, const char *color)
1084 {
1085 EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
1086 EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
1087
1088 d->level = EINA_LOG_LEVEL_UNKNOWN;
1089 d->color = color;
1090 d->deleted = EINA_FALSE;
1091
1092 if ((color) && (!_disable_color))
1093 d->domain_str = eina_log_domain_str_get(name, color);
1094 else
1095 d->domain_str = eina_log_domain_str_get(name, NULL);
1096
1097 d->name = strdup(name);
1098 d->namelen = strlen(name);
1099
1100 t->phase = NULL;
1101
1102 return d;
1103 }
1104
1105 /*
1106 * Frees internal strings of a log domain, keeping the log domain itself as a
1107 * slot for next domain registers.
1108 */
1109 static void
eina_log_domain_free(Eina_Log_Domain * d)1110 eina_log_domain_free(Eina_Log_Domain *d)
1111 {
1112 EINA_SAFETY_ON_NULL_RETURN(d);
1113
1114 free((char *)d->domain_str);
1115 free((char *)d->name);
1116 }
1117
1118 /*
1119 * Parses domain levels passed through the env var.
1120 */
1121 static void
eina_log_domain_parse_pendings(void)1122 eina_log_domain_parse_pendings(void)
1123 {
1124 const char *start;
1125
1126 if (!(start = getenv(EINA_LOG_ENV_LEVELS)))
1127 return;
1128
1129 // name1:level1,name2:level2,name3:level3,...
1130 while (1)
1131 {
1132 Eina_Log_Domain_Level_Pending *p;
1133 char *end = NULL, *tmp = NULL;
1134 ptrdiff_t diff;
1135 long int level;
1136
1137 end = strchr(start, ':');
1138 if (!end) break;
1139
1140 // Parse level, keep going if failed
1141 level = strtol((char *)(end + 1), &tmp, 10);
1142 if (tmp == (end + 1)) goto parse_end;
1143
1144 if (start > end) break;
1145 diff = end - start;
1146 // If the name of the log is more than 64k it's silly so give up
1147 // as it's pointless and in theory could overflow pointer
1148 if (diff > (ptrdiff_t)0xffff) break;
1149
1150 // Parse name
1151 p = malloc(sizeof(Eina_Log_Domain_Level_Pending) + diff + 1);
1152 if (!p) break;
1153
1154 p->namelen = diff;
1155 memcpy((char *)p->name, start, diff);
1156 ((char *)p->name)[diff] = '\0';
1157 p->level = level;
1158
1159 _pending_list = eina_inlist_append(_pending_list, EINA_INLIST_GET(p));
1160
1161 parse_end:
1162 start = strchr(tmp, ',');
1163 if (start) start++;
1164 else break;
1165 }
1166 }
1167
1168 static void
eina_log_domain_parse_pending_globs(void)1169 eina_log_domain_parse_pending_globs(void)
1170 {
1171 const char *start;
1172
1173 if (!(start = getenv(EINA_LOG_ENV_LEVELS_GLOB)))
1174 return;
1175
1176 // name1:level1,name2:level2,name3:level3,...
1177 while (1)
1178 {
1179 Eina_Log_Domain_Level_Pending *p;
1180 char *end = NULL, *tmp = NULL;
1181 ptrdiff_t diff;
1182 long int level;
1183
1184 end = strchr(start, ':');
1185 if (!end) break;
1186
1187 // Parse level, keep going if failed
1188 level = strtol((char *)(end + 1), &tmp, 10);
1189 if (tmp == (end + 1)) goto parse_end;
1190
1191 if (start > end) break;
1192 diff = end - start;
1193 // If the name of the log is more than 64k it's silly so give up
1194 // as it's pointless and in theory could overflow pointer
1195 if (diff > (ptrdiff_t)0xffff) break;
1196
1197 // Parse name
1198 p = malloc(sizeof(Eina_Log_Domain_Level_Pending) + diff + 1);
1199 if (!p) break;
1200
1201 p->namelen = diff;
1202 memcpy((char *)p->name, start, diff);
1203 ((char *)p->name)[diff] = '\0';
1204 p->level = level;
1205
1206 _glob_list = eina_inlist_append(_glob_list, EINA_INLIST_GET(p));
1207
1208 parse_end:
1209 start = strchr(tmp, ',');
1210 if (start) start++;
1211 else break;
1212 }
1213 }
1214
1215 static inline int
eina_log_domain_register_unlocked(const char * name,const char * color)1216 eina_log_domain_register_unlocked(const char *name, const char *color)
1217 {
1218 Eina_Log_Domain_Level_Pending *pending = NULL;
1219 size_t namelen;
1220 unsigned int i;
1221
1222 for (i = 0; i < _log_domains_count; i++)
1223 {
1224 if (_log_domains[i].deleted)
1225 {
1226 // Found a flagged slot, free domain_str and replace slot
1227 eina_log_domain_new(&_log_domains[i], &_log_timing[i], name, color);
1228 goto finish_register;
1229 }
1230 }
1231
1232 if (_log_domains_count >= _log_domains_allocated)
1233 {
1234 Eina_Log_Domain *tmp;
1235 Eina_Log_Timing *tim;
1236 size_t size;
1237
1238 if (!_log_domains)
1239 // special case for init, eina itself will allocate a dozen of domains
1240 size = 64;
1241 else
1242 // grow 8 buckets to minimize reallocs
1243 size = _log_domains_allocated + 8;
1244
1245 tmp = realloc(_log_domains, sizeof(Eina_Log_Domain) * size);
1246 tim = realloc(_log_timing, sizeof (Eina_Log_Timing) * size);
1247
1248 if (tmp && tim)
1249 {
1250 // Success!
1251 _log_domains = tmp;
1252 _log_timing = tim;
1253 _log_domains_allocated = size;
1254 }
1255 else
1256 {
1257 free(tmp);
1258 free(tim);
1259 return -1;
1260 }
1261 }
1262
1263 // Use an allocated slot
1264 eina_log_domain_new(&_log_domains[i], &_log_timing[i], name, color);
1265 _log_domains_count++;
1266
1267 finish_register:
1268 namelen = _log_domains[i].namelen;
1269
1270 EINA_INLIST_FOREACH(_pending_list, pending)
1271 {
1272 if ((namelen == pending->namelen) && (strcmp(pending->name, name) == 0))
1273 {
1274 _log_domains[i].level = pending->level;
1275 break;
1276 }
1277 }
1278
1279 if (_log_domains[i].level == EINA_LOG_LEVEL_UNKNOWN)
1280 {
1281 EINA_INLIST_FOREACH(_glob_list, pending)
1282 {
1283 if (!fnmatch(pending->name, name, 0))
1284 {
1285 _log_domains[i].level = pending->level;
1286 break;
1287 }
1288 }
1289 }
1290
1291 // Check if level is still UNKNOWN, set it to global
1292 if (_log_domains[i].level == EINA_LOG_LEVEL_UNKNOWN)
1293 _log_domains[i].level = _log_level;
1294
1295 eina_log_timing(i, EINA_LOG_STATE_START, EINA_LOG_STATE_INIT);
1296
1297 return i;
1298 }
1299
1300 static inline Eina_Bool
eina_log_term_color_supported(const char * term)1301 eina_log_term_color_supported(const char *term)
1302 {
1303 const char *tail;
1304 size_t len;
1305
1306 if (!term)
1307 return EINA_FALSE;
1308
1309 len = strlen(term);
1310 tail = term + 1;
1311 switch (term[0])
1312 {
1313 /* list of known to support color terminals,
1314 * take from gentoo's portage.
1315 */
1316
1317 case 'x': /* xterm and xterm-(256)color */
1318 return ((strncmp(tail, "term", sizeof("term") - 1) == 0) &&
1319 ((tail[sizeof("term") - 1] == '\0') ||
1320 (strcmp(term + len - sizeof("color") + 1, "color") == 0)));
1321
1322 case 'E': /* Eterm */
1323 case 'a': /* aterm */
1324 case 'k': /* kterm */
1325 return (strcmp(tail, "term") == 0);
1326
1327 case 'r': /* xrvt or rxvt-unicode */
1328 return ((strncmp(tail, "xvt", sizeof("xvt") - 1) == 0) &&
1329 ((tail[sizeof("xvt") - 1] == '\0') ||
1330 (strcmp(tail + sizeof("xvt") - 1, "-unicode") == 0)));
1331
1332 case 's': /* screen */
1333 return ((strncmp(tail, "creen", sizeof("creen") - 1) == 0) &&
1334 ((tail[sizeof("creen") - 1] == '\0') ||
1335 (strcmp(term + len - sizeof("color") + 1, "color") == 0)));
1336
1337 case 'g': /* gnome */
1338 return (strcmp(tail, "nome") == 0);
1339
1340 case 'i': /* interix */
1341 return (strcmp(tail, "nterix") == 0);
1342
1343 default:
1344 return EINA_FALSE;
1345 }
1346 }
1347
1348 static inline void
eina_log_domain_unregister_unlocked(int domain)1349 eina_log_domain_unregister_unlocked(int domain)
1350 {
1351 Eina_Log_Domain *d;
1352
1353 if ((unsigned int)domain >= _log_domains_count)
1354 return;
1355
1356 eina_log_timing(domain, EINA_LOG_STATE_STOP, EINA_LOG_STATE_SHUTDOWN);
1357
1358 d = &_log_domains[domain];
1359 eina_log_domain_free(d);
1360 d->deleted = 1;
1361 }
1362
1363 #ifdef EINA_LOG_BACKTRACE
1364 # define DISPLAY_BACKTRACE(File, Level) \
1365 if (EINA_UNLIKELY(Level <= _backtrace_level)) { \
1366 fprintf(File, \
1367 "## Copy & Paste the below (until EOF) into a terminal, then hit Enter\n\n" \
1368 "eina_btlog << EOF\n"); \
1369 EINA_BT(File); \
1370 fprintf(File, \
1371 "EOF\n\n"); \
1372 }
1373 #else
1374 # define DISPLAY_BACKTRACE(File, Level)
1375 #endif
1376
1377 static inline void
eina_log_print_unlocked(int domain,Eina_Log_Level level,const char * file,const char * fnc,int line,const char * fmt,va_list args)1378 eina_log_print_unlocked(int domain,
1379 Eina_Log_Level level,
1380 const char *file,
1381 const char *fnc,
1382 int line,
1383 const char *fmt,
1384 va_list args)
1385 {
1386 Eina_Log_Domain *d;
1387
1388 #ifdef EINA_SAFETY_CHECKS
1389 if (EINA_UNLIKELY((unsigned int)domain >= _log_domains_count) ||
1390 EINA_UNLIKELY(domain < 0))
1391 {
1392 DECLARE_LEVEL_NAME(level);
1393 if (level > _log_level)
1394 {
1395 fprintf(stderr, "CRI<%u>:eina_log %s:%d %s() unknown log domain %d, "
1396 "original message level was: %s\n", eina_log_pid_get(),
1397 file, line, fnc, domain, name);
1398 }
1399 else
1400 {
1401 if (file && fnc && fmt)
1402 {
1403 fprintf(stderr, "CRI<%u>:eina_log %s:%d %s() unknown log domain %d, "
1404 "original message was: %s: '", eina_log_pid_get(),
1405 file, line, fnc, domain, name);
1406 vfprintf(stderr, fmt, args);
1407 }
1408 else
1409 {
1410 fprintf(stderr, "CRI<%u>:eina_log unknown log domain %d, original "
1411 "message was: %s: '", eina_log_pid_get(), domain, name);
1412 if (fmt)
1413 vfprintf(stderr, fmt, args);
1414 }
1415 fputs("'\n", stderr);
1416 }
1417
1418 DISPLAY_BACKTRACE(stderr, level);
1419 if (EINA_UNLIKELY(_abort_on_critical))
1420 abort();
1421
1422 return;
1423 }
1424
1425 #endif
1426 d = _log_domains + domain;
1427 #ifdef EINA_SAFETY_CHECKS
1428 if (EINA_UNLIKELY(d->deleted))
1429 {
1430 if ((!d->level) || (level > d->level))
1431 fprintf(stderr, "ERR<%u>:eina_log %s:%d %s() log domain %d was deleted\n",
1432 eina_log_pid_get(), file, line, fnc, domain);
1433 else
1434 {
1435 DECLARE_LEVEL_NAME(level);
1436 fprintf(stderr, "ERR<%u>:eina_log %s:%d %s() log domain %d was "
1437 "deleted, original message was: %s: '",
1438 eina_log_pid_get(), file, line, fnc, domain, name);
1439 vfprintf(stderr, fmt, args);
1440 fputs("'\n", stderr);
1441 }
1442 DISPLAY_BACKTRACE(stderr, level);
1443 if (EINA_UNLIKELY(_abort_on_critical) &&
1444 EINA_UNLIKELY(level <= _abort_level_on_critical))
1445 abort();
1446 return;
1447 }
1448
1449 #endif
1450
1451 if ((!d->level) || (level > d->level))
1452 return;
1453
1454 _print_cb(d, level, file, fnc, line, fmt, _print_cb_data, args);
1455
1456 if (EINA_UNLIKELY(_abort_on_critical) &&
1457 EINA_UNLIKELY(level <= _abort_level_on_critical))
1458 abort();
1459 }
1460
1461 #endif
1462
1463 /**
1464 * @endcond
1465 */
1466
1467
1468 /*============================================================================*
1469 * Global *
1470 *============================================================================*/
1471
1472 /**
1473 * @internal
1474 * @brief Initialize the log module.
1475 *
1476 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
1477 *
1478 * This function sets up the log module of Eina. It is called by
1479 * eina_init().
1480 *
1481 * @see eina_init()
1482 *
1483 * @warning Not-MT: just call this function from main thread! The
1484 * place where this function was called the first time is
1485 * considered the main thread.
1486 */
1487 Eina_Bool
eina_log_init(void)1488 eina_log_init(void)
1489 {
1490 #ifdef EINA_ENABLE_LOG
1491 const char *level, *tmp;
1492 int color_disable;
1493 # ifdef _WIN32
1494 DWORD mode;
1495 # endif
1496
1497 assert((sizeof(_names) / sizeof(_names[0])) == EINA_LOG_LEVELS);
1498
1499 #ifdef EINA_LOG_BACKTRACE
1500 if ((tmp = getenv(EINA_LOG_ENV_BACKTRACE)))
1501 _backtrace_level = atoi(tmp);
1502 #endif
1503
1504 if ((tmp = getenv(EINA_LOG_ENV_COLOR_DISABLE)))
1505 color_disable = atoi(tmp);
1506 else
1507 color_disable = -1;
1508
1509 /* Check if color is explicitly disabled */
1510 if (color_disable == 1)
1511 _disable_color = EINA_TRUE;
1512
1513 #ifndef _WIN32
1514 /* color was not explicitly disabled or enabled, guess it */
1515 else if (color_disable == -1)
1516 {
1517 if (!eina_log_term_color_supported(getenv("TERM")))
1518 _disable_color = EINA_TRUE;
1519 else
1520 {
1521 /* if not a terminal, but redirected to a file, disable color */
1522 int fd;
1523
1524 if (_print_cb == eina_log_print_cb_stderr)
1525 fd = STDERR_FILENO;
1526 else if (_print_cb == eina_log_print_cb_stdout)
1527 fd = STDOUT_FILENO;
1528 else
1529 fd = -1;
1530
1531 if ((fd >= 0) && (!isatty(fd)))
1532 _disable_color = EINA_TRUE;
1533 }
1534 }
1535 #else
1536 if (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &mode))
1537 _eina_log_win32_is_console = EINA_TRUE;
1538 #endif
1539
1540 #ifdef HAVE_SYSTEMD
1541 if (getenv("NOTIFY_SOCKET") && !getenv("EFL_RUN_IN_TREE"))
1542 _print_cb = eina_log_print_cb_journald;
1543 #endif
1544
1545 if (getenv("EINA_LOG_TIMING"))
1546 _disable_timing = EINA_FALSE;
1547
1548 if ((tmp = getenv(EINA_LOG_ENV_FILE_DISABLE)) && (atoi(tmp) == 1))
1549 _disable_file = EINA_TRUE;
1550
1551 if ((tmp = getenv(EINA_LOG_ENV_FUNCTION_DISABLE)) && (atoi(tmp) == 1))
1552 _disable_function = EINA_TRUE;
1553
1554 if ((tmp = getenv(EINA_LOG_ENV_ABORT)) && (atoi(tmp) == 1))
1555 _abort_on_critical = EINA_TRUE;
1556
1557 if ((tmp = getenv(EINA_LOG_ENV_ABORT_LEVEL)))
1558 _abort_level_on_critical = atoi(tmp);
1559
1560 eina_log_print_prefix_update();
1561
1562 // Global log level
1563 if ((level = getenv(EINA_LOG_ENV_LEVEL)))
1564 _log_level = atoi(level);
1565
1566 // Register UNKNOWN domain, the default logger
1567 EINA_LOG_DOMAIN_GLOBAL = eina_log_domain_register("", NULL);
1568
1569 if (EINA_LOG_DOMAIN_GLOBAL < 0)
1570 {
1571 fprintf(stderr, "Failed to create global logging domain.\n");
1572 return EINA_FALSE;
1573 }
1574
1575 // Parse pending domains passed through EINA_LOG_LEVELS_GLOB
1576 eina_log_domain_parse_pending_globs();
1577
1578 // Parse pending domains passed through EINA_LOG_LEVELS
1579 eina_log_domain_parse_pendings();
1580
1581 eina_log_timing(EINA_LOG_DOMAIN_GLOBAL,
1582 EINA_LOG_STATE_STOP,
1583 EINA_LOG_STATE_INIT);
1584
1585 #endif
1586 return EINA_TRUE;
1587 }
1588
1589 /**
1590 * @internal
1591 * @brief Shut down the log module.
1592 *
1593 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
1594 *
1595 * This function shuts down the log module set up by
1596 * eina_log_init(). It is called by eina_shutdown().
1597 *
1598 * @see eina_shutdown()
1599 *
1600 * @warning Not-MT: just call this function from main thread! The
1601 * place where eina_log_init() (eina_init()) was called the
1602 * first time is considered the main thread.
1603 */
1604 Eina_Bool
eina_log_shutdown(void)1605 eina_log_shutdown(void)
1606 {
1607 #ifdef EINA_ENABLE_LOG
1608 Eina_Inlist *tmp;
1609
1610 eina_log_timing(EINA_LOG_DOMAIN_GLOBAL,
1611 EINA_LOG_STATE_START,
1612 EINA_LOG_STATE_SHUTDOWN);
1613
1614 while (_log_domains_count--)
1615 {
1616 if (_log_domains[_log_domains_count].deleted)
1617 continue;
1618
1619 eina_log_domain_free(&_log_domains[_log_domains_count]);
1620 }
1621
1622 free(_log_domains);
1623 free(_log_timing);
1624
1625 _log_timing = NULL;
1626 _log_domains = NULL;
1627 _log_domains_count = 0;
1628 _log_domains_allocated = 0;
1629
1630 while (_glob_list)
1631 {
1632 tmp = _glob_list;
1633 _glob_list = _glob_list->next;
1634 free(tmp);
1635 }
1636
1637 while (_pending_list)
1638 {
1639 tmp = _pending_list;
1640 _pending_list = _pending_list->next;
1641 free(tmp);
1642 }
1643
1644 #endif
1645 return EINA_TRUE;
1646 }
1647
1648 /**
1649 * @internal
1650 * @brief Activate the log mutex.
1651 *
1652 * This function activate the mutex in the eina log module. It is called by
1653 * eina_threads_init().
1654 *
1655 * @see eina_threads_init()
1656 */
1657 void
eina_log_threads_init(void)1658 eina_log_threads_init(void)
1659 {
1660 #ifdef EINA_ENABLE_LOG
1661 if (_threads_inited) return;
1662 _main_thread = SELF();
1663 if (!INIT()) return;
1664 _threads_inited = EINA_TRUE;
1665 #endif
1666 }
1667
1668 /**
1669 * @internal
1670 * @brief Shut down the log mutex.
1671 *
1672 * This function shuts down the mutex in the log module.
1673 * It is called by eina_threads_shutdown().
1674 *
1675 * @see eina_threads_shutdown()
1676 */
1677 void
eina_log_threads_shutdown(void)1678 eina_log_threads_shutdown(void)
1679 {
1680 #ifdef EINA_ENABLE_LOG
1681 if (!_threads_inited) return;
1682 CHECK_MAIN();
1683 SHUTDOWN();
1684 _threads_enabled = EINA_FALSE;
1685 _threads_inited = EINA_FALSE;
1686 #endif
1687 }
1688
1689 /*============================================================================*
1690 * API *
1691 *============================================================================*/
1692
1693 /**
1694 * @cond LOCAL
1695 */
1696
1697 EAPI int EINA_LOG_DOMAIN_GLOBAL = 0;
1698
1699 /**
1700 * @endcond
1701 */
1702
1703 EAPI void
eina_log_threads_enable(void)1704 eina_log_threads_enable(void)
1705 {
1706 #ifdef EINA_ENABLE_LOG
1707 if (_threads_enabled) return;
1708 if (!_threads_inited) eina_log_threads_init();
1709 _threads_enabled = EINA_TRUE;
1710 eina_log_print_prefix_update();
1711 #endif
1712 }
1713
1714 EAPI void
eina_log_print_cb_set(Eina_Log_Print_Cb cb,void * data)1715 eina_log_print_cb_set(Eina_Log_Print_Cb cb, void *data)
1716 {
1717 #ifdef EINA_ENABLE_LOG
1718 LOG_LOCK();
1719 if (cb)
1720 _print_cb = cb;
1721 else
1722 _print_cb = eina_log_print_cb_stderr;
1723 _print_cb_data = data;
1724 eina_log_print_prefix_update();
1725 LOG_UNLOCK();
1726 #else
1727 (void) cb;
1728 (void) data;
1729 #endif
1730 }
1731
1732 EAPI void
eina_log_level_set(int level)1733 eina_log_level_set(int level)
1734 {
1735 #ifdef EINA_ENABLE_LOG
1736 _log_level = level;
1737 if (EINA_LIKELY((EINA_LOG_DOMAIN_GLOBAL >= 0) &&
1738 ((unsigned int)EINA_LOG_DOMAIN_GLOBAL < _log_domains_count)))
1739 _log_domains[EINA_LOG_DOMAIN_GLOBAL].level = level;
1740 #else
1741 (void) level;
1742 #endif
1743 }
1744
1745 EAPI int
eina_log_level_get(void)1746 eina_log_level_get(void)
1747 {
1748 #ifdef EINA_ENABLE_LOG
1749 return _log_level;
1750 #else
1751 return 0;
1752 #endif
1753 }
1754
1755 EAPI Eina_Bool
eina_log_main_thread_check(void)1756 eina_log_main_thread_check(void)
1757 {
1758 #ifdef EINA_ENABLE_LOG
1759 return ((!_threads_enabled) || IS_MAIN(SELF()));
1760 #else
1761 return EINA_TRUE;
1762 #endif
1763 }
1764
1765 EAPI void
eina_log_color_disable_set(Eina_Bool disabled)1766 eina_log_color_disable_set(Eina_Bool disabled)
1767 {
1768 #ifdef EINA_ENABLE_LOG
1769 Eina_Log_Domain *domain;
1770 unsigned int i;
1771
1772 _disable_color = disabled;
1773
1774 for (i = 0; i < _log_domains_count; i++)
1775 {
1776 if (_log_domains[i].deleted)
1777 continue;
1778
1779 domain = &_log_domains[i];
1780
1781 free((char *)domain->domain_str);
1782
1783 if ((domain->color) && (!_disable_color))
1784 domain->domain_str = eina_log_domain_str_get(domain->name, domain->color);
1785 else
1786 domain->domain_str = eina_log_domain_str_get(domain->name, NULL);
1787 }
1788
1789 #else
1790 (void) disabled;
1791 #endif
1792 }
1793
1794 EAPI Eina_Bool
eina_log_color_disable_get(void)1795 eina_log_color_disable_get(void)
1796 {
1797 #ifdef EINA_ENABLE_LOG
1798 return _disable_color;
1799 #else
1800 return EINA_TRUE;
1801 #endif
1802 }
1803
1804 EAPI void
eina_log_file_disable_set(Eina_Bool disabled)1805 eina_log_file_disable_set(Eina_Bool disabled)
1806 {
1807 #ifdef EINA_ENABLE_LOG
1808 _disable_file = disabled;
1809 #else
1810 (void) disabled;
1811 #endif
1812 }
1813
1814 EAPI Eina_Bool
eina_log_file_disable_get(void)1815 eina_log_file_disable_get(void)
1816 {
1817 #ifdef EINA_ENABLE_LOG
1818 return _disable_file;
1819 #else
1820 return EINA_TRUE;
1821 #endif
1822 }
1823
1824 EAPI void
eina_log_function_disable_set(Eina_Bool disabled)1825 eina_log_function_disable_set(Eina_Bool disabled)
1826 {
1827 #ifdef EINA_ENABLE_LOG
1828 _disable_function = disabled;
1829 #else
1830 (void) disabled;
1831 #endif
1832 }
1833
1834 EAPI Eina_Bool
eina_log_function_disable_get(void)1835 eina_log_function_disable_get(void)
1836 {
1837 #ifdef EINA_ENABLE_LOG
1838 return _disable_function;
1839 #else
1840 return EINA_TRUE;
1841 #endif
1842 }
1843
1844 EAPI void
eina_log_abort_on_critical_set(Eina_Bool abort_on_critical)1845 eina_log_abort_on_critical_set(Eina_Bool abort_on_critical)
1846 {
1847 #ifdef EINA_ENABLE_LOG
1848 _abort_on_critical = abort_on_critical;
1849 #else
1850 (void) abort_on_critical;
1851 #endif
1852 }
1853
1854 EAPI Eina_Bool
eina_log_abort_on_critical_get(void)1855 eina_log_abort_on_critical_get(void)
1856 {
1857 #ifdef EINA_ENABLE_LOG
1858 return _abort_on_critical;
1859 #else
1860 return EINA_FALSE;
1861 #endif
1862 }
1863
1864 EAPI void
eina_log_abort_on_critical_level_set(int critical_level)1865 eina_log_abort_on_critical_level_set(int critical_level)
1866 {
1867 #ifdef EINA_ENABLE_LOG
1868 _abort_level_on_critical = critical_level;
1869 #else
1870 (void) critical_level;
1871 #endif
1872 }
1873
1874 EAPI int
eina_log_abort_on_critical_level_get(void)1875 eina_log_abort_on_critical_level_get(void)
1876 {
1877 #ifdef EINA_ENABLE_LOG
1878 return _abort_level_on_critical;
1879 #else
1880 return 0;
1881 #endif
1882 }
1883
1884 EAPI int
eina_log_domain_register(const char * name,const char * color)1885 eina_log_domain_register(const char *name, const char *color)
1886 {
1887 #ifdef EINA_ENABLE_LOG
1888 int r;
1889
1890 EINA_SAFETY_ON_NULL_RETURN_VAL(name, -1);
1891
1892 LOG_LOCK();
1893 r = eina_log_domain_register_unlocked(name, color);
1894 LOG_UNLOCK();
1895 return r;
1896 #else
1897 (void) name;
1898 (void) color;
1899 return 0;
1900 #endif
1901 }
1902
1903 EAPI void
eina_log_domain_unregister(int domain)1904 eina_log_domain_unregister(int domain)
1905 {
1906 #ifdef EINA_ENABLE_LOG
1907 EINA_SAFETY_ON_FALSE_RETURN(domain >= 0);
1908 LOG_LOCK();
1909 eina_log_domain_unregister_unlocked(domain);
1910 LOG_UNLOCK();
1911 #else
1912 (void) domain;
1913 #endif
1914 }
1915
1916 EAPI void
eina_log_domain_level_set(const char * domain_name,int level)1917 eina_log_domain_level_set(const char *domain_name, int level)
1918 {
1919 #ifdef EINA_ENABLE_LOG
1920 Eina_Log_Domain_Level_Pending *pending;
1921 size_t namelen;
1922 unsigned int i;
1923
1924 EINA_SAFETY_ON_NULL_RETURN(domain_name);
1925
1926 namelen = strlen(domain_name);
1927
1928 for (i = 0; i < _log_domains_count; i++)
1929 {
1930 if (_log_domains[i].deleted)
1931 continue;
1932
1933 if ((namelen != _log_domains[i].namelen) ||
1934 (strcmp(_log_domains[i].name, domain_name) != 0))
1935 continue;
1936
1937 _log_domains[i].level = level;
1938 return;
1939 }
1940
1941 EINA_INLIST_FOREACH(_pending_list, pending)
1942 {
1943 if ((namelen == pending->namelen) &&
1944 (strcmp(pending->name, domain_name) == 0))
1945 {
1946 pending->level = level;
1947 return;
1948 }
1949 }
1950
1951 pending = malloc(sizeof(Eina_Log_Domain_Level_Pending) + namelen + 1);
1952 if (!pending)
1953 return;
1954
1955 pending->level = level;
1956 pending->namelen = namelen;
1957 memcpy(pending->name, domain_name, namelen + 1);
1958
1959 _pending_list = eina_inlist_append(_pending_list, EINA_INLIST_GET(pending));
1960 #else
1961 (void) domain_name;
1962 (void) level;
1963 #endif
1964 }
1965
1966 EAPI int
eina_log_domain_level_get(const char * domain_name)1967 eina_log_domain_level_get(const char *domain_name)
1968 {
1969 #ifdef EINA_ENABLE_LOG
1970 Eina_Log_Domain_Level_Pending *pending;
1971 size_t namelen;
1972 unsigned int i;
1973
1974 EINA_SAFETY_ON_NULL_RETURN_VAL(domain_name, EINA_LOG_LEVEL_UNKNOWN);
1975
1976 namelen = strlen(domain_name);
1977
1978 for (i = 0; i < _log_domains_count; i++)
1979 {
1980 if (_log_domains[i].deleted)
1981 continue;
1982
1983 if ((namelen != _log_domains[i].namelen) ||
1984 (strcmp(_log_domains[i].name, domain_name) != 0))
1985 continue;
1986
1987 return _log_domains[i].level;
1988 }
1989
1990 EINA_INLIST_FOREACH(_pending_list, pending)
1991 {
1992 if ((namelen == pending->namelen) &&
1993 (strcmp(pending->name, domain_name) == 0))
1994 return pending->level;
1995 }
1996
1997 EINA_INLIST_FOREACH(_glob_list, pending)
1998 {
1999 if (!fnmatch(pending->name, domain_name, 0))
2000 return pending->level;
2001 }
2002
2003 return _log_level;
2004 #else
2005 (void) domain_name;
2006 return 0;
2007 #endif
2008 }
2009
2010 EAPI int
eina_log_domain_registered_level_get(int domain)2011 eina_log_domain_registered_level_get(int domain)
2012 {
2013 #ifdef EINA_ENABLE_LOG
2014 EINA_SAFETY_ON_FALSE_RETURN_VAL(domain >= 0, EINA_LOG_LEVEL_UNKNOWN);
2015 EINA_SAFETY_ON_FALSE_RETURN_VAL((unsigned int)domain < _log_domains_count,
2016 EINA_LOG_LEVEL_UNKNOWN);
2017 EINA_SAFETY_ON_TRUE_RETURN_VAL(_log_domains[domain].deleted,
2018 EINA_LOG_LEVEL_UNKNOWN);
2019 return _log_domains[domain].level;
2020 #else
2021 (void) domain;
2022 return 0;
2023 #endif
2024 }
2025
2026 EAPI void
eina_log_domain_registered_level_set(int domain,int level)2027 eina_log_domain_registered_level_set(int domain, int level)
2028 {
2029 #ifdef EINA_ENABLE_LOG
2030 EINA_SAFETY_ON_FALSE_RETURN(domain >= 0);
2031 EINA_SAFETY_ON_FALSE_RETURN((unsigned int)domain < _log_domains_count);
2032 EINA_SAFETY_ON_TRUE_RETURN(_log_domains[domain].deleted);
2033 _log_domains[domain].level = level;
2034 #else
2035 (void) domain;
2036 (void) level;
2037 #endif
2038 }
2039
2040 EAPI void
eina_log_print_cb_stderr(const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line,const char * fmt,EINA_UNUSED void * data,va_list args)2041 eina_log_print_cb_stderr(const Eina_Log_Domain *d,
2042 Eina_Log_Level level,
2043 const char *file,
2044 const char *fnc,
2045 int line,
2046 const char *fmt,
2047 EINA_UNUSED void *data,
2048 va_list args)
2049 {
2050 #ifdef EINA_ENABLE_LOG
2051 _eina_log_print_prefix(stderr, d, level, file, fnc, line);
2052 vfprintf(stderr, fmt, args);
2053 putc('\n', stderr);
2054 DISPLAY_BACKTRACE(stderr, level);
2055 # ifdef _WIN32
2056 /*
2057 * NOTE: when using mintty-base terminals (like MSYS2, or cygwin one),
2058 * stderr is not flushed, so we force flush in this case.
2059 */
2060 if (!_eina_log_win32_is_console)
2061 fflush(stderr);
2062 # endif
2063 #else
2064 (void) d;
2065 (void) level;
2066 (void) file;
2067 (void) fnc;
2068 (void) line;
2069 (void) fmt;
2070 (void) data;
2071 (void) args;
2072 #endif
2073 }
2074
2075 EAPI void
eina_log_print_cb_stdout(const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line,const char * fmt,EINA_UNUSED void * data,va_list args)2076 eina_log_print_cb_stdout(const Eina_Log_Domain *d,
2077 Eina_Log_Level level,
2078 const char *file,
2079 const char *fnc,
2080 int line,
2081 const char *fmt,
2082 EINA_UNUSED void *data,
2083 va_list args)
2084 {
2085 #ifdef EINA_ENABLE_LOG
2086 _eina_log_print_prefix(stdout, d, level, file, fnc, line);
2087 vprintf(fmt, args);
2088 putchar('\n');
2089 DISPLAY_BACKTRACE(stdout, level);
2090 #else
2091 (void) d;
2092 (void) level;
2093 (void) file;
2094 (void) fnc;
2095 (void) line;
2096 (void) fmt;
2097 (void) data;
2098 (void) args;
2099 #endif
2100 }
2101
2102 #ifdef HAVE_SYSTEMD
2103 static Eina_Module *_libsystemd = NULL;
2104 static Eina_Bool _libsystemd_broken = EINA_FALSE;
2105
2106 static int (*_eina_sd_journal_send_with_location) (const char *file, const char *line, const char *func, const char *format, ...) = NULL;
2107
2108 static void
_eina_sd_init(void)2109 _eina_sd_init(void)
2110 {
2111 if (_libsystemd_broken) return;
2112 _libsystemd = eina_module_new("libsystemd.so.0");
2113 if (_libsystemd)
2114 {
2115 if (!eina_module_load(_libsystemd))
2116 {
2117 eina_module_free(_libsystemd);
2118 _libsystemd = NULL;
2119 }
2120 }
2121 if (!_libsystemd)
2122 {
2123 _libsystemd_broken = EINA_TRUE;
2124 return;
2125 }
2126 _eina_sd_journal_send_with_location =
2127 eina_module_symbol_get(_libsystemd, "sd_journal_send_with_location");
2128 if (!_eina_sd_journal_send_with_location)
2129 {
2130 _eina_sd_journal_send_with_location = NULL;
2131 eina_module_free(_libsystemd);
2132 _libsystemd = NULL;
2133 _libsystemd_broken = EINA_TRUE;
2134 }
2135 }
2136
2137 #endif
2138
2139 EAPI void
eina_log_print_cb_journald(const Eina_Log_Domain * d,Eina_Log_Level level,const char * file,const char * fnc,int line,const char * fmt,void * data EINA_UNUSED,va_list args)2140 eina_log_print_cb_journald(const Eina_Log_Domain *d,
2141 Eina_Log_Level level,
2142 const char *file,
2143 const char *fnc,
2144 int line,
2145 const char *fmt,
2146 void *data EINA_UNUSED,
2147 va_list args)
2148 {
2149 #ifdef HAVE_SYSTEMD
2150 char *file_prefixed = NULL;
2151 char *line_str = NULL;
2152 char *message = NULL;
2153 Eina_Thread cur;
2154 int r;
2155
2156 _eina_sd_init();
2157 if (!_eina_sd_journal_send_with_location) goto nosystemd;
2158
2159 r = asprintf(&file_prefixed, "CODE_FILE=%s", file);
2160 if (r == -1)
2161 {
2162 fputs("ERR: eina_log_print_cb_journald() asprintf failed\n", stderr);
2163 goto finish;
2164 }
2165
2166 r = asprintf(&line_str, "CODE_LINE=%d", line);
2167 if (r == -1)
2168 {
2169 fputs("ERR: eina_log_print_cb_journald() asprintf failed\n", stderr);
2170 goto finish;
2171 }
2172
2173 r = vasprintf(&message, fmt, args);
2174 if (r == -1)
2175 {
2176 fputs("ERR: eina_log_print_cb_journald() vasprintf failed\n", stderr);
2177 goto finish;
2178 }
2179
2180 cur = SELF();
2181
2182 #ifdef EINA_LOG_BACKTRACE
2183 if (EINA_LIKELY(level > _backtrace_level))
2184 #endif
2185 _eina_sd_journal_send_with_location(file_prefixed, line_str, fnc,
2186 "PRIORITY=%i", level,
2187 "MESSAGE=%s", message,
2188 "EFL_DOMAIN=%s", d->domain_str,
2189 "THREAD=%lu", cur,
2190 NULL);
2191 #ifdef EINA_LOG_BACKTRACE
2192 else
2193 {
2194 Eina_Strbuf *bts;
2195 char **strings;
2196 void *bt[256];
2197 int btlen;
2198 int i;
2199
2200 btlen = backtrace((void **)bt, 256);
2201 strings = backtrace_symbols((void **)bt, btlen);
2202
2203 bts = eina_strbuf_new();
2204 for (i = 0; i < btlen; i++)
2205 if (i + 1 == btlen)
2206 eina_strbuf_append_printf(bts, "[%s]", strings[i]);
2207 else
2208 eina_strbuf_append_printf(bts, "[%s], ", strings[i]);
2209
2210 _eina_sd_journal_send_with_location(file_prefixed, line_str, fnc,
2211 "PRIORITY=%i", level,
2212 "MESSAGE=%s", message,
2213 "EFL_DOMAIN=%s", d->domain_str,
2214 "THREAD=%lu", cur,
2215 "BACKTRACE=%s",
2216 eina_strbuf_string_get(bts),
2217 NULL);
2218 eina_strbuf_free(bts);
2219 free(strings);
2220 }
2221 #endif
2222
2223 finish:
2224 free(file_prefixed);
2225 free(line_str);
2226 free(message);
2227 return;
2228 nosystemd:
2229 #endif
2230 eina_log_print_cb_stderr(d, level, file, fnc, line, fmt, data, args);
2231 }
2232
2233 EAPI void
eina_log_print_cb_file(const Eina_Log_Domain * d,EINA_UNUSED Eina_Log_Level level,const char * file,const char * fnc,int line,const char * fmt,void * data,va_list args)2234 eina_log_print_cb_file(const Eina_Log_Domain *d,
2235 EINA_UNUSED Eina_Log_Level level,
2236 const char *file,
2237 const char *fnc,
2238 int line,
2239 const char *fmt,
2240 void *data,
2241 va_list args)
2242 {
2243 #ifdef EINA_ENABLE_LOG
2244 EINA_SAFETY_ON_NULL_RETURN(data);
2245 FILE *f = data;
2246 if (_threads_enabled)
2247 {
2248 Eina_Thread cur;
2249
2250 cur = SELF();
2251 if (IS_OTHER(cur))
2252 {
2253 fprintf(f, "%s[T:%lu] %s:%d %s() ", d->name, (unsigned long)cur,
2254 file, line, fnc);
2255 goto end;
2256 }
2257 }
2258
2259 fprintf(f, "%s<%u> %s:%d %s() ", d->name, eina_log_pid_get(),
2260 file, line, fnc);
2261 DISPLAY_BACKTRACE(f, level);
2262
2263 end:
2264 vfprintf(f, fmt, args);
2265 putc('\n', f);
2266 #else
2267 (void) d;
2268 (void) file;
2269 (void) fnc;
2270 (void) line;
2271 (void) fmt;
2272 (void) data;
2273 (void) args;
2274 #endif
2275 }
2276
2277 EAPI void
eina_log_print(int domain,Eina_Log_Level level,const char * file,const char * fnc,int line,const char * fmt,...)2278 eina_log_print(int domain, Eina_Log_Level level, const char *file,
2279 const char *fnc, int line, const char *fmt, ...)
2280 {
2281 #ifdef EINA_ENABLE_LOG
2282 va_list args;
2283
2284 #ifdef EINA_SAFETY_CHECKS
2285 if (EINA_UNLIKELY(!file))
2286 {
2287 fputs("ERR: eina_log_print() file == NULL\n", stderr);
2288 return;
2289 }
2290
2291 if (EINA_UNLIKELY(!fnc))
2292 {
2293 fputs("ERR: eina_log_print() fnc == NULL\n", stderr);
2294 return;
2295 }
2296
2297 if (EINA_UNLIKELY(!fmt))
2298 {
2299 fputs("ERR: eina_log_print() fmt == NULL\n", stderr);
2300 return;
2301 }
2302
2303 #endif
2304 va_start(args, fmt);
2305 LOG_LOCK();
2306 eina_log_print_unlocked(domain, level, file, fnc, line, fmt, args);
2307 LOG_UNLOCK();
2308 va_end(args);
2309 #else
2310 (void) domain;
2311 (void) level;
2312 (void) file;
2313 (void) fnc;
2314 (void) line;
2315 (void) fmt;
2316 #endif
2317 }
2318
2319 EAPI void
eina_log_vprint(int domain,Eina_Log_Level level,const char * file,const char * fnc,int line,const char * fmt,va_list args)2320 eina_log_vprint(int domain, Eina_Log_Level level, const char *file,
2321 const char *fnc, int line, const char *fmt, va_list args)
2322 {
2323 #ifdef EINA_ENABLE_LOG
2324
2325 #ifdef EINA_SAFETY_CHECKS
2326 if (EINA_UNLIKELY(!file))
2327 {
2328 fputs("ERR: eina_log_print() file == NULL\n", stderr);
2329 return;
2330 }
2331
2332 if (EINA_UNLIKELY(!fnc))
2333 {
2334 fputs("ERR: eina_log_print() fnc == NULL\n", stderr);
2335 return;
2336 }
2337
2338 if (EINA_UNLIKELY(!fmt))
2339 {
2340 fputs("ERR: eina_log_print() fmt == NULL\n", stderr);
2341 return;
2342 }
2343
2344 #endif
2345 LOG_LOCK();
2346 eina_log_print_unlocked(domain, level, file, fnc, line, fmt, args);
2347 LOG_UNLOCK();
2348 #else
2349 (void) domain;
2350 (void) level;
2351 (void) file;
2352 (void) fnc;
2353 (void) line;
2354 (void) fmt;
2355 (void) args;
2356 #endif
2357 }
2358
2359 EAPI void
eina_log_console_color_set(FILE * fp,const char * color)2360 eina_log_console_color_set(FILE *fp, const char *color)
2361 {
2362 #ifdef EINA_ENABLE_LOG
2363
2364 EINA_SAFETY_ON_NULL_RETURN(fp);
2365 EINA_SAFETY_ON_NULL_RETURN(color);
2366 if (_disable_color) return;
2367
2368 #ifdef _WIN32
2369 int attr = eina_log_win32_color_convert(color, NULL);
2370 HANDLE *handle;
2371
2372 if (!attr) return;
2373
2374 if (fp == stderr)
2375 handle = GetStdHandle(STD_ERROR_HANDLE);
2376 else if (fp == stdout)
2377 handle = GetStdHandle(STD_OUTPUT_HANDLE);
2378 else
2379 {
2380 /* Do we have a way to convert FILE* to HANDLE?
2381 * Should we use it?
2382 */
2383 return;
2384 }
2385 SetConsoleTextAttribute(handle, attr);
2386 #else
2387 fputs(color, fp);
2388 #endif
2389
2390 #else
2391 (void)color;
2392 #endif
2393 }
2394
2395 EAPI void
eina_log_timing(int domain,Eina_Log_State state,const char * phase)2396 eina_log_timing(int domain,
2397 Eina_Log_State state,
2398 const char *phase)
2399 {
2400 #ifdef EINA_SAFETY_CHECKS
2401 Eina_Log_Domain *d;
2402 #endif
2403 Eina_Log_Timing *t;
2404
2405 if (_disable_timing) return;
2406
2407 #ifdef EINA_SAFETY_CHECKS
2408 d = _log_domains + domain;
2409 #endif
2410 t = _log_timing + domain;
2411 #ifdef EINA_SAFETY_CHECKS
2412 if (EINA_UNLIKELY(d->deleted))
2413 {
2414 fprintf(stderr,
2415 "ERR: eina_log_print() domain %d is deleted\n",
2416 domain);
2417 return;
2418 }
2419 #endif
2420
2421 if (!t->phase && state == EINA_LOG_STATE_STOP)
2422 return;
2423
2424 if (t->phase == EINA_LOG_STATE_INIT &&
2425 phase == EINA_LOG_STATE_SHUTDOWN)
2426 return;
2427
2428 if (state == EINA_LOG_STATE_START &&
2429 t->phase &&
2430 strcmp(t->phase, phase)) // Different phase
2431 {
2432 fprintf(stderr, "%s vs %s\n", t->phase, phase);
2433 eina_log_timing(domain, EINA_LOG_STATE_STOP, t->phase);
2434 }
2435
2436 switch (state)
2437 {
2438 case EINA_LOG_STATE_START:
2439 {
2440 _eina_time_get(&t->start);
2441 t->phase = phase;
2442 break;
2443 }
2444 case EINA_LOG_STATE_STOP:
2445 {
2446 Eina_Nano_Time end;
2447 long int r;
2448
2449 _eina_time_get(&end);
2450 r = _eina_time_delta(&t->start, &end);
2451 EINA_LOG_DOM_INFO(domain, "%s timing: %li", t->phase, r);
2452
2453 t->phase = NULL;
2454 break;
2455 }
2456 }
2457 }
2458