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