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