• 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 
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 
45 static FILE *LogFile = NULL;
46 
47 
48 static void
output_if_debug(const char * prefixString,const char * outputString,GLboolean newline)49 output_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  */
113 FILE *
_mesa_get_log_file(void)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  */
125 static void
flush_delayed_errors(struct gl_context * ctx)126 flush_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  */
149 void
_mesa_warning(struct gl_context * ctx,const char * fmtString,...)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  */
172 void
_mesa_problem(const struct gl_context * ctx,const char * fmtString,...)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 
194 static GLboolean
should_output(struct gl_context * ctx,GLenum error,const char * fmtString)195 should_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 
231 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)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 
254 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,...)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 
268 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)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  */
308 void
_mesa_error(struct gl_context * ctx,GLenum error,const char * fmtString,...)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 
376 void
_mesa_error_no_memory(const char * caller)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  */
390 void
_mesa_debug(const struct gl_context * ctx,const char * fmtString,...)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 
406 void
_mesa_log(const char * fmtString,...)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 
417 void
_mesa_log_direct(const char * string)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  */
431 void
_mesa_shader_debug(struct gl_context * ctx,GLenum type,GLuint * id,const char * msg)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  */
453 void GLAPIENTRY
_mesa_InternalSetError(GLenum error)454 _mesa_InternalSetError(GLenum error)
455 {
456    GET_CURRENT_CONTEXT(ctx);
457    _mesa_error(ctx, error, "glthread");
458 }
459