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