• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 #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  */
101 FILE *
_mesa_get_log_file(void)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  */
113 static void
flush_delayed_errors(struct gl_context * ctx)114 flush_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  */
137 void
_mesa_warning(struct gl_context * ctx,const char * fmtString,...)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  */
160 void
_mesa_problem(const struct gl_context * ctx,const char * fmtString,...)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 
182 static GLboolean
should_output(struct gl_context * ctx,GLenum error,const char * fmtString)183 should_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 
219 void
_mesa_gl_vdebug(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)220 _mesa_gl_vdebug(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 
235    _mesa_log_msg(ctx, source, type, *id, severity, len, s);
236 }
237 
238 
239 void
_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 * fmtString,...)240 _mesa_gl_debug(struct gl_context *ctx,
241                GLuint *id,
242                enum mesa_debug_source source,
243                enum mesa_debug_type type,
244                enum mesa_debug_severity severity,
245                const char *fmtString, ...)
246 {
247    va_list args;
248    va_start(args, fmtString);
249    _mesa_gl_vdebug(ctx, id, source, type, severity, fmtString, args);
250    va_end(args);
251 }
252 
253 
254 /**
255  * Record an OpenGL state error.  These usually occur when the user
256  * passes invalid parameters to a GL function.
257  *
258  * If debugging is enabled (either at compile-time via the DEBUG macro, or
259  * run-time via the MESA_DEBUG environment variable), report the error with
260  * _mesa_debug().
261  *
262  * \param ctx the GL context.
263  * \param error the error value.
264  * \param fmtString printf() style format string, followed by optional args
265  */
266 void
_mesa_error(struct gl_context * ctx,GLenum error,const char * fmtString,...)267 _mesa_error( struct gl_context *ctx, GLenum error, const char *fmtString, ... )
268 {
269    GLboolean do_output, do_log;
270    /* Ideally this would be set up by the caller, so that we had proper IDs
271     * per different message.
272     */
273    static GLuint error_msg_id = 0;
274 
275    _mesa_debug_get_id(&error_msg_id);
276 
277    do_output = should_output(ctx, error, fmtString);
278 
279    simple_mtx_lock(&ctx->DebugMutex);
280    if (ctx->Debug) {
281       do_log = _mesa_debug_is_message_enabled(ctx->Debug,
282                                               MESA_DEBUG_SOURCE_API,
283                                               MESA_DEBUG_TYPE_ERROR,
284                                               error_msg_id,
285                                               MESA_DEBUG_SEVERITY_HIGH);
286    }
287    else {
288       do_log = GL_FALSE;
289    }
290    simple_mtx_unlock(&ctx->DebugMutex);
291 
292    if (do_output || do_log) {
293       char s[MAX_DEBUG_MESSAGE_LENGTH], s2[MAX_DEBUG_MESSAGE_LENGTH];
294       int len;
295       va_list args;
296 
297       va_start(args, fmtString);
298       len = _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args);
299       va_end(args);
300 
301       if (len >= MAX_DEBUG_MESSAGE_LENGTH) {
302          /* Too long error message. Whoever calls _mesa_error should use
303           * shorter strings.
304           */
305          assert(0);
306          return;
307       }
308 
309       len = _mesa_snprintf(s2, MAX_DEBUG_MESSAGE_LENGTH, "%s in %s",
310                            _mesa_enum_to_string(error), s);
311       if (len >= MAX_DEBUG_MESSAGE_LENGTH) {
312          /* Same as above. */
313          assert(0);
314          return;
315       }
316 
317       /* Print the error to stderr if needed. */
318       if (do_output) {
319          output_if_debug("Mesa: User error", s2, GL_TRUE);
320       }
321 
322       /* Log the error via ARB_debug_output if needed.*/
323       if (do_log) {
324          _mesa_log_msg(ctx, MESA_DEBUG_SOURCE_API, MESA_DEBUG_TYPE_ERROR,
325                        error_msg_id, MESA_DEBUG_SEVERITY_HIGH, len, s2);
326       }
327    }
328 
329    /* Set the GL context error state for glGetError. */
330    if (ctx->ErrorValue == GL_NO_ERROR)
331       ctx->ErrorValue = error;
332 }
333 
334 void
_mesa_error_no_memory(const char * caller)335 _mesa_error_no_memory(const char *caller)
336 {
337    GET_CURRENT_CONTEXT(ctx);
338    _mesa_error(ctx, GL_OUT_OF_MEMORY, "out of memory in %s", caller);
339 }
340 
341 /**
342  * Report debug information.  Print error message to stderr via fprintf().
343  * No-op if DEBUG mode not enabled.
344  *
345  * \param ctx GL context.
346  * \param fmtString printf()-style format string, followed by optional args.
347  */
348 void
_mesa_debug(const struct gl_context * ctx,const char * fmtString,...)349 _mesa_debug( const struct gl_context *ctx, const char *fmtString, ... )
350 {
351 #ifdef DEBUG
352    char s[MAX_DEBUG_MESSAGE_LENGTH];
353    va_list args;
354    va_start(args, fmtString);
355    _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args);
356    va_end(args);
357    output_if_debug("Mesa", s, GL_FALSE);
358 #endif /* DEBUG */
359    (void) ctx;
360    (void) fmtString;
361 }
362 
363 
364 void
_mesa_log(const char * fmtString,...)365 _mesa_log(const char *fmtString, ...)
366 {
367    char s[MAX_DEBUG_MESSAGE_LENGTH];
368    va_list args;
369    va_start(args, fmtString);
370    _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args);
371    va_end(args);
372    output_if_debug("", s, GL_FALSE);
373 }
374 
375 
376 /**
377  * Report debug information from the shader compiler via GL_ARB_debug_output.
378  *
379  * \param ctx GL context.
380  * \param type The namespace to which this message belongs.
381  * \param id The message ID within the given namespace.
382  * \param msg The message to output. Must be null-terminated.
383  */
384 void
_mesa_shader_debug(struct gl_context * ctx,GLenum type,GLuint * id,const char * msg)385 _mesa_shader_debug(struct gl_context *ctx, GLenum type, GLuint *id,
386                    const char *msg)
387 {
388    enum mesa_debug_source source = MESA_DEBUG_SOURCE_SHADER_COMPILER;
389    enum mesa_debug_severity severity = MESA_DEBUG_SEVERITY_HIGH;
390    int len;
391 
392    _mesa_debug_get_id(id);
393 
394    len = strlen(msg);
395 
396    /* Truncate the message if necessary. */
397    if (len >= MAX_DEBUG_MESSAGE_LENGTH)
398       len = MAX_DEBUG_MESSAGE_LENGTH - 1;
399 
400    _mesa_log_msg(ctx, source, type, *id, severity, len, msg);
401 }
402