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