• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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
trace_dump_write(const char * buf,size_t size)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
trace_dump_writes(const char * s)77 trace_dump_writes(const char *s)
78 {
79    trace_dump_write(s, strlen(s));
80 }
81 
82 
83 static inline void
trace_dump_writef(const char * format,...)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
trace_dump_escape(const char * str)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
trace_dump_indent(unsigned level)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
trace_dump_newline(void)130 trace_dump_newline(void)
131 {
132    trace_dump_writes("\n");
133 }
134 
135 
136 static inline void
trace_dump_tag_begin(const char * name)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
trace_dump_tag_begin1(const char * name,const char * attr1,const char * value1)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
trace_dump_tag_end(const char * name)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
trace_dump_trace_flush(void)167 trace_dump_trace_flush(void)
168 {
169    if (stream) {
170       fflush(stream);
171    }
172 }
173 
174 static void
trace_dump_trace_close(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
trace_dump_call_time(int64_t time)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
trace_dump_trace_begin(void)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 
trace_dump_trace_enabled(void)242 boolean trace_dump_trace_enabled(void)
243 {
244    return stream ? TRUE : FALSE;
245 }
246 
247 /*
248  * Call lock
249  */
250 
trace_dump_call_lock(void)251 void trace_dump_call_lock(void)
252 {
253    mtx_lock(&call_mutex);
254 }
255 
trace_dump_call_unlock(void)256 void trace_dump_call_unlock(void)
257 {
258    mtx_unlock(&call_mutex);
259 }
260 
261 /*
262  * Dumping control
263  */
264 
trace_dumping_start_locked(void)265 void trace_dumping_start_locked(void)
266 {
267    dumping = TRUE;
268 }
269 
trace_dumping_stop_locked(void)270 void trace_dumping_stop_locked(void)
271 {
272    dumping = FALSE;
273 }
274 
trace_dumping_enabled_locked(void)275 boolean trace_dumping_enabled_locked(void)
276 {
277    return dumping;
278 }
279 
trace_dumping_start(void)280 void trace_dumping_start(void)
281 {
282    mtx_lock(&call_mutex);
283    trace_dumping_start_locked();
284    mtx_unlock(&call_mutex);
285 }
286 
trace_dumping_stop(void)287 void trace_dumping_stop(void)
288 {
289    mtx_lock(&call_mutex);
290    trace_dumping_stop_locked();
291    mtx_unlock(&call_mutex);
292 }
293 
trace_dumping_enabled(void)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 
trace_dump_call_begin_locked(const char * klass,const char * method)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 
trace_dump_call_end_locked(void)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 
trace_dump_call_begin(const char * klass,const char * method)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 
trace_dump_call_end(void)350 void trace_dump_call_end(void)
351 {
352    trace_dump_call_end_locked();
353    mtx_unlock(&call_mutex);
354 }
355 
trace_dump_arg_begin(const char * name)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 
trace_dump_arg_end(void)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 
trace_dump_ret_begin(void)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 
trace_dump_ret_end(void)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 
trace_dump_bool(int value)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 
trace_dump_int(long long int value)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 
trace_dump_uint(long long unsigned value)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 
trace_dump_float(double value)416 void trace_dump_float(double value)
417 {
418    if (!dumping)
419       return;
420 
421    trace_dump_writef("<float>%g</float>", value);
422 }
423 
trace_dump_bytes(const void * data,size_t size)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 
trace_dump_box_bytes(const void * data,struct pipe_resource * resource,const struct pipe_box * box,unsigned stride,unsigned slice_stride)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 
trace_dump_string(const char * str)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 
trace_dump_enum(const char * value)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 
trace_dump_array_begin(void)492 void trace_dump_array_begin(void)
493 {
494    if (!dumping)
495       return;
496 
497    trace_dump_writes("<array>");
498 }
499 
trace_dump_array_end(void)500 void trace_dump_array_end(void)
501 {
502    if (!dumping)
503       return;
504 
505    trace_dump_writes("</array>");
506 }
507 
trace_dump_elem_begin(void)508 void trace_dump_elem_begin(void)
509 {
510    if (!dumping)
511       return;
512 
513    trace_dump_writes("<elem>");
514 }
515 
trace_dump_elem_end(void)516 void trace_dump_elem_end(void)
517 {
518    if (!dumping)
519       return;
520 
521    trace_dump_writes("</elem>");
522 }
523 
trace_dump_struct_begin(const char * name)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 
trace_dump_struct_end(void)532 void trace_dump_struct_end(void)
533 {
534    if (!dumping)
535       return;
536 
537    trace_dump_writes("</struct>");
538 }
539 
trace_dump_member_begin(const char * name)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 
trace_dump_member_end(void)548 void trace_dump_member_end(void)
549 {
550    if (!dumping)
551       return;
552 
553    trace_dump_writes("</member>");
554 }
555 
trace_dump_null(void)556 void trace_dump_null(void)
557 {
558    if (!dumping)
559       return;
560 
561    trace_dump_writes("<null/>");
562 }
563 
trace_dump_ptr(const void * value)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 
trace_dump_surface_ptr(struct pipe_surface * _surface)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 
trace_dump_transfer_ptr(struct pipe_transfer * _transfer)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