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