• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   uint16_t tp_idx,
250   const void *flush_data,
251   const struct trace_${trace_name} *payload);
252#endif
253%    endif
254void __trace_${trace_name}(
255       struct u_trace *ut
256     , enum u_trace_type enabled_traces
257%    if trace.need_cs_param:
258     , void *cs
259%    endif
260%    for arg in trace.args:
261     , ${arg.type} ${arg.var}
262%    endfor
263);
264static ALWAYS_INLINE void trace_${trace_name}(
265     struct u_trace *ut
266%    if trace.need_cs_param:
267   , void *cs
268%    endif
269%    for arg in trace.args:
270   , ${arg.type} ${arg.var}
271%    endfor
272) {
273   enum u_trace_type enabled_traces = p_atomic_read_relaxed(&ut->utctx->enabled_traces);
274   if (!unlikely(enabled_traces != 0 &&
275                 ${trace.enabled_expr(trace_toggle_name)}))
276      return;
277   __trace_${trace_name}(
278        ut
279      , enabled_traces
280%    if trace.need_cs_param:
281      , cs
282%    endif
283%    for arg in trace.args:
284      , ${arg.var}
285%    endfor
286   );
287}
288% endfor
289
290#ifdef __cplusplus
291}
292#endif
293
294#endif /* ${guard_name} */
295"""
296
297src_template = """\
298/* Copyright (C) 2020 Google, Inc.
299 *
300 * Permission is hereby granted, free of charge, to any person obtaining a
301 * copy of this software and associated documentation files (the "Software"),
302 * to deal in the Software without restriction, including without limitation
303 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
304 * and/or sell copies of the Software, and to permit persons to whom the
305 * Software is furnished to do so, subject to the following conditions:
306 *
307 * The above copyright notice and this permission notice (including the next
308 * paragraph) shall be included in all copies or substantial portions of the
309 * Software.
310 *
311 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
312 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
313 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
314 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
315 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
316 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
317 * IN THE SOFTWARE.
318 */
319
320#include "${hdr}"
321
322% for header in HEADERS:
323#include "${header.hdr}"
324% endfor
325
326#define __NEEDS_TRACE_PRIV
327#include "util/u_debug.h"
328#include "util/perf/u_trace_priv.h"
329
330% if trace_toggle_name is not None:
331static const struct debug_control config_control[] = {
332%    for toggle_name in TRACEPOINTS_TOGGLES.keys():
333   { "${toggle_name}", ${trace_toggle_name.upper()}_${toggle_name.upper()}, },
334%    endfor
335   { NULL, 0, },
336};
337uint64_t ${trace_toggle_name} = 0;
338
339static void
340${trace_toggle_name}_variable_once(void)
341{
342   uint64_t default_value = 0
343%    for name in trace_toggle_defaults:
344     | ${trace_toggle_name.upper()}_${name.upper()}
345%    endfor
346     ;
347
348   ${trace_toggle_name} =
349      parse_enable_string(getenv("${trace_toggle_name.upper()}"),
350                          default_value,
351                          config_control);
352}
353
354void
355${trace_toggle_name}_config_variable(void)
356{
357   static once_flag process_${trace_toggle_name}_variable_flag = ONCE_FLAG_INIT;
358
359   call_once(&process_${trace_toggle_name}_variable_flag,
360             ${trace_toggle_name}_variable_once);
361}
362% endif
363
364% for index, (trace_name, trace) in enumerate(TRACEPOINTS.items()):
365/*
366 * ${trace_name}
367 */
368 % if trace.can_generate_print():
369static void __print_${trace_name}(FILE *out, const void *arg) {
370   const struct trace_${trace_name} *__entry =
371      (const struct trace_${trace_name} *)arg;
372  % if trace.tp_print is not None:
373   fprintf(out, "${trace.tp_print[0]}\\n"
374   % for arg in trace.tp_print[1:]:
375           , ${arg}
376   % endfor
377  % else:
378   fprintf(out, ""
379   % for arg in trace.tp_struct:
380      "${arg.name}=${arg.c_format}, "
381   % endfor
382         "\\n"
383   % for arg in trace.tp_struct:
384    % if arg.to_prim_type:
385   ,${arg.to_prim_type.format('__entry->' + arg.name)}
386    % else:
387   ,__entry->${arg.name}
388    % endif
389   % endfor
390  % endif
391   );
392}
393
394static void __print_json_${trace_name}(FILE *out, const void *arg) {
395   const struct trace_${trace_name} *__entry =
396      (const struct trace_${trace_name} *)arg;
397  % if trace.tp_print is not None:
398   fprintf(out, "\\"unstructured\\": \\"${trace.tp_print[0]}\\""
399   % for arg in trace.tp_print[1:]:
400           , ${arg}
401   % endfor
402  % else:
403   fprintf(out, ""
404   % for arg in trace.tp_struct:
405      "\\"${arg.name}\\": \\"${arg.c_format}\\""
406      % if arg != trace.tp_struct[-1]:
407         ", "
408      % endif
409   % endfor
410   % for arg in trace.tp_struct:
411    % if arg.to_prim_type:
412   ,${arg.to_prim_type.format('__entry->' + arg.name)}
413    % else:
414   ,__entry->${arg.name}
415    % endif
416   % endfor
417  % endif
418   );
419}
420
421 % else:
422#define __print_${trace_name} NULL
423#define __print_json_${trace_name} NULL
424 % endif
425 % if trace.tp_markers is not None:
426
427__attribute__((format(printf, 3, 4))) void ${trace.tp_markers}(struct u_trace_context *utctx, void *, const char *, ...);
428
429static void __emit_label_${trace_name}(struct u_trace_context *utctx, void *cs, struct trace_${trace_name} *entry) {
430   ${trace.tp_markers}(utctx, cs, "${trace_name}("
431   % for idx,arg in enumerate(trace.tp_struct):
432      "${"," if idx != 0 else ""}${arg.name}=${arg.c_format}"
433   % endfor
434      ")"
435   % for arg in trace.tp_struct:
436    % if arg.to_prim_type:
437      ,${arg.to_prim_type.format('entry->' + arg.name)}
438    % else:
439      ,entry->${arg.name}
440    % endif
441   % endfor
442   );
443}
444
445 % endif
446static const struct u_tracepoint __tp_${trace_name} = {
447    ALIGN_POT(sizeof(struct trace_${trace_name}), 8),   /* keep size 64b aligned */
448    "${trace_name}",
449    ${"true" if trace.end_of_pipe else "false"},
450    ${index},
451    __print_${trace_name},
452    __print_json_${trace_name},
453 % if trace.tp_perfetto is not None:
454#ifdef HAVE_PERFETTO
455    (void (*)(void *pctx, uint64_t, uint16_t, const void *, const void *))${trace.tp_perfetto},
456#endif
457 % endif
458};
459void __trace_${trace_name}(
460     struct u_trace *ut
461   , enum u_trace_type enabled_traces
462 % if trace.need_cs_param:
463   , void *cs
464 % endif
465 % for arg in trace.args:
466   , ${arg.type} ${arg.var}
467 % endfor
468) {
469   struct trace_${trace_name} entry;
470   UNUSED struct trace_${trace_name} *__entry =
471      enabled_traces & U_TRACE_TYPE_REQUIRE_QUEUING ?
472 % if trace.has_variable_arg:
473      (struct trace_${trace_name} *)u_trace_appendv(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name},
474                                                    0
475  % for arg in trace.tp_struct:
476   % if arg.length_arg is not None:
477                                                    + ${arg.length_arg}
478   % endif
479  % endfor
480                                                    ) :
481 % else:
482      (struct trace_${trace_name} *)u_trace_append(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name}) :
483 % endif
484      &entry;
485 % for arg in trace.tp_struct:
486  % if arg.length_arg is None:
487   __entry->${arg.name} = ${arg.var};
488  % else:
489   ${arg.copy_func}(__entry->${arg.name}, ${arg.var}, ${arg.length_arg});
490  % endif
491 % endfor
492 % if trace.tp_markers is not None:
493   if (enabled_traces & U_TRACE_TYPE_MARKERS)
494      __emit_label_${trace_name}(ut->utctx, cs, __entry);
495 % endif
496}
497
498% endfor
499"""
500
501def utrace_generate(cpath, hpath, ctx_param, trace_toggle_name=None,
502                    trace_toggle_defaults=[]):
503    """Parameters:
504
505    - cpath: c file to generate.
506    - hpath: h file to generate.
507    - ctx_param: type of the first parameter to the perfetto vfuncs.
508    - trace_toggle_name: (optional) name of the environment variable
509      enabling/disabling tracepoints.
510    - trace_toggle_defaults: (optional) list of tracepoints enabled by default.
511    """
512    if cpath is not None:
513        hdr = os.path.basename(cpath).rsplit('.', 1)[0] + '.h'
514        with open(cpath, 'w', encoding='utf-8') as f:
515            f.write(Template(src_template).render(
516                hdr=hdr,
517                ctx_param=ctx_param,
518                trace_toggle_name=trace_toggle_name,
519                trace_toggle_defaults=trace_toggle_defaults,
520                HEADERS=[h for h in HEADERS if h.scope & HeaderScope.SOURCE],
521                TRACEPOINTS=TRACEPOINTS,
522                TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES))
523
524    if hpath is not None:
525        hdr = os.path.basename(hpath)
526        with open(hpath, 'w', encoding='utf-8') as f:
527            f.write(Template(hdr_template).render(
528                hdrname=hdr.rstrip('.h').upper(),
529                ctx_param=ctx_param,
530                trace_toggle_name=trace_toggle_name,
531                HEADERS=[h for h in HEADERS if h.scope & HeaderScope.HEADER],
532                FORWARD_DECLS=FORWARD_DECLS,
533                TRACEPOINTS=TRACEPOINTS,
534                TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES))
535
536
537perfetto_utils_hdr_template = """\
538/*
539 * Copyright © 2021 Igalia S.L.
540 *
541 * Permission is hereby granted, free of charge, to any person obtaining a
542 * copy of this software and associated documentation files (the "Software"),
543 * to deal in the Software without restriction, including without limitation
544 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
545 * and/or sell copies of the Software, and to permit persons to whom the
546 * Software is furnished to do so, subject to the following conditions:
547 *
548 * The above copyright notice and this permission notice (including the next
549 * paragraph) shall be included in all copies or substantial portions of the
550 * Software.
551 *
552 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
553 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
554 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
555 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
556 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
557 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
558 * SOFTWARE.
559 */
560
561<% guard_name = '_' + hdrname + '_H' %>
562#ifndef ${guard_name}
563#define ${guard_name}
564
565#include <perfetto.h>
566
567% for header in HEADERS:
568#include "${header.hdr}"
569% endfor
570
571UNUSED static const char *${basename}_names[] = {
572% for trace_name, trace in TRACEPOINTS.items():
573   "${trace_name}",
574% endfor
575};
576
577% for trace_name, trace in TRACEPOINTS.items():
578static void UNUSED
579trace_payload_as_extra_${trace_name}(perfetto::protos::pbzero::GpuRenderStageEvent *event,
580                                     const struct trace_${trace_name} *payload)
581{
582 % if all([trace.tp_perfetto, trace.tp_struct]) and len(trace.tp_struct) > 0:
583   char buf[128];
584
585  % for arg in trace.tp_struct:
586   {
587      auto data = event->add_extra_data();
588      data->set_name("${arg.name}");
589
590   % if arg.to_prim_type:
591      sprintf(buf, "${arg.c_format}", ${arg.to_prim_type.format('payload->' + arg.name)});
592   % else:
593      sprintf(buf, "${arg.c_format}", payload->${arg.name});
594   % endif
595
596      data->set_value(buf);
597   }
598  % endfor
599
600 % endif
601}
602% endfor
603
604#endif /* ${guard_name} */
605"""
606
607def utrace_generate_perfetto_utils(hpath,basename="tracepoint"):
608    if hpath is not None:
609        hdr = os.path.basename(hpath)
610        with open(hpath, 'w', encoding='utf-8') as f:
611            f.write(Template(perfetto_utils_hdr_template).render(
612                basename=basename,
613                hdrname=hdr.rstrip('.h').upper(),
614                HEADERS=[h for h in HEADERS if h.scope & HeaderScope.PERFETTO],
615                TRACEPOINTS=TRACEPOINTS))
616