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 "util/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 static mtx_t call_mutex = _MTX_INITIALIZER_NP;
     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_begin(const char *name)
    138 {
    139    trace_dump_writes("<");
    140    trace_dump_writes(name);
    141    trace_dump_writes(">");
    142 }
    143 
    144 static inline void
    145 trace_dump_tag_begin1(const char *name,
    146                       const char *attr1, const char *value1)
    147 {
    148    trace_dump_writes("<");
    149    trace_dump_writes(name);
    150    trace_dump_writes(" ");
    151    trace_dump_writes(attr1);
    152    trace_dump_writes("='");
    153    trace_dump_escape(value1);
    154    trace_dump_writes("'>");
    155 }
    156 
    157 
    158 static inline void
    159 trace_dump_tag_end(const char *name)
    160 {
    161    trace_dump_writes("</");
    162    trace_dump_writes(name);
    163    trace_dump_writes(">");
    164 }
    165 
    166 void
    167 trace_dump_trace_flush(void)
    168 {
    169    if (stream) {
    170       fflush(stream);
    171    }
    172 }
    173 
    174 static void
    175 trace_dump_trace_close(void)
    176 {
    177    if (stream) {
    178       trace_dump_writes("</trace>\n");
    179       if (close_stream) {
    180          fclose(stream);
    181          close_stream = FALSE;
    182          stream = NULL;
    183       }
    184       call_no = 0;
    185    }
    186 }
    187 
    188 
    189 static void
    190 trace_dump_call_time(int64_t time)
    191 {
    192    if (stream) {
    193       trace_dump_indent(2);
    194       trace_dump_tag_begin("time");
    195       trace_dump_int(time);
    196       trace_dump_tag_end("time");
    197       trace_dump_newline();
    198    }
    199 }
    200 
    201 
    202 boolean
    203 trace_dump_trace_begin(void)
    204 {
    205    const char *filename;
    206 
    207    filename = debug_get_option("GALLIUM_TRACE", NULL);
    208    if (!filename)
    209       return FALSE;
    210 
    211    if (!stream) {
    212 
    213       if (strcmp(filename, "stderr") == 0) {
    214          close_stream = FALSE;
    215          stream = stderr;
    216       }
    217       else if (strcmp(filename, "stdout") == 0) {
    218          close_stream = FALSE;
    219          stream = stdout;
    220       }
    221       else {
    222          close_stream = TRUE;
    223          stream = fopen(filename, "wt");
    224          if (!stream)
    225             return FALSE;
    226       }
    227 
    228       trace_dump_writes("<?xml version='1.0' encoding='UTF-8'?>\n");
    229       trace_dump_writes("<?xml-stylesheet type='text/xsl' href='trace.xsl'?>\n");
    230       trace_dump_writes("<trace version='0.1'>\n");
    231 
    232       /* Many applications don't exit cleanly, others may create and destroy a
    233        * screen multiple times, so we only write </trace> tag and close at exit
    234        * time.
    235        */
    236       atexit(trace_dump_trace_close);
    237    }
    238 
    239    return TRUE;
    240 }
    241 
    242 boolean trace_dump_trace_enabled(void)
    243 {
    244    return stream ? TRUE : FALSE;
    245 }
    246 
    247 /*
    248  * Call lock
    249  */
    250 
    251 void trace_dump_call_lock(void)
    252 {
    253    mtx_lock(&call_mutex);
    254 }
    255 
    256 void trace_dump_call_unlock(void)
    257 {
    258    mtx_unlock(&call_mutex);
    259 }
    260 
    261 /*
    262  * Dumping control
    263  */
    264 
    265 void trace_dumping_start_locked(void)
    266 {
    267    dumping = TRUE;
    268 }
    269 
    270 void trace_dumping_stop_locked(void)
    271 {
    272    dumping = FALSE;
    273 }
    274 
    275 boolean trace_dumping_enabled_locked(void)
    276 {
    277    return dumping;
    278 }
    279 
    280 void trace_dumping_start(void)
    281 {
    282    mtx_lock(&call_mutex);
    283    trace_dumping_start_locked();
    284    mtx_unlock(&call_mutex);
    285 }
    286 
    287 void trace_dumping_stop(void)
    288 {
    289    mtx_lock(&call_mutex);
    290    trace_dumping_stop_locked();
    291    mtx_unlock(&call_mutex);
    292 }
    293 
    294 boolean trace_dumping_enabled(void)
    295 {
    296    boolean ret;
    297    mtx_lock(&call_mutex);
    298    ret = trace_dumping_enabled_locked();
    299    mtx_unlock(&call_mutex);
    300    return ret;
    301 }
    302 
    303 /*
    304  * Dump functions
    305  */
    306 
    307 static int64_t call_start_time = 0;
    308 
    309 void trace_dump_call_begin_locked(const char *klass, const char *method)
    310 {
    311    if (!dumping)
    312       return;
    313 
    314    ++call_no;
    315    trace_dump_indent(1);
    316    trace_dump_writes("<call no=\'");
    317    trace_dump_writef("%lu", call_no);
    318    trace_dump_writes("\' class=\'");
    319    trace_dump_escape(klass);
    320    trace_dump_writes("\' method=\'");
    321    trace_dump_escape(method);
    322    trace_dump_writes("\'>");
    323    trace_dump_newline();
    324 
    325    call_start_time = os_time_get();
    326 }
    327 
    328 void trace_dump_call_end_locked(void)
    329 {
    330    int64_t call_end_time;
    331 
    332    if (!dumping)
    333       return;
    334 
    335    call_end_time = os_time_get();
    336 
    337    trace_dump_call_time(call_end_time - call_start_time);
    338    trace_dump_indent(1);
    339    trace_dump_tag_end("call");
    340    trace_dump_newline();
    341    fflush(stream);
    342 }
    343 
    344 void trace_dump_call_begin(const char *klass, const char *method)
    345 {
    346    mtx_lock(&call_mutex);
    347    trace_dump_call_begin_locked(klass, method);
    348 }
    349 
    350 void trace_dump_call_end(void)
    351 {
    352    trace_dump_call_end_locked();
    353    mtx_unlock(&call_mutex);
    354 }
    355 
    356 void trace_dump_arg_begin(const char *name)
    357 {
    358    if (!dumping)
    359       return;
    360 
    361    trace_dump_indent(2);
    362    trace_dump_tag_begin1("arg", "name", name);
    363 }
    364 
    365 void trace_dump_arg_end(void)
    366 {
    367    if (!dumping)
    368       return;
    369 
    370    trace_dump_tag_end("arg");
    371    trace_dump_newline();
    372 }
    373 
    374 void trace_dump_ret_begin(void)
    375 {
    376    if (!dumping)
    377       return;
    378 
    379    trace_dump_indent(2);
    380    trace_dump_tag_begin("ret");
    381 }
    382 
    383 void trace_dump_ret_end(void)
    384 {
    385    if (!dumping)
    386       return;
    387 
    388    trace_dump_tag_end("ret");
    389    trace_dump_newline();
    390 }
    391 
    392 void trace_dump_bool(int value)
    393 {
    394    if (!dumping)
    395       return;
    396 
    397    trace_dump_writef("<bool>%c</bool>", value ? '1' : '0');
    398 }
    399 
    400 void trace_dump_int(long long int value)
    401 {
    402    if (!dumping)
    403       return;
    404 
    405    trace_dump_writef("<int>%lli</int>", value);
    406 }
    407 
    408 void trace_dump_uint(long long unsigned value)
    409 {
    410    if (!dumping)
    411       return;
    412 
    413    trace_dump_writef("<uint>%llu</uint>", value);
    414 }
    415 
    416 void trace_dump_float(double value)
    417 {
    418    if (!dumping)
    419       return;
    420 
    421    trace_dump_writef("<float>%g</float>", value);
    422 }
    423 
    424 void trace_dump_bytes(const void *data,
    425                       size_t size)
    426 {
    427    static const char hex_table[16] = "0123456789ABCDEF";
    428    const uint8_t *p = data;
    429    size_t i;
    430 
    431    if (!dumping)
    432       return;
    433 
    434    trace_dump_writes("<bytes>");
    435    for(i = 0; i < size; ++i) {
    436       uint8_t byte = *p++;
    437       char hex[2];
    438       hex[0] = hex_table[byte >> 4];
    439       hex[1] = hex_table[byte & 0xf];
    440       trace_dump_write(hex, 2);
    441    }
    442    trace_dump_writes("</bytes>");
    443 }
    444 
    445 void trace_dump_box_bytes(const void *data,
    446                           struct pipe_resource *resource,
    447 			  const struct pipe_box *box,
    448 			  unsigned stride,
    449 			  unsigned slice_stride)
    450 {
    451    enum pipe_format format = resource->format;
    452    size_t size;
    453 
    454    assert(box->height > 0);
    455    assert(box->depth > 0);
    456 
    457    size =  util_format_get_nblocksx(format, box->width )      * util_format_get_blocksize(format)
    458         + (util_format_get_nblocksy(format, box->height) - 1) * stride
    459         +                                  (box->depth   - 1) * slice_stride;
    460 
    461    /*
    462     * Only dump buffer transfers to avoid huge files.
    463     * TODO: Make this run-time configurable
    464     */
    465    if (resource->target != PIPE_BUFFER) {
    466       size = 0;
    467    }
    468 
    469    trace_dump_bytes(data, size);
    470 }
    471 
    472 void trace_dump_string(const char *str)
    473 {
    474    if (!dumping)
    475       return;
    476 
    477    trace_dump_writes("<string>");
    478    trace_dump_escape(str);
    479    trace_dump_writes("</string>");
    480 }
    481 
    482 void trace_dump_enum(const char *value)
    483 {
    484    if (!dumping)
    485       return;
    486 
    487    trace_dump_writes("<enum>");
    488    trace_dump_escape(value);
    489    trace_dump_writes("</enum>");
    490 }
    491 
    492 void trace_dump_array_begin(void)
    493 {
    494    if (!dumping)
    495       return;
    496 
    497    trace_dump_writes("<array>");
    498 }
    499 
    500 void trace_dump_array_end(void)
    501 {
    502    if (!dumping)
    503       return;
    504 
    505    trace_dump_writes("</array>");
    506 }
    507 
    508 void trace_dump_elem_begin(void)
    509 {
    510    if (!dumping)
    511       return;
    512 
    513    trace_dump_writes("<elem>");
    514 }
    515 
    516 void trace_dump_elem_end(void)
    517 {
    518    if (!dumping)
    519       return;
    520 
    521    trace_dump_writes("</elem>");
    522 }
    523 
    524 void trace_dump_struct_begin(const char *name)
    525 {
    526    if (!dumping)
    527       return;
    528 
    529    trace_dump_writef("<struct name='%s'>", name);
    530 }
    531 
    532 void trace_dump_struct_end(void)
    533 {
    534    if (!dumping)
    535       return;
    536 
    537    trace_dump_writes("</struct>");
    538 }
    539 
    540 void trace_dump_member_begin(const char *name)
    541 {
    542    if (!dumping)
    543       return;
    544 
    545    trace_dump_writef("<member name='%s'>", name);
    546 }
    547 
    548 void trace_dump_member_end(void)
    549 {
    550    if (!dumping)
    551       return;
    552 
    553    trace_dump_writes("</member>");
    554 }
    555 
    556 void trace_dump_null(void)
    557 {
    558    if (!dumping)
    559       return;
    560 
    561    trace_dump_writes("<null/>");
    562 }
    563 
    564 void trace_dump_ptr(const void *value)
    565 {
    566    if (!dumping)
    567       return;
    568 
    569    if(value)
    570       trace_dump_writef("<ptr>0x%08lx</ptr>", (unsigned long)(uintptr_t)value);
    571    else
    572       trace_dump_null();
    573 }
    574 
    575 void trace_dump_surface_ptr(struct pipe_surface *_surface)
    576 {
    577    if (!dumping)
    578       return;
    579 
    580    if (_surface) {
    581       struct trace_surface *tr_surf = trace_surface(_surface);
    582       trace_dump_ptr(tr_surf->surface);
    583    } else {
    584       trace_dump_null();
    585    }
    586 }
    587 
    588 void trace_dump_transfer_ptr(struct pipe_transfer *_transfer)
    589 {
    590    if (!dumping)
    591       return;
    592 
    593    if (_transfer) {
    594       struct trace_transfer *tr_tran = trace_transfer(_transfer);
    595       trace_dump_ptr(tr_tran->transfer);
    596    } else {
    597       trace_dump_null();
    598    }
    599 }
    600