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