1/** 2 * \file errors.c 3 * Mesa debugging and error handling functions. 4 */ 5 6/* 7 * Mesa 3-D graphics library 8 * 9 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. 10 * 11 * Permission is hereby granted, free of charge, to any person obtaining a 12 * copy of this software and associated documentation files (the "Software"), 13 * to deal in the Software without restriction, including without limitation 14 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 * and/or sell copies of the Software, and to permit persons to whom the 16 * Software is furnished to do so, subject to the following conditions: 17 * 18 * The above copyright notice and this permission notice shall be included 19 * in all copies or substantial portions of the Software. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 * OTHER DEALINGS IN THE SOFTWARE. 28 */ 29 30 31#include <stdarg.h> 32#include <stdio.h> 33#include "errors.h" 34#include "enums.h" 35#include "imports.h" 36#include "context.h" 37#include "debug_output.h" 38 39 40static FILE *LogFile = NULL; 41 42 43static void 44output_if_debug(const char *prefixString, const char *outputString, 45 GLboolean newline) 46{ 47 static int debug = -1; 48 49 /* Init the local 'debug' var once. 50 * Note: the _mesa_init_debug() function should have been called 51 * by now so MESA_DEBUG_FLAGS will be initialized. 52 */ 53 if (debug == -1) { 54 /* If MESA_LOG_FILE env var is set, log Mesa errors, warnings, 55 * etc to the named file. Otherwise, output to stderr. 56 */ 57 const char *logFile = getenv("MESA_LOG_FILE"); 58 if (logFile) 59 LogFile = fopen(logFile, "w"); 60 if (!LogFile) 61 LogFile = stderr; 62#ifdef DEBUG 63 /* in debug builds, print messages unless MESA_DEBUG="silent" */ 64 if (MESA_DEBUG_FLAGS & DEBUG_SILENT) 65 debug = 0; 66 else 67 debug = 1; 68#else 69 /* in release builds, be silent unless MESA_DEBUG is set */ 70 debug = getenv("MESA_DEBUG") != NULL; 71#endif 72 } 73 74 /* Now only print the string if we're required to do so. */ 75 if (debug) { 76 if (prefixString) 77 fprintf(LogFile, "%s: %s", prefixString, outputString); 78 else 79 fprintf(LogFile, "%s", outputString); 80 if (newline) 81 fprintf(LogFile, "\n"); 82 fflush(LogFile); 83 84#if defined(_WIN32) 85 /* stderr from windows applications without console is not usually 86 * visible, so communicate with the debugger instead */ 87 { 88 char buf[4096]; 89 _mesa_snprintf(buf, sizeof(buf), "%s: %s%s", prefixString, outputString, newline ? "\n" : ""); 90 OutputDebugStringA(buf); 91 } 92#endif 93 } 94} 95 96 97/** 98 * Return the file handle to use for debug/logging. Defaults to stderr 99 * unless MESA_LOG_FILE is defined. 100 */ 101FILE * 102_mesa_get_log_file(void) 103{ 104 assert(LogFile); 105 return LogFile; 106} 107 108 109/** 110 * When a new type of error is recorded, print a message describing 111 * previous errors which were accumulated. 112 */ 113static void 114flush_delayed_errors( struct gl_context *ctx ) 115{ 116 char s[MAX_DEBUG_MESSAGE_LENGTH]; 117 118 if (ctx->ErrorDebugCount) { 119 _mesa_snprintf(s, MAX_DEBUG_MESSAGE_LENGTH, "%d similar %s errors", 120 ctx->ErrorDebugCount, 121 _mesa_enum_to_string(ctx->ErrorValue)); 122 123 output_if_debug("Mesa", s, GL_TRUE); 124 125 ctx->ErrorDebugCount = 0; 126 } 127} 128 129 130/** 131 * Report a warning (a recoverable error condition) to stderr if 132 * either DEBUG is defined or the MESA_DEBUG env var is set. 133 * 134 * \param ctx GL context. 135 * \param fmtString printf()-like format string. 136 */ 137void 138_mesa_warning( struct gl_context *ctx, const char *fmtString, ... ) 139{ 140 char str[MAX_DEBUG_MESSAGE_LENGTH]; 141 va_list args; 142 va_start( args, fmtString ); 143 (void) _mesa_vsnprintf( str, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args ); 144 va_end( args ); 145 146 if (ctx) 147 flush_delayed_errors( ctx ); 148 149 output_if_debug("Mesa warning", str, GL_TRUE); 150} 151 152 153/** 154 * Report an internal implementation problem. 155 * Prints the message to stderr via fprintf(). 156 * 157 * \param ctx GL context. 158 * \param fmtString problem description string. 159 */ 160void 161_mesa_problem( const struct gl_context *ctx, const char *fmtString, ... ) 162{ 163 va_list args; 164 char str[MAX_DEBUG_MESSAGE_LENGTH]; 165 static int numCalls = 0; 166 167 (void) ctx; 168 169 if (numCalls < 50) { 170 numCalls++; 171 172 va_start( args, fmtString ); 173 _mesa_vsnprintf( str, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args ); 174 va_end( args ); 175 fprintf(stderr, "Mesa " PACKAGE_VERSION " implementation error: %s\n", 176 str); 177 fprintf(stderr, "Please report at " PACKAGE_BUGREPORT "\n"); 178 } 179} 180 181 182static GLboolean 183should_output(struct gl_context *ctx, GLenum error, const char *fmtString) 184{ 185 static GLint debug = -1; 186 187 /* Check debug environment variable only once: 188 */ 189 if (debug == -1) { 190 const char *debugEnv = getenv("MESA_DEBUG"); 191 192#ifdef DEBUG 193 if (debugEnv && strstr(debugEnv, "silent")) 194 debug = GL_FALSE; 195 else 196 debug = GL_TRUE; 197#else 198 if (debugEnv) 199 debug = GL_TRUE; 200 else 201 debug = GL_FALSE; 202#endif 203 } 204 205 if (debug) { 206 if (ctx->ErrorValue != error || 207 ctx->ErrorDebugFmtString != fmtString) { 208 flush_delayed_errors( ctx ); 209 ctx->ErrorDebugFmtString = fmtString; 210 ctx->ErrorDebugCount = 0; 211 return GL_TRUE; 212 } 213 ctx->ErrorDebugCount++; 214 } 215 return GL_FALSE; 216} 217 218 219void 220_mesa_gl_vdebugf(struct gl_context *ctx, 221 GLuint *id, 222 enum mesa_debug_source source, 223 enum mesa_debug_type type, 224 enum mesa_debug_severity severity, 225 const char *fmtString, 226 va_list args) 227{ 228 char s[MAX_DEBUG_MESSAGE_LENGTH]; 229 int len; 230 231 _mesa_debug_get_id(id); 232 233 len = _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); 234 if (len >= MAX_DEBUG_MESSAGE_LENGTH) 235 /* message was truncated */ 236 len = MAX_DEBUG_MESSAGE_LENGTH - 1; 237 238 _mesa_log_msg(ctx, source, type, *id, severity, len, s); 239} 240 241 242void 243_mesa_gl_debugf(struct gl_context *ctx, 244 GLuint *id, 245 enum mesa_debug_source source, 246 enum mesa_debug_type type, 247 enum mesa_debug_severity severity, 248 const char *fmtString, ...) 249{ 250 va_list args; 251 va_start(args, fmtString); 252 _mesa_gl_vdebugf(ctx, id, source, type, severity, fmtString, args); 253 va_end(args); 254} 255 256size_t 257_mesa_gl_debug(struct gl_context *ctx, 258 GLuint *id, 259 enum mesa_debug_source source, 260 enum mesa_debug_type type, 261 enum mesa_debug_severity severity, 262 const char *msg) 263{ 264 _mesa_debug_get_id(id); 265 266 size_t len = strnlen(msg, MAX_DEBUG_MESSAGE_LENGTH); 267 if (len < MAX_DEBUG_MESSAGE_LENGTH) { 268 _mesa_log_msg(ctx, source, type, *id, severity, len, msg); 269 return len; 270 } 271 272 /* limit the message to fit within KHR_debug buffers */ 273 char s[MAX_DEBUG_MESSAGE_LENGTH]; 274 strncpy(s, msg, MAX_DEBUG_MESSAGE_LENGTH); 275 s[MAX_DEBUG_MESSAGE_LENGTH - 1] = '\0'; 276 len = MAX_DEBUG_MESSAGE_LENGTH - 1; 277 _mesa_log_msg(ctx, source, type, *id, severity, len, s); 278 279 /* report the number of characters that were logged */ 280 return len; 281} 282 283 284/** 285 * Record an OpenGL state error. These usually occur when the user 286 * passes invalid parameters to a GL function. 287 * 288 * If debugging is enabled (either at compile-time via the DEBUG macro, or 289 * run-time via the MESA_DEBUG environment variable), report the error with 290 * _mesa_debug(). 291 * 292 * \param ctx the GL context. 293 * \param error the error value. 294 * \param fmtString printf() style format string, followed by optional args 295 */ 296void 297_mesa_error( struct gl_context *ctx, GLenum error, const char *fmtString, ... ) 298{ 299 GLboolean do_output, do_log; 300 /* Ideally this would be set up by the caller, so that we had proper IDs 301 * per different message. 302 */ 303 static GLuint error_msg_id = 0; 304 305 _mesa_debug_get_id(&error_msg_id); 306 307 do_output = should_output(ctx, error, fmtString); 308 309 simple_mtx_lock(&ctx->DebugMutex); 310 if (ctx->Debug) { 311 do_log = _mesa_debug_is_message_enabled(ctx->Debug, 312 MESA_DEBUG_SOURCE_API, 313 MESA_DEBUG_TYPE_ERROR, 314 error_msg_id, 315 MESA_DEBUG_SEVERITY_HIGH); 316 } 317 else { 318 do_log = GL_FALSE; 319 } 320 simple_mtx_unlock(&ctx->DebugMutex); 321 322 if (do_output || do_log) { 323 char s[MAX_DEBUG_MESSAGE_LENGTH], s2[MAX_DEBUG_MESSAGE_LENGTH]; 324 int len; 325 va_list args; 326 327 va_start(args, fmtString); 328 len = _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); 329 va_end(args); 330 331 if (len >= MAX_DEBUG_MESSAGE_LENGTH) { 332 /* Too long error message. Whoever calls _mesa_error should use 333 * shorter strings. 334 */ 335 assert(0); 336 return; 337 } 338 339 len = _mesa_snprintf(s2, MAX_DEBUG_MESSAGE_LENGTH, "%s in %s", 340 _mesa_enum_to_string(error), s); 341 if (len >= MAX_DEBUG_MESSAGE_LENGTH) { 342 /* Same as above. */ 343 assert(0); 344 return; 345 } 346 347 /* Print the error to stderr if needed. */ 348 if (do_output) { 349 output_if_debug("Mesa: User error", s2, GL_TRUE); 350 } 351 352 /* Log the error via ARB_debug_output if needed.*/ 353 if (do_log) { 354 _mesa_log_msg(ctx, MESA_DEBUG_SOURCE_API, MESA_DEBUG_TYPE_ERROR, 355 error_msg_id, MESA_DEBUG_SEVERITY_HIGH, len, s2); 356 } 357 } 358 359 /* Set the GL context error state for glGetError. */ 360 if (ctx->ErrorValue == GL_NO_ERROR) 361 ctx->ErrorValue = error; 362} 363 364void 365_mesa_error_no_memory(const char *caller) 366{ 367 GET_CURRENT_CONTEXT(ctx); 368 _mesa_error(ctx, GL_OUT_OF_MEMORY, "out of memory in %s", caller); 369} 370 371/** 372 * Report debug information. Print error message to stderr via fprintf(). 373 * No-op if DEBUG mode not enabled. 374 * 375 * \param ctx GL context. 376 * \param fmtString printf()-style format string, followed by optional args. 377 */ 378void 379_mesa_debug( const struct gl_context *ctx, const char *fmtString, ... ) 380{ 381#ifdef DEBUG 382 char s[MAX_DEBUG_MESSAGE_LENGTH]; 383 va_list args; 384 va_start(args, fmtString); 385 _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); 386 va_end(args); 387 output_if_debug("Mesa", s, GL_FALSE); 388#endif /* DEBUG */ 389 (void) ctx; 390 (void) fmtString; 391} 392 393 394void 395_mesa_log(const char *fmtString, ...) 396{ 397 char s[MAX_DEBUG_MESSAGE_LENGTH]; 398 va_list args; 399 va_start(args, fmtString); 400 _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); 401 va_end(args); 402 output_if_debug("", s, GL_FALSE); 403} 404 405 406/** 407 * Report debug information from the shader compiler via GL_ARB_debug_output. 408 * 409 * \param ctx GL context. 410 * \param type The namespace to which this message belongs. 411 * \param id The message ID within the given namespace. 412 * \param msg The message to output. Must be null-terminated. 413 */ 414void 415_mesa_shader_debug(struct gl_context *ctx, GLenum type, GLuint *id, 416 const char *msg) 417{ 418 enum mesa_debug_source source = MESA_DEBUG_SOURCE_SHADER_COMPILER; 419 enum mesa_debug_severity severity = MESA_DEBUG_SEVERITY_HIGH; 420 int len; 421 422 _mesa_debug_get_id(id); 423 424 len = strlen(msg); 425 426 /* Truncate the message if necessary. */ 427 if (len >= MAX_DEBUG_MESSAGE_LENGTH) 428 len = MAX_DEBUG_MESSAGE_LENGTH - 1; 429 430 _mesa_log_msg(ctx, source, type, *id, severity, len, msg); 431} 432