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       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  */
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 
436 /**
437  * Set the parameter as the current GL error. Used by glthread.
438  */
439 void GLAPIENTRY
_mesa_InternalSetError(GLenum error)440 _mesa_InternalSetError(GLenum error)
441 {
442    GET_CURRENT_CONTEXT(ctx);
443    _mesa_error(ctx, error, "glthread");
444 }
445