util: rewrite debug_print_transfer_flags() using debug_dump_flags()
[mesa.git] / src / gallium / auxiliary / util / u_debug.c
1 /**************************************************************************
2 *
3 * Copyright 2008 VMware, Inc.
4 * Copyright (c) 2008 VMware, Inc.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 **************************************************************************/
28
29
30 #include "pipe/p_config.h"
31
32 #include "pipe/p_compiler.h"
33 #include "util/u_debug.h"
34 #include "pipe/p_format.h"
35 #include "pipe/p_state.h"
36 #include "util/u_inlines.h"
37 #include "util/u_format.h"
38 #include "util/u_memory.h"
39 #include "util/u_string.h"
40 #include "util/u_math.h"
41 #include "util/u_tile.h"
42 #include "util/u_prim.h"
43 #include "util/u_surface.h"
44
45 #include <stdio.h>
46 #include <limits.h> /* CHAR_BIT */
47 #include <ctype.h> /* isalnum */
48
49 #ifdef _WIN32
50 #include <windows.h>
51 #include <stdlib.h>
52 #endif
53
54
55 void _debug_vprintf(const char *format, va_list ap)
56 {
57 static char buf[4096] = {'\0'};
58 #if defined(PIPE_OS_WINDOWS) || defined(PIPE_SUBSYSTEM_EMBEDDED)
59 /* We buffer until we find a newline. */
60 size_t len = strlen(buf);
61 int ret = util_vsnprintf(buf + len, sizeof(buf) - len, format, ap);
62 if(ret > (int)(sizeof(buf) - len - 1) || util_strchr(buf + len, '\n')) {
63 os_log_message(buf);
64 buf[0] = '\0';
65 }
66 #else
67 util_vsnprintf(buf, sizeof(buf), format, ap);
68 os_log_message(buf);
69 #endif
70 }
71
72
73 void
74 debug_disable_error_message_boxes(void)
75 {
76 #ifdef _WIN32
77 /* When Windows' error message boxes are disabled for this process (as is
78 * typically the case when running tests in an automated fashion) we disable
79 * CRT message boxes too.
80 */
81 UINT uMode = SetErrorMode(0);
82 SetErrorMode(uMode);
83 if (uMode & SEM_FAILCRITICALERRORS) {
84 /* Disable assertion failure message box.
85 * http://msdn.microsoft.com/en-us/library/sas1dkb2.aspx
86 */
87 _set_error_mode(_OUT_TO_STDERR);
88 #ifdef _MSC_VER
89 /* Disable abort message box.
90 * http://msdn.microsoft.com/en-us/library/e631wekh.aspx
91 */
92 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
93 #endif
94 }
95 #endif /* _WIN32 */
96 }
97
98
99 #ifdef DEBUG
100 void debug_print_blob( const char *name,
101 const void *blob,
102 unsigned size )
103 {
104 const unsigned *ublob = (const unsigned *)blob;
105 unsigned i;
106
107 debug_printf("%s (%d dwords%s)\n", name, size/4,
108 size%4 ? "... plus a few bytes" : "");
109
110 for (i = 0; i < size/4; i++) {
111 debug_printf("%d:\t%08x\n", i, ublob[i]);
112 }
113 }
114 #endif
115
116
117 static boolean
118 debug_get_option_should_print(void)
119 {
120 static boolean first = TRUE;
121 static boolean value = FALSE;
122
123 if (!first)
124 return value;
125
126 /* Oh hey this will call into this function,
127 * but its cool since we set first to false
128 */
129 first = FALSE;
130 value = debug_get_bool_option("GALLIUM_PRINT_OPTIONS", FALSE);
131 /* XXX should we print this option? Currently it wont */
132 return value;
133 }
134
135 const char *
136 debug_get_option(const char *name, const char *dfault)
137 {
138 const char *result;
139
140 result = os_get_option(name);
141 if(!result)
142 result = dfault;
143
144 if (debug_get_option_should_print())
145 debug_printf("%s: %s = %s\n", __FUNCTION__, name, result ? result : "(null)");
146
147 return result;
148 }
149
150 boolean
151 debug_get_bool_option(const char *name, boolean dfault)
152 {
153 const char *str = os_get_option(name);
154 boolean result;
155
156 if(str == NULL)
157 result = dfault;
158 else if(!util_strcmp(str, "n"))
159 result = FALSE;
160 else if(!util_strcmp(str, "no"))
161 result = FALSE;
162 else if(!util_strcmp(str, "0"))
163 result = FALSE;
164 else if(!util_strcmp(str, "f"))
165 result = FALSE;
166 else if(!util_strcmp(str, "F"))
167 result = FALSE;
168 else if(!util_strcmp(str, "false"))
169 result = FALSE;
170 else if(!util_strcmp(str, "FALSE"))
171 result = FALSE;
172 else
173 result = TRUE;
174
175 if (debug_get_option_should_print())
176 debug_printf("%s: %s = %s\n", __FUNCTION__, name, result ? "TRUE" : "FALSE");
177
178 return result;
179 }
180
181
182 long
183 debug_get_num_option(const char *name, long dfault)
184 {
185 long result;
186 const char *str;
187
188 str = os_get_option(name);
189 if(!str)
190 result = dfault;
191 else {
192 long sign;
193 char c;
194 c = *str++;
195 if(c == '-') {
196 sign = -1;
197 c = *str++;
198 }
199 else {
200 sign = 1;
201 }
202 result = 0;
203 while('0' <= c && c <= '9') {
204 result = result*10 + (c - '0');
205 c = *str++;
206 }
207 result *= sign;
208 }
209
210 if (debug_get_option_should_print())
211 debug_printf("%s: %s = %li\n", __FUNCTION__, name, result);
212
213 return result;
214 }
215
216 static boolean str_has_option(const char *str, const char *name)
217 {
218 /* Empty string. */
219 if (!*str) {
220 return FALSE;
221 }
222
223 /* OPTION=all */
224 if (!util_strcmp(str, "all")) {
225 return TRUE;
226 }
227
228 /* Find 'name' in 'str' surrounded by non-alphanumeric characters. */
229 {
230 const char *start = str;
231 unsigned name_len = strlen(name);
232
233 /* 'start' is the beginning of the currently-parsed word,
234 * we increment 'str' each iteration.
235 * if we find either the end of string or a non-alphanumeric character,
236 * we compare 'start' up to 'str-1' with 'name'. */
237
238 while (1) {
239 if (!*str || !(isalnum(*str) || *str == '_')) {
240 if (str-start == name_len &&
241 !memcmp(start, name, name_len)) {
242 return TRUE;
243 }
244
245 if (!*str) {
246 return FALSE;
247 }
248
249 start = str+1;
250 }
251
252 str++;
253 }
254 }
255
256 return FALSE;
257 }
258
259 unsigned long
260 debug_get_flags_option(const char *name,
261 const struct debug_named_value *flags,
262 unsigned long dfault)
263 {
264 unsigned long result;
265 const char *str;
266 const struct debug_named_value *orig = flags;
267 unsigned namealign = 0;
268
269 str = os_get_option(name);
270 if(!str)
271 result = dfault;
272 else if (!util_strcmp(str, "help")) {
273 result = dfault;
274 _debug_printf("%s: help for %s:\n", __FUNCTION__, name);
275 for (; flags->name; ++flags)
276 namealign = MAX2(namealign, strlen(flags->name));
277 for (flags = orig; flags->name; ++flags)
278 _debug_printf("| %*s [0x%0*lx]%s%s\n", namealign, flags->name,
279 (int)sizeof(unsigned long)*CHAR_BIT/4, flags->value,
280 flags->desc ? " " : "", flags->desc ? flags->desc : "");
281 }
282 else {
283 result = 0;
284 while( flags->name ) {
285 if (str_has_option(str, flags->name))
286 result |= flags->value;
287 ++flags;
288 }
289 }
290
291 if (debug_get_option_should_print()) {
292 if (str) {
293 debug_printf("%s: %s = 0x%lx (%s)\n", __FUNCTION__, name, result, str);
294 } else {
295 debug_printf("%s: %s = 0x%lx\n", __FUNCTION__, name, result);
296 }
297 }
298
299 return result;
300 }
301
302
303 void _debug_assert_fail(const char *expr,
304 const char *file,
305 unsigned line,
306 const char *function)
307 {
308 _debug_printf("%s:%u:%s: Assertion `%s' failed.\n", file, line, function, expr);
309 os_abort();
310 }
311
312
313 const char *
314 debug_dump_enum(const struct debug_named_value *names,
315 unsigned long value)
316 {
317 static char rest[64];
318
319 while(names->name) {
320 if(names->value == value)
321 return names->name;
322 ++names;
323 }
324
325 util_snprintf(rest, sizeof(rest), "0x%08lx", value);
326 return rest;
327 }
328
329
330 const char *
331 debug_dump_enum_noprefix(const struct debug_named_value *names,
332 const char *prefix,
333 unsigned long value)
334 {
335 static char rest[64];
336
337 while(names->name) {
338 if(names->value == value) {
339 const char *name = names->name;
340 while (*name == *prefix) {
341 name++;
342 prefix++;
343 }
344 return name;
345 }
346 ++names;
347 }
348
349
350
351 util_snprintf(rest, sizeof(rest), "0x%08lx", value);
352 return rest;
353 }
354
355
356 const char *
357 debug_dump_flags(const struct debug_named_value *names,
358 unsigned long value)
359 {
360 static char output[4096];
361 static char rest[256];
362 int first = 1;
363
364 output[0] = '\0';
365
366 while(names->name) {
367 if((names->value & value) == names->value) {
368 if (!first)
369 util_strncat(output, "|", sizeof(output) - strlen(output) - 1);
370 else
371 first = 0;
372 util_strncat(output, names->name, sizeof(output) - strlen(output) - 1);
373 output[sizeof(output) - 1] = '\0';
374 value &= ~names->value;
375 }
376 ++names;
377 }
378
379 if (value) {
380 if (!first)
381 util_strncat(output, "|", sizeof(output) - strlen(output) - 1);
382 else
383 first = 0;
384
385 util_snprintf(rest, sizeof(rest), "0x%08lx", value);
386 util_strncat(output, rest, sizeof(output) - strlen(output) - 1);
387 output[sizeof(output) - 1] = '\0';
388 }
389
390 if(first)
391 return "0";
392
393 return output;
394 }
395
396
397 #ifdef DEBUG
398 void debug_print_format(const char *msg, unsigned fmt )
399 {
400 debug_printf("%s: %s\n", msg, util_format_name(fmt));
401 }
402 #endif
403
404
405
406 static const struct debug_named_value pipe_prim_names[] = {
407 #ifdef DEBUG
408 DEBUG_NAMED_VALUE(PIPE_PRIM_POINTS),
409 DEBUG_NAMED_VALUE(PIPE_PRIM_LINES),
410 DEBUG_NAMED_VALUE(PIPE_PRIM_LINE_LOOP),
411 DEBUG_NAMED_VALUE(PIPE_PRIM_LINE_STRIP),
412 DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLES),
413 DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLE_STRIP),
414 DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLE_FAN),
415 DEBUG_NAMED_VALUE(PIPE_PRIM_QUADS),
416 DEBUG_NAMED_VALUE(PIPE_PRIM_QUAD_STRIP),
417 DEBUG_NAMED_VALUE(PIPE_PRIM_POLYGON),
418 DEBUG_NAMED_VALUE(PIPE_PRIM_LINES_ADJACENCY),
419 DEBUG_NAMED_VALUE(PIPE_PRIM_LINE_STRIP_ADJACENCY),
420 DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLES_ADJACENCY),
421 DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY),
422 #endif
423 DEBUG_NAMED_VALUE_END
424 };
425
426
427 const char *u_prim_name( unsigned prim )
428 {
429 return debug_dump_enum(pipe_prim_names, prim);
430 }
431
432
433
434 #ifdef DEBUG
435 int fl_indent = 0;
436 const char* fl_function[1024];
437
438 int debug_funclog_enter(const char* f, const int line, const char* file)
439 {
440 int i;
441
442 for (i = 0; i < fl_indent; i++)
443 debug_printf(" ");
444 debug_printf("%s\n", f);
445
446 assert(fl_indent < 1023);
447 fl_function[fl_indent++] = f;
448
449 return 0;
450 }
451
452 void debug_funclog_exit(const char* f, const int line, const char* file)
453 {
454 --fl_indent;
455 assert(fl_indent >= 0);
456 assert(fl_function[fl_indent] == f);
457 }
458
459 void debug_funclog_enter_exit(const char* f, const int line, const char* file)
460 {
461 int i;
462 for (i = 0; i < fl_indent; i++)
463 debug_printf(" ");
464 debug_printf("%s\n", f);
465 }
466 #endif
467
468
469
470 #ifdef DEBUG
471 /**
472 * Dump an image to .ppm file.
473 * \param format PIPE_FORMAT_x
474 * \param cpp bytes per pixel
475 * \param width width in pixels
476 * \param height height in pixels
477 * \param stride row stride in bytes
478 */
479 void debug_dump_image(const char *prefix,
480 enum pipe_format format, unsigned cpp,
481 unsigned width, unsigned height,
482 unsigned stride,
483 const void *data)
484 {
485 /* write a ppm file */
486 char filename[256];
487 unsigned char *rgb8;
488 FILE *f;
489
490 util_snprintf(filename, sizeof(filename), "%s.ppm", prefix);
491
492 rgb8 = MALLOC(height * width * 3);
493 if (!rgb8) {
494 return;
495 }
496
497 util_format_translate(
498 PIPE_FORMAT_R8G8B8_UNORM,
499 rgb8, width * 3,
500 0, 0,
501 format,
502 data, stride,
503 0, 0, width, height);
504
505 /* Must be opened in binary mode or DOS line ending causes data
506 * to be read with one byte offset.
507 */
508 f = fopen(filename, "wb");
509 if (f) {
510 fprintf(f, "P6\n");
511 fprintf(f, "# ppm-file created by gallium\n");
512 fprintf(f, "%i %i\n", width, height);
513 fprintf(f, "255\n");
514 fwrite(rgb8, 1, height * width * 3, f);
515 fclose(f);
516 }
517 else {
518 fprintf(stderr, "Can't open %s for writing\n", filename);
519 }
520
521 FREE(rgb8);
522 }
523
524 /* FIXME: dump resources, not surfaces... */
525 void debug_dump_surface(struct pipe_context *pipe,
526 const char *prefix,
527 struct pipe_surface *surface)
528 {
529 struct pipe_resource *texture;
530 struct pipe_transfer *transfer;
531 void *data;
532
533 if (!surface)
534 return;
535
536 /* XXX: this doesn't necessarily work, as the driver may be using
537 * temporary storage for the surface which hasn't been propagated
538 * back into the texture. Need to nail down the semantics of views
539 * and transfers a bit better before we can say if extra work needs
540 * to be done here:
541 */
542 texture = surface->texture;
543
544 data = pipe_transfer_map(pipe, texture, surface->u.tex.level,
545 surface->u.tex.first_layer,
546 PIPE_TRANSFER_READ,
547 0, 0, surface->width, surface->height, &transfer);
548 if(!data)
549 return;
550
551 debug_dump_image(prefix,
552 texture->format,
553 util_format_get_blocksize(texture->format),
554 util_format_get_nblocksx(texture->format, surface->width),
555 util_format_get_nblocksy(texture->format, surface->height),
556 transfer->stride,
557 data);
558
559 pipe->transfer_unmap(pipe, transfer);
560 }
561
562
563 void debug_dump_texture(struct pipe_context *pipe,
564 const char *prefix,
565 struct pipe_resource *texture)
566 {
567 struct pipe_surface *surface, surf_tmpl;
568
569 if (!texture)
570 return;
571
572 /* XXX for now, just dump image for layer=0, level=0 */
573 u_surface_default_template(&surf_tmpl, texture);
574 surface = pipe->create_surface(pipe, texture, &surf_tmpl);
575 if (surface) {
576 debug_dump_surface(pipe, prefix, surface);
577 pipe->surface_destroy(pipe, surface);
578 }
579 }
580
581
582 #pragma pack(push,2)
583 struct bmp_file_header {
584 uint16_t bfType;
585 uint32_t bfSize;
586 uint16_t bfReserved1;
587 uint16_t bfReserved2;
588 uint32_t bfOffBits;
589 };
590 #pragma pack(pop)
591
592 struct bmp_info_header {
593 uint32_t biSize;
594 int32_t biWidth;
595 int32_t biHeight;
596 uint16_t biPlanes;
597 uint16_t biBitCount;
598 uint32_t biCompression;
599 uint32_t biSizeImage;
600 int32_t biXPelsPerMeter;
601 int32_t biYPelsPerMeter;
602 uint32_t biClrUsed;
603 uint32_t biClrImportant;
604 };
605
606 struct bmp_rgb_quad {
607 uint8_t rgbBlue;
608 uint8_t rgbGreen;
609 uint8_t rgbRed;
610 uint8_t rgbAlpha;
611 };
612
613 void
614 debug_dump_surface_bmp(struct pipe_context *pipe,
615 const char *filename,
616 struct pipe_surface *surface)
617 {
618 struct pipe_transfer *transfer;
619 struct pipe_resource *texture = surface->texture;
620 void *ptr;
621
622 ptr = pipe_transfer_map(pipe, texture, surface->u.tex.level,
623 surface->u.tex.first_layer, PIPE_TRANSFER_READ,
624 0, 0, surface->width, surface->height, &transfer);
625
626 debug_dump_transfer_bmp(pipe, filename, transfer, ptr);
627
628 pipe->transfer_unmap(pipe, transfer);
629 }
630
631 void
632 debug_dump_transfer_bmp(struct pipe_context *pipe,
633 const char *filename,
634 struct pipe_transfer *transfer, void *ptr)
635 {
636 float *rgba;
637
638 if (!transfer)
639 goto error1;
640
641 rgba = MALLOC(transfer->box.width *
642 transfer->box.height *
643 transfer->box.depth *
644 4*sizeof(float));
645 if(!rgba)
646 goto error1;
647
648 pipe_get_tile_rgba(transfer, ptr, 0, 0,
649 transfer->box.width, transfer->box.height,
650 rgba);
651
652 debug_dump_float_rgba_bmp(filename,
653 transfer->box.width, transfer->box.height,
654 rgba, transfer->box.width);
655
656 FREE(rgba);
657 error1:
658 ;
659 }
660
661 void
662 debug_dump_float_rgba_bmp(const char *filename,
663 unsigned width, unsigned height,
664 float *rgba, unsigned stride)
665 {
666 FILE *stream;
667 struct bmp_file_header bmfh;
668 struct bmp_info_header bmih;
669 unsigned x, y;
670
671 if(!rgba)
672 goto error1;
673
674 bmfh.bfType = 0x4d42;
675 bmfh.bfSize = 14 + 40 + height*width*4;
676 bmfh.bfReserved1 = 0;
677 bmfh.bfReserved2 = 0;
678 bmfh.bfOffBits = 14 + 40;
679
680 bmih.biSize = 40;
681 bmih.biWidth = width;
682 bmih.biHeight = height;
683 bmih.biPlanes = 1;
684 bmih.biBitCount = 32;
685 bmih.biCompression = 0;
686 bmih.biSizeImage = height*width*4;
687 bmih.biXPelsPerMeter = 0;
688 bmih.biYPelsPerMeter = 0;
689 bmih.biClrUsed = 0;
690 bmih.biClrImportant = 0;
691
692 stream = fopen(filename, "wb");
693 if(!stream)
694 goto error1;
695
696 fwrite(&bmfh, 14, 1, stream);
697 fwrite(&bmih, 40, 1, stream);
698
699 y = height;
700 while(y--) {
701 float *ptr = rgba + (stride * y * 4);
702 for(x = 0; x < width; ++x)
703 {
704 struct bmp_rgb_quad pixel;
705 pixel.rgbRed = float_to_ubyte(ptr[x*4 + 0]);
706 pixel.rgbGreen = float_to_ubyte(ptr[x*4 + 1]);
707 pixel.rgbBlue = float_to_ubyte(ptr[x*4 + 2]);
708 pixel.rgbAlpha = float_to_ubyte(ptr[x*4 + 3]);
709 fwrite(&pixel, 1, 4, stream);
710 }
711 }
712
713 fclose(stream);
714 error1:
715 ;
716 }
717
718
719 /**
720 * Print PIPE_TRANSFER_x flags with a message.
721 */
722 void
723 debug_print_transfer_flags(const char *msg, unsigned usage)
724 {
725 static const struct debug_named_value names[] = {
726 DEBUG_NAMED_VALUE(PIPE_TRANSFER_READ),
727 DEBUG_NAMED_VALUE(PIPE_TRANSFER_WRITE),
728 DEBUG_NAMED_VALUE(PIPE_TRANSFER_MAP_DIRECTLY),
729 DEBUG_NAMED_VALUE(PIPE_TRANSFER_DISCARD_RANGE),
730 DEBUG_NAMED_VALUE(PIPE_TRANSFER_DONTBLOCK),
731 DEBUG_NAMED_VALUE(PIPE_TRANSFER_UNSYNCHRONIZED),
732 DEBUG_NAMED_VALUE(PIPE_TRANSFER_FLUSH_EXPLICIT),
733 DEBUG_NAMED_VALUE(PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE),
734 DEBUG_NAMED_VALUE(PIPE_TRANSFER_PERSISTENT),
735 DEBUG_NAMED_VALUE(PIPE_TRANSFER_COHERENT),
736 DEBUG_NAMED_VALUE_END
737 };
738
739 debug_printf("%s: %s\n", msg, debug_dump_flags(names, usage));
740 }
741
742
743
744 #endif