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 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#ifndef NDEBUG 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 const char *env = getenv("MESA_DEBUG"); 70 debug = env && strstr(env, "silent") == 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 if (prefixString) 90 snprintf(buf, sizeof(buf), "%s: %s%s", prefixString, outputString, newline ? "\n" : ""); 91 else 92 snprintf(buf, sizeof(buf), "%s%s", outputString, newline ? "\n" : ""); 93 OutputDebugStringA(buf); 94 } 95#endif 96 } 97} 98 99 100/** 101 * Return the file handle to use for debug/logging. Defaults to stderr 102 * unless MESA_LOG_FILE is defined. 103 */ 104FILE * 105_mesa_get_log_file(void) 106{ 107 assert(LogFile); 108 return LogFile; 109} 110 111 112/** 113 * When a new type of error is recorded, print a message describing 114 * previous errors which were accumulated. 115 */ 116static void 117flush_delayed_errors( struct gl_context *ctx ) 118{ 119 char s[MAX_DEBUG_MESSAGE_LENGTH]; 120 121 if (ctx->ErrorDebugCount) { 122 snprintf(s, MAX_DEBUG_MESSAGE_LENGTH, "%d similar %s errors", 123 ctx->ErrorDebugCount, 124 _mesa_enum_to_string(ctx->ErrorValue)); 125 126 output_if_debug("Mesa", s, GL_TRUE); 127 128 ctx->ErrorDebugCount = 0; 129 } 130} 131 132 133/** 134 * Report a warning (a recoverable error condition) to stderr if 135 * either DEBUG is defined or the MESA_DEBUG env var is set. 136 * 137 * \param ctx GL context. 138 * \param fmtString printf()-like format string. 139 */ 140void 141_mesa_warning( struct gl_context *ctx, const char *fmtString, ... ) 142{ 143 char str[MAX_DEBUG_MESSAGE_LENGTH]; 144 va_list args; 145 va_start( args, fmtString ); 146 (void) vsnprintf( str, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args ); 147 va_end( args ); 148 149 if (ctx) 150 flush_delayed_errors( ctx ); 151 152 output_if_debug("Mesa warning", str, GL_TRUE); 153} 154 155 156/** 157 * Report an internal implementation problem. 158 * Prints the message to stderr via fprintf(). 159 * 160 * \param ctx GL context. 161 * \param fmtString problem description string. 162 */ 163void 164_mesa_problem( const struct gl_context *ctx, const char *fmtString, ... ) 165{ 166 va_list args; 167 char str[MAX_DEBUG_MESSAGE_LENGTH]; 168 static int numCalls = 0; 169 170 (void) ctx; 171 172 if (numCalls < 50) { 173 numCalls++; 174 175 va_start( args, fmtString ); 176 vsnprintf( str, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args ); 177 va_end( args ); 178 fprintf(stderr, "Mesa " PACKAGE_VERSION " implementation error: %s\n", 179 str); 180 fprintf(stderr, "Please report at " PACKAGE_BUGREPORT "\n"); 181 } 182} 183 184 185static GLboolean 186should_output(struct gl_context *ctx, GLenum error, const char *fmtString) 187{ 188 static GLint debug = -1; 189 190 /* Check debug environment variable only once: 191 */ 192 if (debug == -1) { 193 const char *debugEnv = getenv("MESA_DEBUG"); 194 195#ifndef NDEBUG 196 if (debugEnv && strstr(debugEnv, "silent")) 197 debug = GL_FALSE; 198 else 199 debug = GL_TRUE; 200#else 201 if (debugEnv) 202 debug = GL_TRUE; 203 else 204 debug = GL_FALSE; 205#endif 206 } 207 208 if (debug) { 209 if (ctx->ErrorValue != error || 210 ctx->ErrorDebugFmtString != fmtString) { 211 flush_delayed_errors( ctx ); 212 ctx->ErrorDebugFmtString = fmtString; 213 ctx->ErrorDebugCount = 0; 214 return GL_TRUE; 215 } 216 ctx->ErrorDebugCount++; 217 } 218 return GL_FALSE; 219} 220 221 222void 223_mesa_gl_vdebugf(struct gl_context *ctx, 224 GLuint *id, 225 enum mesa_debug_source source, 226 enum mesa_debug_type type, 227 enum mesa_debug_severity severity, 228 const char *fmtString, 229 va_list args) 230{ 231 char s[MAX_DEBUG_MESSAGE_LENGTH]; 232 int len; 233 234 _mesa_debug_get_id(id); 235 236 len = vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); 237 if (len >= MAX_DEBUG_MESSAGE_LENGTH) 238 /* message was truncated */ 239 len = MAX_DEBUG_MESSAGE_LENGTH - 1; 240 241 _mesa_log_msg(ctx, source, type, *id, severity, len, s); 242} 243 244 245void 246_mesa_gl_debugf(struct gl_context *ctx, 247 GLuint *id, 248 enum mesa_debug_source source, 249 enum mesa_debug_type type, 250 enum mesa_debug_severity severity, 251 const char *fmtString, ...) 252{ 253 va_list args; 254 va_start(args, fmtString); 255 _mesa_gl_vdebugf(ctx, id, source, type, severity, fmtString, args); 256 va_end(args); 257} 258 259size_t 260_mesa_gl_debug(struct gl_context *ctx, 261 GLuint *id, 262 enum mesa_debug_source source, 263 enum mesa_debug_type type, 264 enum mesa_debug_severity severity, 265 const char *msg) 266{ 267 _mesa_debug_get_id(id); 268 269 size_t len = strnlen(msg, MAX_DEBUG_MESSAGE_LENGTH); 270 if (len < MAX_DEBUG_MESSAGE_LENGTH) { 271 _mesa_log_msg(ctx, source, type, *id, severity, len, msg); 272 return len; 273 } 274 275 /* limit the message to fit within KHR_debug buffers */ 276 char s[MAX_DEBUG_MESSAGE_LENGTH]; 277 strncpy(s, msg, MAX_DEBUG_MESSAGE_LENGTH); 278 s[MAX_DEBUG_MESSAGE_LENGTH - 1] = '\0'; 279 len = MAX_DEBUG_MESSAGE_LENGTH - 1; 280 _mesa_log_msg(ctx, source, type, *id, severity, len, s); 281 282 /* report the number of characters that were logged */ 283 return len; 284} 285 286 287/** 288 * Record an OpenGL state error. These usually occur when the user 289 * passes invalid parameters to a GL function. 290 * 291 * If debugging is enabled (either at compile-time via the DEBUG macro, or 292 * run-time via the MESA_DEBUG environment variable), report the error with 293 * _mesa_debug(). 294 * 295 * \param ctx the GL context. 296 * \param error the error value. 297 * \param fmtString printf() style format string, followed by optional args 298 */ 299void 300_mesa_error( struct gl_context *ctx, GLenum error, const char *fmtString, ... ) 301{ 302 GLboolean do_output, do_log; 303 /* Ideally this would be set up by the caller, so that we had proper IDs 304 * per different message. 305 */ 306 static GLuint error_msg_id = 0; 307 308 _mesa_debug_get_id(&error_msg_id); 309 310 do_output = should_output(ctx, error, fmtString); 311 312 simple_mtx_lock(&ctx->DebugMutex); 313 if (ctx->Debug) { 314 do_log = _mesa_debug_is_message_enabled(ctx->Debug, 315 MESA_DEBUG_SOURCE_API, 316 MESA_DEBUG_TYPE_ERROR, 317 error_msg_id, 318 MESA_DEBUG_SEVERITY_HIGH); 319 } 320 else { 321 do_log = GL_FALSE; 322 } 323 simple_mtx_unlock(&ctx->DebugMutex); 324 325 if (do_output || do_log) { 326 char s[MAX_DEBUG_MESSAGE_LENGTH], s2[MAX_DEBUG_MESSAGE_LENGTH]; 327 int len; 328 va_list args; 329 330 va_start(args, fmtString); 331 len = vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); 332 va_end(args); 333 334 if (len >= MAX_DEBUG_MESSAGE_LENGTH) { 335 /* Too long error message. Whoever calls _mesa_error should use 336 * shorter strings. 337 */ 338 assert(0); 339 return; 340 } 341 342 len = snprintf(s2, MAX_DEBUG_MESSAGE_LENGTH, "%s in %s", 343 _mesa_enum_to_string(error), s); 344 if (len >= MAX_DEBUG_MESSAGE_LENGTH) { 345 /* Same as above. */ 346 assert(0); 347 return; 348 } 349 350 /* Print the error to stderr if needed. */ 351 if (do_output) { 352 output_if_debug("Mesa: User error", s2, GL_TRUE); 353 } 354 355 /* Log the error via ARB_debug_output if needed.*/ 356 if (do_log) { 357 _mesa_log_msg(ctx, MESA_DEBUG_SOURCE_API, MESA_DEBUG_TYPE_ERROR, 358 error_msg_id, MESA_DEBUG_SEVERITY_HIGH, len, s2); 359 } 360 } 361 362 /* Set the GL context error state for glGetError. */ 363 if (ctx->ErrorValue == GL_NO_ERROR) 364 ctx->ErrorValue = error; 365} 366 367void 368_mesa_error_no_memory(const char *caller) 369{ 370 GET_CURRENT_CONTEXT(ctx); 371 _mesa_error(ctx, GL_OUT_OF_MEMORY, "out of memory in %s", caller); 372} 373 374/** 375 * Report debug information. Print error message to stderr via fprintf(). 376 * No-op if DEBUG mode not enabled. 377 * 378 * \param ctx GL context. 379 * \param fmtString printf()-style format string, followed by optional args. 380 */ 381void 382_mesa_debug( const struct gl_context *ctx, const char *fmtString, ... ) 383{ 384#ifndef NDEBUG 385 char s[MAX_DEBUG_MESSAGE_LENGTH]; 386 va_list args; 387 va_start(args, fmtString); 388 vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); 389 va_end(args); 390 output_if_debug("Mesa", s, GL_FALSE); 391#endif /* DEBUG */ 392 (void) ctx; 393 (void) fmtString; 394} 395 396 397void 398_mesa_log(const char *fmtString, ...) 399{ 400 char s[MAX_DEBUG_MESSAGE_LENGTH]; 401 va_list args; 402 va_start(args, fmtString); 403 vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); 404 va_end(args); 405 output_if_debug(NULL, s, GL_FALSE); 406} 407 408 409/** 410 * Report debug information from the shader compiler via GL_ARB_debug_output. 411 * 412 * \param ctx GL context. 413 * \param type The namespace to which this message belongs. 414 * \param id The message ID within the given namespace. 415 * \param msg The message to output. Must be null-terminated. 416 */ 417void 418_mesa_shader_debug(struct gl_context *ctx, GLenum type, GLuint *id, 419 const char *msg) 420{ 421 enum mesa_debug_source source = MESA_DEBUG_SOURCE_SHADER_COMPILER; 422 enum mesa_debug_severity severity = MESA_DEBUG_SEVERITY_HIGH; 423 int len; 424 425 _mesa_debug_get_id(id); 426 427 len = strlen(msg); 428 429 /* Truncate the message if necessary. */ 430 if (len >= MAX_DEBUG_MESSAGE_LENGTH) 431 len = MAX_DEBUG_MESSAGE_LENGTH - 1; 432 433 _mesa_log_msg(ctx, source, type, *id, severity, len, msg); 434} 435 436/** 437 * Set the parameter as the current GL error. Used by glthread. 438 */ 439void GLAPIENTRY 440_mesa_InternalSetError(GLenum error) 441{ 442 GET_CURRENT_CONTEXT(ctx); 443 _mesa_error(ctx, error, "glthread"); 444} 445