Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 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 "errors.h" |
||
32 | #include "enums.h" |
||
33 | #include "imports.h" |
||
34 | #include "context.h" |
||
35 | #include "dispatch.h" |
||
36 | #include "hash.h" |
||
37 | #include "mtypes.h" |
||
38 | #include "version.h" |
||
39 | #include "hash_table.h" |
||
40 | #include "glapi/glthread.h" |
||
41 | |||
42 | _glthread_DECLARE_STATIC_MUTEX(DynamicIDMutex); |
||
43 | static GLuint NextDynamicID = 1; |
||
44 | |||
45 | struct gl_debug_severity |
||
46 | { |
||
47 | struct simple_node link; |
||
48 | GLuint ID; |
||
49 | }; |
||
50 | |||
51 | static char out_of_memory[] = "Debugging error: out of memory"; |
||
52 | |||
53 | static const GLenum debug_source_enums[] = { |
||
54 | GL_DEBUG_SOURCE_API, |
||
55 | GL_DEBUG_SOURCE_WINDOW_SYSTEM, |
||
56 | GL_DEBUG_SOURCE_SHADER_COMPILER, |
||
57 | GL_DEBUG_SOURCE_THIRD_PARTY, |
||
58 | GL_DEBUG_SOURCE_APPLICATION, |
||
59 | GL_DEBUG_SOURCE_OTHER, |
||
60 | }; |
||
61 | |||
62 | static const GLenum debug_type_enums[] = { |
||
63 | GL_DEBUG_TYPE_ERROR, |
||
64 | GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, |
||
65 | GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, |
||
66 | GL_DEBUG_TYPE_PORTABILITY, |
||
67 | GL_DEBUG_TYPE_PERFORMANCE, |
||
68 | GL_DEBUG_TYPE_OTHER, |
||
69 | }; |
||
70 | |||
71 | static const GLenum debug_severity_enums[] = { |
||
72 | GL_DEBUG_SEVERITY_LOW, |
||
73 | GL_DEBUG_SEVERITY_MEDIUM, |
||
74 | GL_DEBUG_SEVERITY_HIGH, |
||
75 | }; |
||
76 | |||
77 | static enum mesa_debug_source |
||
78 | gl_enum_to_debug_source(GLenum e) |
||
79 | { |
||
80 | int i; |
||
81 | |||
82 | for (i = 0; i < Elements(debug_source_enums); i++) { |
||
83 | if (debug_source_enums[i] == e) |
||
84 | break; |
||
85 | } |
||
86 | return i; |
||
87 | } |
||
88 | |||
89 | static enum mesa_debug_type |
||
90 | gl_enum_to_debug_type(GLenum e) |
||
91 | { |
||
92 | int i; |
||
93 | |||
94 | for (i = 0; i < Elements(debug_type_enums); i++) { |
||
95 | if (debug_type_enums[i] == e) |
||
96 | break; |
||
97 | } |
||
98 | return i; |
||
99 | } |
||
100 | |||
101 | static enum mesa_debug_severity |
||
102 | gl_enum_to_debug_severity(GLenum e) |
||
103 | { |
||
104 | int i; |
||
105 | |||
106 | for (i = 0; i < Elements(debug_severity_enums); i++) { |
||
107 | if (debug_severity_enums[i] == e) |
||
108 | break; |
||
109 | } |
||
110 | return i; |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Handles generating a GL_ARB_debug_output message ID generated by the GL or |
||
115 | * GLSL compiler. |
||
116 | * |
||
117 | * The GL API has this "ID" mechanism, where the intention is to allow a |
||
118 | * client to filter in/out messages based on source, type, and ID. Of course, |
||
119 | * building a giant enum list of all debug output messages that Mesa might |
||
120 | * generate is ridiculous, so instead we have our caller pass us a pointer to |
||
121 | * static storage where the ID should get stored. This ID will be shared |
||
122 | * across all contexts for that message (which seems like a desirable |
||
123 | * property, even if it's not expected by the spec), but note that it won't be |
||
124 | * the same between executions if messages aren't generated in the same order. |
||
125 | */ |
||
126 | static void |
||
127 | debug_get_id(GLuint *id) |
||
128 | { |
||
129 | if (!(*id)) { |
||
130 | _glthread_LOCK_MUTEX(DynamicIDMutex); |
||
131 | if (!(*id)) |
||
132 | *id = NextDynamicID++; |
||
133 | _glthread_UNLOCK_MUTEX(DynamicIDMutex); |
||
134 | } |
||
135 | } |
||
136 | |||
137 | /* |
||
138 | * We store a bitfield in the hash table, with five possible values total. |
||
139 | * |
||
140 | * The ENABLED_BIT's purpose is self-explanatory. |
||
141 | * |
||
142 | * The FOUND_BIT is needed to differentiate the value of DISABLED from |
||
143 | * the value returned by HashTableLookup() when it can't find the given key. |
||
144 | * |
||
145 | * The KNOWN_SEVERITY bit is a bit complicated: |
||
146 | * |
||
147 | * A client may call Control() with an array of IDs, then call Control() |
||
148 | * on all message IDs of a certain severity, then Insert() one of the |
||
149 | * previously specified IDs, giving us a known severity level, then call |
||
150 | * Control() on all message IDs of a certain severity level again. |
||
151 | * |
||
152 | * After the first call, those IDs will have a FOUND_BIT, but will not |
||
153 | * exist in any severity-specific list, so the second call will not |
||
154 | * impact them. This is undesirable but unavoidable given the API: |
||
155 | * The only entrypoint that gives a severity for a client-defined ID |
||
156 | * is the Insert() call. |
||
157 | * |
||
158 | * For the sake of Control(), we want to maintain the invariant |
||
159 | * that an ID will either appear in none of the three severity lists, |
||
160 | * or appear once, to minimize pointless duplication and potential surprises. |
||
161 | * |
||
162 | * Because Insert() is the only place that will learn an ID's severity, |
||
163 | * it should insert an ID into the appropriate list, but only if the ID |
||
164 | * doesn't exist in it or any other list yet. Because searching all three |
||
165 | * lists at O(n) is needlessly expensive, we store KNOWN_SEVERITY. |
||
166 | */ |
||
167 | enum { |
||
168 | FOUND_BIT = 1 << 0, |
||
169 | ENABLED_BIT = 1 << 1, |
||
170 | KNOWN_SEVERITY = 1 << 2, |
||
171 | |||
172 | /* HashTable reserves zero as a return value meaning 'not found' */ |
||
173 | NOT_FOUND = 0, |
||
174 | DISABLED = FOUND_BIT, |
||
175 | ENABLED = ENABLED_BIT | FOUND_BIT |
||
176 | }; |
||
177 | |||
178 | /** |
||
179 | * Returns the state of the given message source/type/ID tuple. |
||
180 | */ |
||
181 | static GLboolean |
||
182 | should_log(struct gl_context *ctx, |
||
183 | enum mesa_debug_source source, |
||
184 | enum mesa_debug_type type, |
||
185 | GLuint id, |
||
186 | enum mesa_debug_severity severity) |
||
187 | { |
||
188 | struct gl_debug_namespace *nspace = |
||
189 | &ctx->Debug.Namespaces[source][type]; |
||
190 | uintptr_t state; |
||
191 | |||
192 | /* In addition to not being able to store zero as a value, HashTable also |
||
193 | can't use zero as a key. */ |
||
194 | if (id) |
||
195 | state = (uintptr_t)_mesa_HashLookup(nspace->IDs, id); |
||
196 | else |
||
197 | state = nspace->ZeroID; |
||
198 | |||
199 | /* Only do this once for each ID. This makes sure the ID exists in, |
||
200 | at most, one list, and does not pointlessly appear multiple times. */ |
||
201 | if (!(state & KNOWN_SEVERITY)) { |
||
202 | struct gl_debug_severity *entry; |
||
203 | |||
204 | if (state == NOT_FOUND) { |
||
205 | if (ctx->Debug.Defaults[severity][source][type]) |
||
206 | state = ENABLED; |
||
207 | else |
||
208 | state = DISABLED; |
||
209 | } |
||
210 | |||
211 | entry = malloc(sizeof *entry); |
||
212 | if (!entry) |
||
213 | goto out; |
||
214 | |||
215 | state |= KNOWN_SEVERITY; |
||
216 | |||
217 | if (id) |
||
218 | _mesa_HashInsert(nspace->IDs, id, (void*)state); |
||
219 | else |
||
220 | nspace->ZeroID = state; |
||
221 | |||
222 | entry->ID = id; |
||
223 | insert_at_tail(&nspace->Severity[severity], &entry->link); |
||
224 | } |
||
225 | |||
226 | out: |
||
227 | return !!(state & ENABLED_BIT); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Sets the state of the given message source/type/ID tuple. |
||
232 | */ |
||
233 | static void |
||
234 | set_message_state(struct gl_context *ctx, |
||
235 | enum mesa_debug_source source, |
||
236 | enum mesa_debug_type type, |
||
237 | GLuint id, GLboolean enabled) |
||
238 | { |
||
239 | struct gl_debug_namespace *nspace = |
||
240 | &ctx->Debug.Namespaces[source][type]; |
||
241 | uintptr_t state; |
||
242 | |||
243 | /* In addition to not being able to store zero as a value, HashTable also |
||
244 | can't use zero as a key. */ |
||
245 | if (id) |
||
246 | state = (uintptr_t)_mesa_HashLookup(nspace->IDs, id); |
||
247 | else |
||
248 | state = nspace->ZeroID; |
||
249 | |||
250 | if (state == NOT_FOUND) |
||
251 | state = enabled ? ENABLED : DISABLED; |
||
252 | else { |
||
253 | if (enabled) |
||
254 | state |= ENABLED_BIT; |
||
255 | else |
||
256 | state &= ~ENABLED_BIT; |
||
257 | } |
||
258 | |||
259 | if (id) |
||
260 | _mesa_HashInsert(nspace->IDs, id, (void*)state); |
||
261 | else |
||
262 | nspace->ZeroID = state; |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * 'buf' is not necessarily a null-terminated string. When logging, copy |
||
267 | * 'len' characters from it, store them in a new, null-terminated string, |
||
268 | * and remember the number of bytes used by that string, *including* |
||
269 | * the null terminator this time. |
||
270 | */ |
||
271 | static void |
||
272 | _mesa_log_msg(struct gl_context *ctx, enum mesa_debug_source source, |
||
273 | enum mesa_debug_type type, GLuint id, |
||
274 | enum mesa_debug_severity severity, GLint len, const char *buf) |
||
275 | { |
||
276 | GLint nextEmpty; |
||
277 | struct gl_debug_msg *emptySlot; |
||
278 | |||
279 | assert(len >= 0 && len < MAX_DEBUG_MESSAGE_LENGTH); |
||
280 | |||
281 | if (!should_log(ctx, source, type, id, severity)) |
||
282 | return; |
||
283 | |||
284 | if (ctx->Debug.Callback) { |
||
285 | ctx->Debug.Callback(debug_source_enums[source], |
||
286 | debug_type_enums[type], |
||
287 | id, |
||
288 | debug_severity_enums[severity], |
||
289 | len, buf, ctx->Debug.CallbackData); |
||
290 | return; |
||
291 | } |
||
292 | |||
293 | if (ctx->Debug.NumMessages == MAX_DEBUG_LOGGED_MESSAGES) |
||
294 | return; |
||
295 | |||
296 | nextEmpty = (ctx->Debug.NextMsg + ctx->Debug.NumMessages) |
||
297 | % MAX_DEBUG_LOGGED_MESSAGES; |
||
298 | emptySlot = &ctx->Debug.Log[nextEmpty]; |
||
299 | |||
300 | assert(!emptySlot->message && !emptySlot->length); |
||
301 | |||
302 | emptySlot->message = malloc(len+1); |
||
303 | if (emptySlot->message) { |
||
304 | (void) strncpy(emptySlot->message, buf, (size_t)len); |
||
305 | emptySlot->message[len] = '\0'; |
||
306 | |||
307 | emptySlot->length = len+1; |
||
308 | emptySlot->source = source; |
||
309 | emptySlot->type = type; |
||
310 | emptySlot->id = id; |
||
311 | emptySlot->severity = severity; |
||
312 | } else { |
||
313 | static GLuint oom_msg_id = 0; |
||
314 | debug_get_id(&oom_msg_id); |
||
315 | |||
316 | /* malloc failed! */ |
||
317 | emptySlot->message = out_of_memory; |
||
318 | emptySlot->length = strlen(out_of_memory)+1; |
||
319 | emptySlot->source = MESA_DEBUG_SOURCE_OTHER; |
||
320 | emptySlot->type = MESA_DEBUG_TYPE_ERROR; |
||
321 | emptySlot->id = oom_msg_id; |
||
322 | emptySlot->severity = MESA_DEBUG_SEVERITY_HIGH; |
||
323 | } |
||
324 | |||
325 | if (ctx->Debug.NumMessages == 0) |
||
326 | ctx->Debug.NextMsgLength = ctx->Debug.Log[ctx->Debug.NextMsg].length; |
||
327 | |||
328 | ctx->Debug.NumMessages++; |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * Pop the oldest debug message out of the log. |
||
333 | * Writes the message string, including the null terminator, into 'buf', |
||
334 | * using up to 'bufSize' bytes. If 'bufSize' is too small, or |
||
335 | * if 'buf' is NULL, nothing is written. |
||
336 | * |
||
337 | * Returns the number of bytes written on success, or when 'buf' is NULL, |
||
338 | * the number that would have been written. A return value of 0 |
||
339 | * indicates failure. |
||
340 | */ |
||
341 | static GLsizei |
||
342 | _mesa_get_msg(struct gl_context *ctx, GLenum *source, GLenum *type, |
||
343 | GLuint *id, GLenum *severity, GLsizei bufSize, char *buf) |
||
344 | { |
||
345 | struct gl_debug_msg *msg; |
||
346 | GLsizei length; |
||
347 | |||
348 | if (ctx->Debug.NumMessages == 0) |
||
349 | return 0; |
||
350 | |||
351 | msg = &ctx->Debug.Log[ctx->Debug.NextMsg]; |
||
352 | length = msg->length; |
||
353 | |||
354 | assert(length > 0 && length == ctx->Debug.NextMsgLength); |
||
355 | |||
356 | if (bufSize < length && buf != NULL) |
||
357 | return 0; |
||
358 | |||
359 | if (severity) |
||
360 | *severity = debug_severity_enums[msg->severity]; |
||
361 | if (source) |
||
362 | *source = debug_source_enums[msg->source]; |
||
363 | if (type) |
||
364 | *type = debug_type_enums[msg->type]; |
||
365 | if (id) |
||
366 | *id = msg->id; |
||
367 | |||
368 | if (buf) { |
||
369 | assert(msg->message[length-1] == '\0'); |
||
370 | (void) strncpy(buf, msg->message, (size_t)length); |
||
371 | } |
||
372 | |||
373 | if (msg->message != (char*)out_of_memory) |
||
374 | free(msg->message); |
||
375 | msg->message = NULL; |
||
376 | msg->length = 0; |
||
377 | |||
378 | ctx->Debug.NumMessages--; |
||
379 | ctx->Debug.NextMsg++; |
||
380 | ctx->Debug.NextMsg %= MAX_DEBUG_LOGGED_MESSAGES; |
||
381 | ctx->Debug.NextMsgLength = ctx->Debug.Log[ctx->Debug.NextMsg].length; |
||
382 | |||
383 | return length; |
||
384 | } |
||
385 | |||
386 | /** |
||
387 | * Verify that source, type, and severity are valid enums. |
||
388 | * glDebugMessageInsertARB only accepts two values for 'source', |
||
389 | * and glDebugMessageControlARB will additionally accept GL_DONT_CARE |
||
390 | * in any parameter, so handle those cases specially. |
||
391 | */ |
||
392 | static GLboolean |
||
393 | validate_params(struct gl_context *ctx, unsigned caller, |
||
394 | GLenum source, GLenum type, GLenum severity) |
||
395 | { |
||
396 | #define INSERT 1 |
||
397 | #define CONTROL 2 |
||
398 | switch(source) { |
||
399 | case GL_DEBUG_SOURCE_APPLICATION_ARB: |
||
400 | case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: |
||
401 | break; |
||
402 | case GL_DEBUG_SOURCE_API_ARB: |
||
403 | case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: |
||
404 | case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: |
||
405 | case GL_DEBUG_SOURCE_OTHER_ARB: |
||
406 | if (caller != INSERT) |
||
407 | break; |
||
408 | case GL_DONT_CARE: |
||
409 | if (caller == CONTROL) |
||
410 | break; |
||
411 | default: |
||
412 | goto error; |
||
413 | } |
||
414 | |||
415 | switch(type) { |
||
416 | case GL_DEBUG_TYPE_ERROR_ARB: |
||
417 | case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: |
||
418 | case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: |
||
419 | case GL_DEBUG_TYPE_PERFORMANCE_ARB: |
||
420 | case GL_DEBUG_TYPE_PORTABILITY_ARB: |
||
421 | case GL_DEBUG_TYPE_OTHER_ARB: |
||
422 | break; |
||
423 | case GL_DONT_CARE: |
||
424 | if (caller == CONTROL) |
||
425 | break; |
||
426 | default: |
||
427 | goto error; |
||
428 | } |
||
429 | |||
430 | switch(severity) { |
||
431 | case GL_DEBUG_SEVERITY_HIGH_ARB: |
||
432 | case GL_DEBUG_SEVERITY_MEDIUM_ARB: |
||
433 | case GL_DEBUG_SEVERITY_LOW_ARB: |
||
434 | break; |
||
435 | case GL_DONT_CARE: |
||
436 | if (caller == CONTROL) |
||
437 | break; |
||
438 | default: |
||
439 | goto error; |
||
440 | } |
||
441 | return GL_TRUE; |
||
442 | |||
443 | error: |
||
444 | { |
||
445 | const char *callerstr; |
||
446 | if (caller == INSERT) |
||
447 | callerstr = "glDebugMessageInsertARB"; |
||
448 | else if (caller == CONTROL) |
||
449 | callerstr = "glDebugMessageControlARB"; |
||
450 | else |
||
451 | return GL_FALSE; |
||
452 | |||
453 | _mesa_error( ctx, GL_INVALID_ENUM, "bad values passed to %s" |
||
454 | "(source=0x%x, type=0x%x, severity=0x%x)", callerstr, |
||
455 | source, type, severity); |
||
456 | } |
||
457 | return GL_FALSE; |
||
458 | } |
||
459 | |||
460 | void GLAPIENTRY |
||
461 | _mesa_DebugMessageInsertARB(GLenum source, GLenum type, GLuint id, |
||
462 | GLenum severity, GLint length, |
||
463 | const GLcharARB* buf) |
||
464 | { |
||
465 | GET_CURRENT_CONTEXT(ctx); |
||
466 | |||
467 | if (!validate_params(ctx, INSERT, source, type, severity)) |
||
468 | return; /* GL_INVALID_ENUM */ |
||
469 | |||
470 | if (length < 0) |
||
471 | length = strlen(buf); |
||
472 | |||
473 | if (length >= MAX_DEBUG_MESSAGE_LENGTH) { |
||
474 | _mesa_error(ctx, GL_INVALID_VALUE, "glDebugMessageInsertARB" |
||
475 | "(length=%d, which is not less than " |
||
476 | "GL_MAX_DEBUG_MESSAGE_LENGTH_ARB=%d)", length, |
||
477 | MAX_DEBUG_MESSAGE_LENGTH); |
||
478 | return; |
||
479 | } |
||
480 | |||
481 | _mesa_log_msg(ctx, |
||
482 | gl_enum_to_debug_source(source), |
||
483 | gl_enum_to_debug_type(type), id, |
||
484 | gl_enum_to_debug_severity(severity), length, buf); |
||
485 | } |
||
486 | |||
487 | GLuint GLAPIENTRY |
||
488 | _mesa_GetDebugMessageLogARB(GLuint count, GLsizei logSize, GLenum* sources, |
||
489 | GLenum* types, GLenum* ids, GLenum* severities, |
||
490 | GLsizei* lengths, GLcharARB* messageLog) |
||
491 | { |
||
492 | GET_CURRENT_CONTEXT(ctx); |
||
493 | GLuint ret; |
||
494 | |||
495 | if (!messageLog) |
||
496 | logSize = 0; |
||
497 | |||
498 | if (logSize < 0) { |
||
499 | _mesa_error(ctx, GL_INVALID_VALUE, "glGetDebugMessageLogARB" |
||
500 | "(logSize=%d : logSize must not be negative)", logSize); |
||
501 | return 0; |
||
502 | } |
||
503 | |||
504 | for (ret = 0; ret < count; ret++) { |
||
505 | GLsizei written = _mesa_get_msg(ctx, sources, types, ids, severities, |
||
506 | logSize, messageLog); |
||
507 | if (!written) |
||
508 | break; |
||
509 | |||
510 | if (messageLog) { |
||
511 | messageLog += written; |
||
512 | logSize -= written; |
||
513 | } |
||
514 | if (lengths) { |
||
515 | *lengths = written; |
||
516 | lengths++; |
||
517 | } |
||
518 | |||
519 | if (severities) |
||
520 | severities++; |
||
521 | if (sources) |
||
522 | sources++; |
||
523 | if (types) |
||
524 | types++; |
||
525 | if (ids) |
||
526 | ids++; |
||
527 | } |
||
528 | |||
529 | return ret; |
||
530 | } |
||
531 | |||
532 | /** |
||
533 | * Set the state of all message IDs found in the given intersection of |
||
534 | * 'source', 'type', and 'severity'. The _COUNT enum can be used for |
||
535 | * GL_DONT_CARE (include all messages in the class). |
||
536 | * |
||
537 | * This requires both setting the state of all previously seen message |
||
538 | * IDs in the hash table, and setting the default state for all |
||
539 | * applicable combinations of source/type/severity, so that all the |
||
540 | * yet-unknown message IDs that may be used in the future will be |
||
541 | * impacted as if they were already known. |
||
542 | */ |
||
543 | static void |
||
544 | control_messages(struct gl_context *ctx, |
||
545 | enum mesa_debug_source source, |
||
546 | enum mesa_debug_type type, |
||
547 | enum mesa_debug_severity severity, |
||
548 | GLboolean enabled) |
||
549 | { |
||
550 | int s, t, sev, smax, tmax, sevmax; |
||
551 | |||
552 | if (source == MESA_DEBUG_SOURCE_COUNT) { |
||
553 | source = 0; |
||
554 | smax = MESA_DEBUG_SOURCE_COUNT; |
||
555 | } else { |
||
556 | smax = source+1; |
||
557 | } |
||
558 | |||
559 | if (type == MESA_DEBUG_TYPE_COUNT) { |
||
560 | type = 0; |
||
561 | tmax = MESA_DEBUG_TYPE_COUNT; |
||
562 | } else { |
||
563 | tmax = type+1; |
||
564 | } |
||
565 | |||
566 | if (severity == MESA_DEBUG_SEVERITY_COUNT) { |
||
567 | severity = 0; |
||
568 | sevmax = MESA_DEBUG_SEVERITY_COUNT; |
||
569 | } else { |
||
570 | sevmax = severity+1; |
||
571 | } |
||
572 | |||
573 | for (sev = severity; sev < sevmax; sev++) |
||
574 | for (s = source; s < smax; s++) |
||
575 | for (t = type; t < tmax; t++) { |
||
576 | struct simple_node *node; |
||
577 | struct gl_debug_severity *entry; |
||
578 | |||
579 | /* change the default for IDs we've never seen before. */ |
||
580 | ctx->Debug.Defaults[sev][s][t] = enabled; |
||
581 | |||
582 | /* Now change the state of IDs we *have* seen... */ |
||
583 | foreach(node, &ctx->Debug.Namespaces[s][t].Severity[sev]) { |
||
584 | entry = (struct gl_debug_severity *)node; |
||
585 | set_message_state(ctx, s, t, entry->ID, enabled); |
||
586 | } |
||
587 | } |
||
588 | } |
||
589 | |||
590 | /** |
||
591 | * Debugging-message namespaces with the source APPLICATION or THIRD_PARTY |
||
592 | * require special handling, since the IDs in them are controlled by clients, |
||
593 | * not the OpenGL implementation. |
||
594 | * |
||
595 | * 'count' is the length of the array 'ids'. If 'count' is nonzero, all |
||
596 | * the given IDs in the namespace defined by 'esource' and 'etype' |
||
597 | * will be affected. |
||
598 | * |
||
599 | * If 'count' is zero, this sets the state of all IDs that match |
||
600 | * the combination of 'esource', 'etype', and 'eseverity'. |
||
601 | */ |
||
602 | static void |
||
603 | control_app_messages(struct gl_context *ctx, GLenum esource, GLenum etype, |
||
604 | GLenum eseverity, GLsizei count, const GLuint *ids, |
||
605 | GLboolean enabled) |
||
606 | { |
||
607 | GLsizei i; |
||
608 | enum mesa_debug_source source = gl_enum_to_debug_source(esource); |
||
609 | enum mesa_debug_type type = gl_enum_to_debug_type(etype); |
||
610 | enum mesa_debug_severity severity = gl_enum_to_debug_severity(eseverity); |
||
611 | |||
612 | for (i = 0; i < count; i++) |
||
613 | set_message_state(ctx, source, type, ids[i], enabled); |
||
614 | |||
615 | if (count) |
||
616 | return; |
||
617 | |||
618 | control_messages(ctx, source, type, severity, enabled); |
||
619 | } |
||
620 | |||
621 | void GLAPIENTRY |
||
622 | _mesa_DebugMessageControlARB(GLenum gl_source, GLenum gl_type, |
||
623 | GLenum gl_severity, |
||
624 | GLsizei count, const GLuint *ids, |
||
625 | GLboolean enabled) |
||
626 | { |
||
627 | GET_CURRENT_CONTEXT(ctx); |
||
628 | |||
629 | if (count < 0) { |
||
630 | _mesa_error(ctx, GL_INVALID_VALUE, "glDebugMessageControlARB" |
||
631 | "(count=%d : count must not be negative)", count); |
||
632 | return; |
||
633 | } |
||
634 | |||
635 | if (!validate_params(ctx, CONTROL, gl_source, gl_type, gl_severity)) |
||
636 | return; /* GL_INVALID_ENUM */ |
||
637 | |||
638 | if (count && (gl_severity != GL_DONT_CARE || gl_type == GL_DONT_CARE |
||
639 | || gl_source == GL_DONT_CARE)) { |
||
640 | _mesa_error(ctx, GL_INVALID_OPERATION, "glDebugMessageControlARB" |
||
641 | "(When passing an array of ids, severity must be" |
||
642 | " GL_DONT_CARE, and source and type must not be GL_DONT_CARE."); |
||
643 | return; |
||
644 | } |
||
645 | |||
646 | control_app_messages(ctx, gl_source, gl_type, gl_severity, |
||
647 | count, ids, enabled); |
||
648 | } |
||
649 | |||
650 | void GLAPIENTRY |
||
651 | _mesa_DebugMessageCallbackARB(GLDEBUGPROCARB callback, const void *userParam) |
||
652 | { |
||
653 | GET_CURRENT_CONTEXT(ctx); |
||
654 | ctx->Debug.Callback = callback; |
||
655 | ctx->Debug.CallbackData = userParam; |
||
656 | } |
||
657 | |||
658 | void |
||
659 | _mesa_init_errors(struct gl_context *ctx) |
||
660 | { |
||
661 | int s, t, sev; |
||
662 | |||
663 | ctx->Debug.Callback = NULL; |
||
664 | ctx->Debug.SyncOutput = GL_FALSE; |
||
665 | ctx->Debug.Log[0].length = 0; |
||
666 | ctx->Debug.NumMessages = 0; |
||
667 | ctx->Debug.NextMsg = 0; |
||
668 | ctx->Debug.NextMsgLength = 0; |
||
669 | |||
670 | /* Enable all the messages with severity HIGH or MEDIUM by default. */ |
||
671 | memset(ctx->Debug.Defaults[MESA_DEBUG_SEVERITY_HIGH], GL_TRUE, |
||
672 | sizeof ctx->Debug.Defaults[MESA_DEBUG_SEVERITY_HIGH]); |
||
673 | memset(ctx->Debug.Defaults[MESA_DEBUG_SEVERITY_MEDIUM], GL_TRUE, |
||
674 | sizeof ctx->Debug.Defaults[MESA_DEBUG_SEVERITY_MEDIUM]); |
||
675 | memset(ctx->Debug.Defaults[MESA_DEBUG_SEVERITY_LOW], GL_FALSE, |
||
676 | sizeof ctx->Debug.Defaults[MESA_DEBUG_SEVERITY_LOW]); |
||
677 | |||
678 | /* Initialize state for filtering known debug messages. */ |
||
679 | for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) |
||
680 | for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) { |
||
681 | ctx->Debug.Namespaces[s][t].IDs = _mesa_NewHashTable(); |
||
682 | assert(ctx->Debug.Namespaces[s][t].IDs); |
||
683 | |||
684 | for (sev = 0; sev < MESA_DEBUG_SEVERITY_COUNT; sev++) |
||
685 | make_empty_list(&ctx->Debug.Namespaces[s][t].Severity[sev]); |
||
686 | } |
||
687 | } |
||
688 | |||
689 | static void |
||
690 | do_nothing(GLuint key, void *data, void *userData) |
||
691 | { |
||
692 | } |
||
693 | |||
694 | void |
||
695 | _mesa_free_errors_data(struct gl_context *ctx) |
||
696 | { |
||
697 | enum mesa_debug_type t; |
||
698 | enum mesa_debug_source s; |
||
699 | enum mesa_debug_severity sev; |
||
700 | |||
701 | /* Tear down state for filtering debug messages. */ |
||
702 | for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) |
||
703 | for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) { |
||
704 | _mesa_HashDeleteAll(ctx->Debug.Namespaces[s][t].IDs, do_nothing, NULL); |
||
705 | _mesa_DeleteHashTable(ctx->Debug.Namespaces[s][t].IDs); |
||
706 | for (sev = 0; sev < MESA_DEBUG_SEVERITY_COUNT; sev++) { |
||
707 | struct simple_node *node, *tmp; |
||
708 | struct gl_debug_severity *entry; |
||
709 | |||
710 | foreach_s(node, tmp, &ctx->Debug.Namespaces[s][t].Severity[sev]) { |
||
711 | entry = (struct gl_debug_severity *)node; |
||
712 | free(entry); |
||
713 | } |
||
714 | } |
||
715 | } |
||
716 | } |
||
717 | |||
718 | /**********************************************************************/ |
||
719 | /** \name Diagnostics */ |
||
720 | /*@{*/ |
||
721 | |||
722 | static void |
||
723 | output_if_debug(const char *prefixString, const char *outputString, |
||
724 | GLboolean newline) |
||
725 | { |
||
726 | static int debug = -1; |
||
727 | static FILE *fout = NULL; |
||
728 | |||
729 | /* Init the local 'debug' var once. |
||
730 | * Note: the _mesa_init_debug() function should have been called |
||
731 | * by now so MESA_DEBUG_FLAGS will be initialized. |
||
732 | */ |
||
733 | if (debug == -1) { |
||
734 | /* If MESA_LOG_FILE env var is set, log Mesa errors, warnings, |
||
735 | * etc to the named file. Otherwise, output to stderr. |
||
736 | */ |
||
737 | const char *logFile = _mesa_getenv("MESA_LOG_FILE"); |
||
738 | if (logFile) |
||
739 | fout = fopen(logFile, "w"); |
||
740 | if (!fout) |
||
741 | fout = stderr; |
||
742 | #ifdef DEBUG |
||
743 | /* in debug builds, print messages unless MESA_DEBUG="silent" */ |
||
744 | if (MESA_DEBUG_FLAGS & DEBUG_SILENT) |
||
745 | debug = 0; |
||
746 | else |
||
747 | debug = 1; |
||
748 | #else |
||
749 | /* in release builds, be silent unless MESA_DEBUG is set */ |
||
750 | debug = _mesa_getenv("MESA_DEBUG") != NULL; |
||
751 | #endif |
||
752 | } |
||
753 | |||
754 | /* Now only print the string if we're required to do so. */ |
||
755 | if (debug) { |
||
756 | fprintf(fout, "%s: %s", prefixString, outputString); |
||
757 | if (newline) |
||
758 | fprintf(fout, "\n"); |
||
759 | fflush(fout); |
||
760 | |||
761 | #if defined(_WIN32) && !defined(_WIN32_WCE) |
||
762 | /* stderr from windows applications without console is not usually |
||
763 | * visible, so communicate with the debugger instead */ |
||
764 | { |
||
765 | char buf[4096]; |
||
766 | _mesa_snprintf(buf, sizeof(buf), "%s: %s%s", prefixString, outputString, newline ? "\n" : ""); |
||
767 | OutputDebugStringA(buf); |
||
768 | } |
||
769 | #endif |
||
770 | } |
||
771 | } |
||
772 | |||
773 | /** |
||
774 | * When a new type of error is recorded, print a message describing |
||
775 | * previous errors which were accumulated. |
||
776 | */ |
||
777 | static void |
||
778 | flush_delayed_errors( struct gl_context *ctx ) |
||
779 | { |
||
780 | char s[MAX_DEBUG_MESSAGE_LENGTH]; |
||
781 | |||
782 | if (ctx->ErrorDebugCount) { |
||
783 | _mesa_snprintf(s, MAX_DEBUG_MESSAGE_LENGTH, "%d similar %s errors", |
||
784 | ctx->ErrorDebugCount, |
||
785 | _mesa_lookup_enum_by_nr(ctx->ErrorValue)); |
||
786 | |||
787 | output_if_debug("Mesa", s, GL_TRUE); |
||
788 | |||
789 | ctx->ErrorDebugCount = 0; |
||
790 | } |
||
791 | } |
||
792 | |||
793 | |||
794 | /** |
||
795 | * Report a warning (a recoverable error condition) to stderr if |
||
796 | * either DEBUG is defined or the MESA_DEBUG env var is set. |
||
797 | * |
||
798 | * \param ctx GL context. |
||
799 | * \param fmtString printf()-like format string. |
||
800 | */ |
||
801 | void |
||
802 | _mesa_warning( struct gl_context *ctx, const char *fmtString, ... ) |
||
803 | { |
||
804 | char str[MAX_DEBUG_MESSAGE_LENGTH]; |
||
805 | va_list args; |
||
806 | va_start( args, fmtString ); |
||
807 | (void) _mesa_vsnprintf( str, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args ); |
||
808 | va_end( args ); |
||
809 | |||
810 | if (ctx) |
||
811 | flush_delayed_errors( ctx ); |
||
812 | |||
813 | output_if_debug("Mesa warning", str, GL_TRUE); |
||
814 | } |
||
815 | |||
816 | |||
817 | /** |
||
818 | * Report an internal implementation problem. |
||
819 | * Prints the message to stderr via fprintf(). |
||
820 | * |
||
821 | * \param ctx GL context. |
||
822 | * \param fmtString problem description string. |
||
823 | */ |
||
824 | void |
||
825 | _mesa_problem( const struct gl_context *ctx, const char *fmtString, ... ) |
||
826 | { |
||
827 | va_list args; |
||
828 | char str[MAX_DEBUG_MESSAGE_LENGTH]; |
||
829 | static int numCalls = 0; |
||
830 | |||
831 | (void) ctx; |
||
832 | |||
833 | if (numCalls < 50) { |
||
834 | numCalls++; |
||
835 | |||
836 | va_start( args, fmtString ); |
||
837 | _mesa_vsnprintf( str, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args ); |
||
838 | va_end( args ); |
||
839 | fprintf(stderr, "Mesa %s implementation error: %s\n", |
||
840 | PACKAGE_VERSION, str); |
||
841 | fprintf(stderr, "Please report at " PACKAGE_BUGREPORT "\n"); |
||
842 | } |
||
843 | } |
||
844 | |||
845 | static GLboolean |
||
846 | should_output(struct gl_context *ctx, GLenum error, const char *fmtString) |
||
847 | { |
||
848 | static GLint debug = -1; |
||
849 | |||
850 | /* Check debug environment variable only once: |
||
851 | */ |
||
852 | if (debug == -1) { |
||
853 | const char *debugEnv = _mesa_getenv("MESA_DEBUG"); |
||
854 | |||
855 | #ifdef DEBUG |
||
856 | if (debugEnv && strstr(debugEnv, "silent")) |
||
857 | debug = GL_FALSE; |
||
858 | else |
||
859 | debug = GL_TRUE; |
||
860 | #else |
||
861 | if (debugEnv) |
||
862 | debug = GL_TRUE; |
||
863 | else |
||
864 | debug = GL_FALSE; |
||
865 | #endif |
||
866 | } |
||
867 | |||
868 | if (debug) { |
||
869 | if (ctx->ErrorValue != error || |
||
870 | ctx->ErrorDebugFmtString != fmtString) { |
||
871 | flush_delayed_errors( ctx ); |
||
872 | ctx->ErrorDebugFmtString = fmtString; |
||
873 | ctx->ErrorDebugCount = 0; |
||
874 | return GL_TRUE; |
||
875 | } |
||
876 | ctx->ErrorDebugCount++; |
||
877 | } |
||
878 | return GL_FALSE; |
||
879 | } |
||
880 | |||
881 | void |
||
882 | _mesa_gl_debug(struct gl_context *ctx, |
||
883 | GLuint *id, |
||
884 | enum mesa_debug_type type, |
||
885 | enum mesa_debug_severity severity, |
||
886 | const char *fmtString, ...) |
||
887 | { |
||
888 | char s[MAX_DEBUG_MESSAGE_LENGTH]; |
||
889 | int len; |
||
890 | va_list args; |
||
891 | |||
892 | debug_get_id(id); |
||
893 | |||
894 | va_start(args, fmtString); |
||
895 | len = _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); |
||
896 | va_end(args); |
||
897 | |||
898 | _mesa_log_msg(ctx, MESA_DEBUG_SOURCE_API, type, |
||
899 | *id, severity, len, s); |
||
900 | } |
||
901 | |||
902 | |||
903 | /** |
||
904 | * Record an OpenGL state error. These usually occur when the user |
||
905 | * passes invalid parameters to a GL function. |
||
906 | * |
||
907 | * If debugging is enabled (either at compile-time via the DEBUG macro, or |
||
908 | * run-time via the MESA_DEBUG environment variable), report the error with |
||
909 | * _mesa_debug(). |
||
910 | * |
||
911 | * \param ctx the GL context. |
||
912 | * \param error the error value. |
||
913 | * \param fmtString printf() style format string, followed by optional args |
||
914 | */ |
||
915 | void |
||
916 | _mesa_error( struct gl_context *ctx, GLenum error, const char *fmtString, ... ) |
||
917 | { |
||
918 | GLboolean do_output, do_log; |
||
919 | /* Ideally this would be set up by the caller, so that we had proper IDs |
||
920 | * per different message. |
||
921 | */ |
||
922 | static GLuint error_msg_id = 0; |
||
923 | |||
924 | debug_get_id(&error_msg_id); |
||
925 | |||
926 | do_output = should_output(ctx, error, fmtString); |
||
927 | do_log = should_log(ctx, |
||
928 | MESA_DEBUG_SOURCE_API, |
||
929 | MESA_DEBUG_TYPE_ERROR, |
||
930 | error_msg_id, |
||
931 | MESA_DEBUG_SEVERITY_HIGH); |
||
932 | |||
933 | if (do_output || do_log) { |
||
934 | char s[MAX_DEBUG_MESSAGE_LENGTH], s2[MAX_DEBUG_MESSAGE_LENGTH]; |
||
935 | int len; |
||
936 | va_list args; |
||
937 | |||
938 | va_start(args, fmtString); |
||
939 | len = _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); |
||
940 | va_end(args); |
||
941 | |||
942 | if (len >= MAX_DEBUG_MESSAGE_LENGTH) { |
||
943 | /* Too long error message. Whoever calls _mesa_error should use |
||
944 | * shorter strings. */ |
||
945 | ASSERT(0); |
||
946 | return; |
||
947 | } |
||
948 | |||
949 | len = _mesa_snprintf(s2, MAX_DEBUG_MESSAGE_LENGTH, "%s in %s", |
||
950 | _mesa_lookup_enum_by_nr(error), s); |
||
951 | if (len >= MAX_DEBUG_MESSAGE_LENGTH) { |
||
952 | /* Same as above. */ |
||
953 | ASSERT(0); |
||
954 | return; |
||
955 | } |
||
956 | |||
957 | /* Print the error to stderr if needed. */ |
||
958 | if (do_output) { |
||
959 | output_if_debug("Mesa: User error", s2, GL_TRUE); |
||
960 | } |
||
961 | |||
962 | /* Log the error via ARB_debug_output if needed.*/ |
||
963 | if (do_log) { |
||
964 | _mesa_log_msg(ctx, |
||
965 | MESA_DEBUG_SOURCE_API, |
||
966 | MESA_DEBUG_TYPE_ERROR, |
||
967 | error_msg_id, |
||
968 | MESA_DEBUG_SEVERITY_HIGH, len, s2); |
||
969 | } |
||
970 | } |
||
971 | |||
972 | /* Set the GL context error state for glGetError. */ |
||
973 | _mesa_record_error(ctx, error); |
||
974 | } |
||
975 | |||
976 | |||
977 | /** |
||
978 | * Report debug information. Print error message to stderr via fprintf(). |
||
979 | * No-op if DEBUG mode not enabled. |
||
980 | * |
||
981 | * \param ctx GL context. |
||
982 | * \param fmtString printf()-style format string, followed by optional args. |
||
983 | */ |
||
984 | void |
||
985 | _mesa_debug( const struct gl_context *ctx, const char *fmtString, ... ) |
||
986 | { |
||
987 | #ifdef DEBUG |
||
988 | char s[MAX_DEBUG_MESSAGE_LENGTH]; |
||
989 | va_list args; |
||
990 | va_start(args, fmtString); |
||
991 | _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); |
||
992 | va_end(args); |
||
993 | output_if_debug("Mesa", s, GL_FALSE); |
||
994 | #endif /* DEBUG */ |
||
995 | (void) ctx; |
||
996 | (void) fmtString; |
||
997 | } |
||
998 | |||
999 | |||
1000 | /** |
||
1001 | * Report debug information from the shader compiler via GL_ARB_debug_output. |
||
1002 | * |
||
1003 | * \param ctx GL context. |
||
1004 | * \param type The namespace to which this message belongs. |
||
1005 | * \param id The message ID within the given namespace. |
||
1006 | * \param msg The message to output. Need not be null-terminated. |
||
1007 | * \param len The length of 'msg'. If negative, 'msg' must be null-terminated. |
||
1008 | */ |
||
1009 | void |
||
1010 | _mesa_shader_debug( struct gl_context *ctx, GLenum type, GLuint *id, |
||
1011 | const char *msg, int len ) |
||
1012 | { |
||
1013 | enum mesa_debug_source source = MESA_DEBUG_SOURCE_SHADER_COMPILER; |
||
1014 | enum mesa_debug_severity severity = MESA_DEBUG_SEVERITY_HIGH; |
||
1015 | |||
1016 | debug_get_id(id); |
||
1017 | |||
1018 | if (len < 0) |
||
1019 | len = strlen(msg); |
||
1020 | |||
1021 | /* Truncate the message if necessary. */ |
||
1022 | if (len >= MAX_DEBUG_MESSAGE_LENGTH) |
||
1023 | len = MAX_DEBUG_MESSAGE_LENGTH - 1; |
||
1024 | |||
1025 | _mesa_log_msg(ctx, source, type, *id, severity, len, msg); |
||
1026 | } |
||
1027 | |||
1028 | /*@}*/>>>>>>>>>>>>>>>>>>><>><>><>>>> |