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 "util/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;
62static mtx_t call_mutex = _MTX_INITIALIZER_NP;
63static long unsigned call_no = 0;
64static boolean dumping = FALSE;
65
66static inline void
67trace_dump_write(const char *buf, size_t size)
68{
69   if (stream) {
70      fwrite(buf, size, 1, stream);
71   }
72}
73
74
75static inline void
76trace_dump_writes(const char *s)
77{
78   trace_dump_write(s, strlen(s));
79}
80
81
82static inline void
83trace_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
95static inline void
96trace_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
119static inline void
120trace_dump_indent(unsigned level)
121{
122   unsigned i;
123   for(i = 0; i < level; ++i)
124      trace_dump_writes("\t");
125}
126
127
128static inline void
129trace_dump_newline(void)
130{
131   trace_dump_writes("\n");
132}
133
134
135static inline void
136trace_dump_tag_begin(const char *name)
137{
138   trace_dump_writes("<");
139   trace_dump_writes(name);
140   trace_dump_writes(">");
141}
142
143static inline void
144trace_dump_tag_begin1(const char *name,
145                      const char *attr1, const char *value1)
146{
147   trace_dump_writes("<");
148   trace_dump_writes(name);
149   trace_dump_writes(" ");
150   trace_dump_writes(attr1);
151   trace_dump_writes("='");
152   trace_dump_escape(value1);
153   trace_dump_writes("'>");
154}
155
156
157static inline void
158trace_dump_tag_end(const char *name)
159{
160   trace_dump_writes("</");
161   trace_dump_writes(name);
162   trace_dump_writes(">");
163}
164
165void
166trace_dump_trace_flush(void)
167{
168   if (stream) {
169      fflush(stream);
170   }
171}
172
173static boolean trace_dump_has_begun = FALSE;
174
175static void __attribute__((__destructor__))
176trace_dump_trace_close(void)
177{
178   if (!trace_dump_has_begun)
179      return;
180
181   if (stream) {
182      trace_dump_writes("</trace>\n");
183      if (close_stream) {
184         fclose(stream);
185         close_stream = FALSE;
186         stream = NULL;
187      }
188      call_no = 0;
189   }
190}
191
192
193static void
194trace_dump_call_time(int64_t time)
195{
196   if (stream) {
197      trace_dump_indent(2);
198      trace_dump_tag_begin("time");
199      trace_dump_int(time);
200      trace_dump_tag_end("time");
201      trace_dump_newline();
202   }
203}
204
205
206boolean
207trace_dump_trace_begin(void)
208{
209   const char *filename;
210
211   filename = debug_get_option("GALLIUM_TRACE", NULL);
212   if (!filename)
213      return FALSE;
214
215   if (!stream) {
216
217      if (strcmp(filename, "stderr") == 0) {
218         close_stream = FALSE;
219         stream = stderr;
220      }
221      else if (strcmp(filename, "stdout") == 0) {
222         close_stream = FALSE;
223         stream = stdout;
224      }
225      else {
226         close_stream = TRUE;
227         stream = fopen(filename, "wt");
228         if (!stream)
229            return FALSE;
230      }
231
232      trace_dump_writes("<?xml version='1.0' encoding='UTF-8'?>\n");
233      trace_dump_writes("<?xml-stylesheet type='text/xsl' href='trace.xsl'?>\n");
234      trace_dump_writes("<trace version='0.1'>\n");
235
236      /* Many applications don't exit cleanly, others may create and destroy a
237       * screen multiple times, so we only write </trace> tag and close at exit
238       * time.
239       */
240      trace_dump_has_begun = TRUE;
241   }
242
243   return TRUE;
244}
245
246boolean trace_dump_trace_enabled(void)
247{
248   return stream ? TRUE : FALSE;
249}
250
251/*
252 * Call lock
253 */
254
255void trace_dump_call_lock(void)
256{
257   mtx_lock(&call_mutex);
258}
259
260void trace_dump_call_unlock(void)
261{
262   mtx_unlock(&call_mutex);
263}
264
265/*
266 * Dumping control
267 */
268
269void trace_dumping_start_locked(void)
270{
271   dumping = TRUE;
272}
273
274void trace_dumping_stop_locked(void)
275{
276   dumping = FALSE;
277}
278
279boolean trace_dumping_enabled_locked(void)
280{
281   return dumping;
282}
283
284void trace_dumping_start(void)
285{
286   mtx_lock(&call_mutex);
287   trace_dumping_start_locked();
288   mtx_unlock(&call_mutex);
289}
290
291void trace_dumping_stop(void)
292{
293   mtx_lock(&call_mutex);
294   trace_dumping_stop_locked();
295   mtx_unlock(&call_mutex);
296}
297
298boolean trace_dumping_enabled(void)
299{
300   boolean ret;
301   mtx_lock(&call_mutex);
302   ret = trace_dumping_enabled_locked();
303   mtx_unlock(&call_mutex);
304   return ret;
305}
306
307/*
308 * Dump functions
309 */
310
311static int64_t call_start_time = 0;
312
313void trace_dump_call_begin_locked(const char *klass, const char *method)
314{
315   if (!dumping)
316      return;
317
318   ++call_no;
319   trace_dump_indent(1);
320   trace_dump_writes("<call no=\'");
321   trace_dump_writef("%lu", call_no);
322   trace_dump_writes("\' class=\'");
323   trace_dump_escape(klass);
324   trace_dump_writes("\' method=\'");
325   trace_dump_escape(method);
326   trace_dump_writes("\'>");
327   trace_dump_newline();
328
329   call_start_time = os_time_get();
330}
331
332void trace_dump_call_end_locked(void)
333{
334   int64_t call_end_time;
335
336   if (!dumping)
337      return;
338
339   call_end_time = os_time_get();
340
341   trace_dump_call_time(call_end_time - call_start_time);
342   trace_dump_indent(1);
343   trace_dump_tag_end("call");
344   trace_dump_newline();
345   fflush(stream);
346}
347
348void trace_dump_call_begin(const char *klass, const char *method)
349{
350   mtx_lock(&call_mutex);
351   trace_dump_call_begin_locked(klass, method);
352}
353
354void trace_dump_call_end(void)
355{
356   trace_dump_call_end_locked();
357   mtx_unlock(&call_mutex);
358}
359
360void trace_dump_arg_begin(const char *name)
361{
362   if (!dumping)
363      return;
364
365   trace_dump_indent(2);
366   trace_dump_tag_begin1("arg", "name", name);
367}
368
369void trace_dump_arg_end(void)
370{
371   if (!dumping)
372      return;
373
374   trace_dump_tag_end("arg");
375   trace_dump_newline();
376}
377
378void trace_dump_ret_begin(void)
379{
380   if (!dumping)
381      return;
382
383   trace_dump_indent(2);
384   trace_dump_tag_begin("ret");
385}
386
387void trace_dump_ret_end(void)
388{
389   if (!dumping)
390      return;
391
392   trace_dump_tag_end("ret");
393   trace_dump_newline();
394}
395
396void trace_dump_bool(int value)
397{
398   if (!dumping)
399      return;
400
401   trace_dump_writef("<bool>%c</bool>", value ? '1' : '0');
402}
403
404void trace_dump_int(long long int value)
405{
406   if (!dumping)
407      return;
408
409   trace_dump_writef("<int>%lli</int>", value);
410}
411
412void trace_dump_uint(long long unsigned value)
413{
414   if (!dumping)
415      return;
416
417   trace_dump_writef("<uint>%llu</uint>", value);
418}
419
420void trace_dump_float(double value)
421{
422   if (!dumping)
423      return;
424
425   trace_dump_writef("<float>%g</float>", value);
426}
427
428void trace_dump_bytes(const void *data,
429                      size_t size)
430{
431   static const char hex_table[16] = "0123456789ABCDEF";
432   const uint8_t *p = data;
433   size_t i;
434
435   if (!dumping)
436      return;
437
438   trace_dump_writes("<bytes>");
439   for(i = 0; i < size; ++i) {
440      uint8_t byte = *p++;
441      char hex[2];
442      hex[0] = hex_table[byte >> 4];
443      hex[1] = hex_table[byte & 0xf];
444      trace_dump_write(hex, 2);
445   }
446   trace_dump_writes("</bytes>");
447}
448
449void trace_dump_box_bytes(const void *data,
450                          struct pipe_resource *resource,
451			  const struct pipe_box *box,
452			  unsigned stride,
453			  unsigned slice_stride)
454{
455   enum pipe_format format = resource->format;
456   size_t size;
457
458   assert(box->height > 0);
459   assert(box->depth > 0);
460
461   size =  util_format_get_nblocksx(format, box->width )      * util_format_get_blocksize(format)
462        + (util_format_get_nblocksy(format, box->height) - 1) * stride
463        +                                  (box->depth   - 1) * slice_stride;
464
465   /*
466    * Only dump buffer transfers to avoid huge files.
467    * TODO: Make this run-time configurable
468    */
469   if (resource->target != PIPE_BUFFER) {
470      size = 0;
471   }
472
473   trace_dump_bytes(data, size);
474}
475
476void trace_dump_string(const char *str)
477{
478   if (!dumping)
479      return;
480
481   trace_dump_writes("<string>");
482   trace_dump_escape(str);
483   trace_dump_writes("</string>");
484}
485
486void trace_dump_enum(const char *value)
487{
488   if (!dumping)
489      return;
490
491   trace_dump_writes("<enum>");
492   trace_dump_escape(value);
493   trace_dump_writes("</enum>");
494}
495
496void trace_dump_array_begin(void)
497{
498   if (!dumping)
499      return;
500
501   trace_dump_writes("<array>");
502}
503
504void trace_dump_array_end(void)
505{
506   if (!dumping)
507      return;
508
509   trace_dump_writes("</array>");
510}
511
512void trace_dump_elem_begin(void)
513{
514   if (!dumping)
515      return;
516
517   trace_dump_writes("<elem>");
518}
519
520void trace_dump_elem_end(void)
521{
522   if (!dumping)
523      return;
524
525   trace_dump_writes("</elem>");
526}
527
528void trace_dump_struct_begin(const char *name)
529{
530   if (!dumping)
531      return;
532
533   trace_dump_writef("<struct name='%s'>", name);
534}
535
536void trace_dump_struct_end(void)
537{
538   if (!dumping)
539      return;
540
541   trace_dump_writes("</struct>");
542}
543
544void trace_dump_member_begin(const char *name)
545{
546   if (!dumping)
547      return;
548
549   trace_dump_writef("<member name='%s'>", name);
550}
551
552void trace_dump_member_end(void)
553{
554   if (!dumping)
555      return;
556
557   trace_dump_writes("</member>");
558}
559
560void trace_dump_null(void)
561{
562   if (!dumping)
563      return;
564
565   trace_dump_writes("<null/>");
566}
567
568void trace_dump_ptr(const void *value)
569{
570   if (!dumping)
571      return;
572
573   if(value)
574      trace_dump_writef("<ptr>0x%08lx</ptr>", (unsigned long)(uintptr_t)value);
575   else
576      trace_dump_null();
577}
578
579void trace_dump_surface_ptr(struct pipe_surface *_surface)
580{
581   if (!dumping)
582      return;
583
584   if (_surface) {
585      struct trace_surface *tr_surf = trace_surface(_surface);
586      trace_dump_ptr(tr_surf->surface);
587   } else {
588      trace_dump_null();
589   }
590}
591
592void trace_dump_transfer_ptr(struct pipe_transfer *_transfer)
593{
594   if (!dumping)
595      return;
596
597   if (_transfer) {
598      struct trace_transfer *tr_tran = trace_transfer(_transfer);
599      trace_dump_ptr(tr_tran->transfer);
600   } else {
601      trace_dump_null();
602   }
603}
604