1 /************************************************************************** 2 * 3 * Copyright 2008 VMware, Inc. 4 * Copyright 2009-2010 Chia-I Wu <olvaffe (at) 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 #include <string.h> 42 #include <strings.h> 43 #include "c11/threads.h" 44 45 #include "egllog.h" 46 47 #define MAXSTRING 1000 48 #define FALLBACK_LOG_LEVEL _EGL_WARNING 49 50 51 static struct { 52 mtx_t mutex; 53 54 EGLBoolean initialized; 55 EGLint level; 56 _EGLLogProc logger; 57 EGLint num_messages; 58 } logging = { 59 _MTX_INITIALIZER_NP, 60 EGL_FALSE, 61 FALLBACK_LOG_LEVEL, 62 NULL, 63 0 64 }; 65 66 static const char *level_strings[] = { 67 /* the order is important */ 68 "fatal", 69 "warning", 70 "info", 71 "debug", 72 NULL 73 }; 74 75 76 /** 77 * Set the function to be called when there is a message to log. 78 * Note that the function will be called with an internal lock held. 79 * Recursive logging is not allowed. 80 */ 81 void 82 _eglSetLogProc(_EGLLogProc logger) 83 { 84 EGLint num_messages = 0; 85 86 mtx_lock(&logging.mutex); 87 88 if (logging.logger != logger) { 89 logging.logger = logger; 90 91 num_messages = logging.num_messages; 92 logging.num_messages = 0; 93 } 94 95 mtx_unlock(&logging.mutex); 96 97 if (num_messages) 98 _eglLog(_EGL_DEBUG, 99 "New logger installed. " 100 "Messages before the new logger might not be available."); 101 } 102 103 104 /** 105 * Set the log reporting level. 106 */ 107 void 108 _eglSetLogLevel(EGLint level) 109 { 110 switch (level) { 111 case _EGL_FATAL: 112 case _EGL_WARNING: 113 case _EGL_INFO: 114 case _EGL_DEBUG: 115 mtx_lock(&logging.mutex); 116 logging.level = level; 117 mtx_unlock(&logging.mutex); 118 break; 119 default: 120 break; 121 } 122 } 123 124 125 /** 126 * The default logger. It prints the message to stderr. 127 */ 128 static void 129 _eglDefaultLogger(EGLint level, const char *msg) 130 { 131 fprintf(stderr, "libEGL %s: %s\n", level_strings[level], msg); 132 } 133 134 135 /** 136 * Initialize the logging facility. 137 */ 138 static void 139 _eglInitLogger(void) 140 { 141 const char *log_env; 142 EGLint i, level = -1; 143 144 if (logging.initialized) 145 return; 146 147 log_env = getenv("EGL_LOG_LEVEL"); 148 if (log_env) { 149 for (i = 0; level_strings[i]; i++) { 150 if (strcasecmp(log_env, level_strings[i]) == 0) { 151 level = i; 152 break; 153 } 154 } 155 } 156 else { 157 level = FALLBACK_LOG_LEVEL; 158 } 159 160 logging.logger = _eglDefaultLogger; 161 logging.level = (level >= 0) ? level : FALLBACK_LOG_LEVEL; 162 logging.initialized = EGL_TRUE; 163 164 /* it is fine to call _eglLog now */ 165 if (log_env && level < 0) { 166 _eglLog(_EGL_WARNING, 167 "Unrecognized EGL_LOG_LEVEL environment variable value. " 168 "Expected one of \"fatal\", \"warning\", \"info\", \"debug\". " 169 "Got \"%s\". Falling back to \"%s\".", 170 log_env, level_strings[FALLBACK_LOG_LEVEL]); 171 } 172 } 173 174 175 /** 176 * Log a message with message logger. 177 * \param level one of _EGL_FATAL, _EGL_WARNING, _EGL_INFO, _EGL_DEBUG. 178 */ 179 void 180 _eglLog(EGLint level, const char *fmtStr, ...) 181 { 182 va_list args; 183 char msg[MAXSTRING]; 184 int ret; 185 186 /* one-time initialization; a little race here is fine */ 187 if (!logging.initialized) 188 _eglInitLogger(); 189 if (level > logging.level || level < 0) 190 return; 191 192 mtx_lock(&logging.mutex); 193 194 if (logging.logger) { 195 va_start(args, fmtStr); 196 ret = vsnprintf(msg, MAXSTRING, fmtStr, args); 197 if (ret < 0 || ret >= MAXSTRING) 198 strcpy(msg, "<message truncated>"); 199 va_end(args); 200 201 logging.logger(level, msg); 202 logging.num_messages++; 203 } 204 205 mtx_unlock(&logging.mutex); 206 207 if (level == _EGL_FATAL) 208 exit(1); /* or abort()? */ 209 } 210