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 "imports.h"
36#include "context.h"
37#include "debug_output.h"
38
39
40static FILE *LogFile = NULL;
41
42
43static void
44output_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#ifdef DEBUG
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         _mesa_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 */
101FILE *
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 */
113static void
114flush_delayed_errors( struct gl_context *ctx )
115{
116   char s[MAX_DEBUG_MESSAGE_LENGTH];
117
118   if (ctx->ErrorDebugCount) {
119      _mesa_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 */
137void
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) _mesa_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 */
160void
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      _mesa_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
182static GLboolean
183should_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#ifdef DEBUG
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
219void
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 = _mesa_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
242void
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
256size_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 */
296void
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 = _mesa_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 = _mesa_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
364void
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 */
378void
379_mesa_debug( const struct gl_context *ctx, const char *fmtString, ... )
380{
381#ifdef DEBUG
382   char s[MAX_DEBUG_MESSAGE_LENGTH];
383   va_list args;
384   va_start(args, fmtString);
385   _mesa_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
394void
395_mesa_log(const char *fmtString, ...)
396{
397   char s[MAX_DEBUG_MESSAGE_LENGTH];
398   va_list args;
399   va_start(args, fmtString);
400   _mesa_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 */
414void
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}
432