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
60static boolean close_stream = FALSE;
61static FILE *stream = NULL;
62pipe_static_mutex(call_mutex);
63static long unsigned call_no = 0;
64static boolean dumping = FALSE;
65
66
67static inline void
68trace_dump_write(const char *buf, size_t size)
69{
70   if (stream) {
71      fwrite(buf, size, 1, stream);
72   }
73}
74
75
76static inline void
77trace_dump_writes(const char *s)
78{
79   trace_dump_write(s, strlen(s));
80}
81
82
83static inline void
84trace_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
96static inline void
97trace_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("&lt;");
104      else if(c == '>')
105         trace_dump_writes("&gt;");
106      else if(c == '&')
107         trace_dump_writes("&amp;");
108      else if(c == '\'')
109         trace_dump_writes("&apos;");
110      else if(c == '\"')
111         trace_dump_writes("&quot;");
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
120static inline void
121trace_dump_indent(unsigned level)
122{
123   unsigned i;
124   for(i = 0; i < level; ++i)
125      trace_dump_writes("\t");
126}
127
128
129static inline void
130trace_dump_newline(void)
131{
132   trace_dump_writes("\n");
133}
134
135
136static inline void
137trace_dump_tag(const char *name)
138{
139   trace_dump_writes("<");
140   trace_dump_writes(name);
141   trace_dump_writes("/>");
142}
143
144
145static inline void
146trace_dump_tag_begin(const char *name)
147{
148   trace_dump_writes("<");
149   trace_dump_writes(name);
150   trace_dump_writes(">");
151}
152
153static inline void
154trace_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
167static inline void
168trace_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
186static inline void
187trace_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
210static inline void
211trace_dump_tag_end(const char *name)
212{
213   trace_dump_writes("</");
214   trace_dump_writes(name);
215   trace_dump_writes(">");
216}
217
218void
219trace_dump_trace_flush(void)
220{
221   if (stream) {
222      fflush(stream);
223   }
224}
225
226static void
227trace_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
241static void
242trace_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
254boolean
255trace_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
294boolean trace_dump_trace_enabled(void)
295{
296   return stream ? TRUE : FALSE;
297}
298
299/*
300 * Call lock
301 */
302
303void trace_dump_call_lock(void)
304{
305   pipe_mutex_lock(call_mutex);
306}
307
308void trace_dump_call_unlock(void)
309{
310   pipe_mutex_unlock(call_mutex);
311}
312
313/*
314 * Dumping control
315 */
316
317void trace_dumping_start_locked(void)
318{
319   dumping = TRUE;
320}
321
322void trace_dumping_stop_locked(void)
323{
324   dumping = FALSE;
325}
326
327boolean trace_dumping_enabled_locked(void)
328{
329   return dumping;
330}
331
332void trace_dumping_start(void)
333{
334   pipe_mutex_lock(call_mutex);
335   trace_dumping_start_locked();
336   pipe_mutex_unlock(call_mutex);
337}
338
339void trace_dumping_stop(void)
340{
341   pipe_mutex_lock(call_mutex);
342   trace_dumping_stop_locked();
343   pipe_mutex_unlock(call_mutex);
344}
345
346boolean 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
359static int64_t call_start_time = 0;
360
361void 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
380void 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
396void 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
402void trace_dump_call_end(void)
403{
404   trace_dump_call_end_locked();
405   pipe_mutex_unlock(call_mutex);
406}
407
408void 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
417void trace_dump_arg_end(void)
418{
419   if (!dumping)
420      return;
421
422   trace_dump_tag_end("arg");
423   trace_dump_newline();
424}
425
426void 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
435void trace_dump_ret_end(void)
436{
437   if (!dumping)
438      return;
439
440   trace_dump_tag_end("ret");
441   trace_dump_newline();
442}
443
444void trace_dump_bool(int value)
445{
446   if (!dumping)
447      return;
448
449   trace_dump_writef("<bool>%c</bool>", value ? '1' : '0');
450}
451
452void trace_dump_int(long long int value)
453{
454   if (!dumping)
455      return;
456
457   trace_dump_writef("<int>%lli</int>", value);
458}
459
460void trace_dump_uint(long long unsigned value)
461{
462   if (!dumping)
463      return;
464
465   trace_dump_writef("<uint>%llu</uint>", value);
466}
467
468void trace_dump_float(double value)
469{
470   if (!dumping)
471      return;
472
473   trace_dump_writef("<float>%g</float>", value);
474}
475
476void 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
497void 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
525void 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
535void 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
545void trace_dump_array_begin(void)
546{
547   if (!dumping)
548      return;
549
550   trace_dump_writes("<array>");
551}
552
553void trace_dump_array_end(void)
554{
555   if (!dumping)
556      return;
557
558   trace_dump_writes("</array>");
559}
560
561void trace_dump_elem_begin(void)
562{
563   if (!dumping)
564      return;
565
566   trace_dump_writes("<elem>");
567}
568
569void trace_dump_elem_end(void)
570{
571   if (!dumping)
572      return;
573
574   trace_dump_writes("</elem>");
575}
576
577void trace_dump_struct_begin(const char *name)
578{
579   if (!dumping)
580      return;
581
582   trace_dump_writef("<struct name='%s'>", name);
583}
584
585void trace_dump_struct_end(void)
586{
587   if (!dumping)
588      return;
589
590   trace_dump_writes("</struct>");
591}
592
593void trace_dump_member_begin(const char *name)
594{
595   if (!dumping)
596      return;
597
598   trace_dump_writef("<member name='%s'>", name);
599}
600
601void trace_dump_member_end(void)
602{
603   if (!dumping)
604      return;
605
606   trace_dump_writes("</member>");
607}
608
609void trace_dump_null(void)
610{
611   if (!dumping)
612      return;
613
614   trace_dump_writes("<null/>");
615}
616
617void 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
629void 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
642void 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
655void 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