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