errors.c revision c68f0b8cabaa7adf3fac3bf07661491ad4001021
1/**
2 * \file errors.c
3 * Mesa debugging and error handling functions.
4 */
5
6/*
7 * Mesa 3-D graphics library
8 * Version:  7.1
9 *
10 * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30
31#include "errors.h"
32
33#include "imports.h"
34#include "context.h"
35#include "dispatch.h"
36#include "mtypes.h"
37#include "version.h"
38
39
40#define MAXSTRING MAX_DEBUG_MESSAGE_LENGTH
41
42
43static char out_of_memory[] = "Debugging error: out of memory";
44
45#define enum_is(e, kind1, kind2) \
46   ((e) == GL_DEBUG_##kind1##_##kind2##_ARB || (e) == GL_DONT_CARE)
47#define severity_is(sev, kind) enum_is(sev, SEVERITY, kind)
48#define source_is(s, kind) enum_is(s, SOURCE, kind)
49#define type_is(t, kind) enum_is(t, TYPE, kind)
50
51/**
52 * Whether a debugging message should be logged or not.
53 * For implementation-controlled namespaces, we keep an array
54 * of booleans per namespace, per context, recording whether
55 * each individual message is enabled or not. The message ID
56 * is an index into the namespace's array.
57 */
58static GLboolean
59should_log(struct gl_context *ctx, GLenum source, GLenum type,
60           GLuint id, GLenum severity)
61{
62   if (type_is(type, ERROR)) {
63      if (source_is(source, API))
64         return ctx->Debug.ApiErrors[id];
65      if (source_is(source, WINDOW_SYSTEM))
66         return ctx->Debug.WinsysErrors[id];
67      if (source_is(source, SHADER_COMPILER))
68         return ctx->Debug.ShaderErrors[id];
69      if (source_is(source, OTHER))
70         return ctx->Debug.OtherErrors[id];
71   }
72
73   /* TODO: tracking client messages' state. */
74   return (severity != GL_DEBUG_SEVERITY_LOW_ARB);
75}
76
77/**
78 * 'buf' is not necessarily a null-terminated string. When logging, copy
79 * 'len' characters from it, store them in a new, null-terminated string,
80 * and remember the number of bytes used by that string, *including*
81 * the null terminator this time.
82 */
83static void
84_mesa_log_msg(struct gl_context *ctx, GLenum source, GLenum type,
85              GLuint id, GLenum severity, GLint len, const char *buf)
86{
87   GLint nextEmpty;
88   struct gl_debug_msg *emptySlot;
89
90   assert(len >= 0 && len < MAX_DEBUG_MESSAGE_LENGTH);
91
92   if (!should_log(ctx, source, type, id, severity))
93      return;
94
95   if (ctx->Debug.Callback) {
96      ctx->Debug.Callback(source, type, id, severity,
97                          len, buf, ctx->Debug.CallbackData);
98      return;
99   }
100
101   if (ctx->Debug.NumMessages == MAX_DEBUG_LOGGED_MESSAGES)
102      return;
103
104   nextEmpty = (ctx->Debug.NextMsg + ctx->Debug.NumMessages)
105                          % MAX_DEBUG_LOGGED_MESSAGES;
106   emptySlot = &ctx->Debug.Log[nextEmpty];
107
108   assert(!emptySlot->message && !emptySlot->length);
109
110   emptySlot->message = MALLOC(len+1);
111   if (emptySlot->message) {
112      (void) strncpy(emptySlot->message, buf, (size_t)len);
113      emptySlot->message[len] = '\0';
114
115      emptySlot->length = len+1;
116      emptySlot->source = source;
117      emptySlot->type = type;
118      emptySlot->id = id;
119      emptySlot->severity = severity;
120   } else {
121      /* malloc failed! */
122      emptySlot->message = out_of_memory;
123      emptySlot->length = strlen(out_of_memory)+1;
124      emptySlot->source = GL_DEBUG_SOURCE_OTHER_ARB;
125      emptySlot->type = GL_DEBUG_TYPE_ERROR_ARB;
126      emptySlot->id = OTHER_ERROR_OUT_OF_MEMORY;
127      emptySlot->severity = GL_DEBUG_SEVERITY_HIGH_ARB;
128   }
129
130   if (ctx->Debug.NumMessages == 0)
131      ctx->Debug.NextMsgLength = ctx->Debug.Log[ctx->Debug.NextMsg].length;
132
133   ctx->Debug.NumMessages++;
134}
135
136/**
137 * Pop the oldest debug message out of the log.
138 * Writes the message string, including the null terminator, into 'buf',
139 * using up to 'bufSize' bytes. If 'bufSize' is too small, or
140 * if 'buf' is NULL, nothing is written.
141 *
142 * Returns the number of bytes written on success, or when 'buf' is NULL,
143 * the number that would have been written. A return value of 0
144 * indicates failure.
145 */
146static GLsizei
147_mesa_get_msg(struct gl_context *ctx, GLenum *source, GLenum *type,
148              GLuint *id, GLenum *severity, GLsizei bufSize, char *buf)
149{
150   struct gl_debug_msg *msg;
151   GLsizei length;
152
153   if (ctx->Debug.NumMessages == 0)
154      return 0;
155
156   msg = &ctx->Debug.Log[ctx->Debug.NextMsg];
157   length = msg->length;
158
159   assert(length > 0 && length == ctx->Debug.NextMsgLength);
160
161   if (bufSize < length && buf != NULL)
162      return 0;
163
164   if (severity)
165      *severity = msg->severity;
166   if (source)
167      *source = msg->source;
168   if (type)
169      *type = msg->type;
170   if (id)
171      *id = msg->id;
172
173   if (buf) {
174      assert(msg->message[length-1] == '\0');
175      (void) strncpy(buf, msg->message, (size_t)length);
176   }
177
178   if (msg->message != (char*)out_of_memory)
179      FREE(msg->message);
180   msg->message = NULL;
181   msg->length = 0;
182
183   ctx->Debug.NumMessages--;
184   ctx->Debug.NextMsg++;
185   ctx->Debug.NextMsg %= MAX_DEBUG_LOGGED_MESSAGES;
186   ctx->Debug.NextMsgLength = ctx->Debug.Log[ctx->Debug.NextMsg].length;
187
188   return length;
189}
190
191/**
192 * Verify that source, type, and severity are valid enums.
193 * glDebugMessageInsertARB only accepts two values for 'source',
194 * and glDebugMessageControlARB will additionally accept GL_DONT_CARE
195 * in any parameter, so handle those cases specially.
196 */
197static GLboolean
198validate_params(struct gl_context *ctx, unsigned caller,
199                GLenum source, GLenum type, GLenum severity)
200{
201#define INSERT 1
202#define CONTROL 2
203   switch(source) {
204   case GL_DEBUG_SOURCE_APPLICATION_ARB:
205   case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
206      break;
207   case GL_DEBUG_SOURCE_API_ARB:
208   case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
209   case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
210   case GL_DEBUG_SOURCE_OTHER_ARB:
211      if (caller != INSERT)
212         break;
213   case GL_DONT_CARE:
214      if (caller == CONTROL)
215         break;
216   default:
217      goto error;
218   }
219
220   switch(type) {
221   case GL_DEBUG_TYPE_ERROR_ARB:
222   case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
223   case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
224   case GL_DEBUG_TYPE_PERFORMANCE_ARB:
225   case GL_DEBUG_TYPE_PORTABILITY_ARB:
226   case GL_DEBUG_TYPE_OTHER_ARB:
227      break;
228   case GL_DONT_CARE:
229      if (caller == CONTROL)
230         break;
231   default:
232      goto error;
233   }
234
235   switch(severity) {
236   case GL_DEBUG_SEVERITY_HIGH_ARB:
237   case GL_DEBUG_SEVERITY_MEDIUM_ARB:
238   case GL_DEBUG_SEVERITY_LOW_ARB:
239      break;
240   case GL_DONT_CARE:
241      if (caller == CONTROL)
242         break;
243   default:
244      goto error;
245   }
246   return GL_TRUE;
247
248error:
249   {
250      const char *callerstr;
251      if (caller == INSERT)
252         callerstr = "glDebugMessageInsertARB";
253      else if (caller == CONTROL)
254         callerstr = "glDebugMessageControlARB";
255      else
256         return GL_FALSE;
257
258      _mesa_error( ctx, GL_INVALID_ENUM, "bad values passed to %s"
259                  "(source=0x%x, type=0x%x, severity=0x%x)", callerstr,
260                  source, type, severity);
261   }
262   return GL_FALSE;
263}
264
265static void GLAPIENTRY
266_mesa_DebugMessageInsertARB(GLenum source, GLenum type, GLuint id,
267                            GLenum severity, GLint length,
268                            const GLcharARB* buf)
269{
270   GET_CURRENT_CONTEXT(ctx);
271
272   if (!validate_params(ctx, INSERT, source, type, severity))
273      return; /* GL_INVALID_ENUM */
274
275   if (length < 0)
276      length = strlen(buf);
277
278   if (length >= MAX_DEBUG_MESSAGE_LENGTH) {
279      _mesa_error(ctx, GL_INVALID_VALUE, "glDebugMessageInsertARB"
280                 "(length=%d, which is not less than "
281                 "GL_MAX_DEBUG_MESSAGE_LENGTH_ARB=%d)", length,
282                 MAX_DEBUG_MESSAGE_LENGTH);
283      return;
284   }
285
286   _mesa_log_msg(ctx, source, type, id, severity, length, buf);
287}
288
289static GLuint GLAPIENTRY
290_mesa_GetDebugMessageLogARB(GLuint count, GLsizei logSize, GLenum* sources,
291                            GLenum* types, GLenum* ids, GLenum* severities,
292                            GLsizei* lengths, GLcharARB* messageLog)
293{
294   GET_CURRENT_CONTEXT(ctx);
295   GLuint ret;
296
297   if (!messageLog)
298      logSize = 0;
299
300   if (logSize < 0) {
301      _mesa_error(ctx, GL_INVALID_VALUE, "glGetDebugMessageLogARB"
302                 "(logSize=%d : logSize must not be negative)", logSize);
303      return 0;
304   }
305
306   for (ret = 0; ret < count; ret++) {
307      GLsizei written = _mesa_get_msg(ctx, sources, types, ids, severities,
308                                      logSize, messageLog);
309      if (!written)
310         break;
311
312      if (messageLog) {
313         messageLog += written;
314         logSize -= written;
315      }
316      if (lengths) {
317         *lengths = written;
318         lengths++;
319      }
320
321      if (severities)
322         severities++;
323      if (sources)
324         sources++;
325      if (types)
326         types++;
327      if (ids)
328         ids++;
329   }
330
331   return ret;
332}
333
334/**
335 * 'array' is an array representing a particular debugging-message namespace.
336 * I.e., the set of all API errors, or the set of all Shader Compiler errors.
337 * 'size' is the size of 'array'. 'count' is the size of 'ids', an array
338 * of indices into 'array'. All the elements of 'array' at the indices
339 * listed in 'ids' will be overwritten with the value of 'enabled'.
340 *
341 * If 'count' is zero, all elements in 'array' are overwritten with the
342 * value of 'enabled'.
343 */
344static void
345control_messages(GLboolean *array, GLuint size,
346                 GLsizei count, const GLuint *ids, GLboolean enabled)
347{
348   GLsizei i;
349
350   if (!count) {
351      GLuint id;
352      for (id = 0; id < size; id++) {
353         array[id] = enabled;
354      }
355      return;
356   }
357
358   for (i = 0; i < count; i++) {
359      if (ids[i] >= size) {
360         /* XXX: The spec doesn't say what to do with a non-existent ID. */
361         continue;
362      }
363      array[ids[i]] = enabled;
364   }
365}
366
367/**
368 * Debugging-message namespaces with the source APPLICATION or THIRD_PARTY
369 * require special handling, since the IDs in them are controlled by clients,
370 * not the OpenGL implementation.
371 */
372static void
373control_app_messages(struct gl_context *ctx, GLenum source, GLenum type,
374                     GLenum severity, GLsizei count, const GLuint *ids,
375                     GLboolean enabled)
376{
377   assert(0 && "glDebugMessageControlARB():"
378          " Controlling application debugging messages not implemented");
379}
380
381static void GLAPIENTRY
382_mesa_DebugMessageControlARB(GLenum source, GLenum type, GLenum severity,
383                             GLsizei count, const GLuint *ids,
384                             GLboolean enabled)
385{
386   GET_CURRENT_CONTEXT(ctx);
387
388   if (count < 0) {
389      _mesa_error(ctx, GL_INVALID_VALUE, "glDebugMessageControlARB"
390                 "(count=%d : count must not be negative)", count);
391      return;
392   }
393
394   if (!validate_params(ctx, CONTROL, source, type, severity))
395      return; /* GL_INVALID_ENUM */
396
397   if (count && (severity != GL_DONT_CARE || type == GL_DONT_CARE
398                 || source == GL_DONT_CARE)) {
399      _mesa_error(ctx, GL_INVALID_OPERATION, "glDebugMessageControlARB"
400                 "(When passing an array of ids, severity must be"
401         " GL_DONT_CARE, and source and type must not be GL_DONT_CARE.");
402      return;
403   }
404
405   if (source_is(source, APPLICATION) || source_is(source, THIRD_PARTY))
406      control_app_messages(ctx, source, type, severity, count, ids, enabled);
407
408   if (severity_is(severity, HIGH)) {
409      if (type_is(type, ERROR)) {
410         if (source_is(source, API))
411            control_messages(ctx->Debug.ApiErrors, API_ERROR_COUNT,
412                             count, ids, enabled);
413         if (source_is(source, WINDOW_SYSTEM))
414            control_messages(ctx->Debug.WinsysErrors, WINSYS_ERROR_COUNT,
415                             count, ids, enabled);
416         if (source_is(source, SHADER_COMPILER))
417            control_messages(ctx->Debug.ShaderErrors, SHADER_ERROR_COUNT,
418                             count, ids, enabled);
419         if (source_is(source, OTHER))
420            control_messages(ctx->Debug.OtherErrors, OTHER_ERROR_COUNT,
421                             count, ids, enabled);
422      }
423   }
424}
425
426static void GLAPIENTRY
427_mesa_DebugMessageCallbackARB(GLvoid *callback, GLvoid *userParam)
428{
429   GET_CURRENT_CONTEXT(ctx);
430   ctx->Debug.Callback = (GLDEBUGPROCARB)callback;
431   ctx->Debug.CallbackData = userParam;
432}
433
434void
435_mesa_init_errors_dispatch(struct _glapi_table *disp)
436{
437   SET_DebugMessageCallbackARB(disp, _mesa_DebugMessageCallbackARB);
438   SET_DebugMessageControlARB(disp, _mesa_DebugMessageControlARB);
439   SET_DebugMessageInsertARB(disp, _mesa_DebugMessageInsertARB);
440   SET_GetDebugMessageLogARB(disp, _mesa_GetDebugMessageLogARB);
441}
442
443void
444_mesa_init_errors(struct gl_context *ctx)
445{
446   ctx->Debug.Callback = NULL;
447   ctx->Debug.SyncOutput = GL_FALSE;
448   ctx->Debug.Log[0].length = 0;
449   ctx->Debug.NumMessages = 0;
450   ctx->Debug.NextMsg = 0;
451   ctx->Debug.NextMsgLength = 0;
452
453   /* Enable all the messages with severity HIGH or MEDIUM by default. */
454   memset(ctx->Debug.ApiErrors, GL_TRUE, sizeof ctx->Debug.ApiErrors);
455   memset(ctx->Debug.WinsysErrors, GL_TRUE, sizeof ctx->Debug.WinsysErrors);
456   memset(ctx->Debug.ShaderErrors, GL_TRUE, sizeof ctx->Debug.ShaderErrors);
457   memset(ctx->Debug.OtherErrors, GL_TRUE, sizeof ctx->Debug.OtherErrors);
458}
459
460/**********************************************************************/
461/** \name Diagnostics */
462/*@{*/
463
464static void
465output_if_debug(const char *prefixString, const char *outputString,
466                GLboolean newline)
467{
468   static int debug = -1;
469
470   /* Check the MESA_DEBUG environment variable if it hasn't
471    * been checked yet.  We only have to check it once...
472    */
473   if (debug == -1) {
474      char *env = _mesa_getenv("MESA_DEBUG");
475
476      /* In a debug build, we print warning messages *unless*
477       * MESA_DEBUG is 0.  In a non-debug build, we don't
478       * print warning messages *unless* MESA_DEBUG is
479       * set *to any value*.
480       */
481#ifdef DEBUG
482      debug = (env != NULL && atoi(env) == 0) ? 0 : 1;
483#else
484      debug = (env != NULL) ? 1 : 0;
485#endif
486   }
487
488   /* Now only print the string if we're required to do so. */
489   if (debug) {
490      fprintf(stderr, "%s: %s", prefixString, outputString);
491      if (newline)
492         fprintf(stderr, "\n");
493
494#if defined(_WIN32) && !defined(_WIN32_WCE)
495      /* stderr from windows applications without console is not usually
496       * visible, so communicate with the debugger instead */
497      {
498         char buf[4096];
499         _mesa_snprintf(buf, sizeof(buf), "%s: %s%s", prefixString, outputString, newline ? "\n" : "");
500         OutputDebugStringA(buf);
501      }
502#endif
503   }
504}
505
506
507/**
508 * Return string version of GL error code.
509 */
510static const char *
511error_string( GLenum error )
512{
513   switch (error) {
514   case GL_NO_ERROR:
515      return "GL_NO_ERROR";
516   case GL_INVALID_VALUE:
517      return "GL_INVALID_VALUE";
518   case GL_INVALID_ENUM:
519      return "GL_INVALID_ENUM";
520   case GL_INVALID_OPERATION:
521      return "GL_INVALID_OPERATION";
522   case GL_STACK_OVERFLOW:
523      return "GL_STACK_OVERFLOW";
524   case GL_STACK_UNDERFLOW:
525      return "GL_STACK_UNDERFLOW";
526   case GL_OUT_OF_MEMORY:
527      return "GL_OUT_OF_MEMORY";
528   case GL_TABLE_TOO_LARGE:
529      return "GL_TABLE_TOO_LARGE";
530   case GL_INVALID_FRAMEBUFFER_OPERATION_EXT:
531      return "GL_INVALID_FRAMEBUFFER_OPERATION";
532   default:
533      return "unknown";
534   }
535}
536
537
538/**
539 * When a new type of error is recorded, print a message describing
540 * previous errors which were accumulated.
541 */
542static void
543flush_delayed_errors( struct gl_context *ctx )
544{
545   char s[MAXSTRING];
546
547   if (ctx->ErrorDebugCount) {
548      _mesa_snprintf(s, MAXSTRING, "%d similar %s errors",
549                     ctx->ErrorDebugCount,
550                     error_string(ctx->ErrorValue));
551
552      output_if_debug("Mesa", s, GL_TRUE);
553
554      ctx->ErrorDebugCount = 0;
555   }
556}
557
558
559/**
560 * Report a warning (a recoverable error condition) to stderr if
561 * either DEBUG is defined or the MESA_DEBUG env var is set.
562 *
563 * \param ctx GL context.
564 * \param fmtString printf()-like format string.
565 */
566void
567_mesa_warning( struct gl_context *ctx, const char *fmtString, ... )
568{
569   char str[MAXSTRING];
570   va_list args;
571   va_start( args, fmtString );
572   (void) _mesa_vsnprintf( str, MAXSTRING, fmtString, args );
573   va_end( args );
574
575   if (ctx)
576      flush_delayed_errors( ctx );
577
578   output_if_debug("Mesa warning", str, GL_TRUE);
579}
580
581
582/**
583 * Report an internal implementation problem.
584 * Prints the message to stderr via fprintf().
585 *
586 * \param ctx GL context.
587 * \param fmtString problem description string.
588 */
589void
590_mesa_problem( const struct gl_context *ctx, const char *fmtString, ... )
591{
592   va_list args;
593   char str[MAXSTRING];
594   static int numCalls = 0;
595
596   (void) ctx;
597
598   if (numCalls < 50) {
599      numCalls++;
600
601      va_start( args, fmtString );
602      _mesa_vsnprintf( str, MAXSTRING, fmtString, args );
603      va_end( args );
604      fprintf(stderr, "Mesa %s implementation error: %s\n",
605              MESA_VERSION_STRING, str);
606      fprintf(stderr, "Please report at bugs.freedesktop.org\n");
607   }
608}
609
610
611/**
612 * Record an OpenGL state error.  These usually occur when the user
613 * passes invalid parameters to a GL function.
614 *
615 * If debugging is enabled (either at compile-time via the DEBUG macro, or
616 * run-time via the MESA_DEBUG environment variable), report the error with
617 * _mesa_debug().
618 *
619 * \param ctx the GL context.
620 * \param error the error value.
621 * \param fmtString printf() style format string, followed by optional args
622 */
623void
624_mesa_error( struct gl_context *ctx, GLenum error, const char *fmtString, ... )
625{
626   static GLint debug = -1;
627
628   /* Check debug environment variable only once:
629    */
630   if (debug == -1) {
631      const char *debugEnv = _mesa_getenv("MESA_DEBUG");
632
633#ifdef DEBUG
634      if (debugEnv && strstr(debugEnv, "silent"))
635         debug = GL_FALSE;
636      else
637         debug = GL_TRUE;
638#else
639      if (debugEnv)
640         debug = GL_TRUE;
641      else
642         debug = GL_FALSE;
643#endif
644   }
645
646   if (debug) {
647      if (ctx->ErrorValue == error &&
648          ctx->ErrorDebugFmtString == fmtString) {
649         ctx->ErrorDebugCount++;
650      }
651      else {
652         char s[MAXSTRING], s2[MAXSTRING];
653         va_list args;
654
655         flush_delayed_errors( ctx );
656
657         va_start(args, fmtString);
658         _mesa_vsnprintf(s, MAXSTRING, fmtString, args);
659         va_end(args);
660
661         _mesa_snprintf(s2, MAXSTRING, "%s in %s", error_string(error), s);
662         output_if_debug("Mesa: User error", s2, GL_TRUE);
663
664         ctx->ErrorDebugFmtString = fmtString;
665         ctx->ErrorDebugCount = 0;
666      }
667   }
668
669   _mesa_record_error(ctx, error);
670}
671
672
673/**
674 * Report debug information.  Print error message to stderr via fprintf().
675 * No-op if DEBUG mode not enabled.
676 *
677 * \param ctx GL context.
678 * \param fmtString printf()-style format string, followed by optional args.
679 */
680void
681_mesa_debug( const struct gl_context *ctx, const char *fmtString, ... )
682{
683#ifdef DEBUG
684   char s[MAXSTRING];
685   va_list args;
686   va_start(args, fmtString);
687   _mesa_vsnprintf(s, MAXSTRING, fmtString, args);
688   va_end(args);
689   output_if_debug("Mesa", s, GL_FALSE);
690#endif /* DEBUG */
691   (void) ctx;
692   (void) fmtString;
693}
694
695/*@}*/
696