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