trace: Never close stdout/stderr.
[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 #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 static unsigned refcount = 0;
63 pipe_static_mutex(call_mutex);
64 static long unsigned call_no = 0;
65 static boolean dumping = FALSE;
66
67
68 static INLINE void
69 trace_dump_write(const char *buf, size_t size)
70 {
71 if (stream) {
72 fwrite(buf, size, 1, stream);
73 }
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 void
220 trace_dump_trace_flush(void)
221 {
222 if(stream) {
223 fflush(stream);
224 }
225 }
226
227 static void
228 trace_dump_trace_close(void)
229 {
230 if(stream) {
231 trace_dump_writes("</trace>\n");
232 if (close_stream) {
233 fclose(stream);
234 close_stream = FALSE;
235 stream = NULL;
236 }
237 refcount = 0;
238 call_no = 0;
239 }
240 }
241
242
243 static void
244 trace_dump_call_time(int64_t time)
245 {
246 if (stream) {
247 trace_dump_indent(2);
248 trace_dump_tag_begin("time");
249 trace_dump_int(time);
250 trace_dump_tag_end("time");
251 trace_dump_newline();
252 }
253 }
254
255
256 boolean
257 trace_dump_trace_begin(void)
258 {
259 const char *filename;
260
261 filename = debug_get_option("GALLIUM_TRACE", NULL);
262 if(!filename)
263 return FALSE;
264
265 if(!stream) {
266
267 if (strcmp(filename, "stderr") == 0) {
268 close_stream = FALSE;
269 stream = stderr;
270 }
271 else if (strcmp(filename, "stdout") == 0) {
272 close_stream = FALSE;
273 stream = stdout;
274 }
275 else {
276 close_stream = TRUE;
277 stream = fopen(filename, "wt");
278 if (!stream)
279 return FALSE;
280 }
281
282 trace_dump_writes("<?xml version='1.0' encoding='UTF-8'?>\n");
283 trace_dump_writes("<?xml-stylesheet type='text/xsl' href='trace.xsl'?>\n");
284 trace_dump_writes("<trace version='0.1'>\n");
285
286 #if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_APPLE)
287 /* Linux applications rarely cleanup GL / Gallium resources so catch
288 * application exit here */
289 atexit(trace_dump_trace_close);
290 #endif
291 }
292
293 ++refcount;
294
295 return TRUE;
296 }
297
298 boolean trace_dump_trace_enabled(void)
299 {
300 return stream ? TRUE : FALSE;
301 }
302
303 void trace_dump_trace_end(void)
304 {
305 if(stream)
306 if(!--refcount)
307 trace_dump_trace_close();
308 }
309
310 /*
311 * Call lock
312 */
313
314 void trace_dump_call_lock(void)
315 {
316 pipe_mutex_lock(call_mutex);
317 }
318
319 void trace_dump_call_unlock(void)
320 {
321 pipe_mutex_unlock(call_mutex);
322 }
323
324 /*
325 * Dumping control
326 */
327
328 void trace_dumping_start_locked(void)
329 {
330 dumping = TRUE;
331 }
332
333 void trace_dumping_stop_locked(void)
334 {
335 dumping = FALSE;
336 }
337
338 boolean trace_dumping_enabled_locked(void)
339 {
340 return dumping;
341 }
342
343 void trace_dumping_start(void)
344 {
345 pipe_mutex_lock(call_mutex);
346 trace_dumping_start_locked();
347 pipe_mutex_unlock(call_mutex);
348 }
349
350 void trace_dumping_stop(void)
351 {
352 pipe_mutex_lock(call_mutex);
353 trace_dumping_stop_locked();
354 pipe_mutex_unlock(call_mutex);
355 }
356
357 boolean trace_dumping_enabled(void)
358 {
359 boolean ret;
360 pipe_mutex_lock(call_mutex);
361 ret = trace_dumping_enabled_locked();
362 pipe_mutex_unlock(call_mutex);
363 return ret;
364 }
365
366 /*
367 * Dump functions
368 */
369
370 static int64_t call_start_time = 0;
371
372 void trace_dump_call_begin_locked(const char *klass, const char *method)
373 {
374 if (!dumping)
375 return;
376
377 ++call_no;
378 trace_dump_indent(1);
379 trace_dump_writes("<call no=\'");
380 trace_dump_writef("%lu", call_no);
381 trace_dump_writes("\' class=\'");
382 trace_dump_escape(klass);
383 trace_dump_writes("\' method=\'");
384 trace_dump_escape(method);
385 trace_dump_writes("\'>");
386 trace_dump_newline();
387
388 call_start_time = os_time_get();
389 }
390
391 void trace_dump_call_end_locked(void)
392 {
393 int64_t call_end_time;
394
395 if (!dumping)
396 return;
397
398 call_end_time = os_time_get();
399
400 trace_dump_call_time(call_end_time - call_start_time);
401 trace_dump_indent(1);
402 trace_dump_tag_end("call");
403 trace_dump_newline();
404 fflush(stream);
405 }
406
407 void trace_dump_call_begin(const char *klass, const char *method)
408 {
409 pipe_mutex_lock(call_mutex);
410 trace_dump_call_begin_locked(klass, method);
411 }
412
413 void trace_dump_call_end(void)
414 {
415 trace_dump_call_end_locked();
416 pipe_mutex_unlock(call_mutex);
417 }
418
419 void trace_dump_arg_begin(const char *name)
420 {
421 if (!dumping)
422 return;
423
424 trace_dump_indent(2);
425 trace_dump_tag_begin1("arg", "name", name);
426 }
427
428 void trace_dump_arg_end(void)
429 {
430 if (!dumping)
431 return;
432
433 trace_dump_tag_end("arg");
434 trace_dump_newline();
435 }
436
437 void trace_dump_ret_begin(void)
438 {
439 if (!dumping)
440 return;
441
442 trace_dump_indent(2);
443 trace_dump_tag_begin("ret");
444 }
445
446 void trace_dump_ret_end(void)
447 {
448 if (!dumping)
449 return;
450
451 trace_dump_tag_end("ret");
452 trace_dump_newline();
453 }
454
455 void trace_dump_bool(int value)
456 {
457 if (!dumping)
458 return;
459
460 trace_dump_writef("<bool>%c</bool>", value ? '1' : '0');
461 }
462
463 void trace_dump_int(long long int value)
464 {
465 if (!dumping)
466 return;
467
468 trace_dump_writef("<int>%lli</int>", value);
469 }
470
471 void trace_dump_uint(long long unsigned value)
472 {
473 if (!dumping)
474 return;
475
476 trace_dump_writef("<uint>%llu</uint>", value);
477 }
478
479 void trace_dump_float(double value)
480 {
481 if (!dumping)
482 return;
483
484 trace_dump_writef("<float>%g</float>", value);
485 }
486
487 void trace_dump_bytes(const void *data,
488 size_t size)
489 {
490 static const char hex_table[16] = "0123456789ABCDEF";
491 const uint8_t *p = data;
492 size_t i;
493
494 if (!dumping)
495 return;
496
497 trace_dump_writes("<bytes>");
498 for(i = 0; i < size; ++i) {
499 uint8_t byte = *p++;
500 char hex[2];
501 hex[0] = hex_table[byte >> 4];
502 hex[1] = hex_table[byte & 0xf];
503 trace_dump_write(hex, 2);
504 }
505 trace_dump_writes("</bytes>");
506 }
507
508 void trace_dump_box_bytes(const void *data,
509 enum pipe_format format,
510 const struct pipe_box *box,
511 unsigned stride,
512 unsigned slice_stride)
513 {
514 size_t size;
515
516 if (slice_stride)
517 size = box->depth * slice_stride;
518 else if (stride)
519 size = util_format_get_nblocksy(format, box->height) * stride;
520 else {
521 size = util_format_get_nblocksx(format, box->width) * util_format_get_blocksize(format);
522 }
523
524 trace_dump_bytes(data, size);
525 }
526
527 void trace_dump_string(const char *str)
528 {
529 if (!dumping)
530 return;
531
532 trace_dump_writes("<string>");
533 trace_dump_escape(str);
534 trace_dump_writes("</string>");
535 }
536
537 void trace_dump_enum(const char *value)
538 {
539 if (!dumping)
540 return;
541
542 trace_dump_writes("<enum>");
543 trace_dump_escape(value);
544 trace_dump_writes("</enum>");
545 }
546
547 void trace_dump_array_begin(void)
548 {
549 if (!dumping)
550 return;
551
552 trace_dump_writes("<array>");
553 }
554
555 void trace_dump_array_end(void)
556 {
557 if (!dumping)
558 return;
559
560 trace_dump_writes("</array>");
561 }
562
563 void trace_dump_elem_begin(void)
564 {
565 if (!dumping)
566 return;
567
568 trace_dump_writes("<elem>");
569 }
570
571 void trace_dump_elem_end(void)
572 {
573 if (!dumping)
574 return;
575
576 trace_dump_writes("</elem>");
577 }
578
579 void trace_dump_struct_begin(const char *name)
580 {
581 if (!dumping)
582 return;
583
584 trace_dump_writef("<struct name='%s'>", name);
585 }
586
587 void trace_dump_struct_end(void)
588 {
589 if (!dumping)
590 return;
591
592 trace_dump_writes("</struct>");
593 }
594
595 void trace_dump_member_begin(const char *name)
596 {
597 if (!dumping)
598 return;
599
600 trace_dump_writef("<member name='%s'>", name);
601 }
602
603 void trace_dump_member_end(void)
604 {
605 if (!dumping)
606 return;
607
608 trace_dump_writes("</member>");
609 }
610
611 void trace_dump_null(void)
612 {
613 if (!dumping)
614 return;
615
616 trace_dump_writes("<null/>");
617 }
618
619 void trace_dump_ptr(const void *value)
620 {
621 if (!dumping)
622 return;
623
624 if(value)
625 trace_dump_writef("<ptr>0x%08lx</ptr>", (unsigned long)(uintptr_t)value);
626 else
627 trace_dump_null();
628 }
629
630
631 void trace_dump_resource_ptr(struct pipe_resource *_resource)
632 {
633 if (!dumping)
634 return;
635
636 if (_resource) {
637 struct trace_resource *tr_resource = trace_resource(_resource);
638 trace_dump_ptr(tr_resource->resource);
639 } else {
640 trace_dump_null();
641 }
642 }
643
644 void trace_dump_surface_ptr(struct pipe_surface *_surface)
645 {
646 if (!dumping)
647 return;
648
649 if (_surface) {
650 struct trace_surface *tr_surf = trace_surface(_surface);
651 trace_dump_ptr(tr_surf->surface);
652 } else {
653 trace_dump_null();
654 }
655 }
656
657 void trace_dump_transfer_ptr(struct pipe_transfer *_transfer)
658 {
659 if (!dumping)
660 return;
661
662 if (_transfer) {
663 struct trace_transfer *tr_tran = trace_transfer(_transfer);
664 trace_dump_ptr(tr_tran->transfer);
665 } else {
666 trace_dump_null();
667 }
668 }