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