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/* for access() */
47#ifdef _WIN32
48# include <io.h>
49#endif
50
51#include "pipe/p_compiler.h"
52#include "os/os_thread.h"
53#include "util/os_time.h"
54#include "util/u_debug.h"
55#include "util/u_memory.h"
56#include "util/u_string.h"
57#include "util/u_math.h"
58#include "util/format/u_format.h"
59#include "compiler/nir/nir.h"
60
61#include "tr_dump.h"
62#include "tr_screen.h"
63#include "tr_texture.h"
64
65
66static bool close_stream = false;
67static FILE *stream = NULL;
68static mtx_t call_mutex = _MTX_INITIALIZER_NP;
69static long unsigned call_no = 0;
70static bool dumping = false;
71static long nir_count = 0;
72
73static bool trigger_active = true;
74static char *trigger_filename = NULL;
75
76void
77trace_dump_trigger_active(bool active)
78{
79   trigger_active = active;
80}
81
82void
83trace_dump_check_trigger(void)
84{
85   if (!trigger_filename)
86      return;
87
88   mtx_lock(&call_mutex);
89   if (trigger_active) {
90      trigger_active = false;
91   } else {
92      if (!access(trigger_filename, 2 /* W_OK but compiles on Windows */)) {
93         if (!unlink(trigger_filename)) {
94            trigger_active = true;
95         } else {
96            fprintf(stderr, "error removing trigger file\n");
97            trigger_active = false;
98         }
99      }
100   }
101   mtx_unlock(&call_mutex);
102}
103
104bool
105trace_dump_is_triggered(void)
106{
107   return trigger_active && !!trigger_filename;
108}
109
110static inline void
111trace_dump_write(const char *buf, size_t size)
112{
113   if (stream && trigger_active) {
114      fwrite(buf, size, 1, stream);
115   }
116}
117
118
119static inline void
120trace_dump_writes(const char *s)
121{
122   trace_dump_write(s, strlen(s));
123}
124
125
126static inline void
127trace_dump_writef(const char *format, ...)
128{
129   static char buf[1024];
130   unsigned len;
131   va_list ap;
132   va_start(ap, format);
133   len = vsnprintf(buf, sizeof(buf), format, ap);
134   va_end(ap);
135   trace_dump_write(buf, len);
136}
137
138
139static inline void
140trace_dump_escape(const char *str)
141{
142   const unsigned char *p = (const unsigned char *)str;
143   unsigned char c;
144   while((c = *p++) != 0) {
145      if(c == '<')
146         trace_dump_writes("&lt;");
147      else if(c == '>')
148         trace_dump_writes("&gt;");
149      else if(c == '&')
150         trace_dump_writes("&amp;");
151      else if(c == '\'')
152         trace_dump_writes("&apos;");
153      else if(c == '\"')
154         trace_dump_writes("&quot;");
155      else if(c >= 0x20 && c <= 0x7e)
156         trace_dump_writef("%c", c);
157      else
158         trace_dump_writef("&#%u;", c);
159   }
160}
161
162
163static inline void
164trace_dump_indent(unsigned level)
165{
166   unsigned i;
167   for(i = 0; i < level; ++i)
168      trace_dump_writes("\t");
169}
170
171
172static inline void
173trace_dump_newline(void)
174{
175   trace_dump_writes("\n");
176}
177
178
179static inline void
180trace_dump_tag_begin(const char *name)
181{
182   trace_dump_writes("<");
183   trace_dump_writes(name);
184   trace_dump_writes(">");
185}
186
187static inline void
188trace_dump_tag_begin1(const char *name,
189                      const char *attr1, const char *value1)
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}
199
200
201static inline void
202trace_dump_tag_end(const char *name)
203{
204   trace_dump_writes("</");
205   trace_dump_writes(name);
206   trace_dump_writes(">");
207}
208
209void
210trace_dump_trace_flush(void)
211{
212   if (stream) {
213      fflush(stream);
214   }
215}
216
217static void
218trace_dump_trace_close(void)
219{
220   if (stream) {
221      trigger_active = true;
222      trace_dump_writes("</trace>\n");
223      if (close_stream) {
224         fclose(stream);
225         close_stream = false;
226         stream = NULL;
227      }
228      call_no = 0;
229      free(trigger_filename);
230   }
231}
232
233
234static void
235trace_dump_call_time(int64_t time)
236{
237   if (stream) {
238      trace_dump_indent(2);
239      trace_dump_tag_begin("time");
240      trace_dump_int(time);
241      trace_dump_tag_end("time");
242      trace_dump_newline();
243   }
244}
245
246
247bool
248trace_dump_trace_begin(void)
249{
250   const char *filename;
251
252   filename = debug_get_option("GALLIUM_TRACE", NULL);
253   if (!filename)
254      return false;
255
256   nir_count = debug_get_num_option("GALLIUM_TRACE_NIR", 32);
257
258   if (!stream) {
259
260      if (strcmp(filename, "stderr") == 0) {
261         close_stream = false;
262         stream = stderr;
263      }
264      else if (strcmp(filename, "stdout") == 0) {
265         close_stream = false;
266         stream = stdout;
267      }
268      else {
269         close_stream = true;
270         stream = fopen(filename, "wt");
271         if (!stream)
272            return false;
273      }
274
275      trace_dump_writes("<?xml version='1.0' encoding='UTF-8'?>\n");
276      trace_dump_writes("<?xml-stylesheet type='text/xsl' href='trace.xsl'?>\n");
277      trace_dump_writes("<trace version='0.1'>\n");
278
279      /* Many applications don't exit cleanly, others may create and destroy a
280       * screen multiple times, so we only write </trace> tag and close at exit
281       * time.
282       */
283      atexit(trace_dump_trace_close);
284
285      const char *trigger = debug_get_option("GALLIUM_TRACE_TRIGGER", NULL);
286      if (trigger) {
287         trigger_filename = strdup(trigger);
288         trigger_active = false;
289      } else
290         trigger_active = true;
291   }
292
293   return true;
294}
295
296bool trace_dump_trace_enabled(void)
297{
298   return stream ? true : false;
299}
300
301/*
302 * Call lock
303 */
304
305void trace_dump_call_lock(void)
306{
307   mtx_lock(&call_mutex);
308}
309
310void trace_dump_call_unlock(void)
311{
312   mtx_unlock(&call_mutex);
313}
314
315/*
316 * Dumping control
317 */
318
319void trace_dumping_start_locked(void)
320{
321   dumping = true;
322}
323
324void trace_dumping_stop_locked(void)
325{
326   dumping = false;
327}
328
329bool trace_dumping_enabled_locked(void)
330{
331   return dumping;
332}
333
334void trace_dumping_start(void)
335{
336   mtx_lock(&call_mutex);
337   trace_dumping_start_locked();
338   mtx_unlock(&call_mutex);
339}
340
341void trace_dumping_stop(void)
342{
343   mtx_lock(&call_mutex);
344   trace_dumping_stop_locked();
345   mtx_unlock(&call_mutex);
346}
347
348bool trace_dumping_enabled(void)
349{
350   bool ret;
351   mtx_lock(&call_mutex);
352   ret = trace_dumping_enabled_locked();
353   mtx_unlock(&call_mutex);
354   return ret;
355}
356
357/*
358 * Dump functions
359 */
360
361static int64_t call_start_time = 0;
362
363void trace_dump_call_begin_locked(const char *klass, const char *method)
364{
365   if (!dumping)
366      return;
367
368   ++call_no;
369   trace_dump_indent(1);
370   trace_dump_writes("<call no=\'");
371   trace_dump_writef("%lu", call_no);
372   trace_dump_writes("\' class=\'");
373   trace_dump_escape(klass);
374   trace_dump_writes("\' method=\'");
375   trace_dump_escape(method);
376   trace_dump_writes("\'>");
377   trace_dump_newline();
378
379   call_start_time = os_time_get();
380}
381
382void trace_dump_call_end_locked(void)
383{
384   int64_t call_end_time;
385
386   if (!dumping)
387      return;
388
389   call_end_time = os_time_get();
390
391   trace_dump_call_time(call_end_time - call_start_time);
392   trace_dump_indent(1);
393   trace_dump_tag_end("call");
394   trace_dump_newline();
395   fflush(stream);
396}
397
398void trace_dump_call_begin(const char *klass, const char *method)
399{
400   mtx_lock(&call_mutex);
401   trace_dump_call_begin_locked(klass, method);
402}
403
404void trace_dump_call_end(void)
405{
406   trace_dump_call_end_locked();
407   mtx_unlock(&call_mutex);
408}
409
410void trace_dump_arg_begin(const char *name)
411{
412   if (!dumping)
413      return;
414
415   trace_dump_indent(2);
416   trace_dump_tag_begin1("arg", "name", name);
417}
418
419void trace_dump_arg_end(void)
420{
421   if (!dumping)
422      return;
423
424   trace_dump_tag_end("arg");
425   trace_dump_newline();
426}
427
428void trace_dump_ret_begin(void)
429{
430   if (!dumping)
431      return;
432
433   trace_dump_indent(2);
434   trace_dump_tag_begin("ret");
435}
436
437void trace_dump_ret_end(void)
438{
439   if (!dumping)
440      return;
441
442   trace_dump_tag_end("ret");
443   trace_dump_newline();
444}
445
446void trace_dump_bool(int value)
447{
448   if (!dumping)
449      return;
450
451   trace_dump_writef("<bool>%c</bool>", value ? '1' : '0');
452}
453
454void trace_dump_int(long long int value)
455{
456   if (!dumping)
457      return;
458
459   trace_dump_writef("<int>%lli</int>", value);
460}
461
462void trace_dump_uint(long long unsigned value)
463{
464   if (!dumping)
465      return;
466
467   trace_dump_writef("<uint>%llu</uint>", value);
468}
469
470void trace_dump_float(double value)
471{
472   if (!dumping)
473      return;
474
475   trace_dump_writef("<float>%g</float>", value);
476}
477
478void trace_dump_bytes(const void *data,
479                      size_t size)
480{
481   static const char hex_table[16] = "0123456789ABCDEF";
482   const uint8_t *p = data;
483   size_t i;
484
485   if (!dumping)
486      return;
487
488   trace_dump_writes("<bytes>");
489   for(i = 0; i < size; ++i) {
490      uint8_t byte = *p++;
491      char hex[2];
492      hex[0] = hex_table[byte >> 4];
493      hex[1] = hex_table[byte & 0xf];
494      trace_dump_write(hex, 2);
495   }
496   trace_dump_writes("</bytes>");
497}
498
499void trace_dump_box_bytes(const void *data,
500                          struct pipe_resource *resource,
501			  const struct pipe_box *box,
502			  unsigned stride,
503			  unsigned slice_stride)
504{
505   enum pipe_format format = resource->format;
506   size_t size;
507
508   assert(box->height > 0);
509   assert(box->depth > 0);
510
511   size =  util_format_get_nblocksx(format, box->width )      * util_format_get_blocksize(format)
512        + (util_format_get_nblocksy(format, box->height) - 1) * stride
513        +                                  (box->depth   - 1) * slice_stride;
514
515   /*
516    * Only dump buffer transfers to avoid huge files.
517    * TODO: Make this run-time configurable
518    */
519   if (resource->target != PIPE_BUFFER) {
520      size = 0;
521   }
522
523   trace_dump_bytes(data, size);
524}
525
526void trace_dump_string(const char *str)
527{
528   if (!dumping)
529      return;
530
531   trace_dump_writes("<string>");
532   trace_dump_escape(str);
533   trace_dump_writes("</string>");
534}
535
536void trace_dump_enum(const char *value)
537{
538   if (!dumping)
539      return;
540
541   trace_dump_writes("<enum>");
542   trace_dump_escape(value);
543   trace_dump_writes("</enum>");
544}
545
546void trace_dump_array_begin(void)
547{
548   if (!dumping)
549      return;
550
551   trace_dump_writes("<array>");
552}
553
554void trace_dump_array_end(void)
555{
556   if (!dumping)
557      return;
558
559   trace_dump_writes("</array>");
560}
561
562void trace_dump_elem_begin(void)
563{
564   if (!dumping)
565      return;
566
567   trace_dump_writes("<elem>");
568}
569
570void trace_dump_elem_end(void)
571{
572   if (!dumping)
573      return;
574
575   trace_dump_writes("</elem>");
576}
577
578void trace_dump_struct_begin(const char *name)
579{
580   if (!dumping)
581      return;
582
583   trace_dump_writef("<struct name='%s'>", name);
584}
585
586void trace_dump_struct_end(void)
587{
588   if (!dumping)
589      return;
590
591   trace_dump_writes("</struct>");
592}
593
594void trace_dump_member_begin(const char *name)
595{
596   if (!dumping)
597      return;
598
599   trace_dump_writef("<member name='%s'>", name);
600}
601
602void trace_dump_member_end(void)
603{
604   if (!dumping)
605      return;
606
607   trace_dump_writes("</member>");
608}
609
610void trace_dump_null(void)
611{
612   if (!dumping)
613      return;
614
615   trace_dump_writes("<null/>");
616}
617
618void trace_dump_ptr(const void *value)
619{
620   if (!dumping)
621      return;
622
623   if(value)
624      trace_dump_writef("<ptr>0x%08lx</ptr>", (unsigned long)(uintptr_t)value);
625   else
626      trace_dump_null();
627}
628
629void trace_dump_surface_ptr(struct pipe_surface *_surface)
630{
631   if (!dumping)
632      return;
633
634   if (_surface) {
635      struct trace_surface *tr_surf = trace_surface(_surface);
636      trace_dump_ptr(tr_surf->surface);
637   } else {
638      trace_dump_null();
639   }
640}
641
642void trace_dump_transfer_ptr(struct pipe_transfer *_transfer)
643{
644   if (!dumping)
645      return;
646
647   if (_transfer) {
648      struct trace_transfer *tr_tran = trace_transfer(_transfer);
649      trace_dump_ptr(tr_tran->transfer);
650   } else {
651      trace_dump_null();
652   }
653}
654
655void trace_dump_nir(void *nir)
656{
657   if (!dumping)
658      return;
659
660   if (nir_count < 0) {
661      fputs("<string>...</string>", stream);
662      return;
663   }
664
665   if ((nir_count--) == 0) {
666      fputs("<string>Set GALLIUM_TRACE_NIR to a sufficiently big number "
667            "to enable NIR shader dumping.</string>", stream);
668      return;
669   }
670
671   // NIR doesn't have a print to string function.  Use CDATA and hope for the
672   // best.
673   if (stream) {
674      fputs("<string><![CDATA[", stream);
675      nir_print_shader(nir, stream);
676      fputs("]]></string>", stream);
677   }
678}
679