Merge branch '7.8'
[mesa.git] / src / gallium / drivers / trace / tr_dump.c
1 /**************************************************************************
2 *
3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
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 TUNGSTEN GRAPHICS 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 <jrfonseca@tungstengraphics.com>
39 */
40
41 #include "pipe/p_config.h"
42
43 #if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_APPLE)
44 #include <stdlib.h>
45 #endif
46
47 #include "pipe/p_compiler.h"
48 #include "os/os_thread.h"
49 #include "os/os_stream.h"
50 #include "util/u_debug.h"
51 #include "util/u_memory.h"
52 #include "util/u_string.h"
53 #include "util/u_math.h"
54 #include "util/u_format.h"
55
56 #include "tr_dump.h"
57 #include "tr_screen.h"
58 #include "tr_texture.h"
59
60
61 static struct os_stream *stream = NULL;
62 static unsigned refcount = 0;
63 static pipe_mutex call_mutex;
64 static long unsigned call_no = 0;
65 static boolean dumping = FALSE;
66 static boolean initialized = FALSE;
67
68
69 static INLINE void
70 trace_dump_write(const char *buf, size_t size)
71 {
72 if(stream)
73 os_stream_write(stream, buf, size);
74 }
75
76
77 static INLINE void
78 trace_dump_writes(const char *s)
79 {
80 trace_dump_write(s, strlen(s));
81 }
82
83
84 static INLINE void
85 trace_dump_writef(const char *format, ...)
86 {
87 static char buf[1024];
88 unsigned len;
89 va_list ap;
90 va_start(ap, format);
91 len = util_vsnprintf(buf, sizeof(buf), format, ap);
92 va_end(ap);
93 trace_dump_write(buf, len);
94 }
95
96
97 static INLINE void
98 trace_dump_escape(const char *str)
99 {
100 const unsigned char *p = (const unsigned char *)str;
101 unsigned char c;
102 while((c = *p++) != 0) {
103 if(c == '<')
104 trace_dump_writes("&lt;");
105 else if(c == '>')
106 trace_dump_writes("&gt;");
107 else if(c == '&')
108 trace_dump_writes("&amp;");
109 else if(c == '\'')
110 trace_dump_writes("&apos;");
111 else if(c == '\"')
112 trace_dump_writes("&quot;");
113 else if(c >= 0x20 && c <= 0x7e)
114 trace_dump_writef("%c", c);
115 else
116 trace_dump_writef("&#%u;", c);
117 }
118 }
119
120
121 static INLINE void
122 trace_dump_indent(unsigned level)
123 {
124 unsigned i;
125 for(i = 0; i < level; ++i)
126 trace_dump_writes("\t");
127 }
128
129
130 static INLINE void
131 trace_dump_newline(void)
132 {
133 trace_dump_writes("\n");
134 }
135
136
137 static INLINE void
138 trace_dump_tag(const char *name)
139 {
140 trace_dump_writes("<");
141 trace_dump_writes(name);
142 trace_dump_writes("/>");
143 }
144
145
146 static INLINE void
147 trace_dump_tag_begin(const char *name)
148 {
149 trace_dump_writes("<");
150 trace_dump_writes(name);
151 trace_dump_writes(">");
152 }
153
154 static INLINE void
155 trace_dump_tag_begin1(const char *name,
156 const char *attr1, const char *value1)
157 {
158 trace_dump_writes("<");
159 trace_dump_writes(name);
160 trace_dump_writes(" ");
161 trace_dump_writes(attr1);
162 trace_dump_writes("='");
163 trace_dump_escape(value1);
164 trace_dump_writes("'>");
165 }
166
167
168 static INLINE void
169 trace_dump_tag_begin2(const char *name,
170 const char *attr1, const char *value1,
171 const char *attr2, const char *value2)
172 {
173 trace_dump_writes("<");
174 trace_dump_writes(name);
175 trace_dump_writes(" ");
176 trace_dump_writes(attr1);
177 trace_dump_writes("=\'");
178 trace_dump_escape(value1);
179 trace_dump_writes("\' ");
180 trace_dump_writes(attr2);
181 trace_dump_writes("=\'");
182 trace_dump_escape(value2);
183 trace_dump_writes("\'>");
184 }
185
186
187 static INLINE void
188 trace_dump_tag_begin3(const char *name,
189 const char *attr1, const char *value1,
190 const char *attr2, const char *value2,
191 const char *attr3, const char *value3)
192 {
193 trace_dump_writes("<");
194 trace_dump_writes(name);
195 trace_dump_writes(" ");
196 trace_dump_writes(attr1);
197 trace_dump_writes("=\'");
198 trace_dump_escape(value1);
199 trace_dump_writes("\' ");
200 trace_dump_writes(attr2);
201 trace_dump_writes("=\'");
202 trace_dump_escape(value2);
203 trace_dump_writes("\' ");
204 trace_dump_writes(attr3);
205 trace_dump_writes("=\'");
206 trace_dump_escape(value3);
207 trace_dump_writes("\'>");
208 }
209
210
211 static INLINE void
212 trace_dump_tag_end(const char *name)
213 {
214 trace_dump_writes("</");
215 trace_dump_writes(name);
216 trace_dump_writes(">");
217 }
218
219 static void
220 trace_dump_trace_close(void)
221 {
222 if(stream) {
223 trace_dump_writes("</trace>\n");
224 os_stream_close(stream);
225 stream = NULL;
226 refcount = 0;
227 call_no = 0;
228 pipe_mutex_destroy(call_mutex);
229 }
230 }
231
232 void trace_dump_init()
233 {
234 if (initialized)
235 return;
236
237 pipe_mutex_init(call_mutex);
238 dumping = FALSE;
239 initialized = TRUE;
240 }
241
242 boolean trace_dump_trace_begin()
243 {
244 const char *filename;
245
246 assert(initialized);
247
248 filename = debug_get_option("GALLIUM_TRACE", NULL);
249 if(!filename)
250 return FALSE;
251
252 if(!stream) {
253
254 stream = os_file_stream_create(filename);
255 if(!stream)
256 return FALSE;
257
258 trace_dump_writes("<?xml version='1.0' encoding='UTF-8'?>\n");
259 trace_dump_writes("<?xml-stylesheet type='text/xsl' href='trace.xsl'?>\n");
260 trace_dump_writes("<trace version='0.1'>\n");
261
262 #if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_APPLE)
263 /* Linux applications rarely cleanup GL / Gallium resources so catch
264 * application exit here */
265 atexit(trace_dump_trace_close);
266 #endif
267 }
268
269 ++refcount;
270
271 return TRUE;
272 }
273
274 boolean trace_dump_trace_enabled(void)
275 {
276 return stream ? TRUE : FALSE;
277 }
278
279 void trace_dump_trace_end(void)
280 {
281 if(stream)
282 if(!--refcount)
283 trace_dump_trace_close();
284 }
285
286 /*
287 * Call lock
288 */
289
290 void trace_dump_call_lock(void)
291 {
292 pipe_mutex_lock(call_mutex);
293 }
294
295 void trace_dump_call_unlock(void)
296 {
297 pipe_mutex_unlock(call_mutex);
298 }
299
300 /*
301 * Dumping control
302 */
303
304 void trace_dumping_start_locked(void)
305 {
306 dumping = TRUE;
307 }
308
309 void trace_dumping_stop_locked(void)
310 {
311 dumping = FALSE;
312 }
313
314 boolean trace_dumping_enabled_locked(void)
315 {
316 return dumping;
317 }
318
319 void trace_dumping_start(void)
320 {
321 pipe_mutex_lock(call_mutex);
322 trace_dumping_start_locked();
323 pipe_mutex_unlock(call_mutex);
324 }
325
326 void trace_dumping_stop(void)
327 {
328 pipe_mutex_lock(call_mutex);
329 trace_dumping_stop_locked();
330 pipe_mutex_unlock(call_mutex);
331 }
332
333 boolean trace_dumping_enabled(void)
334 {
335 boolean ret;
336 pipe_mutex_lock(call_mutex);
337 ret = trace_dumping_enabled_locked();
338 pipe_mutex_unlock(call_mutex);
339 return ret;
340 }
341
342 /*
343 * Dump functions
344 */
345
346 void trace_dump_call_begin_locked(const char *klass, const char *method)
347 {
348 if (!dumping)
349 return;
350
351 ++call_no;
352 trace_dump_indent(1);
353 trace_dump_writes("<call no=\'");
354 trace_dump_writef("%lu", call_no);
355 trace_dump_writes("\' class=\'");
356 trace_dump_escape(klass);
357 trace_dump_writes("\' method=\'");
358 trace_dump_escape(method);
359 trace_dump_writes("\'>");
360 trace_dump_newline();
361 }
362
363 void trace_dump_call_end_locked(void)
364 {
365 if (!dumping)
366 return;
367
368 trace_dump_indent(1);
369 trace_dump_tag_end("call");
370 trace_dump_newline();
371 os_stream_flush(stream);
372 }
373
374 void trace_dump_call_begin(const char *klass, const char *method)
375 {
376 pipe_mutex_lock(call_mutex);
377 trace_dump_call_begin_locked(klass, method);
378 }
379
380 void trace_dump_call_end(void)
381 {
382 trace_dump_call_end_locked();
383 pipe_mutex_unlock(call_mutex);
384 }
385
386 void trace_dump_arg_begin(const char *name)
387 {
388 if (!dumping)
389 return;
390
391 trace_dump_indent(2);
392 trace_dump_tag_begin1("arg", "name", name);
393 }
394
395 void trace_dump_arg_end(void)
396 {
397 if (!dumping)
398 return;
399
400 trace_dump_tag_end("arg");
401 trace_dump_newline();
402 }
403
404 void trace_dump_ret_begin(void)
405 {
406 if (!dumping)
407 return;
408
409 trace_dump_indent(2);
410 trace_dump_tag_begin("ret");
411 }
412
413 void trace_dump_ret_end(void)
414 {
415 if (!dumping)
416 return;
417
418 trace_dump_tag_end("ret");
419 trace_dump_newline();
420 }
421
422 void trace_dump_bool(int value)
423 {
424 if (!dumping)
425 return;
426
427 trace_dump_writef("<bool>%c</bool>", value ? '1' : '0');
428 }
429
430 void trace_dump_int(long long int value)
431 {
432 if (!dumping)
433 return;
434
435 trace_dump_writef("<int>%lli</int>", value);
436 }
437
438 void trace_dump_uint(long long unsigned value)
439 {
440 if (!dumping)
441 return;
442
443 trace_dump_writef("<uint>%llu</uint>", value);
444 }
445
446 void trace_dump_float(double value)
447 {
448 if (!dumping)
449 return;
450
451 trace_dump_writef("<float>%g</float>", value);
452 }
453
454 void trace_dump_bytes(const void *data,
455 size_t size)
456 {
457 static const char hex_table[16] = "0123456789ABCDEF";
458 const uint8_t *p = data;
459 size_t i;
460
461 if (!dumping)
462 return;
463
464 trace_dump_writes("<bytes>");
465 for(i = 0; i < size; ++i) {
466 uint8_t byte = *p++;
467 char hex[2];
468 hex[0] = hex_table[byte >> 4];
469 hex[1] = hex_table[byte & 0xf];
470 trace_dump_write(hex, 2);
471 }
472 trace_dump_writes("</bytes>");
473 }
474
475 void trace_dump_box_bytes(const void *data,
476 enum pipe_format format,
477 const struct pipe_box *box,
478 unsigned stride,
479 unsigned slice_stride)
480 {
481 size_t size;
482
483 if (slice_stride)
484 size = box->depth * slice_stride;
485 else if (stride)
486 size = util_format_get_nblocksy(format, box->height) * stride;
487 else {
488 size = util_format_get_nblocksx(format, box->width) * util_format_get_blocksize(format);
489 }
490
491 trace_dump_bytes(data, size);
492 }
493
494 void trace_dump_string(const char *str)
495 {
496 if (!dumping)
497 return;
498
499 trace_dump_writes("<string>");
500 trace_dump_escape(str);
501 trace_dump_writes("</string>");
502 }
503
504 void trace_dump_enum(const char *value)
505 {
506 if (!dumping)
507 return;
508
509 trace_dump_writes("<enum>");
510 trace_dump_escape(value);
511 trace_dump_writes("</enum>");
512 }
513
514 void trace_dump_array_begin(void)
515 {
516 if (!dumping)
517 return;
518
519 trace_dump_writes("<array>");
520 }
521
522 void trace_dump_array_end(void)
523 {
524 if (!dumping)
525 return;
526
527 trace_dump_writes("</array>");
528 }
529
530 void trace_dump_elem_begin(void)
531 {
532 if (!dumping)
533 return;
534
535 trace_dump_writes("<elem>");
536 }
537
538 void trace_dump_elem_end(void)
539 {
540 if (!dumping)
541 return;
542
543 trace_dump_writes("</elem>");
544 }
545
546 void trace_dump_struct_begin(const char *name)
547 {
548 if (!dumping)
549 return;
550
551 trace_dump_writef("<struct name='%s'>", name);
552 }
553
554 void trace_dump_struct_end(void)
555 {
556 if (!dumping)
557 return;
558
559 trace_dump_writes("</struct>");
560 }
561
562 void trace_dump_member_begin(const char *name)
563 {
564 if (!dumping)
565 return;
566
567 trace_dump_writef("<member name='%s'>", name);
568 }
569
570 void trace_dump_member_end(void)
571 {
572 if (!dumping)
573 return;
574
575 trace_dump_writes("</member>");
576 }
577
578 void trace_dump_null(void)
579 {
580 if (!dumping)
581 return;
582
583 trace_dump_writes("<null/>");
584 }
585
586 void trace_dump_ptr(const void *value)
587 {
588 if (!dumping)
589 return;
590
591 if(value)
592 trace_dump_writef("<ptr>0x%08lx</ptr>", (unsigned long)(uintptr_t)value);
593 else
594 trace_dump_null();
595 }
596
597
598 void trace_dump_resource_ptr(struct pipe_resource *_resource)
599 {
600 if (!dumping)
601 return;
602
603 if (_resource) {
604 struct trace_resource *tr_resource = trace_resource(_resource);
605 trace_dump_ptr(tr_resource->resource);
606 } else {
607 trace_dump_null();
608 }
609 }
610
611 void trace_dump_surface_ptr(struct pipe_surface *_surface)
612 {
613 if (!dumping)
614 return;
615
616 if (_surface) {
617 struct trace_surface *tr_surf = trace_surface(_surface);
618 trace_dump_ptr(tr_surf->surface);
619 } else {
620 trace_dump_null();
621 }
622 }
623
624 void trace_dump_transfer_ptr(struct pipe_transfer *_transfer)
625 {
626 if (!dumping)
627 return;
628
629 if (_transfer) {
630 struct trace_transfer *tr_tran = trace_transfer(_transfer);
631 trace_dump_ptr(tr_tran->transfer);
632 } else {
633 trace_dump_null();
634 }
635 }