• 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 "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
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(const char * name)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
trace_dump_tag_begin(const char * name)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
trace_dump_tag_begin1(const char * name,const char * attr1,const char * value1)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
trace_dump_tag_begin2(const char * name,const char * attr1,const char * value1,const char * attr2,const char * value2)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
trace_dump_tag_begin3(const char * name,const char * attr1,const char * value1,const char * attr2,const char * value2,const char * attr3,const char * value3)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
trace_dump_tag_end(const char * name)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
trace_dump_trace_flush(void)219 trace_dump_trace_flush(void)
220 {
221    if (stream) {
222       fflush(stream);
223    }
224 }
225 
226 static void
trace_dump_trace_close(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
trace_dump_call_time(int64_t time)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
trace_dump_trace_begin(void)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 
trace_dump_trace_enabled(void)294 boolean trace_dump_trace_enabled(void)
295 {
296    return stream ? TRUE : FALSE;
297 }
298 
299 /*
300  * Call lock
301  */
302 
trace_dump_call_lock(void)303 void trace_dump_call_lock(void)
304 {
305    pipe_mutex_lock(call_mutex);
306 }
307 
trace_dump_call_unlock(void)308 void trace_dump_call_unlock(void)
309 {
310    pipe_mutex_unlock(call_mutex);
311 }
312 
313 /*
314  * Dumping control
315  */
316 
trace_dumping_start_locked(void)317 void trace_dumping_start_locked(void)
318 {
319    dumping = TRUE;
320 }
321 
trace_dumping_stop_locked(void)322 void trace_dumping_stop_locked(void)
323 {
324    dumping = FALSE;
325 }
326 
trace_dumping_enabled_locked(void)327 boolean trace_dumping_enabled_locked(void)
328 {
329    return dumping;
330 }
331 
trace_dumping_start(void)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 
trace_dumping_stop(void)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 
trace_dumping_enabled(void)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 
trace_dump_call_begin_locked(const char * klass,const char * method)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 
trace_dump_call_end_locked(void)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 
trace_dump_call_begin(const char * klass,const char * method)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 
trace_dump_call_end(void)402 void trace_dump_call_end(void)
403 {
404    trace_dump_call_end_locked();
405    pipe_mutex_unlock(call_mutex);
406 }
407 
trace_dump_arg_begin(const char * name)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 
trace_dump_arg_end(void)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 
trace_dump_ret_begin(void)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 
trace_dump_ret_end(void)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 
trace_dump_bool(int value)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 
trace_dump_int(long long int value)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 
trace_dump_uint(long long unsigned value)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 
trace_dump_float(double value)468 void trace_dump_float(double value)
469 {
470    if (!dumping)
471       return;
472 
473    trace_dump_writef("<float>%g</float>", value);
474 }
475 
trace_dump_bytes(const void * data,size_t size)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 
trace_dump_box_bytes(const void * data,struct pipe_resource * resource,const struct pipe_box * box,unsigned stride,unsigned slice_stride)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 
trace_dump_string(const char * str)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 
trace_dump_enum(const char * value)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 
trace_dump_array_begin(void)545 void trace_dump_array_begin(void)
546 {
547    if (!dumping)
548       return;
549 
550    trace_dump_writes("<array>");
551 }
552 
trace_dump_array_end(void)553 void trace_dump_array_end(void)
554 {
555    if (!dumping)
556       return;
557 
558    trace_dump_writes("</array>");
559 }
560 
trace_dump_elem_begin(void)561 void trace_dump_elem_begin(void)
562 {
563    if (!dumping)
564       return;
565 
566    trace_dump_writes("<elem>");
567 }
568 
trace_dump_elem_end(void)569 void trace_dump_elem_end(void)
570 {
571    if (!dumping)
572       return;
573 
574    trace_dump_writes("</elem>");
575 }
576 
trace_dump_struct_begin(const char * name)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 
trace_dump_struct_end(void)585 void trace_dump_struct_end(void)
586 {
587    if (!dumping)
588       return;
589 
590    trace_dump_writes("</struct>");
591 }
592 
trace_dump_member_begin(const char * name)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 
trace_dump_member_end(void)601 void trace_dump_member_end(void)
602 {
603    if (!dumping)
604       return;
605 
606    trace_dump_writes("</member>");
607 }
608 
trace_dump_null(void)609 void trace_dump_null(void)
610 {
611    if (!dumping)
612       return;
613 
614    trace_dump_writes("<null/>");
615 }
616 
trace_dump_ptr(const void * value)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 
trace_dump_resource_ptr(struct pipe_resource * _resource)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 
trace_dump_surface_ptr(struct pipe_surface * _surface)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 
trace_dump_transfer_ptr(struct pipe_transfer * _transfer)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