| # |
| # Copyright (C) 2020 Google, Inc. |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining a |
| # copy of this software and associated documentation files (the "Software"), |
| # to deal in the Software without restriction, including without limitation |
| # the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| # and/or sell copies of the Software, and to permit persons to whom the |
| # Software is furnished to do so, subject to the following conditions: |
| # |
| # The above copyright notice and this permission notice (including the next |
| # paragraph) shall be included in all copies or substantial portions of the |
| # Software. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| # IN THE SOFTWARE. |
| # |
| |
| from mako.template import Template |
| from collections import namedtuple |
| from enum import IntEnum |
| import os |
| |
| TRACEPOINTS = {} |
| |
| class Tracepoint(object): |
| """Class that represents all the information about a tracepoint |
| """ |
| def __init__(self, name, args=[], tp_struct=None, tp_print=None, tp_perfetto=None): |
| """Parameters: |
| |
| - name: the tracepoint name, a tracepoint function with the given |
| name (prefixed by 'trace_') will be generated with the specied |
| args (following a u_trace ptr). Calling this tracepoint will |
| emit a trace, if tracing is enabled. |
| - args: the tracepoint func args, an array of TracepointArg |
| - tp_print: (optional) array of format string followed by expressions |
| - tp_perfetto: (optional) driver provided callback which can generate |
| perfetto events |
| """ |
| assert isinstance(name, str) |
| assert isinstance(args, list) |
| assert name not in TRACEPOINTS |
| |
| self.name = name |
| self.args = args |
| if tp_struct is None: |
| tp_struct = args |
| self.tp_struct = tp_struct |
| self.tp_print = tp_print |
| self.tp_perfetto = tp_perfetto |
| |
| TRACEPOINTS[name] = self |
| |
| class TracepointArgStruct(): |
| """Represents struct that is being passed as an argument |
| """ |
| def __init__(self, type, var): |
| """Parameters: |
| |
| - type: argument's C type. |
| - var: name of the argument |
| """ |
| assert isinstance(type, str) |
| assert isinstance(var, str) |
| |
| self.type = type |
| self.var = var |
| |
| class TracepointArg(object): |
| """Class that represents either an argument being passed or a field in a struct |
| """ |
| def __init__(self, type, var, c_format, name=None, to_prim_type=None): |
| """Parameters: |
| |
| - type: argument's C type. |
| - var: either an argument name or a field in the struct |
| - c_format: printf format to print the value. |
| - name: (optional) name that will be used in intermidiate structs and will |
| be displayed in output or perfetto, otherwise var will be used. |
| - to_prim_type: (optional) C function to convert from arg's type to a type |
| compatible with c_format. |
| """ |
| assert isinstance(type, str) |
| assert isinstance(var, str) |
| assert isinstance(c_format, str) |
| |
| self.type = type |
| self.var = var |
| self.c_format = c_format |
| if name is None: |
| name = var |
| self.name = name |
| self.to_prim_type = to_prim_type |
| |
| |
| HEADERS = [] |
| |
| class HeaderScope(IntEnum): |
| HEADER = (1 << 0) |
| SOURCE = (1 << 1) |
| |
| class Header(object): |
| """Class that represents a header file dependency of generated tracepoints |
| """ |
| def __init__(self, hdr, scope=HeaderScope.HEADER|HeaderScope.SOURCE): |
| """Parameters: |
| |
| - hdr: the required header path |
| """ |
| assert isinstance(hdr, str) |
| self.hdr = hdr |
| self.scope = scope |
| |
| HEADERS.append(self) |
| |
| |
| FORWARD_DECLS = [] |
| |
| class ForwardDecl(object): |
| """Class that represents a forward declaration |
| """ |
| def __init__(self, decl): |
| assert isinstance(decl, str) |
| self.decl = decl |
| |
| FORWARD_DECLS.append(self) |
| |
| |
| hdr_template = """\ |
| /* Copyright (C) 2020 Google, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| <% guard_name = '_' + hdrname + '_H' %> |
| #ifndef ${guard_name} |
| #define ${guard_name} |
| |
| % for header in HEADERS: |
| #include "${header.hdr}" |
| % endfor |
| |
| #include "util/perf/u_trace.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| % for declaration in FORWARD_DECLS: |
| ${declaration.decl}; |
| % endfor |
| |
| % for trace_name, trace in TRACEPOINTS.items(): |
| /* |
| * ${trace_name} |
| */ |
| struct trace_${trace_name} { |
| % for arg in trace.tp_struct: |
| ${arg.type} ${arg.name}; |
| % endfor |
| % if len(trace.args) == 0: |
| #ifdef __cplusplus |
| /* avoid warnings about empty struct size mis-match in C vs C++.. |
| * the size mis-match is harmless because (a) nothing will deref |
| * the empty struct, and (b) the code that cares about allocating |
| * sizeof(struct trace_${trace_name}) (and wants this to be zero |
| * if there is no payload) is C |
| */ |
| uint8_t dummy; |
| #endif |
| % endif |
| }; |
| % if trace.tp_perfetto is not None: |
| #ifdef HAVE_PERFETTO |
| void ${trace.tp_perfetto}(${ctx_param}, uint64_t ts_ns, const void *flush_data, const struct trace_${trace_name} *payload); |
| #endif |
| % endif |
| void __trace_${trace_name}(struct u_trace *ut, void *cs |
| % for arg in trace.args: |
| , ${arg.type} ${arg.var} |
| % endfor |
| ); |
| static inline void trace_${trace_name}(struct u_trace *ut, void *cs |
| % for arg in trace.args: |
| , ${arg.type} ${arg.var} |
| % endfor |
| ) { |
| % if trace.tp_perfetto is not None: |
| if (!unlikely(ut->enabled || ut_perfetto_enabled)) |
| % else: |
| if (!unlikely(ut->enabled)) |
| % endif |
| return; |
| __trace_${trace_name}(ut, cs |
| % for arg in trace.args: |
| , ${arg.var} |
| % endfor |
| ); |
| } |
| % endfor |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* ${guard_name} */ |
| """ |
| |
| src_template = """\ |
| /* Copyright (C) 2020 Google, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| % for header in HEADERS: |
| #include "${header.hdr}" |
| % endfor |
| |
| #include "${hdr}" |
| |
| #define __NEEDS_TRACE_PRIV |
| #include "util/perf/u_trace_priv.h" |
| |
| % for trace_name, trace in TRACEPOINTS.items(): |
| /* |
| * ${trace_name} |
| */ |
| % if trace.args is not None and len(trace.args) > 0: |
| static void __print_${trace_name}(FILE *out, const void *arg) { |
| const struct trace_${trace_name} *__entry = |
| (const struct trace_${trace_name} *)arg; |
| % if trace.tp_print is not None: |
| fprintf(out, "${trace.tp_print[0]}\\n" |
| % for arg in trace.tp_print[1:]: |
| , ${arg} |
| % endfor |
| % else: |
| fprintf(out, "" |
| % for arg in trace.tp_struct: |
| "${arg.name}=${arg.c_format}, " |
| % endfor |
| "\\n" |
| % for arg in trace.tp_struct: |
| % if arg.to_prim_type: |
| ,${arg.to_prim_type.format('__entry->' + arg.name)} |
| % else: |
| ,__entry->${arg.name} |
| % endif |
| % endfor |
| %endif |
| ); |
| } |
| % else: |
| #define __print_${trace_name} NULL |
| % endif |
| static const struct u_tracepoint __tp_${trace_name} = { |
| ALIGN_POT(sizeof(struct trace_${trace_name}), 8), /* keep size 64b aligned */ |
| "${trace_name}", |
| __print_${trace_name}, |
| % if trace.tp_perfetto is not None: |
| #ifdef HAVE_PERFETTO |
| (void (*)(void *pctx, uint64_t, const void *, const void *))${trace.tp_perfetto}, |
| #endif |
| % endif |
| }; |
| void __trace_${trace_name}(struct u_trace *ut, void *cs |
| % for arg in trace.args: |
| , ${arg.type} ${arg.var} |
| % endfor |
| ) { |
| struct trace_${trace_name} *__entry = |
| (struct trace_${trace_name} *)u_trace_append(ut, cs, &__tp_${trace_name}); |
| (void)__entry; |
| % for arg in trace.tp_struct: |
| __entry->${arg.name} = ${arg.var}; |
| % endfor |
| } |
| |
| % endfor |
| """ |
| |
| def utrace_generate(cpath, hpath, ctx_param): |
| if cpath is not None: |
| hdr = os.path.basename(cpath).rsplit('.', 1)[0] + '.h' |
| with open(cpath, 'w') as f: |
| f.write(Template(src_template).render( |
| hdr=hdr, |
| ctx_param=ctx_param, |
| HEADERS=[h for h in HEADERS if h.scope & HeaderScope.SOURCE], |
| TRACEPOINTS=TRACEPOINTS)) |
| |
| if hpath is not None: |
| hdr = os.path.basename(hpath) |
| with open(hpath, 'w') as f: |
| f.write(Template(hdr_template).render( |
| hdrname=hdr.rstrip('.h').upper(), |
| ctx_param=ctx_param, |
| HEADERS=[h for h in HEADERS if h.scope & HeaderScope.HEADER], |
| FORWARD_DECLS=FORWARD_DECLS, |
| TRACEPOINTS=TRACEPOINTS)) |
| |
| |
| perfetto_utils_hdr_template = """\ |
| /* |
| * Copyright © 2021 Igalia S.L. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| <% guard_name = '_' + hdrname + '_H' %> |
| #ifndef ${guard_name} |
| #define ${guard_name} |
| |
| #include <perfetto.h> |
| |
| % for trace_name, trace in TRACEPOINTS.items(): |
| static void UNUSED |
| trace_payload_as_extra_${trace_name}(perfetto::protos::pbzero::GpuRenderStageEvent *event, |
| const struct trace_${trace_name} *payload) |
| { |
| % if all([trace.tp_perfetto, trace.tp_struct]) and len(trace.tp_struct) > 0: |
| char buf[128]; |
| |
| % for arg in trace.tp_struct: |
| { |
| auto data = event->add_extra_data(); |
| data->set_name("${arg.name}"); |
| |
| % if arg.to_prim_type: |
| sprintf(buf, "${arg.c_format}", ${arg.to_prim_type.format('payload->' + arg.name)}); |
| % else: |
| sprintf(buf, "${arg.c_format}", payload->${arg.name}); |
| % endif |
| |
| data->set_value(buf); |
| } |
| % endfor |
| |
| % endif |
| } |
| % endfor |
| |
| #endif /* ${guard_name} */ |
| """ |
| |
| def utrace_generate_perfetto_utils(hpath): |
| if hpath is not None: |
| hdr = os.path.basename(hpath) |
| with open(hpath, 'wb') as f: |
| f.write(Template(perfetto_utils_hdr_template, output_encoding='utf-8').render( |
| hdrname=hdr.rstrip('.h').upper(), |
| TRACEPOINTS=TRACEPOINTS)) |