Home | History | Annotate | Download | only in trace
      1 /**************************************************************************
      2  *
      3  * Copyright 2008 VMware, Inc.
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 
     29 /**
     30  * @file
     31  * Trace dumping functions.
     32  *
     33  * For now we just use standard XML for dumping the trace calls, as this is
     34  * simple to write, parse, and visually inspect, but the actual representation
     35  * is abstracted out of this file, so that we can switch to a binary
     36  * representation if/when it becomes justified.
     37  *
     38  * @author Jose Fonseca <jfonseca (at) vmware.com>
     39  */
     40 
     41 #include "pipe/p_config.h"
     42 
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 
     46 #include "pipe/p_compiler.h"
     47 #include "os/os_thread.h"
     48 #include "os/os_time.h"
     49 #include "util/u_debug.h"
     50 #include "util/u_memory.h"
     51 #include "util/u_string.h"
     52 #include "util/u_math.h"
     53 #include "util/u_format.h"
     54 
     55 #include "tr_dump.h"
     56 #include "tr_screen.h"
     57 #include "tr_texture.h"
     58 
     59 
     60 static boolean close_stream = FALSE;
     61 static FILE *stream = NULL;
     62 pipe_static_mutex(call_mutex);
     63 static long unsigned call_no = 0;
     64 static boolean dumping = FALSE;
     65 
     66 
     67 static inline void
     68 trace_dump_write(const char *buf, size_t size)
     69 {
     70    if (stream) {
     71       fwrite(buf, size, 1, stream);
     72    }
     73 }
     74 
     75 
     76 static inline void
     77 trace_dump_writes(const char *s)
     78 {
     79    trace_dump_write(s, strlen(s));
     80 }
     81 
     82 
     83 static inline void
     84 trace_dump_writef(const char *format, ...)
     85 {
     86    static char buf[1024];
     87    unsigned len;
     88    va_list ap;
     89    va_start(ap, format);
     90    len = util_vsnprintf(buf, sizeof(buf), format, ap);
     91    va_end(ap);
     92    trace_dump_write(buf, len);
     93 }
     94 
     95 
     96 static inline void
     97 trace_dump_escape(const char *str)
     98 {
     99    const unsigned char *p = (const unsigned char *)str;
    100    unsigned char c;
    101    while((c = *p++) != 0) {
    102       if(c == '<')
    103          trace_dump_writes("&lt;");
    104       else if(c == '>')
    105          trace_dump_writes("&gt;");
    106       else if(c == '&')
    107          trace_dump_writes("&amp;");
    108       else if(c == '\'')
    109          trace_dump_writes("&apos;");
    110       else if(c == '\"')
    111          trace_dump_writes("&quot;");
    112       else if(c >= 0x20 && c <= 0x7e)
    113          trace_dump_writef("%c", c);
    114       else
    115          trace_dump_writef("&#%u;", c);
    116    }
    117 }
    118 
    119 
    120 static inline void
    121 trace_dump_indent(unsigned level)
    122 {
    123    unsigned i;
    124    for(i = 0; i < level; ++i)
    125       trace_dump_writes("\t");
    126 }
    127 
    128 
    129 static inline void
    130 trace_dump_newline(void)
    131 {
    132    trace_dump_writes("\n");
    133 }
    134 
    135 
    136 static inline void
    137 trace_dump_tag(const char *name)
    138 {
    139    trace_dump_writes("<");
    140    trace_dump_writes(name);
    141    trace_dump_writes("/>");
    142 }
    143 
    144 
    145 static inline void
    146 trace_dump_tag_begin(const char *name)
    147 {
    148    trace_dump_writes("<");
    149    trace_dump_writes(name);
    150    trace_dump_writes(">");
    151 }
    152 
    153 static inline void
    154 trace_dump_tag_begin1(const char *name,
    155                       const char *attr1, const char *value1)
    156 {
    157    trace_dump_writes("<");
    158    trace_dump_writes(name);
    159    trace_dump_writes(" ");
    160    trace_dump_writes(attr1);
    161    trace_dump_writes("='");
    162    trace_dump_escape(value1);
    163    trace_dump_writes("'>");
    164 }
    165 
    166 
    167 static inline void
    168 trace_dump_tag_begin2(const char *name,
    169                       const char *attr1, const char *value1,
    170                       const char *attr2, const char *value2)
    171 {
    172    trace_dump_writes("<");
    173    trace_dump_writes(name);
    174    trace_dump_writes(" ");
    175    trace_dump_writes(attr1);
    176    trace_dump_writes("=\'");
    177    trace_dump_escape(value1);
    178    trace_dump_writes("\' ");
    179    trace_dump_writes(attr2);
    180    trace_dump_writes("=\'");
    181    trace_dump_escape(value2);
    182    trace_dump_writes("\'>");
    183 }
    184 
    185 
    186 static inline void
    187 trace_dump_tag_begin3(const char *name,
    188                       const char *attr1, const char *value1,
    189                       const char *attr2, const char *value2,
    190                       const char *attr3, const char *value3)
    191 {
    192    trace_dump_writes("<");
    193    trace_dump_writes(name);
    194    trace_dump_writes(" ");
    195    trace_dump_writes(attr1);
    196    trace_dump_writes("=\'");
    197    trace_dump_escape(value1);
    198    trace_dump_writes("\' ");
    199    trace_dump_writes(attr2);
    200    trace_dump_writes("=\'");
    201    trace_dump_escape(value2);
    202    trace_dump_writes("\' ");
    203    trace_dump_writes(attr3);
    204    trace_dump_writes("=\'");
    205    trace_dump_escape(value3);
    206    trace_dump_writes("\'>");
    207 }
    208 
    209 
    210 static inline void
    211 trace_dump_tag_end(const char *name)
    212 {
    213    trace_dump_writes("</");
    214    trace_dump_writes(name);
    215    trace_dump_writes(">");
    216 }
    217 
    218 void
    219 trace_dump_trace_flush(void)
    220 {
    221    if (stream) {
    222       fflush(stream);
    223    }
    224 }
    225 
    226 static void
    227 trace_dump_trace_close(void)
    228 {
    229    if (stream) {
    230       trace_dump_writes("</trace>\n");
    231       if (close_stream) {
    232          fclose(stream);
    233          close_stream = FALSE;
    234          stream = NULL;
    235       }
    236       call_no = 0;
    237    }
    238 }
    239 
    240 
    241 static void
    242 trace_dump_call_time(int64_t time)
    243 {
    244    if (stream) {
    245       trace_dump_indent(2);
    246       trace_dump_tag_begin("time");
    247       trace_dump_int(time);
    248       trace_dump_tag_end("time");
    249       trace_dump_newline();
    250    }
    251 }
    252 
    253 
    254 boolean
    255 trace_dump_trace_begin(void)
    256 {
    257    const char *filename;
    258 
    259    filename = debug_get_option("GALLIUM_TRACE", NULL);
    260    if (!filename)
    261       return FALSE;
    262 
    263    if (!stream) {
    264 
    265       if (strcmp(filename, "stderr") == 0) {
    266          close_stream = FALSE;
    267          stream = stderr;
    268       }
    269       else if (strcmp(filename, "stdout") == 0) {
    270          close_stream = FALSE;
    271          stream = stdout;
    272       }
    273       else {
    274          close_stream = TRUE;
    275          stream = fopen(filename, "wt");
    276          if (!stream)
    277             return FALSE;
    278       }
    279 
    280       trace_dump_writes("<?xml version='1.0' encoding='UTF-8'?>\n");
    281       trace_dump_writes("<?xml-stylesheet type='text/xsl' href='trace.xsl'?>\n");
    282       trace_dump_writes("<trace version='0.1'>\n");
    283 
    284       /* Many applications don't exit cleanly, others may create and destroy a
    285        * screen multiple times, so we only write </trace> tag and close at exit
    286        * time.
    287        */
    288       atexit(trace_dump_trace_close);
    289    }
    290 
    291    return TRUE;
    292 }
    293 
    294 boolean trace_dump_trace_enabled(void)
    295 {
    296    return stream ? TRUE : FALSE;
    297 }
    298 
    299 /*
    300  * Call lock
    301  */
    302 
    303 void trace_dump_call_lock(void)
    304 {
    305    pipe_mutex_lock(call_mutex);
    306 }
    307 
    308 void trace_dump_call_unlock(void)
    309 {
    310    pipe_mutex_unlock(call_mutex);
    311 }
    312 
    313 /*
    314  * Dumping control
    315  */
    316 
    317 void trace_dumping_start_locked(void)
    318 {
    319    dumping = TRUE;
    320 }
    321 
    322 void trace_dumping_stop_locked(void)
    323 {
    324    dumping = FALSE;
    325 }
    326 
    327 boolean trace_dumping_enabled_locked(void)
    328 {
    329    return dumping;
    330 }
    331 
    332 void trace_dumping_start(void)
    333 {
    334    pipe_mutex_lock(call_mutex);
    335    trace_dumping_start_locked();
    336    pipe_mutex_unlock(call_mutex);
    337 }
    338 
    339 void trace_dumping_stop(void)
    340 {
    341    pipe_mutex_lock(call_mutex);
    342    trace_dumping_stop_locked();
    343    pipe_mutex_unlock(call_mutex);
    344 }
    345 
    346 boolean trace_dumping_enabled(void)
    347 {
    348    boolean ret;
    349    pipe_mutex_lock(call_mutex);
    350    ret = trace_dumping_enabled_locked();
    351    pipe_mutex_unlock(call_mutex);
    352    return ret;
    353 }
    354 
    355 /*
    356  * Dump functions
    357  */
    358 
    359 static int64_t call_start_time = 0;
    360 
    361 void trace_dump_call_begin_locked(const char *klass, const char *method)
    362 {
    363    if (!dumping)
    364       return;
    365 
    366    ++call_no;
    367    trace_dump_indent(1);
    368    trace_dump_writes("<call no=\'");
    369    trace_dump_writef("%lu", call_no);
    370    trace_dump_writes("\' class=\'");
    371    trace_dump_escape(klass);
    372    trace_dump_writes("\' method=\'");
    373    trace_dump_escape(method);
    374    trace_dump_writes("\'>");
    375    trace_dump_newline();
    376 
    377    call_start_time = os_time_get();
    378 }
    379 
    380 void trace_dump_call_end_locked(void)
    381 {
    382    int64_t call_end_time;
    383 
    384    if (!dumping)
    385       return;
    386 
    387    call_end_time = os_time_get();
    388 
    389    trace_dump_call_time(call_end_time - call_start_time);
    390    trace_dump_indent(1);
    391    trace_dump_tag_end("call");
    392    trace_dump_newline();
    393    fflush(stream);
    394 }
    395 
    396 void trace_dump_call_begin(const char *klass, const char *method)
    397 {
    398    pipe_mutex_lock(call_mutex);
    399    trace_dump_call_begin_locked(klass, method);
    400 }
    401 
    402 void trace_dump_call_end(void)
    403 {
    404    trace_dump_call_end_locked();
    405    pipe_mutex_unlock(call_mutex);
    406 }
    407 
    408 void trace_dump_arg_begin(const char *name)
    409 {
    410    if (!dumping)
    411       return;
    412 
    413    trace_dump_indent(2);
    414    trace_dump_tag_begin1("arg", "name", name);
    415 }
    416 
    417 void trace_dump_arg_end(void)
    418 {
    419    if (!dumping)
    420       return;
    421 
    422    trace_dump_tag_end("arg");
    423    trace_dump_newline();
    424 }
    425 
    426 void trace_dump_ret_begin(void)
    427 {
    428    if (!dumping)
    429       return;
    430 
    431    trace_dump_indent(2);
    432    trace_dump_tag_begin("ret");
    433 }
    434 
    435 void trace_dump_ret_end(void)
    436 {
    437    if (!dumping)
    438       return;
    439 
    440    trace_dump_tag_end("ret");
    441    trace_dump_newline();
    442 }
    443 
    444 void trace_dump_bool(int value)
    445 {
    446    if (!dumping)
    447       return;
    448 
    449    trace_dump_writef("<bool>%c</bool>", value ? '1' : '0');
    450 }
    451 
    452 void trace_dump_int(long long int value)
    453 {
    454    if (!dumping)
    455       return;
    456 
    457    trace_dump_writef("<int>%lli</int>", value);
    458 }
    459 
    460 void trace_dump_uint(long long unsigned value)
    461 {
    462    if (!dumping)
    463       return;
    464 
    465    trace_dump_writef("<uint>%llu</uint>", value);
    466 }
    467 
    468 void trace_dump_float(double value)
    469 {
    470    if (!dumping)
    471       return;
    472 
    473    trace_dump_writef("<float>%g</float>", value);
    474 }
    475 
    476 void trace_dump_bytes(const void *data,
    477                       size_t size)
    478 {
    479    static const char hex_table[16] = "0123456789ABCDEF";
    480    const uint8_t *p = data;
    481    size_t i;
    482 
    483    if (!dumping)
    484       return;
    485 
    486    trace_dump_writes("<bytes>");
    487    for(i = 0; i < size; ++i) {
    488       uint8_t byte = *p++;
    489       char hex[2];
    490       hex[0] = hex_table[byte >> 4];
    491       hex[1] = hex_table[byte & 0xf];
    492       trace_dump_write(hex, 2);
    493    }
    494    trace_dump_writes("</bytes>");
    495 }
    496 
    497 void trace_dump_box_bytes(const void *data,
    498                           struct pipe_resource *resource,
    499 			  const struct pipe_box *box,
    500 			  unsigned stride,
    501 			  unsigned slice_stride)
    502 {
    503    size_t size;
    504 
    505    /*
    506     * Only dump buffer transfers to avoid huge files.
    507     * TODO: Make this run-time configurable
    508     */
    509    if (resource->target != PIPE_BUFFER) {
    510       size = 0;
    511    } else {
    512       enum pipe_format format = resource->format;
    513       if (slice_stride)
    514          size = box->depth * slice_stride;
    515       else if (stride)
    516          size = util_format_get_nblocksy(format, box->height) * stride;
    517       else {
    518          size = util_format_get_nblocksx(format, box->width) * util_format_get_blocksize(format);
    519       }
    520    }
    521 
    522    trace_dump_bytes(data, size);
    523 }
    524 
    525 void trace_dump_string(const char *str)
    526 {
    527    if (!dumping)
    528       return;
    529 
    530    trace_dump_writes("<string>");
    531    trace_dump_escape(str);
    532    trace_dump_writes("</string>");
    533 }
    534 
    535 void trace_dump_enum(const char *value)
    536 {
    537    if (!dumping)
    538       return;
    539 
    540    trace_dump_writes("<enum>");
    541    trace_dump_escape(value);
    542    trace_dump_writes("</enum>");
    543 }
    544 
    545 void trace_dump_array_begin(void)
    546 {
    547    if (!dumping)
    548       return;
    549 
    550    trace_dump_writes("<array>");
    551 }
    552 
    553 void trace_dump_array_end(void)
    554 {
    555    if (!dumping)
    556       return;
    557 
    558    trace_dump_writes("</array>");
    559 }
    560 
    561 void trace_dump_elem_begin(void)
    562 {
    563    if (!dumping)
    564       return;
    565 
    566    trace_dump_writes("<elem>");
    567 }
    568 
    569 void trace_dump_elem_end(void)
    570 {
    571    if (!dumping)
    572       return;
    573 
    574    trace_dump_writes("</elem>");
    575 }
    576 
    577 void trace_dump_struct_begin(const char *name)
    578 {
    579    if (!dumping)
    580       return;
    581 
    582    trace_dump_writef("<struct name='%s'>", name);
    583 }
    584 
    585 void trace_dump_struct_end(void)
    586 {
    587    if (!dumping)
    588       return;
    589 
    590    trace_dump_writes("</struct>");
    591 }
    592 
    593 void trace_dump_member_begin(const char *name)
    594 {
    595    if (!dumping)
    596       return;
    597 
    598    trace_dump_writef("<member name='%s'>", name);
    599 }
    600 
    601 void trace_dump_member_end(void)
    602 {
    603    if (!dumping)
    604       return;
    605 
    606    trace_dump_writes("</member>");
    607 }
    608 
    609 void trace_dump_null(void)
    610 {
    611    if (!dumping)
    612       return;
    613 
    614    trace_dump_writes("<null/>");
    615 }
    616 
    617 void trace_dump_ptr(const void *value)
    618 {
    619    if (!dumping)
    620       return;
    621 
    622    if(value)
    623       trace_dump_writef("<ptr>0x%08lx</ptr>", (unsigned long)(uintptr_t)value);
    624    else
    625       trace_dump_null();
    626 }
    627 
    628 
    629 void trace_dump_resource_ptr(struct pipe_resource *_resource)
    630 {
    631    if (!dumping)
    632       return;
    633 
    634    if (_resource) {
    635       struct trace_resource *tr_resource = trace_resource(_resource);
    636       trace_dump_ptr(tr_resource->resource);
    637    } else {
    638       trace_dump_null();
    639    }
    640 }
    641 
    642 void trace_dump_surface_ptr(struct pipe_surface *_surface)
    643 {
    644    if (!dumping)
    645       return;
    646 
    647    if (_surface) {
    648       struct trace_surface *tr_surf = trace_surface(_surface);
    649       trace_dump_ptr(tr_surf->surface);
    650    } else {
    651       trace_dump_null();
    652    }
    653 }
    654 
    655 void trace_dump_transfer_ptr(struct pipe_transfer *_transfer)
    656 {
    657    if (!dumping)
    658       return;
    659 
    660    if (_transfer) {
    661       struct trace_transfer *tr_tran = trace_transfer(_transfer);
    662       trace_dump_ptr(tr_tran->transfer);
    663    } else {
    664       trace_dump_null();
    665    }
    666 }
    667