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