Subversion Repositories Kolibri OS

Rev

Rev 4358 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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