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
40 static FILE *LogFile = NULL;
41
42
43 static void
output_if_debug(const char * prefixString,const char * outputString,GLboolean newline)44 output_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 /* 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 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 */
104 FILE *
_mesa_get_log_file(void)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 */
116 static void
flush_delayed_errors(struct gl_context * ctx)117 flush_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 */
140 void
_mesa_warning(struct gl_context * ctx,const char * fmtString,...)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 */
163 void
_mesa_problem(const struct gl_context * ctx,const char * fmtString,...)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
185 static GLboolean
should_output(struct gl_context * ctx,GLenum error,const char * fmtString)186 should_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
222 void
_mesa_gl_vdebugf(struct gl_context * ctx,GLuint * id,enum mesa_debug_source source,enum mesa_debug_type type,enum mesa_debug_severity severity,const char * fmtString,va_list args)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
245 void
_mesa_gl_debugf(struct gl_context * ctx,GLuint * id,enum mesa_debug_source source,enum mesa_debug_type type,enum mesa_debug_severity severity,const char * fmtString,...)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
259 size_t
_mesa_gl_debug(struct gl_context * ctx,GLuint * id,enum mesa_debug_source source,enum mesa_debug_type type,enum mesa_debug_severity severity,const char * msg)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 */
299 void
_mesa_error(struct gl_context * ctx,GLenum error,const char * fmtString,...)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
367 void
_mesa_error_no_memory(const char * caller)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 */
381 void
_mesa_debug(const struct gl_context * ctx,const char * fmtString,...)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
397 void
_mesa_log(const char * fmtString,...)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 */
417 void
_mesa_shader_debug(struct gl_context * ctx,GLenum type,GLuint * id,const char * msg)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