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