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