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