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("<");
104 else if(c == '>')
105 trace_dump_writes(">");
106 else if(c == '&')
107 trace_dump_writes("&");
108 else if(c == '\'')
109 trace_dump_writes("'");
110 else if(c == '\"')
111 trace_dump_writes(""");
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