Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /**************************************************************************
  2.  *
  3.  * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
  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 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.
  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. 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)");
  114.    
  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;
  123.    
  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");
  145.    
  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;
  155.    
  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++;
  166.       }
  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
  228. debug_get_flags_option(const char *name,
  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;
  236.    
  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.  
  271. void _debug_assert_fail(const char *expr,
  272.                         const char *file,
  273.                         unsigned line,
  274.                         const char *function)
  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 *
  285. debug_dump_enum(const struct debug_named_value *names,
  286.                 unsigned long value)
  287. {
  288.    static char rest[64];
  289.    
  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 *
  302. debug_dump_enum_noprefix(const struct debug_named_value *names,
  303.                          const char *prefix,
  304.                          unsigned long value)
  305. {
  306.    static char rest[64];
  307.    
  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.    
  321.  
  322.    util_snprintf(rest, sizeof(rest), "0x%08lx", value);
  323.    return rest;
  324. }
  325.  
  326.  
  327. const char *
  328. debug_dump_flags(const struct debug_named_value *names,
  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.    }
  349.    
  350.    if (value) {
  351.       if (!first)
  352.          util_strncat(output, "|", sizeof(output));
  353.       else
  354.          first = 0;
  355.      
  356.       util_snprintf(rest, sizeof(rest), "0x%08lx", value);
  357.       util_strncat(output, rest, sizeof(output) - 1);
  358.       output[sizeof(output) - 1] = '\0';
  359.    }
  360.    
  361.    if(first)
  362.       return "0";
  363.    
  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,
  454.                       const void *data)    
  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
  731.