1#
2# Copyright (C) 2020 Google, Inc.
3#
4# Permission is hereby granted, free of charge, to any person obtaining a
5# copy of this software and associated documentation files (the "Software"),
6# to deal in the Software without restriction, including without limitation
7# the rights to use, copy, modify, merge, publish, distribute, sublicense,
8# and/or sell copies of the Software, and to permit persons to whom the
9# Software is furnished to do so, subject to the following conditions:
10#
11# The above copyright notice and this permission notice (including the next
12# paragraph) shall be included in all copies or substantial portions of the
13# Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21# IN THE SOFTWARE.
22#
23
24from mako.template import Template
25from collections import namedtuple
26from enum import IntEnum
27import os
28
29TRACEPOINTS = {}
30TRACEPOINTS_TOGGLES = {}
31
32class Tracepoint(object):
33    """Class that represents all the information about a tracepoint
34    """
35    def __init__(self, name, args=[], toggle_name=None,
36                 tp_struct=None, tp_print=None, tp_perfetto=None,
37                 tp_markers=None, end_of_pipe=False, need_cs_param=True):
38        """Parameters:
39
40        - name: the tracepoint name, a tracepoint function with the given
41          name (prefixed by 'trace_') will be generated with the specied
42          args (following a u_trace ptr).  Calling this tracepoint will
43          emit a trace, if tracing is enabled.
44        - args: the tracepoint func args, an array of TracepointArg
45        - tp_print: (optional) array of format string followed by expressions
46        - tp_perfetto: (optional) driver provided callback which can generate
47          perfetto events
48        - tp_markers: (optional) driver provided printf-style callback which can
49          generate CS markers, this requires 'need_cs_param' as the first param
50          is the CS that the label should be emitted into
51        - need_cs_param: whether tracepoint functions need an additional cs
52          parameter.
53        """
54        assert isinstance(name, str)
55        assert isinstance(args, list)
56        assert name not in TRACEPOINTS
57
58
59        self.name = name
60        self.args = args
61        if tp_struct is None:
62           tp_struct = args
63        self.tp_struct = tp_struct
64        self.has_variable_arg = False
65        for arg in self.tp_struct:
66            if arg.length_arg != None:
67                self.has_variable_arg = True
68                break
69        self.tp_print = tp_print
70        self.tp_perfetto = tp_perfetto
71        self.tp_markers = tp_markers
72        self.end_of_pipe = end_of_pipe
73        self.toggle_name = toggle_name
74        self.need_cs_param = need_cs_param
75
76        TRACEPOINTS[name] = self
77        if toggle_name is not None and toggle_name not in TRACEPOINTS_TOGGLES:
78            TRACEPOINTS_TOGGLES[toggle_name] = len(TRACEPOINTS_TOGGLES)
79
80    def can_generate_print(self):
81        return self.args is not None and len(self.args) > 0
82
83    def enabled_expr(self, trace_toggle_name):
84        if trace_toggle_name is None:
85            return "true"
86        assert self.toggle_name is not None
87        return "({0} & {1}_{2})".format(trace_toggle_name,
88                                        trace_toggle_name.upper(),
89                                        self.toggle_name.upper())
90
91class TracepointArgStruct():
92    """Represents struct that is being passed as an argument
93    """
94    def __init__(self, type, var):
95        """Parameters:
96
97        - type: argument's C type.
98        - var: name of the argument
99        """
100        assert isinstance(type, str)
101        assert isinstance(var, str)
102
103        self.type = type
104        self.var = var
105
106class TracepointArg(object):
107    """Class that represents either an argument being passed or a field in a struct
108    """
109    def __init__(self, type, var, c_format, name=None, to_prim_type=None, length_arg=None, copy_func=None):
110        """Parameters:
111
112        - type: argument's C type.
113        - var: either an argument name or a field in the struct
114        - c_format: printf format to print the value.
115        - name: (optional) name that will be used in intermidiate structs and will
116          be displayed in output or perfetto, otherwise var will be used.
117        - to_prim_type: (optional) C function to convert from arg's type to a type
118          compatible with c_format.
119        - length_arg: whether this argument is a variable length array
120        """
121        assert isinstance(type, str)
122        assert isinstance(var, str)
123        assert isinstance(c_format, str)
124
125        self.type = type
126        self.var = var
127        self.c_format = c_format
128        if name is None:
129           name = var
130        self.name = name
131        self.to_prim_type = to_prim_type
132        self.length_arg = length_arg
133        self.copy_func = copy_func
134
135
136HEADERS = []
137
138class HeaderScope(IntEnum):
139    HEADER = (1 << 0)
140    SOURCE = (1 << 1)
141    PERFETTO = (1 << 2)
142
143class Header(object):
144    """Class that represents a header file dependency of generated tracepoints
145    """
146    def __init__(self, hdr, scope=HeaderScope.HEADER):
147        """Parameters:
148
149        - hdr: the required header path
150        """
151        assert isinstance(hdr, str)
152        self.hdr = hdr
153        self.scope = scope
154
155        HEADERS.append(self)
156
157
158FORWARD_DECLS = []
159
160class ForwardDecl(object):
161   """Class that represents a forward declaration
162   """
163   def __init__(self, decl):
164        assert isinstance(decl, str)
165        self.decl = decl
166
167        FORWARD_DECLS.append(self)
168
169
170hdr_template = """\
171/* Copyright (C) 2020 Google, Inc.
172 *
173 * Permission is hereby granted, free of charge, to any person obtaining a
174 * copy of this software and associated documentation files (the "Software"),
175 * to deal in the Software without restriction, including without limitation
176 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
177 * and/or sell copies of the Software, and to permit persons to whom the
178 * Software is furnished to do so, subject to the following conditions:
179 *
180 * The above copyright notice and this permission notice (including the next
181 * paragraph) shall be included in all copies or substantial portions of the
182 * Software.
183 *
184 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
185 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
186 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
188 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
189 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
190 * IN THE SOFTWARE.
191 */
192
193<% guard_name = '_' + hdrname + '_H' %>
194#ifndef ${guard_name}
195#define ${guard_name}
196
197% for header in HEADERS:
198#include "${header.hdr}"
199% endfor
200
201#include "util/perf/u_trace.h"
202
203#ifdef __cplusplus
204extern "C" {
205#endif
206
207% for declaration in FORWARD_DECLS:
208${declaration.decl};
209% endfor
210
211% if trace_toggle_name is not None:
212enum ${trace_toggle_name.lower()} {
213%    for toggle_name, config_id in TRACEPOINTS_TOGGLES.items():
214   ${trace_toggle_name.upper()}_${toggle_name.upper()} = 1ull << ${config_id},
215%    endfor
216};
217
218extern uint64_t ${trace_toggle_name};
219
220void ${trace_toggle_name}_config_variable(void);
221% endif
222
223% for trace_name, trace in TRACEPOINTS.items():
224
225/*
226 * ${trace_name}
227 */
228struct trace_${trace_name} {
229%    for arg in trace.tp_struct:
230   ${arg.type} ${arg.name}${"[0]" if arg.length_arg else ""};
231%    endfor
232%    if len(trace.args) == 0:
233#ifdef __cplusplus
234   /* avoid warnings about empty struct size mis-match in C vs C++..
235    * the size mis-match is harmless because (a) nothing will deref
236    * the empty struct, and (b) the code that cares about allocating
237    * sizeof(struct trace_${trace_name}) (and wants this to be zero
238    * if there is no payload) is C
239    */
240   uint8_t dummy;
241#endif
242%    endif
243};
244%    if trace.tp_perfetto is not None:
245#ifdef HAVE_PERFETTO
246void ${trace.tp_perfetto}(
247   ${ctx_param},
248   uint64_t ts_ns,
249   const void *flush_data,
250   const struct trace_${trace_name} *payload);
251#endif
252%    endif
253void __trace_${trace_name}(
254       struct u_trace *ut
255     , enum u_trace_type enabled_traces
256%    if trace.need_cs_param:
257     , void *cs
258%    endif
259%    for arg in trace.args:
260     , ${arg.type} ${arg.var}
261%    endfor
262);
263static ALWAYS_INLINE void trace_${trace_name}(
264     struct u_trace *ut
265%    if trace.need_cs_param:
266   , void *cs
267%    endif
268%    for arg in trace.args:
269   , ${arg.type} ${arg.var}
270%    endfor
271) {
272   enum u_trace_type enabled_traces = p_atomic_read_relaxed(&ut->utctx->enabled_traces);
273   if (!unlikely(enabled_traces != 0 &&
274                 ${trace.enabled_expr(trace_toggle_name)}))
275      return;
276   __trace_${trace_name}(
277        ut
278      , enabled_traces
279%    if trace.need_cs_param:
280      , cs
281%    endif
282%    for arg in trace.args:
283      , ${arg.var}
284%    endfor
285   );
286}
287% endfor
288
289#ifdef __cplusplus
290}
291#endif
292
293#endif /* ${guard_name} */
294"""
295
296src_template = """\
297/* Copyright (C) 2020 Google, Inc.
298 *
299 * Permission is hereby granted, free of charge, to any person obtaining a
300 * copy of this software and associated documentation files (the "Software"),
301 * to deal in the Software without restriction, including without limitation
302 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
303 * and/or sell copies of the Software, and to permit persons to whom the
304 * Software is furnished to do so, subject to the following conditions:
305 *
306 * The above copyright notice and this permission notice (including the next
307 * paragraph) shall be included in all copies or substantial portions of the
308 * Software.
309 *
310 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
311 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
312 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
313 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
314 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
315 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
316 * IN THE SOFTWARE.
317 */
318
319#include "${hdr}"
320
321% for header in HEADERS:
322#include "${header.hdr}"
323% endfor
324
325#define __NEEDS_TRACE_PRIV
326#include "util/u_debug.h"
327#include "util/perf/u_trace_priv.h"
328
329% if trace_toggle_name is not None:
330static const struct debug_control config_control[] = {
331%    for toggle_name in TRACEPOINTS_TOGGLES.keys():
332   { "${toggle_name}", ${trace_toggle_name.upper()}_${toggle_name.upper()}, },
333%    endfor
334   { NULL, 0, },
335};
336uint64_t ${trace_toggle_name} = 0;
337
338static void
339${trace_toggle_name}_variable_once(void)
340{
341   uint64_t default_value = 0
342%    for name in trace_toggle_defaults:
343     | ${trace_toggle_name.upper()}_${name.upper()}
344%    endfor
345     ;
346
347   ${trace_toggle_name} =
348      parse_enable_string(getenv("${trace_toggle_name.upper()}"),
349                          default_value,
350                          config_control);
351}
352
353void
354${trace_toggle_name}_config_variable(void)
355{
356   static once_flag process_${trace_toggle_name}_variable_flag = ONCE_FLAG_INIT;
357
358   call_once(&process_${trace_toggle_name}_variable_flag,
359             ${trace_toggle_name}_variable_once);
360}
361% endif
362
363% for trace_name, trace in TRACEPOINTS.items():
364/*
365 * ${trace_name}
366 */
367 % if trace.can_generate_print():
368static void __print_${trace_name}(FILE *out, const void *arg) {
369   const struct trace_${trace_name} *__entry =
370      (const struct trace_${trace_name} *)arg;
371  % if trace.tp_print is not None:
372   fprintf(out, "${trace.tp_print[0]}\\n"
373   % for arg in trace.tp_print[1:]:
374           , ${arg}
375   % endfor
376  % else:
377   fprintf(out, ""
378   % for arg in trace.tp_struct:
379      "${arg.name}=${arg.c_format}, "
380   % endfor
381         "\\n"
382   % for arg in trace.tp_struct:
383    % if arg.to_prim_type:
384   ,${arg.to_prim_type.format('__entry->' + arg.name)}
385    % else:
386   ,__entry->${arg.name}
387    % endif
388   % endfor
389  % endif
390   );
391}
392
393static void __print_json_${trace_name}(FILE *out, const void *arg) {
394   const struct trace_${trace_name} *__entry =
395      (const struct trace_${trace_name} *)arg;
396  % if trace.tp_print is not None:
397   fprintf(out, "\\"unstructured\\": \\"${trace.tp_print[0]}\\""
398   % for arg in trace.tp_print[1:]:
399           , ${arg}
400   % endfor
401  % else:
402   fprintf(out, ""
403   % for arg in trace.tp_struct:
404      "\\"${arg.name}\\": \\"${arg.c_format}\\""
405      % if arg != trace.tp_struct[-1]:
406         ", "
407      % endif
408   % endfor
409   % for arg in trace.tp_struct:
410    % if arg.to_prim_type:
411   ,${arg.to_prim_type.format('__entry->' + arg.name)}
412    % else:
413   ,__entry->${arg.name}
414    % endif
415   % endfor
416  % endif
417   );
418}
419
420 % else:
421#define __print_${trace_name} NULL
422#define __print_json_${trace_name} NULL
423 % endif
424 % if trace.tp_markers is not None:
425
426__attribute__((format(printf, 3, 4))) void ${trace.tp_markers}(struct u_trace_context *utctx, void *, const char *, ...);
427
428static void __emit_label_${trace_name}(struct u_trace_context *utctx, void *cs, struct trace_${trace_name} *entry) {
429   ${trace.tp_markers}(utctx, cs, "${trace_name}("
430   % for idx,arg in enumerate(trace.tp_struct):
431      "${"," if idx != 0 else ""}${arg.name}=${arg.c_format}"
432   % endfor
433      ")"
434   % for arg in trace.tp_struct:
435    % if arg.to_prim_type:
436      ,${arg.to_prim_type.format('entry->' + arg.name)}
437    % else:
438      ,entry->${arg.name}
439    % endif
440   % endfor
441   );
442}
443
444 % endif
445static const struct u_tracepoint __tp_${trace_name} = {
446    ALIGN_POT(sizeof(struct trace_${trace_name}), 8),   /* keep size 64b aligned */
447    "${trace_name}",
448    ${"true" if trace.end_of_pipe else "false"},
449    __print_${trace_name},
450    __print_json_${trace_name},
451 % if trace.tp_perfetto is not None:
452#ifdef HAVE_PERFETTO
453    (void (*)(void *pctx, uint64_t, const void *, const void *))${trace.tp_perfetto},
454#endif
455 % endif
456};
457void __trace_${trace_name}(
458     struct u_trace *ut
459   , enum u_trace_type enabled_traces
460 % if trace.need_cs_param:
461   , void *cs
462 % endif
463 % for arg in trace.args:
464   , ${arg.type} ${arg.var}
465 % endfor
466) {
467   struct trace_${trace_name} entry;
468   UNUSED struct trace_${trace_name} *__entry =
469      enabled_traces & U_TRACE_TYPE_REQUIRE_QUEUING ?
470 % if trace.has_variable_arg:
471      (struct trace_${trace_name} *)u_trace_appendv(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name},
472                                                    0
473  % for arg in trace.tp_struct:
474   % if arg.length_arg is not None:
475                                                    + ${arg.length_arg}
476   % endif
477  % endfor
478                                                    ) :
479 % else:
480      (struct trace_${trace_name} *)u_trace_append(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name}) :
481 % endif
482      &entry;
483 % for arg in trace.tp_struct:
484  % if arg.length_arg is None:
485   __entry->${arg.name} = ${arg.var};
486  % else:
487   ${arg.copy_func}(__entry->${arg.name}, ${arg.var}, ${arg.length_arg});
488  % endif
489 % endfor
490 % if trace.tp_markers is not None:
491   if (enabled_traces & U_TRACE_TYPE_MARKERS)
492      __emit_label_${trace_name}(ut->utctx, cs, __entry);
493 % endif
494}
495
496% endfor
497"""
498
499def utrace_generate(cpath, hpath, ctx_param, trace_toggle_name=None,
500                    trace_toggle_defaults=[]):
501    """Parameters:
502
503    - cpath: c file to generate.
504    - hpath: h file to generate.
505    - ctx_param: type of the first parameter to the perfetto vfuncs.
506    - trace_toggle_name: (optional) name of the environment variable
507      enabling/disabling tracepoints.
508    - trace_toggle_defaults: (optional) list of tracepoints enabled by default.
509    """
510    if cpath is not None:
511        hdr = os.path.basename(cpath).rsplit('.', 1)[0] + '.h'
512        with open(cpath, 'w') as f:
513            f.write(Template(src_template).render(
514                hdr=hdr,
515                ctx_param=ctx_param,
516                trace_toggle_name=trace_toggle_name,
517                trace_toggle_defaults=trace_toggle_defaults,
518                HEADERS=[h for h in HEADERS if h.scope & HeaderScope.SOURCE],
519                TRACEPOINTS=TRACEPOINTS,
520                TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES))
521
522    if hpath is not None:
523        hdr = os.path.basename(hpath)
524        with open(hpath, 'w') as f:
525            f.write(Template(hdr_template).render(
526                hdrname=hdr.rstrip('.h').upper(),
527                ctx_param=ctx_param,
528                trace_toggle_name=trace_toggle_name,
529                HEADERS=[h for h in HEADERS if h.scope & HeaderScope.HEADER],
530                FORWARD_DECLS=FORWARD_DECLS,
531                TRACEPOINTS=TRACEPOINTS,
532                TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES))
533
534
535perfetto_utils_hdr_template = """\
536/*
537 * Copyright © 2021 Igalia S.L.
538 *
539 * Permission is hereby granted, free of charge, to any person obtaining a
540 * copy of this software and associated documentation files (the "Software"),
541 * to deal in the Software without restriction, including without limitation
542 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
543 * and/or sell copies of the Software, and to permit persons to whom the
544 * Software is furnished to do so, subject to the following conditions:
545 *
546 * The above copyright notice and this permission notice (including the next
547 * paragraph) shall be included in all copies or substantial portions of the
548 * Software.
549 *
550 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
551 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
552 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
553 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
554 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
555 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
556 * SOFTWARE.
557 */
558
559<% guard_name = '_' + hdrname + '_H' %>
560#ifndef ${guard_name}
561#define ${guard_name}
562
563#include <perfetto.h>
564
565% for header in HEADERS:
566#include "${header.hdr}"
567% endfor
568
569% for trace_name, trace in TRACEPOINTS.items():
570static void UNUSED
571trace_payload_as_extra_${trace_name}(perfetto::protos::pbzero::GpuRenderStageEvent *event,
572                                     const struct trace_${trace_name} *payload)
573{
574 % if all([trace.tp_perfetto, trace.tp_struct]) and len(trace.tp_struct) > 0:
575   char buf[128];
576
577  % for arg in trace.tp_struct:
578   {
579      auto data = event->add_extra_data();
580      data->set_name("${arg.name}");
581
582   % if arg.to_prim_type:
583      sprintf(buf, "${arg.c_format}", ${arg.to_prim_type.format('payload->' + arg.name)});
584   % else:
585      sprintf(buf, "${arg.c_format}", payload->${arg.name});
586   % endif
587
588      data->set_value(buf);
589   }
590  % endfor
591
592 % endif
593}
594% endfor
595
596#endif /* ${guard_name} */
597"""
598
599def utrace_generate_perfetto_utils(hpath):
600    if hpath is not None:
601        hdr = os.path.basename(hpath)
602        with open(hpath, 'wb') as f:
603            f.write(Template(perfetto_utils_hdr_template, output_encoding='utf-8').render(
604                hdrname=hdr.rstrip('.h').upper(),
605                HEADERS=[h for h in HEADERS if h.scope & HeaderScope.PERFETTO],
606                TRACEPOINTS=TRACEPOINTS))
607