1/**************************************************************************
2 *
3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
5 * Copyright 2010 LunarG, Inc.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sub license, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial portions
18 * of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 *
28 **************************************************************************/
29
30
31/**
32 * Logging facility for debug/info messages.
33 * _EGL_FATAL messages are printed to stderr
34 * The EGL_LOG_LEVEL var controls the output of other warning/info/debug msgs.
35 */
36
37
38#include <stdarg.h>
39#include <stdio.h>
40#include <stdlib.h>
41
42#include "egllog.h"
43#include "eglstring.h"
44#include "eglmutex.h"
45
46#define MAXSTRING 1000
47#define FALLBACK_LOG_LEVEL _EGL_WARNING
48
49
50static struct {
51   _EGLMutex mutex;
52
53   EGLBoolean initialized;
54   EGLint level;
55   _EGLLogProc logger;
56   EGLint num_messages;
57} logging = {
58   _EGL_MUTEX_INITIALIZER,
59   EGL_FALSE,
60   FALLBACK_LOG_LEVEL,
61   NULL,
62   0
63};
64
65static const char *level_strings[] = {
66   /* the order is important */
67   "fatal",
68   "warning",
69   "info",
70   "debug",
71   NULL
72};
73
74
75/**
76 * Set the function to be called when there is a message to log.
77 * Note that the function will be called with an internal lock held.
78 * Recursive logging is not allowed.
79 */
80void
81_eglSetLogProc(_EGLLogProc logger)
82{
83   EGLint num_messages = 0;
84
85   _eglLockMutex(&logging.mutex);
86
87   if (logging.logger != logger) {
88      logging.logger = logger;
89
90      num_messages = logging.num_messages;
91      logging.num_messages = 0;
92   }
93
94   _eglUnlockMutex(&logging.mutex);
95
96   if (num_messages)
97      _eglLog(_EGL_DEBUG,
98              "New logger installed. "
99              "Messages before the new logger might not be available.");
100}
101
102
103/**
104 * Set the log reporting level.
105 */
106void
107_eglSetLogLevel(EGLint level)
108{
109   switch (level) {
110   case _EGL_FATAL:
111   case _EGL_WARNING:
112   case _EGL_INFO:
113   case _EGL_DEBUG:
114      _eglLockMutex(&logging.mutex);
115      logging.level = level;
116      _eglUnlockMutex(&logging.mutex);
117      break;
118   default:
119      break;
120   }
121}
122
123
124/**
125 * The default logger.  It prints the message to stderr.
126 */
127static void
128_eglDefaultLogger(EGLint level, const char *msg)
129{
130   fprintf(stderr, "libEGL %s: %s\n", level_strings[level], msg);
131}
132
133
134/**
135 * Initialize the logging facility.
136 */
137static void
138_eglInitLogger(void)
139{
140   const char *log_env;
141   EGLint i, level = -1;
142
143   if (logging.initialized)
144      return;
145
146   log_env = getenv("EGL_LOG_LEVEL");
147   if (log_env) {
148      for (i = 0; level_strings[i]; i++) {
149         if (_eglstrcasecmp(log_env, level_strings[i]) == 0) {
150            level = i;
151            break;
152         }
153      }
154   }
155   else {
156      level = FALLBACK_LOG_LEVEL;
157   }
158
159   logging.logger = _eglDefaultLogger;
160   logging.level = (level >= 0) ? level : FALLBACK_LOG_LEVEL;
161   logging.initialized = EGL_TRUE;
162
163   /* it is fine to call _eglLog now */
164   if (log_env && level < 0) {
165      _eglLog(_EGL_WARNING,
166              "Unrecognized EGL_LOG_LEVEL environment variable value. "
167              "Expected one of \"fatal\", \"warning\", \"info\", \"debug\". "
168              "Got \"%s\". Falling back to \"%s\".",
169              log_env, level_strings[FALLBACK_LOG_LEVEL]);
170   }
171}
172
173
174/**
175 * Log a message with message logger.
176 * \param level one of _EGL_FATAL, _EGL_WARNING, _EGL_INFO, _EGL_DEBUG.
177 */
178void
179_eglLog(EGLint level, const char *fmtStr, ...)
180{
181   va_list args;
182   char msg[MAXSTRING];
183   int ret;
184
185   /* one-time initialization; a little race here is fine */
186   if (!logging.initialized)
187      _eglInitLogger();
188   if (level > logging.level || level < 0)
189      return;
190
191   _eglLockMutex(&logging.mutex);
192
193   if (logging.logger) {
194      va_start(args, fmtStr);
195      ret = vsnprintf(msg, MAXSTRING, fmtStr, args);
196      if (ret < 0 || ret >= MAXSTRING)
197         strcpy(msg, "<message truncated>");
198      va_end(args);
199
200      logging.logger(level, msg);
201      logging.num_messages++;
202   }
203
204   _eglUnlockMutex(&logging.mutex);
205
206   if (level == _EGL_FATAL)
207      exit(1); /* or abort()? */
208}
209