• 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 /* for access() */
47 #ifdef _WIN32
48 # include <io.h>
49 #endif
50 
51 #include "pipe/p_compiler.h"
52 #include "os/os_thread.h"
53 #include "util/os_time.h"
54 #include "util/u_debug.h"
55 #include "util/u_memory.h"
56 #include "util/u_string.h"
57 #include "util/u_math.h"
58 #include "util/format/u_format.h"
59 #include "compiler/nir/nir.h"
60 
61 #include "tr_dump.h"
62 #include "tr_screen.h"
63 #include "tr_texture.h"
64 
65 
66 static bool close_stream = false;
67 static FILE *stream = NULL;
68 static mtx_t call_mutex = _MTX_INITIALIZER_NP;
69 static long unsigned call_no = 0;
70 static bool dumping = false;
71 static long nir_count = 0;
72 
73 static bool trigger_active = true;
74 static char *trigger_filename = NULL;
75 
76 void
trace_dump_trigger_active(bool active)77 trace_dump_trigger_active(bool active)
78 {
79    trigger_active = active;
80 }
81 
82 void
trace_dump_check_trigger(void)83 trace_dump_check_trigger(void)
84 {
85    if (!trigger_filename)
86       return;
87 
88    mtx_lock(&call_mutex);
89    if (trigger_active) {
90       trigger_active = false;
91    } else {
92       if (!access(trigger_filename, 2 /* W_OK but compiles on Windows */)) {
93          if (!unlink(trigger_filename)) {
94             trigger_active = true;
95          } else {
96             fprintf(stderr, "error removing trigger file\n");
97             trigger_active = false;
98          }
99       }
100    }
101    mtx_unlock(&call_mutex);
102 }
103 
104 bool
trace_dump_is_triggered(void)105 trace_dump_is_triggered(void)
106 {
107    return trigger_active && !!trigger_filename;
108 }
109 
110 static inline void
trace_dump_write(const char * buf,size_t size)111 trace_dump_write(const char *buf, size_t size)
112 {
113    if (stream && trigger_active) {
114       fwrite(buf, size, 1, stream);
115    }
116 }
117 
118 
119 static inline void
trace_dump_writes(const char * s)120 trace_dump_writes(const char *s)
121 {
122    trace_dump_write(s, strlen(s));
123 }
124 
125 
126 static inline void
trace_dump_writef(const char * format,...)127 trace_dump_writef(const char *format, ...)
128 {
129    static char buf[1024];
130    unsigned len;
131    va_list ap;
132    va_start(ap, format);
133    len = vsnprintf(buf, sizeof(buf), format, ap);
134    va_end(ap);
135    trace_dump_write(buf, len);
136 }
137 
138 
139 static inline void
trace_dump_escape(const char * str)140 trace_dump_escape(const char *str)
141 {
142    const unsigned char *p = (const unsigned char *)str;
143    unsigned char c;
144    while((c = *p++) != 0) {
145       if(c == '<')
146          trace_dump_writes("&lt;");
147       else if(c == '>')
148          trace_dump_writes("&gt;");
149       else if(c == '&')
150          trace_dump_writes("&amp;");
151       else if(c == '\'')
152          trace_dump_writes("&apos;");
153       else if(c == '\"')
154          trace_dump_writes("&quot;");
155       else if(c >= 0x20 && c <= 0x7e)
156          trace_dump_writef("%c", c);
157       else
158          trace_dump_writef("&#%u;", c);
159    }
160 }
161 
162 
163 static inline void
trace_dump_indent(unsigned level)164 trace_dump_indent(unsigned level)
165 {
166    unsigned i;
167    for(i = 0; i < level; ++i)
168       trace_dump_writes("\t");
169 }
170 
171 
172 static inline void
trace_dump_newline(void)173 trace_dump_newline(void)
174 {
175    trace_dump_writes("\n");
176 }
177 
178 
179 static inline void
trace_dump_tag_begin(const char * name)180 trace_dump_tag_begin(const char *name)
181 {
182    trace_dump_writes("<");
183    trace_dump_writes(name);
184    trace_dump_writes(">");
185 }
186 
187 static inline void
trace_dump_tag_begin1(const char * name,const char * attr1,const char * value1)188 trace_dump_tag_begin1(const char *name,
189                       const char *attr1, const char *value1)
190 {
191    trace_dump_writes("<");
192    trace_dump_writes(name);
193    trace_dump_writes(" ");
194    trace_dump_writes(attr1);
195    trace_dump_writes("='");
196    trace_dump_escape(value1);
197    trace_dump_writes("'>");
198 }
199 
200 
201 static inline void
trace_dump_tag_end(const char * name)202 trace_dump_tag_end(const char *name)
203 {
204    trace_dump_writes("</");
205    trace_dump_writes(name);
206    trace_dump_writes(">");
207 }
208 
209 void
trace_dump_trace_flush(void)210 trace_dump_trace_flush(void)
211 {
212    if (stream) {
213       fflush(stream);
214    }
215 }
216 
217 static void
trace_dump_trace_close(void)218 trace_dump_trace_close(void)
219 {
220    if (stream) {
221       trigger_active = true;
222       trace_dump_writes("</trace>\n");
223       if (close_stream) {
224          fclose(stream);
225          close_stream = false;
226          stream = NULL;
227       }
228       call_no = 0;
229       free(trigger_filename);
230    }
231 }
232 
233 
234 static void
trace_dump_call_time(int64_t time)235 trace_dump_call_time(int64_t time)
236 {
237    if (stream) {
238       trace_dump_indent(2);
239       trace_dump_tag_begin("time");
240       trace_dump_int(time);
241       trace_dump_tag_end("time");
242       trace_dump_newline();
243    }
244 }
245 
246 
247 bool
trace_dump_trace_begin(void)248 trace_dump_trace_begin(void)
249 {
250    const char *filename;
251 
252    filename = debug_get_option("GALLIUM_TRACE", NULL);
253    if (!filename)
254       return false;
255 
256    nir_count = debug_get_num_option("GALLIUM_TRACE_NIR", 32);
257 
258    if (!stream) {
259 
260       if (strcmp(filename, "stderr") == 0) {
261          close_stream = false;
262          stream = stderr;
263       }
264       else if (strcmp(filename, "stdout") == 0) {
265          close_stream = false;
266          stream = stdout;
267       }
268       else {
269          close_stream = true;
270          stream = fopen(filename, "wt");
271          if (!stream)
272             return false;
273       }
274 
275       trace_dump_writes("<?xml version='1.0' encoding='UTF-8'?>\n");
276       trace_dump_writes("<?xml-stylesheet type='text/xsl' href='trace.xsl'?>\n");
277       trace_dump_writes("<trace version='0.1'>\n");
278 
279       /* Many applications don't exit cleanly, others may create and destroy a
280        * screen multiple times, so we only write </trace> tag and close at exit
281        * time.
282        */
283       atexit(trace_dump_trace_close);
284 
285       const char *trigger = debug_get_option("GALLIUM_TRACE_TRIGGER", NULL);
286       if (trigger) {
287          trigger_filename = strdup(trigger);
288          trigger_active = false;
289       } else
290          trigger_active = true;
291    }
292 
293    return true;
294 }
295 
trace_dump_trace_enabled(void)296 bool trace_dump_trace_enabled(void)
297 {
298    return stream ? true : false;
299 }
300 
301 /*
302  * Call lock
303  */
304 
trace_dump_call_lock(void)305 void trace_dump_call_lock(void)
306 {
307    mtx_lock(&call_mutex);
308 }
309 
trace_dump_call_unlock(void)310 void trace_dump_call_unlock(void)
311 {
312    mtx_unlock(&call_mutex);
313 }
314 
315 /*
316  * Dumping control
317  */
318 
trace_dumping_start_locked(void)319 void trace_dumping_start_locked(void)
320 {
321    dumping = true;
322 }
323 
trace_dumping_stop_locked(void)324 void trace_dumping_stop_locked(void)
325 {
326    dumping = false;
327 }
328 
trace_dumping_enabled_locked(void)329 bool trace_dumping_enabled_locked(void)
330 {
331    return dumping;
332 }
333 
trace_dumping_start(void)334 void trace_dumping_start(void)
335 {
336    mtx_lock(&call_mutex);
337    trace_dumping_start_locked();
338    mtx_unlock(&call_mutex);
339 }
340 
trace_dumping_stop(void)341 void trace_dumping_stop(void)
342 {
343    mtx_lock(&call_mutex);
344    trace_dumping_stop_locked();
345    mtx_unlock(&call_mutex);
346 }
347 
trace_dumping_enabled(void)348 bool trace_dumping_enabled(void)
349 {
350    bool ret;
351    mtx_lock(&call_mutex);
352    ret = trace_dumping_enabled_locked();
353    mtx_unlock(&call_mutex);
354    return ret;
355 }
356 
357 /*
358  * Dump functions
359  */
360 
361 static int64_t call_start_time = 0;
362 
trace_dump_call_begin_locked(const char * klass,const char * method)363 void trace_dump_call_begin_locked(const char *klass, const char *method)
364 {
365    if (!dumping)
366       return;
367 
368    ++call_no;
369    trace_dump_indent(1);
370    trace_dump_writes("<call no=\'");
371    trace_dump_writef("%lu", call_no);
372    trace_dump_writes("\' class=\'");
373    trace_dump_escape(klass);
374    trace_dump_writes("\' method=\'");
375    trace_dump_escape(method);
376    trace_dump_writes("\'>");
377    trace_dump_newline();
378 
379    call_start_time = os_time_get();
380 }
381 
trace_dump_call_end_locked(void)382 void trace_dump_call_end_locked(void)
383 {
384    int64_t call_end_time;
385 
386    if (!dumping)
387       return;
388 
389    call_end_time = os_time_get();
390 
391    trace_dump_call_time(call_end_time - call_start_time);
392    trace_dump_indent(1);
393    trace_dump_tag_end("call");
394    trace_dump_newline();
395    fflush(stream);
396 }
397 
trace_dump_call_begin(const char * klass,const char * method)398 void trace_dump_call_begin(const char *klass, const char *method)
399 {
400    mtx_lock(&call_mutex);
401    trace_dump_call_begin_locked(klass, method);
402 }
403 
trace_dump_call_end(void)404 void trace_dump_call_end(void)
405 {
406    trace_dump_call_end_locked();
407    mtx_unlock(&call_mutex);
408 }
409 
trace_dump_arg_begin(const char * name)410 void trace_dump_arg_begin(const char *name)
411 {
412    if (!dumping)
413       return;
414 
415    trace_dump_indent(2);
416    trace_dump_tag_begin1("arg", "name", name);
417 }
418 
trace_dump_arg_end(void)419 void trace_dump_arg_end(void)
420 {
421    if (!dumping)
422       return;
423 
424    trace_dump_tag_end("arg");
425    trace_dump_newline();
426 }
427 
trace_dump_ret_begin(void)428 void trace_dump_ret_begin(void)
429 {
430    if (!dumping)
431       return;
432 
433    trace_dump_indent(2);
434    trace_dump_tag_begin("ret");
435 }
436 
trace_dump_ret_end(void)437 void trace_dump_ret_end(void)
438 {
439    if (!dumping)
440       return;
441 
442    trace_dump_tag_end("ret");
443    trace_dump_newline();
444 }
445 
trace_dump_bool(int value)446 void trace_dump_bool(int value)
447 {
448    if (!dumping)
449       return;
450 
451    trace_dump_writef("<bool>%c</bool>", value ? '1' : '0');
452 }
453 
trace_dump_int(long long int value)454 void trace_dump_int(long long int value)
455 {
456    if (!dumping)
457       return;
458 
459    trace_dump_writef("<int>%lli</int>", value);
460 }
461 
trace_dump_uint(long long unsigned value)462 void trace_dump_uint(long long unsigned value)
463 {
464    if (!dumping)
465       return;
466 
467    trace_dump_writef("<uint>%llu</uint>", value);
468 }
469 
trace_dump_float(double value)470 void trace_dump_float(double value)
471 {
472    if (!dumping)
473       return;
474 
475    trace_dump_writef("<float>%g</float>", value);
476 }
477 
trace_dump_bytes(const void * data,size_t size)478 void trace_dump_bytes(const void *data,
479                       size_t size)
480 {
481    static const char hex_table[16] = "0123456789ABCDEF";
482    const uint8_t *p = data;
483    size_t i;
484 
485    if (!dumping)
486       return;
487 
488    trace_dump_writes("<bytes>");
489    for(i = 0; i < size; ++i) {
490       uint8_t byte = *p++;
491       char hex[2];
492       hex[0] = hex_table[byte >> 4];
493       hex[1] = hex_table[byte & 0xf];
494       trace_dump_write(hex, 2);
495    }
496    trace_dump_writes("</bytes>");
497 }
498 
trace_dump_box_bytes(const void * data,struct pipe_resource * resource,const struct pipe_box * box,unsigned stride,unsigned slice_stride)499 void trace_dump_box_bytes(const void *data,
500                           struct pipe_resource *resource,
501 			  const struct pipe_box *box,
502 			  unsigned stride,
503 			  unsigned slice_stride)
504 {
505    enum pipe_format format = resource->format;
506    size_t size;
507 
508    assert(box->height > 0);
509    assert(box->depth > 0);
510 
511    size =  util_format_get_nblocksx(format, box->width )      * util_format_get_blocksize(format)
512         + (util_format_get_nblocksy(format, box->height) - 1) * stride
513         +                                  (box->depth   - 1) * slice_stride;
514 
515    /*
516     * Only dump buffer transfers to avoid huge files.
517     * TODO: Make this run-time configurable
518     */
519    if (resource->target != PIPE_BUFFER) {
520       size = 0;
521    }
522 
523    trace_dump_bytes(data, size);
524 }
525 
trace_dump_string(const char * str)526 void trace_dump_string(const char *str)
527 {
528    if (!dumping)
529       return;
530 
531    trace_dump_writes("<string>");
532    trace_dump_escape(str);
533    trace_dump_writes("</string>");
534 }
535 
trace_dump_enum(const char * value)536 void trace_dump_enum(const char *value)
537 {
538    if (!dumping)
539       return;
540 
541    trace_dump_writes("<enum>");
542    trace_dump_escape(value);
543    trace_dump_writes("</enum>");
544 }
545 
trace_dump_array_begin(void)546 void trace_dump_array_begin(void)
547 {
548    if (!dumping)
549       return;
550 
551    trace_dump_writes("<array>");
552 }
553 
trace_dump_array_end(void)554 void trace_dump_array_end(void)
555 {
556    if (!dumping)
557       return;
558 
559    trace_dump_writes("</array>");
560 }
561 
trace_dump_elem_begin(void)562 void trace_dump_elem_begin(void)
563 {
564    if (!dumping)
565       return;
566 
567    trace_dump_writes("<elem>");
568 }
569 
trace_dump_elem_end(void)570 void trace_dump_elem_end(void)
571 {
572    if (!dumping)
573       return;
574 
575    trace_dump_writes("</elem>");
576 }
577 
trace_dump_struct_begin(const char * name)578 void trace_dump_struct_begin(const char *name)
579 {
580    if (!dumping)
581       return;
582 
583    trace_dump_writef("<struct name='%s'>", name);
584 }
585 
trace_dump_struct_end(void)586 void trace_dump_struct_end(void)
587 {
588    if (!dumping)
589       return;
590 
591    trace_dump_writes("</struct>");
592 }
593 
trace_dump_member_begin(const char * name)594 void trace_dump_member_begin(const char *name)
595 {
596    if (!dumping)
597       return;
598 
599    trace_dump_writef("<member name='%s'>", name);
600 }
601 
trace_dump_member_end(void)602 void trace_dump_member_end(void)
603 {
604    if (!dumping)
605       return;
606 
607    trace_dump_writes("</member>");
608 }
609 
trace_dump_null(void)610 void trace_dump_null(void)
611 {
612    if (!dumping)
613       return;
614 
615    trace_dump_writes("<null/>");
616 }
617 
trace_dump_ptr(const void * value)618 void trace_dump_ptr(const void *value)
619 {
620    if (!dumping)
621       return;
622 
623    if(value)
624       trace_dump_writef("<ptr>0x%08lx</ptr>", (unsigned long)(uintptr_t)value);
625    else
626       trace_dump_null();
627 }
628 
trace_dump_surface_ptr(struct pipe_surface * _surface)629 void trace_dump_surface_ptr(struct pipe_surface *_surface)
630 {
631    if (!dumping)
632       return;
633 
634    if (_surface) {
635       struct trace_surface *tr_surf = trace_surface(_surface);
636       trace_dump_ptr(tr_surf->surface);
637    } else {
638       trace_dump_null();
639    }
640 }
641 
trace_dump_transfer_ptr(struct pipe_transfer * _transfer)642 void trace_dump_transfer_ptr(struct pipe_transfer *_transfer)
643 {
644    if (!dumping)
645       return;
646 
647    if (_transfer) {
648       struct trace_transfer *tr_tran = trace_transfer(_transfer);
649       trace_dump_ptr(tr_tran->transfer);
650    } else {
651       trace_dump_null();
652    }
653 }
654 
trace_dump_nir(void * nir)655 void trace_dump_nir(void *nir)
656 {
657    if (!dumping)
658       return;
659 
660    if (nir_count < 0) {
661       fputs("<string>...</string>", stream);
662       return;
663    }
664 
665    if ((nir_count--) == 0) {
666       fputs("<string>Set GALLIUM_TRACE_NIR to a sufficiently big number "
667             "to enable NIR shader dumping.</string>", stream);
668       return;
669    }
670 
671    // NIR doesn't have a print to string function.  Use CDATA and hope for the
672    // best.
673    if (stream) {
674       fputs("<string><![CDATA[", stream);
675       nir_print_shader(nir, stream);
676       fputs("]]></string>", stream);
677    }
678 }
679