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("<");
147 else if(c == '>')
148 trace_dump_writes(">");
149 else if(c == '&')
150 trace_dump_writes("&");
151 else if(c == '\'')
152 trace_dump_writes("'");
153 else if(c == '\"')
154 trace_dump_writes(""");
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