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