Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | 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 |
||
32 | #include |
||
33 | #include "errors.h" |
||
34 | #include "enums.h" |
||
35 | #include "imports.h" |
||
36 | #include "context.h" |
||
37 | #include "dispatch.h" |
||
38 | #include "hash.h" |
||
39 | #include "mtypes.h" |
||
40 | #include "version.h" |
||
41 | #include "util/hash_table.h" |
||
42 | |||
43 | static mtx_t DynamicIDMutex = _MTX_INITIALIZER_NP; |
||
44 | static GLuint NextDynamicID = 1; |
||
45 | |||
46 | /** |
||
47 | * A namespace element. |
||
48 | */ |
||
49 | struct gl_debug_element |
||
50 | { |
||
51 | struct simple_node link; |
||
52 | |||
53 | GLuint ID; |
||
54 | /* at which severity levels (mesa_debug_severity) is the message enabled */ |
||
55 | GLbitfield State; |
||
56 | }; |
||
57 | |||
58 | struct gl_debug_namespace |
||
59 | { |
||
60 | struct simple_node Elements; |
||
61 | GLbitfield DefaultState; |
||
62 | }; |
||
63 | |||
64 | struct gl_debug_group { |
||
65 | struct gl_debug_namespace Namespaces[MESA_DEBUG_SOURCE_COUNT][MESA_DEBUG_TYPE_COUNT]; |
||
66 | }; |
||
67 | |||
68 | /** |
||
69 | * An error, warning, or other piece of debug information for an application |
||
70 | * to consume via GL_ARB_debug_output/GL_KHR_debug. |
||
71 | */ |
||
72 | struct gl_debug_message |
||
73 | { |
||
74 | enum mesa_debug_source source; |
||
75 | enum mesa_debug_type type; |
||
76 | GLuint id; |
||
77 | enum mesa_debug_severity severity; |
||
78 | GLsizei length; |
||
79 | GLcharARB *message; |
||
80 | }; |
||
81 | |||
82 | /** |
||
83 | * Debug message log. It works like a ring buffer. |
||
84 | */ |
||
85 | struct gl_debug_log { |
||
86 | struct gl_debug_message Messages[MAX_DEBUG_LOGGED_MESSAGES]; |
||
87 | GLint NextMessage; |
||
88 | GLint NumMessages; |
||
89 | }; |
||
90 | |||
91 | struct gl_debug_state |
||
92 | { |
||
93 | GLDEBUGPROC Callback; |
||
94 | const void *CallbackData; |
||
95 | GLboolean SyncOutput; |
||
96 | GLboolean DebugOutput; |
||
97 | |||
98 | struct gl_debug_group *Groups[MAX_DEBUG_GROUP_STACK_DEPTH]; |
||
99 | struct gl_debug_message GroupMessages[MAX_DEBUG_GROUP_STACK_DEPTH]; |
||
100 | GLint GroupStackDepth; |
||
101 | |||
102 | struct gl_debug_log Log; |
||
103 | }; |
||
104 | |||
105 | static char out_of_memory[] = "Debugging error: out of memory"; |
||
106 | |||
107 | static const GLenum debug_source_enums[] = { |
||
108 | GL_DEBUG_SOURCE_API, |
||
109 | GL_DEBUG_SOURCE_WINDOW_SYSTEM, |
||
110 | GL_DEBUG_SOURCE_SHADER_COMPILER, |
||
111 | GL_DEBUG_SOURCE_THIRD_PARTY, |
||
112 | GL_DEBUG_SOURCE_APPLICATION, |
||
113 | GL_DEBUG_SOURCE_OTHER, |
||
114 | }; |
||
115 | |||
116 | static const GLenum debug_type_enums[] = { |
||
117 | GL_DEBUG_TYPE_ERROR, |
||
118 | GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, |
||
119 | GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, |
||
120 | GL_DEBUG_TYPE_PORTABILITY, |
||
121 | GL_DEBUG_TYPE_PERFORMANCE, |
||
122 | GL_DEBUG_TYPE_OTHER, |
||
123 | GL_DEBUG_TYPE_MARKER, |
||
124 | GL_DEBUG_TYPE_PUSH_GROUP, |
||
125 | GL_DEBUG_TYPE_POP_GROUP, |
||
126 | }; |
||
127 | |||
128 | static const GLenum debug_severity_enums[] = { |
||
129 | GL_DEBUG_SEVERITY_LOW, |
||
130 | GL_DEBUG_SEVERITY_MEDIUM, |
||
131 | GL_DEBUG_SEVERITY_HIGH, |
||
132 | GL_DEBUG_SEVERITY_NOTIFICATION, |
||
133 | }; |
||
134 | |||
135 | |||
136 | static enum mesa_debug_source |
||
137 | gl_enum_to_debug_source(GLenum e) |
||
138 | { |
||
139 | unsigned i; |
||
140 | |||
141 | for (i = 0; i < ARRAY_SIZE(debug_source_enums); i++) { |
||
142 | if (debug_source_enums[i] == e) |
||
143 | break; |
||
144 | } |
||
145 | return i; |
||
146 | } |
||
147 | |||
148 | static enum mesa_debug_type |
||
149 | gl_enum_to_debug_type(GLenum e) |
||
150 | { |
||
151 | unsigned i; |
||
152 | |||
153 | for (i = 0; i < ARRAY_SIZE(debug_type_enums); i++) { |
||
154 | if (debug_type_enums[i] == e) |
||
155 | break; |
||
156 | } |
||
157 | return i; |
||
158 | } |
||
159 | |||
160 | static enum mesa_debug_severity |
||
161 | gl_enum_to_debug_severity(GLenum e) |
||
162 | { |
||
163 | unsigned i; |
||
164 | |||
165 | for (i = 0; i < ARRAY_SIZE(debug_severity_enums); i++) { |
||
166 | if (debug_severity_enums[i] == e) |
||
167 | break; |
||
168 | } |
||
169 | return i; |
||
170 | } |
||
171 | |||
172 | |||
173 | /** |
||
174 | * Handles generating a GL_ARB_debug_output message ID generated by the GL or |
||
175 | * GLSL compiler. |
||
176 | * |
||
177 | * The GL API has this "ID" mechanism, where the intention is to allow a |
||
178 | * client to filter in/out messages based on source, type, and ID. Of course, |
||
179 | * building a giant enum list of all debug output messages that Mesa might |
||
180 | * generate is ridiculous, so instead we have our caller pass us a pointer to |
||
181 | * static storage where the ID should get stored. This ID will be shared |
||
182 | * across all contexts for that message (which seems like a desirable |
||
183 | * property, even if it's not expected by the spec), but note that it won't be |
||
184 | * the same between executions if messages aren't generated in the same order. |
||
185 | */ |
||
186 | static void |
||
187 | debug_get_id(GLuint *id) |
||
188 | { |
||
189 | if (!(*id)) { |
||
190 | mtx_lock(&DynamicIDMutex); |
||
191 | if (!(*id)) |
||
192 | *id = NextDynamicID++; |
||
193 | mtx_unlock(&DynamicIDMutex); |
||
194 | } |
||
195 | } |
||
196 | |||
197 | static void |
||
198 | debug_message_clear(struct gl_debug_message *msg) |
||
199 | { |
||
200 | if (msg->message != (char*)out_of_memory) |
||
201 | free(msg->message); |
||
202 | msg->message = NULL; |
||
203 | msg->length = 0; |
||
204 | } |
||
205 | |||
206 | static void |
||
207 | debug_message_store(struct gl_debug_message *msg, |
||
208 | enum mesa_debug_source source, |
||
209 | enum mesa_debug_type type, GLuint id, |
||
210 | enum mesa_debug_severity severity, |
||
211 | GLsizei len, const char *buf) |
||
212 | { |
||
213 | assert(!msg->message && !msg->length); |
||
214 | |||
215 | msg->message = malloc(len+1); |
||
216 | if (msg->message) { |
||
217 | (void) strncpy(msg->message, buf, (size_t)len); |
||
218 | msg->message[len] = '\0'; |
||
219 | |||
220 | msg->length = len+1; |
||
221 | msg->source = source; |
||
222 | msg->type = type; |
||
223 | msg->id = id; |
||
224 | msg->severity = severity; |
||
225 | } else { |
||
226 | static GLuint oom_msg_id = 0; |
||
227 | debug_get_id(&oom_msg_id); |
||
228 | |||
229 | /* malloc failed! */ |
||
230 | msg->message = out_of_memory; |
||
231 | msg->length = strlen(out_of_memory)+1; |
||
232 | msg->source = MESA_DEBUG_SOURCE_OTHER; |
||
233 | msg->type = MESA_DEBUG_TYPE_ERROR; |
||
234 | msg->id = oom_msg_id; |
||
235 | msg->severity = MESA_DEBUG_SEVERITY_HIGH; |
||
236 | } |
||
237 | } |
||
238 | |||
239 | static void |
||
240 | debug_namespace_init(struct gl_debug_namespace *ns) |
||
241 | { |
||
242 | make_empty_list(&ns->Elements); |
||
243 | |||
244 | /* Enable all the messages with severity HIGH or MEDIUM by default */ |
||
245 | ns->DefaultState = (1 << MESA_DEBUG_SEVERITY_HIGH) | |
||
246 | (1 << MESA_DEBUG_SEVERITY_MEDIUM); |
||
247 | } |
||
248 | |||
249 | static void |
||
250 | debug_namespace_clear(struct gl_debug_namespace *ns) |
||
251 | { |
||
252 | struct simple_node *node, *tmp; |
||
253 | |||
254 | foreach_s(node, tmp, &ns->Elements) |
||
255 | free(node); |
||
256 | } |
||
257 | |||
258 | static bool |
||
259 | debug_namespace_copy(struct gl_debug_namespace *dst, |
||
260 | const struct gl_debug_namespace *src) |
||
261 | { |
||
262 | struct simple_node *node; |
||
263 | |||
264 | dst->DefaultState = src->DefaultState; |
||
265 | |||
266 | make_empty_list(&dst->Elements); |
||
267 | foreach(node, &src->Elements) { |
||
268 | const struct gl_debug_element *elem = |
||
269 | (const struct gl_debug_element *) node; |
||
270 | struct gl_debug_element *copy; |
||
271 | |||
272 | copy = malloc(sizeof(*copy)); |
||
273 | if (!copy) { |
||
274 | debug_namespace_clear(dst); |
||
275 | return false; |
||
276 | } |
||
277 | |||
278 | copy->ID = elem->ID; |
||
279 | copy->State = elem->State; |
||
280 | insert_at_tail(&dst->Elements, ©->link); |
||
281 | } |
||
282 | |||
283 | return true; |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Set the state of \p id in the namespace. |
||
288 | */ |
||
289 | static bool |
||
290 | debug_namespace_set(struct gl_debug_namespace *ns, |
||
291 | GLuint id, bool enabled) |
||
292 | { |
||
293 | const uint32_t state = (enabled) ? |
||
294 | ((1 << MESA_DEBUG_SEVERITY_COUNT) - 1) : 0; |
||
295 | struct gl_debug_element *elem = NULL; |
||
296 | struct simple_node *node; |
||
297 | |||
298 | /* find the element */ |
||
299 | foreach(node, &ns->Elements) { |
||
300 | struct gl_debug_element *tmp = (struct gl_debug_element *) node; |
||
301 | if (tmp->ID == id) { |
||
302 | elem = tmp; |
||
303 | break; |
||
304 | } |
||
305 | } |
||
306 | |||
307 | /* we do not need the element if it has the default state */ |
||
308 | if (ns->DefaultState == state) { |
||
309 | if (elem) { |
||
310 | remove_from_list(&elem->link); |
||
311 | free(elem); |
||
312 | } |
||
313 | return true; |
||
314 | } |
||
315 | |||
316 | if (!elem) { |
||
317 | elem = malloc(sizeof(*elem)); |
||
318 | if (!elem) |
||
319 | return false; |
||
320 | |||
321 | elem->ID = id; |
||
322 | insert_at_tail(&ns->Elements, &elem->link); |
||
323 | } |
||
324 | |||
325 | elem->State = state; |
||
326 | |||
327 | return true; |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Set the default state of the namespace for \p severity. When \p severity |
||
332 | * is MESA_DEBUG_SEVERITY_COUNT, the default values for all severities are |
||
333 | * updated. |
||
334 | */ |
||
335 | static void |
||
336 | debug_namespace_set_all(struct gl_debug_namespace *ns, |
||
337 | enum mesa_debug_severity severity, |
||
338 | bool enabled) |
||
339 | { |
||
340 | struct simple_node *node, *tmp; |
||
341 | uint32_t mask, val; |
||
342 | |||
343 | /* set all elements to the same state */ |
||
344 | if (severity == MESA_DEBUG_SEVERITY_COUNT) { |
||
345 | ns->DefaultState = (enabled) ? ((1 << severity) - 1) : 0; |
||
346 | debug_namespace_clear(ns); |
||
347 | make_empty_list(&ns->Elements); |
||
348 | return; |
||
349 | } |
||
350 | |||
351 | mask = 1 << severity; |
||
352 | val = (enabled) ? mask : 0; |
||
353 | |||
354 | ns->DefaultState = (ns->DefaultState & ~mask) | val; |
||
355 | |||
356 | foreach_s(node, tmp, &ns->Elements) { |
||
357 | struct gl_debug_element *elem = (struct gl_debug_element *) node; |
||
358 | |||
359 | elem->State = (elem->State & ~mask) | val; |
||
360 | if (elem->State == ns->DefaultState) { |
||
361 | remove_from_list(node); |
||
362 | free(node); |
||
363 | } |
||
364 | } |
||
365 | } |
||
366 | |||
367 | /** |
||
368 | * Get the state of \p id in the namespace. |
||
369 | */ |
||
370 | static bool |
||
371 | debug_namespace_get(const struct gl_debug_namespace *ns, GLuint id, |
||
372 | enum mesa_debug_severity severity) |
||
373 | { |
||
374 | struct simple_node *node; |
||
375 | uint32_t state; |
||
376 | |||
377 | state = ns->DefaultState; |
||
378 | foreach(node, &ns->Elements) { |
||
379 | struct gl_debug_element *elem = (struct gl_debug_element *) node; |
||
380 | |||
381 | if (elem->ID == id) { |
||
382 | state = elem->State; |
||
383 | break; |
||
384 | } |
||
385 | } |
||
386 | |||
387 | return (state & (1 << severity)); |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * Allocate and initialize context debug state. |
||
392 | */ |
||
393 | static struct gl_debug_state * |
||
394 | debug_create(void) |
||
395 | { |
||
396 | struct gl_debug_state *debug; |
||
397 | int s, t; |
||
398 | |||
399 | debug = CALLOC_STRUCT(gl_debug_state); |
||
400 | if (!debug) |
||
401 | return NULL; |
||
402 | |||
403 | debug->Groups[0] = malloc(sizeof(*debug->Groups[0])); |
||
404 | if (!debug->Groups[0]) { |
||
405 | free(debug); |
||
406 | return NULL; |
||
407 | } |
||
408 | |||
409 | /* Initialize state for filtering known debug messages. */ |
||
410 | for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) { |
||
411 | for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) |
||
412 | debug_namespace_init(&debug->Groups[0]->Namespaces[s][t]); |
||
413 | } |
||
414 | |||
415 | return debug; |
||
416 | } |
||
417 | |||
418 | /** |
||
419 | * Return true if the top debug group points to the group below it. |
||
420 | */ |
||
421 | static bool |
||
422 | debug_is_group_read_only(const struct gl_debug_state *debug) |
||
423 | { |
||
424 | const GLint gstack = debug->GroupStackDepth; |
||
425 | return (gstack > 0 && debug->Groups[gstack] == debug->Groups[gstack - 1]); |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * Make the top debug group writable. |
||
430 | */ |
||
431 | static bool |
||
432 | debug_make_group_writable(struct gl_debug_state *debug) |
||
433 | { |
||
434 | const GLint gstack = debug->GroupStackDepth; |
||
435 | const struct gl_debug_group *src = debug->Groups[gstack]; |
||
436 | struct gl_debug_group *dst; |
||
437 | int s, t; |
||
438 | |||
439 | if (!debug_is_group_read_only(debug)) |
||
440 | return true; |
||
441 | |||
442 | dst = malloc(sizeof(*dst)); |
||
443 | if (!dst) |
||
444 | return false; |
||
445 | |||
446 | for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) { |
||
447 | for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) { |
||
448 | if (!debug_namespace_copy(&dst->Namespaces[s][t], |
||
449 | &src->Namespaces[s][t])) { |
||
450 | /* error path! */ |
||
451 | for (t = t - 1; t >= 0; t--) |
||
452 | debug_namespace_clear(&dst->Namespaces[s][t]); |
||
453 | for (s = s - 1; s >= 0; s--) { |
||
454 | for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) |
||
455 | debug_namespace_clear(&dst->Namespaces[s][t]); |
||
456 | } |
||
457 | free(dst); |
||
458 | return false; |
||
459 | } |
||
460 | } |
||
461 | } |
||
462 | |||
463 | debug->Groups[gstack] = dst; |
||
464 | |||
465 | return true; |
||
466 | } |
||
467 | |||
468 | /** |
||
469 | * Free the top debug group. |
||
470 | */ |
||
471 | static void |
||
472 | debug_clear_group(struct gl_debug_state *debug) |
||
473 | { |
||
474 | const GLint gstack = debug->GroupStackDepth; |
||
475 | |||
476 | if (!debug_is_group_read_only(debug)) { |
||
477 | struct gl_debug_group *grp = debug->Groups[gstack]; |
||
478 | int s, t; |
||
479 | |||
480 | for (s = 0; s < MESA_DEBUG_SOURCE_COUNT; s++) { |
||
481 | for (t = 0; t < MESA_DEBUG_TYPE_COUNT; t++) |
||
482 | debug_namespace_clear(&grp->Namespaces[s][t]); |
||
483 | } |
||
484 | |||
485 | free(grp); |
||
486 | } |
||
487 | |||
488 | debug->Groups[gstack] = NULL; |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * Loop through debug group stack tearing down states for |
||
493 | * filtering debug messages. Then free debug output state. |
||
494 | */ |
||
495 | static void |
||
496 | debug_destroy(struct gl_debug_state *debug) |
||
497 | { |
||
498 | while (debug->GroupStackDepth > 0) { |
||
499 | debug_clear_group(debug); |
||
500 | debug->GroupStackDepth--; |
||
501 | } |
||
502 | |||
503 | debug_clear_group(debug); |
||
504 | free(debug); |
||
505 | } |
||
506 | |||
507 | /** |
||
508 | * Sets the state of the given message source/type/ID tuple. |
||
509 | */ |
||
510 | static void |
||
511 | debug_set_message_enable(struct gl_debug_state *debug, |
||
512 | enum mesa_debug_source source, |
||
513 | enum mesa_debug_type type, |
||
514 | GLuint id, GLboolean enabled) |
||
515 | { |
||
516 | const GLint gstack = debug->GroupStackDepth; |
||
517 | struct gl_debug_namespace *ns; |
||
518 | |||
519 | debug_make_group_writable(debug); |
||
520 | ns = &debug->Groups[gstack]->Namespaces[source][type]; |
||
521 | |||
522 | debug_namespace_set(ns, id, enabled); |
||
523 | } |
||
524 | |||
525 | /* |
||
526 | * Set the state of all message IDs found in the given intersection of |
||
527 | * 'source', 'type', and 'severity'. The _COUNT enum can be used for |
||
528 | * GL_DONT_CARE (include all messages in the class). |
||
529 | * |
||
530 | * This requires both setting the state of all previously seen message |
||
531 | * IDs in the hash table, and setting the default state for all |
||
532 | * applicable combinations of source/type/severity, so that all the |
||
533 | * yet-unknown message IDs that may be used in the future will be |
||
534 | * impacted as if they were already known. |
||
535 | */ |
||
536 | static void |
||
537 | debug_set_message_enable_all(struct gl_debug_state *debug, |
||
538 | enum mesa_debug_source source, |
||
539 | enum mesa_debug_type type, |
||
540 | enum mesa_debug_severity severity, |
||
541 | GLboolean enabled) |
||
542 | { |
||
543 | const GLint gstack = debug->GroupStackDepth; |
||
544 | int s, t, smax, tmax; |
||
545 | |||
546 | if (source == MESA_DEBUG_SOURCE_COUNT) { |
||
547 | source = 0; |
||
548 | smax = MESA_DEBUG_SOURCE_COUNT; |
||
549 | } else { |
||
550 | smax = source+1; |
||
551 | } |
||
552 | |||
553 | if (type == MESA_DEBUG_TYPE_COUNT) { |
||
554 | type = 0; |
||
555 | tmax = MESA_DEBUG_TYPE_COUNT; |
||
556 | } else { |
||
557 | tmax = type+1; |
||
558 | } |
||
559 | |||
560 | debug_make_group_writable(debug); |
||
561 | |||
562 | for (s = source; s < smax; s++) { |
||
563 | for (t = type; t < tmax; t++) { |
||
564 | struct gl_debug_namespace *nspace = |
||
565 | &debug->Groups[gstack]->Namespaces[s][t]; |
||
566 | debug_namespace_set_all(nspace, severity, enabled); |
||
567 | } |
||
568 | } |
||
569 | } |
||
570 | |||
571 | /** |
||
572 | * Returns if the given message source/type/ID tuple is enabled. |
||
573 | */ |
||
574 | static bool |
||
575 | debug_is_message_enabled(const struct gl_debug_state *debug, |
||
576 | enum mesa_debug_source source, |
||
577 | enum mesa_debug_type type, |
||
578 | GLuint id, |
||
579 | enum mesa_debug_severity severity) |
||
580 | { |
||
581 | const GLint gstack = debug->GroupStackDepth; |
||
582 | struct gl_debug_group *grp = debug->Groups[gstack]; |
||
583 | struct gl_debug_namespace *nspace = &grp->Namespaces[source][type]; |
||
584 | |||
585 | if (!debug->DebugOutput) |
||
586 | return false; |
||
587 | |||
588 | return debug_namespace_get(nspace, id, severity); |
||
589 | } |
||
590 | |||
591 | /** |
||
592 | * 'buf' is not necessarily a null-terminated string. When logging, copy |
||
593 | * 'len' characters from it, store them in a new, null-terminated string, |
||
594 | * and remember the number of bytes used by that string, *including* |
||
595 | * the null terminator this time. |
||
596 | */ |
||
597 | static void |
||
598 | debug_log_message(struct gl_debug_state *debug, |
||
599 | enum mesa_debug_source source, |
||
600 | enum mesa_debug_type type, GLuint id, |
||
601 | enum mesa_debug_severity severity, |
||
602 | GLsizei len, const char *buf) |
||
603 | { |
||
604 | struct gl_debug_log *log = &debug->Log; |
||
605 | GLint nextEmpty; |
||
606 | struct gl_debug_message *emptySlot; |
||
607 | |||
608 | assert(len >= 0 && len < MAX_DEBUG_MESSAGE_LENGTH); |
||
609 | |||
610 | if (log->NumMessages == MAX_DEBUG_LOGGED_MESSAGES) |
||
611 | return; |
||
612 | |||
613 | nextEmpty = (log->NextMessage + log->NumMessages) |
||
614 | % MAX_DEBUG_LOGGED_MESSAGES; |
||
615 | emptySlot = &log->Messages[nextEmpty]; |
||
616 | |||
617 | debug_message_store(emptySlot, source, type, |
||
618 | id, severity, len, buf); |
||
619 | |||
620 | log->NumMessages++; |
||
621 | } |
||
622 | |||
623 | /** |
||
624 | * Return the oldest debug message out of the log. |
||
625 | */ |
||
626 | static const struct gl_debug_message * |
||
627 | debug_fetch_message(const struct gl_debug_state *debug) |
||
628 | { |
||
629 | const struct gl_debug_log *log = &debug->Log; |
||
630 | |||
631 | return (log->NumMessages) ? &log->Messages[log->NextMessage] : NULL; |
||
632 | } |
||
633 | |||
634 | /** |
||
635 | * Delete the oldest debug messages out of the log. |
||
636 | */ |
||
637 | static void |
||
638 | debug_delete_messages(struct gl_debug_state *debug, int count) |
||
639 | { |
||
640 | struct gl_debug_log *log = &debug->Log; |
||
641 | |||
642 | if (count > log->NumMessages) |
||
643 | count = log->NumMessages; |
||
644 | |||
645 | while (count--) { |
||
646 | struct gl_debug_message *msg = &log->Messages[log->NextMessage]; |
||
647 | |||
648 | debug_message_clear(msg); |
||
649 | |||
650 | log->NumMessages--; |
||
651 | log->NextMessage++; |
||
652 | log->NextMessage %= MAX_DEBUG_LOGGED_MESSAGES; |
||
653 | } |
||
654 | } |
||
655 | |||
656 | static struct gl_debug_message * |
||
657 | debug_get_group_message(struct gl_debug_state *debug) |
||
658 | { |
||
659 | return &debug->GroupMessages[debug->GroupStackDepth]; |
||
660 | } |
||
661 | |||
662 | static void |
||
663 | debug_push_group(struct gl_debug_state *debug) |
||
664 | { |
||
665 | const GLint gstack = debug->GroupStackDepth; |
||
666 | |||
667 | /* just point to the previous stack */ |
||
668 | debug->Groups[gstack + 1] = debug->Groups[gstack]; |
||
669 | debug->GroupStackDepth++; |
||
670 | } |
||
671 | |||
672 | static void |
||
673 | debug_pop_group(struct gl_debug_state *debug) |
||
674 | { |
||
675 | debug_clear_group(debug); |
||
676 | debug->GroupStackDepth--; |
||
677 | } |
||
678 | |||
679 | |||
680 | /** |
||
681 | * Lock and return debug state for the context. The debug state will be |
||
682 | * allocated and initialized upon the first call. When NULL is returned, the |
||
683 | * debug state is not locked. |
||
684 | */ |
||
685 | static struct gl_debug_state * |
||
686 | _mesa_lock_debug_state(struct gl_context *ctx) |
||
687 | { |
||
688 | mtx_lock(&ctx->DebugMutex); |
||
689 | |||
690 | if (!ctx->Debug) { |
||
691 | ctx->Debug = debug_create(); |
||
692 | if (!ctx->Debug) { |
||
693 | GET_CURRENT_CONTEXT(cur); |
||
694 | mtx_unlock(&ctx->DebugMutex); |
||
695 | |||
696 | /* |
||
697 | * This function may be called from other threads. When that is the |
||
698 | * case, we cannot record this OOM error. |
||
699 | */ |
||
700 | if (ctx == cur) |
||
701 | _mesa_error(ctx, GL_OUT_OF_MEMORY, "allocating debug state"); |
||
702 | |||
703 | return NULL; |
||
704 | } |
||
705 | } |
||
706 | |||
707 | return ctx->Debug; |
||
708 | } |
||
709 | |||
710 | static void |
||
711 | _mesa_unlock_debug_state(struct gl_context *ctx) |
||
712 | { |
||
713 | mtx_unlock(&ctx->DebugMutex); |
||
714 | } |
||
715 | |||
716 | /** |
||
717 | * Set the integer debug state specified by \p pname. This can be called from |
||
718 | * _mesa_set_enable for example. |
||
719 | */ |
||
720 | bool |
||
721 | _mesa_set_debug_state_int(struct gl_context *ctx, GLenum pname, GLint val) |
||
722 | { |
||
723 | struct gl_debug_state *debug = _mesa_lock_debug_state(ctx); |
||
724 | |||
725 | if (!debug) |
||
726 | return false; |
||
727 | |||
728 | switch (pname) { |
||
729 | case GL_DEBUG_OUTPUT: |
||
730 | debug->DebugOutput = (val != 0); |
||
731 | break; |
||
732 | case GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB: |
||
733 | debug->SyncOutput = (val != 0); |
||
734 | break; |
||
735 | default: |
||
736 | assert(!"unknown debug output param"); |
||
737 | break; |
||
738 | } |
||
739 | |||
740 | _mesa_unlock_debug_state(ctx); |
||
741 | |||
742 | return true; |
||
743 | } |
||
744 | |||
745 | /** |
||
746 | * Query the integer debug state specified by \p pname. This can be called |
||
747 | * _mesa_GetIntegerv for example. |
||
748 | */ |
||
749 | GLint |
||
750 | _mesa_get_debug_state_int(struct gl_context *ctx, GLenum pname) |
||
751 | { |
||
752 | struct gl_debug_state *debug; |
||
753 | GLint val; |
||
754 | |||
755 | mtx_lock(&ctx->DebugMutex); |
||
756 | debug = ctx->Debug; |
||
757 | if (!debug) { |
||
758 | mtx_unlock(&ctx->DebugMutex); |
||
759 | return 0; |
||
760 | } |
||
761 | |||
762 | switch (pname) { |
||
763 | case GL_DEBUG_OUTPUT: |
||
764 | val = debug->DebugOutput; |
||
765 | break; |
||
766 | case GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB: |
||
767 | val = debug->SyncOutput; |
||
768 | break; |
||
769 | case GL_DEBUG_LOGGED_MESSAGES: |
||
770 | val = debug->Log.NumMessages; |
||
771 | break; |
||
772 | case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH: |
||
773 | val = (debug->Log.NumMessages) ? |
||
774 | debug->Log.Messages[debug->Log.NextMessage].length : 0; |
||
775 | break; |
||
776 | case GL_DEBUG_GROUP_STACK_DEPTH: |
||
777 | val = debug->GroupStackDepth; |
||
778 | break; |
||
779 | default: |
||
780 | assert(!"unknown debug output param"); |
||
781 | val = 0; |
||
782 | break; |
||
783 | } |
||
784 | |||
785 | mtx_unlock(&ctx->DebugMutex); |
||
786 | |||
787 | return val; |
||
788 | } |
||
789 | |||
790 | /** |
||
791 | * Query the pointer debug state specified by \p pname. This can be called |
||
792 | * _mesa_GetPointerv for example. |
||
793 | */ |
||
794 | void * |
||
795 | _mesa_get_debug_state_ptr(struct gl_context *ctx, GLenum pname) |
||
796 | { |
||
797 | struct gl_debug_state *debug; |
||
798 | void *val; |
||
799 | |||
800 | mtx_lock(&ctx->DebugMutex); |
||
801 | debug = ctx->Debug; |
||
802 | if (!debug) { |
||
803 | mtx_unlock(&ctx->DebugMutex); |
||
804 | return NULL; |
||
805 | } |
||
806 | |||
807 | switch (pname) { |
||
808 | case GL_DEBUG_CALLBACK_FUNCTION_ARB: |
||
809 | val = (void *) debug->Callback; |
||
810 | break; |
||
811 | case GL_DEBUG_CALLBACK_USER_PARAM_ARB: |
||
812 | val = (void *) debug->CallbackData; |
||
813 | break; |
||
814 | default: |
||
815 | assert(!"unknown debug output param"); |
||
816 | val = NULL; |
||
817 | break; |
||
818 | } |
||
819 | |||
820 | mtx_unlock(&ctx->DebugMutex); |
||
821 | |||
822 | return val; |
||
823 | } |
||
824 | |||
825 | /** |
||
826 | * Insert a debug message. The mutex is assumed to be locked, and will be |
||
827 | * unlocked by this call. |
||
828 | */ |
||
829 | static void |
||
830 | log_msg_locked_and_unlock(struct gl_context *ctx, |
||
831 | enum mesa_debug_source source, |
||
832 | enum mesa_debug_type type, GLuint id, |
||
833 | enum mesa_debug_severity severity, |
||
834 | GLint len, const char *buf) |
||
835 | { |
||
836 | struct gl_debug_state *debug = ctx->Debug; |
||
837 | |||
838 | if (!debug_is_message_enabled(debug, source, type, id, severity)) { |
||
839 | _mesa_unlock_debug_state(ctx); |
||
840 | return; |
||
841 | } |
||
842 | |||
843 | if (ctx->Debug->Callback) { |
||
844 | GLenum gl_source = debug_source_enums[source]; |
||
845 | GLenum gl_type = debug_type_enums[type]; |
||
846 | GLenum gl_severity = debug_severity_enums[severity]; |
||
847 | GLDEBUGPROC callback = ctx->Debug->Callback; |
||
848 | const void *data = ctx->Debug->CallbackData; |
||
849 | |||
850 | /* |
||
851 | * When ctx->Debug->SyncOutput is GL_FALSE, the client is prepared for |
||
852 | * unsynchronous calls. When it is GL_TRUE, we will not spawn threads. |
||
853 | * In either case, we can call the callback unlocked. |
||
854 | */ |
||
855 | _mesa_unlock_debug_state(ctx); |
||
856 | callback(gl_source, gl_type, id, gl_severity, len, buf, data); |
||
857 | } |
||
858 | else { |
||
859 | debug_log_message(ctx->Debug, source, type, id, severity, len, buf); |
||
860 | _mesa_unlock_debug_state(ctx); |
||
861 | } |
||
862 | } |
||
863 | |||
864 | /** |
||
865 | * Log a client or driver debug message. |
||
866 | */ |
||
867 | static void |
||
868 | log_msg(struct gl_context *ctx, enum mesa_debug_source source, |
||
869 | enum mesa_debug_type type, GLuint id, |
||
870 | enum mesa_debug_severity severity, GLint len, const char *buf) |
||
871 | { |
||
872 | struct gl_debug_state *debug = _mesa_lock_debug_state(ctx); |
||
873 | |||
874 | if (!debug) |
||
875 | return; |
||
876 | |||
877 | log_msg_locked_and_unlock(ctx, source, type, id, severity, len, buf); |
||
878 | } |
||
879 | |||
880 | |||
881 | /** |
||
882 | * Verify that source, type, and severity are valid enums. |
||
883 | * |
||
884 | * The 'caller' param is used for handling values available |
||
885 | * only in glDebugMessageInsert or glDebugMessageControl |
||
886 | */ |
||
887 | static GLboolean |
||
888 | validate_params(struct gl_context *ctx, unsigned caller, |
||
889 | const char *callerstr, GLenum source, GLenum type, |
||
890 | GLenum severity) |
||
891 | { |
||
892 | #define INSERT 1 |
||
893 | #define CONTROL 2 |
||
894 | switch(source) { |
||
895 | case GL_DEBUG_SOURCE_APPLICATION_ARB: |
||
896 | case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: |
||
897 | break; |
||
898 | case GL_DEBUG_SOURCE_API_ARB: |
||
899 | case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: |
||
900 | case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: |
||
901 | case GL_DEBUG_SOURCE_OTHER_ARB: |
||
902 | if (caller != INSERT) |
||
903 | break; |
||
904 | else |
||
905 | goto error; |
||
906 | case GL_DONT_CARE: |
||
907 | if (caller == CONTROL) |
||
908 | break; |
||
909 | else |
||
910 | goto error; |
||
911 | default: |
||
912 | goto error; |
||
913 | } |
||
914 | |||
915 | switch(type) { |
||
916 | case GL_DEBUG_TYPE_ERROR_ARB: |
||
917 | case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: |
||
918 | case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: |
||
919 | case GL_DEBUG_TYPE_PERFORMANCE_ARB: |
||
920 | case GL_DEBUG_TYPE_PORTABILITY_ARB: |
||
921 | case GL_DEBUG_TYPE_OTHER_ARB: |
||
922 | case GL_DEBUG_TYPE_MARKER: |
||
923 | break; |
||
924 | case GL_DEBUG_TYPE_PUSH_GROUP: |
||
925 | case GL_DEBUG_TYPE_POP_GROUP: |
||
926 | case GL_DONT_CARE: |
||
927 | if (caller == CONTROL) |
||
928 | break; |
||
929 | else |
||
930 | goto error; |
||
931 | default: |
||
932 | goto error; |
||
933 | } |
||
934 | |||
935 | switch(severity) { |
||
936 | case GL_DEBUG_SEVERITY_HIGH_ARB: |
||
937 | case GL_DEBUG_SEVERITY_MEDIUM_ARB: |
||
938 | case GL_DEBUG_SEVERITY_LOW_ARB: |
||
939 | case GL_DEBUG_SEVERITY_NOTIFICATION: |
||
940 | break; |
||
941 | case GL_DONT_CARE: |
||
942 | if (caller == CONTROL) |
||
943 | break; |
||
944 | else |
||
945 | goto error; |
||
946 | default: |
||
947 | goto error; |
||
948 | } |
||
949 | return GL_TRUE; |
||
950 | |||
951 | error: |
||
952 | _mesa_error(ctx, GL_INVALID_ENUM, "bad values passed to %s" |
||
953 | "(source=0x%x, type=0x%x, severity=0x%x)", callerstr, |
||
954 | source, type, severity); |
||
955 | |||
956 | return GL_FALSE; |
||
957 | } |
||
958 | |||
959 | |||
960 | static GLboolean |
||
961 | validate_length(struct gl_context *ctx, const char *callerstr, GLsizei length) |
||
962 | { |
||
963 | if (length >= MAX_DEBUG_MESSAGE_LENGTH) { |
||
964 | _mesa_error(ctx, GL_INVALID_VALUE, |
||
965 | "%s(length=%d, which is not less than " |
||
966 | "GL_MAX_DEBUG_MESSAGE_LENGTH=%d)", callerstr, length, |
||
967 | MAX_DEBUG_MESSAGE_LENGTH); |
||
968 | return GL_FALSE; |
||
969 | } |
||
970 | |||
971 | return GL_TRUE; |
||
972 | } |
||
973 | |||
974 | |||
975 | void GLAPIENTRY |
||
976 | _mesa_DebugMessageInsert(GLenum source, GLenum type, GLuint id, |
||
977 | GLenum severity, GLint length, |
||
978 | const GLchar *buf) |
||
979 | { |
||
980 | const char *callerstr = "glDebugMessageInsert"; |
||
981 | |||
982 | GET_CURRENT_CONTEXT(ctx); |
||
983 | |||
984 | if (!validate_params(ctx, INSERT, callerstr, source, type, severity)) |
||
985 | return; /* GL_INVALID_ENUM */ |
||
986 | |||
987 | if (length < 0) |
||
988 | length = strlen(buf); |
||
989 | if (!validate_length(ctx, callerstr, length)) |
||
990 | return; /* GL_INVALID_VALUE */ |
||
991 | |||
992 | log_msg(ctx, gl_enum_to_debug_source(source), |
||
993 | gl_enum_to_debug_type(type), id, |
||
994 | gl_enum_to_debug_severity(severity), |
||
995 | length, buf); |
||
996 | } |
||
997 | |||
998 | |||
999 | GLuint GLAPIENTRY |
||
1000 | _mesa_GetDebugMessageLog(GLuint count, GLsizei logSize, GLenum *sources, |
||
1001 | GLenum *types, GLenum *ids, GLenum *severities, |
||
1002 | GLsizei *lengths, GLchar *messageLog) |
||
1003 | { |
||
1004 | GET_CURRENT_CONTEXT(ctx); |
||
1005 | struct gl_debug_state *debug; |
||
1006 | GLuint ret; |
||
1007 | |||
1008 | if (!messageLog) |
||
1009 | logSize = 0; |
||
1010 | |||
1011 | if (logSize < 0) { |
||
1012 | _mesa_error(ctx, GL_INVALID_VALUE, |
||
1013 | "glGetDebugMessageLog(logSize=%d : logSize must not be" |
||
1014 | " negative)", logSize); |
||
1015 | return 0; |
||
1016 | } |
||
1017 | |||
1018 | debug = _mesa_lock_debug_state(ctx); |
||
1019 | if (!debug) |
||
1020 | return 0; |
||
1021 | |||
1022 | for (ret = 0; ret < count; ret++) { |
||
1023 | const struct gl_debug_message *msg = debug_fetch_message(debug); |
||
1024 | |||
1025 | if (!msg) |
||
1026 | break; |
||
1027 | |||
1028 | if (logSize < msg->length && messageLog != NULL) |
||
1029 | break; |
||
1030 | |||
1031 | if (messageLog) { |
||
1032 | assert(msg->message[msg->length-1] == '\0'); |
||
1033 | (void) strncpy(messageLog, msg->message, (size_t)msg->length); |
||
1034 | |||
1035 | messageLog += msg->length; |
||
1036 | logSize -= msg->length; |
||
1037 | } |
||
1038 | |||
1039 | if (lengths) |
||
1040 | *lengths++ = msg->length; |
||
1041 | if (severities) |
||
1042 | *severities++ = debug_severity_enums[msg->severity]; |
||
1043 | if (sources) |
||
1044 | *sources++ = debug_source_enums[msg->source]; |
||
1045 | if (types) |
||
1046 | *types++ = debug_type_enums[msg->type]; |
||
1047 | if (ids) |
||
1048 | *ids++ = msg->id; |
||
1049 | |||
1050 | debug_delete_messages(debug, 1); |
||
1051 | } |
||
1052 | |||
1053 | _mesa_unlock_debug_state(ctx); |
||
1054 | |||
1055 | return ret; |
||
1056 | } |
||
1057 | |||
1058 | |||
1059 | void GLAPIENTRY |
||
1060 | _mesa_DebugMessageControl(GLenum gl_source, GLenum gl_type, |
||
1061 | GLenum gl_severity, GLsizei count, |
||
1062 | const GLuint *ids, GLboolean enabled) |
||
1063 | { |
||
1064 | GET_CURRENT_CONTEXT(ctx); |
||
1065 | enum mesa_debug_source source = gl_enum_to_debug_source(gl_source); |
||
1066 | enum mesa_debug_type type = gl_enum_to_debug_type(gl_type); |
||
1067 | enum mesa_debug_severity severity = gl_enum_to_debug_severity(gl_severity); |
||
1068 | const char *callerstr = "glDebugMessageControl"; |
||
1069 | struct gl_debug_state *debug; |
||
1070 | |||
1071 | if (count < 0) { |
||
1072 | _mesa_error(ctx, GL_INVALID_VALUE, |
||
1073 | "%s(count=%d : count must not be negative)", callerstr, |
||
1074 | count); |
||
1075 | return; |
||
1076 | } |
||
1077 | |||
1078 | if (!validate_params(ctx, CONTROL, callerstr, gl_source, gl_type, |
||
1079 | gl_severity)) |
||
1080 | return; /* GL_INVALID_ENUM */ |
||
1081 | |||
1082 | if (count && (gl_severity != GL_DONT_CARE || gl_type == GL_DONT_CARE |
||
1083 | || gl_source == GL_DONT_CARE)) { |
||
1084 | _mesa_error(ctx, GL_INVALID_OPERATION, |
||
1085 | "%s(When passing an array of ids, severity must be" |
||
1086 | " GL_DONT_CARE, and source and type must not be GL_DONT_CARE.", |
||
1087 | callerstr); |
||
1088 | return; |
||
1089 | } |
||
1090 | |||
1091 | debug = _mesa_lock_debug_state(ctx); |
||
1092 | if (!debug) |
||
1093 | return; |
||
1094 | |||
1095 | if (count) { |
||
1096 | GLsizei i; |
||
1097 | for (i = 0; i < count; i++) |
||
1098 | debug_set_message_enable(debug, source, type, ids[i], enabled); |
||
1099 | } |
||
1100 | else { |
||
1101 | debug_set_message_enable_all(debug, source, type, severity, enabled); |
||
1102 | } |
||
1103 | |||
1104 | _mesa_unlock_debug_state(ctx); |
||
1105 | } |
||
1106 | |||
1107 | |||
1108 | void GLAPIENTRY |
||
1109 | _mesa_DebugMessageCallback(GLDEBUGPROC callback, const void *userParam) |
||
1110 | { |
||
1111 | GET_CURRENT_CONTEXT(ctx); |
||
1112 | struct gl_debug_state *debug = _mesa_lock_debug_state(ctx); |
||
1113 | if (debug) { |
||
1114 | debug->Callback = callback; |
||
1115 | debug->CallbackData = userParam; |
||
1116 | _mesa_unlock_debug_state(ctx); |
||
1117 | } |
||
1118 | } |
||
1119 | |||
1120 | |||
1121 | void GLAPIENTRY |
||
1122 | _mesa_PushDebugGroup(GLenum source, GLuint id, GLsizei length, |
||
1123 | const GLchar *message) |
||
1124 | { |
||
1125 | GET_CURRENT_CONTEXT(ctx); |
||
1126 | const char *callerstr = "glPushDebugGroup"; |
||
1127 | struct gl_debug_state *debug; |
||
1128 | struct gl_debug_message *emptySlot; |
||
1129 | |||
1130 | switch(source) { |
||
1131 | case GL_DEBUG_SOURCE_APPLICATION: |
||
1132 | case GL_DEBUG_SOURCE_THIRD_PARTY: |
||
1133 | break; |
||
1134 | default: |
||
1135 | _mesa_error(ctx, GL_INVALID_ENUM, "bad value passed to %s" |
||
1136 | "(source=0x%x)", callerstr, source); |
||
1137 | return; |
||
1138 | } |
||
1139 | |||
1140 | if (length < 0) |
||
1141 | length = strlen(message); |
||
1142 | if (!validate_length(ctx, callerstr, length)) |
||
1143 | return; /* GL_INVALID_VALUE */ |
||
1144 | |||
1145 | debug = _mesa_lock_debug_state(ctx); |
||
1146 | if (!debug) |
||
1147 | return; |
||
1148 | |||
1149 | if (debug->GroupStackDepth >= MAX_DEBUG_GROUP_STACK_DEPTH-1) { |
||
1150 | _mesa_unlock_debug_state(ctx); |
||
1151 | _mesa_error(ctx, GL_STACK_OVERFLOW, "%s", callerstr); |
||
1152 | return; |
||
1153 | } |
||
1154 | |||
1155 | /* pop reuses the message details from push so we store this */ |
||
1156 | emptySlot = debug_get_group_message(debug); |
||
1157 | debug_message_store(emptySlot, |
||
1158 | gl_enum_to_debug_source(source), |
||
1159 | gl_enum_to_debug_type(GL_DEBUG_TYPE_PUSH_GROUP), |
||
1160 | id, |
||
1161 | gl_enum_to_debug_severity(GL_DEBUG_SEVERITY_NOTIFICATION), |
||
1162 | length, message); |
||
1163 | |||
1164 | debug_push_group(debug); |
||
1165 | |||
1166 | log_msg_locked_and_unlock(ctx, |
||
1167 | gl_enum_to_debug_source(source), |
||
1168 | MESA_DEBUG_TYPE_PUSH_GROUP, id, |
||
1169 | MESA_DEBUG_SEVERITY_NOTIFICATION, length, |
||
1170 | message); |
||
1171 | } |
||
1172 | |||
1173 | |||
1174 | void GLAPIENTRY |
||
1175 | _mesa_PopDebugGroup(void) |
||
1176 | { |
||
1177 | GET_CURRENT_CONTEXT(ctx); |
||
1178 | const char *callerstr = "glPopDebugGroup"; |
||
1179 | struct gl_debug_state *debug; |
||
1180 | struct gl_debug_message *gdmessage, msg; |
||
1181 | |||
1182 | debug = _mesa_lock_debug_state(ctx); |
||
1183 | if (!debug) |
||
1184 | return; |
||
1185 | |||
1186 | if (debug->GroupStackDepth <= 0) { |
||
1187 | _mesa_unlock_debug_state(ctx); |
||
1188 | _mesa_error(ctx, GL_STACK_UNDERFLOW, "%s", callerstr); |
||
1189 | return; |
||
1190 | } |
||
1191 | |||
1192 | debug_pop_group(debug); |
||
1193 | |||
1194 | /* make a shallow copy */ |
||
1195 | gdmessage = debug_get_group_message(debug); |
||
1196 | msg = *gdmessage; |
||
1197 | gdmessage->message = NULL; |
||
1198 | gdmessage->length = 0; |
||
1199 | |||
1200 | log_msg_locked_and_unlock(ctx, |
||
1201 | msg.source, |
||
1202 | gl_enum_to_debug_type(GL_DEBUG_TYPE_POP_GROUP), |
||
1203 | msg.id, |
||
1204 | gl_enum_to_debug_severity(GL_DEBUG_SEVERITY_NOTIFICATION), |
||
1205 | msg.length, msg.message); |
||
1206 | |||
1207 | debug_message_clear(&msg); |
||
1208 | } |
||
1209 | |||
1210 | |||
1211 | void |
||
1212 | _mesa_init_errors(struct gl_context *ctx) |
||
1213 | { |
||
1214 | mtx_init(&ctx->DebugMutex, mtx_plain); |
||
1215 | } |
||
1216 | |||
1217 | |||
1218 | void |
||
1219 | _mesa_free_errors_data(struct gl_context *ctx) |
||
1220 | { |
||
1221 | if (ctx->Debug) { |
||
1222 | debug_destroy(ctx->Debug); |
||
1223 | /* set to NULL just in case it is used before context is completely gone. */ |
||
1224 | ctx->Debug = NULL; |
||
1225 | } |
||
1226 | |||
1227 | mtx_destroy(&ctx->DebugMutex); |
||
1228 | } |
||
1229 | |||
1230 | |||
1231 | /**********************************************************************/ |
||
1232 | /** \name Diagnostics */ |
||
1233 | /*@{*/ |
||
1234 | |||
1235 | static FILE *LogFile = NULL; |
||
1236 | |||
1237 | |||
1238 | static void |
||
1239 | output_if_debug(const char *prefixString, const char *outputString, |
||
1240 | GLboolean newline) |
||
1241 | { |
||
1242 | static int debug = -1; |
||
1243 | |||
1244 | /* Init the local 'debug' var once. |
||
1245 | * Note: the _mesa_init_debug() function should have been called |
||
1246 | * by now so MESA_DEBUG_FLAGS will be initialized. |
||
1247 | */ |
||
1248 | if (debug == -1) { |
||
1249 | /* If MESA_LOG_FILE env var is set, log Mesa errors, warnings, |
||
1250 | * etc to the named file. Otherwise, output to stderr. |
||
1251 | */ |
||
1252 | const char *logFile = getenv("MESA_LOG_FILE"); |
||
1253 | if (logFile) |
||
1254 | LogFile = fopen(logFile, "w"); |
||
1255 | if (!LogFile) |
||
1256 | LogFile = stderr; |
||
1257 | #ifdef DEBUG |
||
1258 | /* in debug builds, print messages unless MESA_DEBUG="silent" */ |
||
1259 | if (MESA_DEBUG_FLAGS & DEBUG_SILENT) |
||
1260 | debug = 0; |
||
1261 | else |
||
1262 | debug = 1; |
||
1263 | #else |
||
1264 | /* in release builds, be silent unless MESA_DEBUG is set */ |
||
1265 | debug = getenv("MESA_DEBUG") != NULL; |
||
1266 | #endif |
||
1267 | } |
||
1268 | |||
1269 | /* Now only print the string if we're required to do so. */ |
||
1270 | if (debug) { |
||
1271 | if (prefixString) |
||
1272 | fprintf(LogFile, "%s: %s", prefixString, outputString); |
||
1273 | else |
||
1274 | fprintf(LogFile, "%s", outputString); |
||
1275 | if (newline) |
||
1276 | fprintf(LogFile, "\n"); |
||
1277 | fflush(LogFile); |
||
1278 | |||
1279 | #if defined(_WIN32) |
||
1280 | /* stderr from windows applications without console is not usually |
||
1281 | * visible, so communicate with the debugger instead */ |
||
1282 | { |
||
1283 | char buf[4096]; |
||
1284 | _mesa_snprintf(buf, sizeof(buf), "%s: %s%s", prefixString, outputString, newline ? "\n" : ""); |
||
1285 | OutputDebugStringA(buf); |
||
1286 | } |
||
1287 | #endif |
||
1288 | } |
||
1289 | } |
||
1290 | |||
1291 | |||
1292 | /** |
||
1293 | * Return the file handle to use for debug/logging. Defaults to stderr |
||
1294 | * unless MESA_LOG_FILE is defined. |
||
1295 | */ |
||
1296 | FILE * |
||
1297 | _mesa_get_log_file(void) |
||
1298 | { |
||
1299 | assert(LogFile); |
||
1300 | return LogFile; |
||
1301 | } |
||
1302 | |||
1303 | |||
1304 | /** |
||
1305 | * When a new type of error is recorded, print a message describing |
||
1306 | * previous errors which were accumulated. |
||
1307 | */ |
||
1308 | static void |
||
1309 | flush_delayed_errors( struct gl_context *ctx ) |
||
1310 | { |
||
1311 | char s[MAX_DEBUG_MESSAGE_LENGTH]; |
||
1312 | |||
1313 | if (ctx->ErrorDebugCount) { |
||
1314 | _mesa_snprintf(s, MAX_DEBUG_MESSAGE_LENGTH, "%d similar %s errors", |
||
1315 | ctx->ErrorDebugCount, |
||
1316 | _mesa_lookup_enum_by_nr(ctx->ErrorValue)); |
||
1317 | |||
1318 | output_if_debug("Mesa", s, GL_TRUE); |
||
1319 | |||
1320 | ctx->ErrorDebugCount = 0; |
||
1321 | } |
||
1322 | } |
||
1323 | |||
1324 | |||
1325 | /** |
||
1326 | * Report a warning (a recoverable error condition) to stderr if |
||
1327 | * either DEBUG is defined or the MESA_DEBUG env var is set. |
||
1328 | * |
||
1329 | * \param ctx GL context. |
||
1330 | * \param fmtString printf()-like format string. |
||
1331 | */ |
||
1332 | void |
||
1333 | _mesa_warning( struct gl_context *ctx, const char *fmtString, ... ) |
||
1334 | { |
||
1335 | char str[MAX_DEBUG_MESSAGE_LENGTH]; |
||
1336 | va_list args; |
||
1337 | va_start( args, fmtString ); |
||
1338 | (void) _mesa_vsnprintf( str, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args ); |
||
1339 | va_end( args ); |
||
1340 | |||
1341 | if (ctx) |
||
1342 | flush_delayed_errors( ctx ); |
||
1343 | |||
1344 | output_if_debug("Mesa warning", str, GL_TRUE); |
||
1345 | } |
||
1346 | |||
1347 | |||
1348 | /** |
||
1349 | * Report an internal implementation problem. |
||
1350 | * Prints the message to stderr via fprintf(). |
||
1351 | * |
||
1352 | * \param ctx GL context. |
||
1353 | * \param fmtString problem description string. |
||
1354 | */ |
||
1355 | void |
||
1356 | _mesa_problem( const struct gl_context *ctx, const char *fmtString, ... ) |
||
1357 | { |
||
1358 | va_list args; |
||
1359 | char str[MAX_DEBUG_MESSAGE_LENGTH]; |
||
1360 | static int numCalls = 0; |
||
1361 | |||
1362 | (void) ctx; |
||
1363 | |||
1364 | if (numCalls < 50) { |
||
1365 | numCalls++; |
||
1366 | |||
1367 | va_start( args, fmtString ); |
||
1368 | _mesa_vsnprintf( str, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args ); |
||
1369 | va_end( args ); |
||
1370 | fprintf(stderr, "Mesa %s implementation error: %s\n", |
||
1371 | PACKAGE_VERSION, str); |
||
1372 | fprintf(stderr, "Please report at " PACKAGE_BUGREPORT "\n"); |
||
1373 | } |
||
1374 | } |
||
1375 | |||
1376 | |||
1377 | static GLboolean |
||
1378 | should_output(struct gl_context *ctx, GLenum error, const char *fmtString) |
||
1379 | { |
||
1380 | static GLint debug = -1; |
||
1381 | |||
1382 | /* Check debug environment variable only once: |
||
1383 | */ |
||
1384 | if (debug == -1) { |
||
1385 | const char *debugEnv = getenv("MESA_DEBUG"); |
||
1386 | |||
1387 | #ifdef DEBUG |
||
1388 | if (debugEnv && strstr(debugEnv, "silent")) |
||
1389 | debug = GL_FALSE; |
||
1390 | else |
||
1391 | debug = GL_TRUE; |
||
1392 | #else |
||
1393 | if (debugEnv) |
||
1394 | debug = GL_TRUE; |
||
1395 | else |
||
1396 | debug = GL_FALSE; |
||
1397 | #endif |
||
1398 | } |
||
1399 | |||
1400 | if (debug) { |
||
1401 | if (ctx->ErrorValue != error || |
||
1402 | ctx->ErrorDebugFmtString != fmtString) { |
||
1403 | flush_delayed_errors( ctx ); |
||
1404 | ctx->ErrorDebugFmtString = fmtString; |
||
1405 | ctx->ErrorDebugCount = 0; |
||
1406 | return GL_TRUE; |
||
1407 | } |
||
1408 | ctx->ErrorDebugCount++; |
||
1409 | } |
||
1410 | return GL_FALSE; |
||
1411 | } |
||
1412 | |||
1413 | |||
1414 | void |
||
1415 | _mesa_gl_debug(struct gl_context *ctx, |
||
1416 | GLuint *id, |
||
1417 | enum mesa_debug_source source, |
||
1418 | enum mesa_debug_type type, |
||
1419 | enum mesa_debug_severity severity, |
||
1420 | const char *fmtString, ...) |
||
1421 | { |
||
1422 | char s[MAX_DEBUG_MESSAGE_LENGTH]; |
||
1423 | int len; |
||
1424 | va_list args; |
||
1425 | |||
1426 | debug_get_id(id); |
||
1427 | |||
1428 | va_start(args, fmtString); |
||
1429 | len = _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); |
||
1430 | va_end(args); |
||
1431 | |||
1432 | log_msg(ctx, source, type, *id, severity, len, s); |
||
1433 | } |
||
1434 | |||
1435 | |||
1436 | /** |
||
1437 | * Record an OpenGL state error. These usually occur when the user |
||
1438 | * passes invalid parameters to a GL function. |
||
1439 | * |
||
1440 | * If debugging is enabled (either at compile-time via the DEBUG macro, or |
||
1441 | * run-time via the MESA_DEBUG environment variable), report the error with |
||
1442 | * _mesa_debug(). |
||
1443 | * |
||
1444 | * \param ctx the GL context. |
||
1445 | * \param error the error value. |
||
1446 | * \param fmtString printf() style format string, followed by optional args |
||
1447 | */ |
||
1448 | void |
||
1449 | _mesa_error( struct gl_context *ctx, GLenum error, const char *fmtString, ... ) |
||
1450 | { |
||
1451 | GLboolean do_output, do_log; |
||
1452 | /* Ideally this would be set up by the caller, so that we had proper IDs |
||
1453 | * per different message. |
||
1454 | */ |
||
1455 | static GLuint error_msg_id = 0; |
||
1456 | |||
1457 | debug_get_id(&error_msg_id); |
||
1458 | |||
1459 | do_output = should_output(ctx, error, fmtString); |
||
1460 | |||
1461 | mtx_lock(&ctx->DebugMutex); |
||
1462 | if (ctx->Debug) { |
||
1463 | do_log = debug_is_message_enabled(ctx->Debug, |
||
1464 | MESA_DEBUG_SOURCE_API, |
||
1465 | MESA_DEBUG_TYPE_ERROR, |
||
1466 | error_msg_id, |
||
1467 | MESA_DEBUG_SEVERITY_HIGH); |
||
1468 | } |
||
1469 | else { |
||
1470 | do_log = GL_FALSE; |
||
1471 | } |
||
1472 | mtx_unlock(&ctx->DebugMutex); |
||
1473 | |||
1474 | if (do_output || do_log) { |
||
1475 | char s[MAX_DEBUG_MESSAGE_LENGTH], s2[MAX_DEBUG_MESSAGE_LENGTH]; |
||
1476 | int len; |
||
1477 | va_list args; |
||
1478 | |||
1479 | va_start(args, fmtString); |
||
1480 | len = _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); |
||
1481 | va_end(args); |
||
1482 | |||
1483 | if (len >= MAX_DEBUG_MESSAGE_LENGTH) { |
||
1484 | /* Too long error message. Whoever calls _mesa_error should use |
||
1485 | * shorter strings. |
||
1486 | */ |
||
1487 | assert(0); |
||
1488 | return; |
||
1489 | } |
||
1490 | |||
1491 | len = _mesa_snprintf(s2, MAX_DEBUG_MESSAGE_LENGTH, "%s in %s", |
||
1492 | _mesa_lookup_enum_by_nr(error), s); |
||
1493 | if (len >= MAX_DEBUG_MESSAGE_LENGTH) { |
||
1494 | /* Same as above. */ |
||
1495 | assert(0); |
||
1496 | return; |
||
1497 | } |
||
1498 | |||
1499 | /* Print the error to stderr if needed. */ |
||
1500 | if (do_output) { |
||
1501 | output_if_debug("Mesa: User error", s2, GL_TRUE); |
||
1502 | } |
||
1503 | |||
1504 | /* Log the error via ARB_debug_output if needed.*/ |
||
1505 | if (do_log) { |
||
1506 | log_msg(ctx, MESA_DEBUG_SOURCE_API, MESA_DEBUG_TYPE_ERROR, |
||
1507 | error_msg_id, MESA_DEBUG_SEVERITY_HIGH, len, s2); |
||
1508 | } |
||
1509 | } |
||
1510 | |||
1511 | /* Set the GL context error state for glGetError. */ |
||
1512 | _mesa_record_error(ctx, error); |
||
1513 | } |
||
1514 | |||
1515 | void |
||
1516 | _mesa_error_no_memory(const char *caller) |
||
1517 | { |
||
1518 | GET_CURRENT_CONTEXT(ctx); |
||
1519 | _mesa_error(ctx, GL_OUT_OF_MEMORY, "out of memory in %s", caller); |
||
1520 | } |
||
1521 | |||
1522 | /** |
||
1523 | * Report debug information. Print error message to stderr via fprintf(). |
||
1524 | * No-op if DEBUG mode not enabled. |
||
1525 | * |
||
1526 | * \param ctx GL context. |
||
1527 | * \param fmtString printf()-style format string, followed by optional args. |
||
1528 | */ |
||
1529 | void |
||
1530 | _mesa_debug( const struct gl_context *ctx, const char *fmtString, ... ) |
||
1531 | { |
||
1532 | #ifdef DEBUG |
||
1533 | char s[MAX_DEBUG_MESSAGE_LENGTH]; |
||
1534 | va_list args; |
||
1535 | va_start(args, fmtString); |
||
1536 | _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); |
||
1537 | va_end(args); |
||
1538 | output_if_debug("Mesa", s, GL_FALSE); |
||
1539 | #endif /* DEBUG */ |
||
1540 | (void) ctx; |
||
1541 | (void) fmtString; |
||
1542 | } |
||
1543 | |||
1544 | |||
1545 | void |
||
1546 | _mesa_log(const char *fmtString, ...) |
||
1547 | { |
||
1548 | char s[MAX_DEBUG_MESSAGE_LENGTH]; |
||
1549 | va_list args; |
||
1550 | va_start(args, fmtString); |
||
1551 | _mesa_vsnprintf(s, MAX_DEBUG_MESSAGE_LENGTH, fmtString, args); |
||
1552 | va_end(args); |
||
1553 | output_if_debug("", s, GL_FALSE); |
||
1554 | } |
||
1555 | |||
1556 | |||
1557 | /** |
||
1558 | * Report debug information from the shader compiler via GL_ARB_debug_output. |
||
1559 | * |
||
1560 | * \param ctx GL context. |
||
1561 | * \param type The namespace to which this message belongs. |
||
1562 | * \param id The message ID within the given namespace. |
||
1563 | * \param msg The message to output. Need not be null-terminated. |
||
1564 | * \param len The length of 'msg'. If negative, 'msg' must be null-terminated. |
||
1565 | */ |
||
1566 | void |
||
1567 | _mesa_shader_debug( struct gl_context *ctx, GLenum type, GLuint *id, |
||
1568 | const char *msg, int len ) |
||
1569 | { |
||
1570 | enum mesa_debug_source source = MESA_DEBUG_SOURCE_SHADER_COMPILER; |
||
1571 | enum mesa_debug_severity severity = MESA_DEBUG_SEVERITY_HIGH; |
||
1572 | |||
1573 | debug_get_id(id); |
||
1574 | |||
1575 | if (len < 0) |
||
1576 | len = strlen(msg); |
||
1577 | |||
1578 | /* Truncate the message if necessary. */ |
||
1579 | if (len >= MAX_DEBUG_MESSAGE_LENGTH) |
||
1580 | len = MAX_DEBUG_MESSAGE_LENGTH - 1; |
||
1581 | |||
1582 | log_msg(ctx, source, type, *id, severity, len, msg); |
||
1583 | } |
||
1584 | |||
1585 | /*@}*/>>=>>>>>>>>>>>>>>>>>>><>><>><>><>><>><>>>> |